~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to libblkid/src/superblocks/zfs.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2009-2010 by Andreas Dilger <adilger@sun.com>
 
3
 *
 
4
 * This file may be redistributed under the terms of the
 
5
 * GNU Lesser General Public License.
 
6
 */
 
7
 
 
8
#include <stdio.h>
 
9
#include <stdlib.h>
 
10
#include <unistd.h>
 
11
#include <string.h>
 
12
#include <errno.h>
 
13
#include <ctype.h>
 
14
#include <inttypes.h>
 
15
 
 
16
#include "superblocks.h"
 
17
 
 
18
#define VDEV_LABEL_UBERBLOCK    (128 * 1024ULL)
 
19
#define VDEV_LABEL_NVPAIR       ( 16 * 1024ULL)
 
20
#define VDEV_LABEL_SIZE         (256 * 1024ULL)
 
21
 
 
22
/* #include <sys/uberblock_impl.h> */
 
23
#define UBERBLOCK_MAGIC         0x00bab10c              /* oo-ba-bloc!  */
 
24
struct zfs_uberblock {
 
25
        uint64_t        ub_magic;       /* UBERBLOCK_MAGIC              */
 
26
        uint64_t        ub_version;     /* SPA_VERSION                  */
 
27
        uint64_t        ub_txg;         /* txg of last sync             */
 
28
        uint64_t        ub_guid_sum;    /* sum of all vdev guids        */
 
29
        uint64_t        ub_timestamp;   /* UTC time of last sync        */
 
30
        char            ub_rootbp;      /* MOS objset_phys_t            */
 
31
} __attribute__((packed));
 
32
 
 
33
#define ZFS_TRIES       64
 
34
#define ZFS_WANT         4
 
35
 
 
36
#define DATA_TYPE_UINT64 8
 
37
#define DATA_TYPE_STRING 9
 
38
 
 
39
struct nvpair {
 
40
        uint32_t        nvp_size;
 
41
        uint32_t        nvp_unkown;
 
42
        uint32_t        nvp_namelen;
 
43
        char            nvp_name[0]; /* aligned to 4 bytes */
 
44
        /* aligned ptr array for string arrays */
 
45
        /* aligned array of data for value */
 
46
};
 
47
 
 
48
struct nvstring {
 
49
        uint32_t        nvs_type;
 
50
        uint32_t        nvs_elem;
 
51
        uint32_t        nvs_strlen;
 
52
        unsigned char   nvs_string[0];
 
53
};
 
54
 
 
55
struct nvuint64 {
 
56
        uint32_t        nvu_type;
 
57
        uint32_t        nvu_elem;
 
58
        uint64_t        nvu_value;
 
59
};
 
60
 
 
61
struct nvlist {
 
62
        uint32_t        nvl_unknown[3];
 
63
        struct nvpair   nvl_nvpair;
 
64
};
 
65
 
 
66
#define nvdebug(fmt, ...)       do { } while(0)
 
67
/*#define nvdebug(fmt, a...)    printf(fmt, ##a)*/
 
68
 
 
69
static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
 
70
{
 
71
        struct nvlist *nvl;
 
72
        struct nvpair *nvp;
 
73
        size_t left = 4096;
 
74
        int found = 0;
 
75
 
 
76
        offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR;
 
77
 
 
78
        /* Note that we currently assume that the desired fields are within
 
79
         * the first 4k (left) of the nvlist.  This is true for all pools
 
80
         * I've seen, and simplifies this code somewhat, because we don't
 
81
         * have to handle an nvpair crossing a buffer boundary. */
 
82
        nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left);
 
83
        if (nvl == NULL)
 
84
                return;
 
85
 
 
86
        nvdebug("zfs_extract: nvlist offset %llu\n", offset);
 
87
 
 
88
        nvp = &nvl->nvl_nvpair;
 
89
        while (left > sizeof(*nvp) && nvp->nvp_size != 0 && found < 3) {
 
90
                int avail;   /* tracks that name/value data fits in nvp_size */
 
91
                int namesize;
 
92
 
 
93
                nvp->nvp_size = be32_to_cpu(nvp->nvp_size);
 
94
                nvp->nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
 
95
                avail = nvp->nvp_size - nvp->nvp_namelen - sizeof(*nvp);
 
96
 
 
97
                nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size);
 
98
                if (left < nvp->nvp_size || avail < 0)
 
99
                        break;
 
100
 
 
101
                namesize = (nvp->nvp_namelen + 3) & ~3;
 
102
 
 
103
                nvdebug("nvlist: size %u, namelen %u, name %*s\n",
 
104
                        nvp->nvp_size, nvp->nvp_namelen, nvp->nvp_namelen,
 
105
                        nvp->nvp_name);
 
106
                if (strncmp(nvp->nvp_name, "name", nvp->nvp_namelen) == 0) {
 
107
                        struct nvstring *nvs = (void *)(nvp->nvp_name+namesize);
 
108
 
 
109
                        nvs->nvs_type = be32_to_cpu(nvs->nvs_type);
 
110
                        nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
 
111
                        avail -= nvs->nvs_strlen + sizeof(*nvs);
 
112
                        nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type,
 
113
                                nvs->nvs_strlen, nvs->nvs_string);
 
114
                        if (nvs->nvs_type == DATA_TYPE_STRING && avail >= 0)
 
115
                                blkid_probe_set_label(pr, nvs->nvs_string,
 
116
                                                      nvs->nvs_strlen);
 
117
                        found++;
 
118
                } else if (strncmp(nvp->nvp_name, "guid",
 
119
                                   nvp->nvp_namelen) == 0) {
 
120
                        struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
 
121
                        uint64_t nvu_value;
 
122
 
 
123
                        memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
 
124
                        nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
 
125
                        nvu_value = be64_to_cpu(nvu_value);
 
126
                        avail -= sizeof(*nvu);
 
127
                        nvdebug("nvuint64: type %u value %"PRIu64"\n",
 
128
                                nvu->nvu_type, nvu_value);
 
129
                        if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
 
130
                                blkid_probe_sprintf_value(pr, "UUID_SUB",
 
131
                                                          "%"PRIu64, nvu_value);
 
132
                        found++;
 
133
                } else if (strncmp(nvp->nvp_name, "pool_guid",
 
134
                                   nvp->nvp_namelen) == 0) {
 
135
                        struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
 
136
                        uint64_t nvu_value;
 
137
 
 
138
                        memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
 
139
                        nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
 
140
                        nvu_value = be64_to_cpu(nvu_value);
 
141
                        avail -= sizeof(*nvu);
 
142
                        nvdebug("nvuint64: type %u value %"PRIu64"\n",
 
143
                                nvu->nvu_type, nvu_value);
 
144
                        if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
 
145
                                blkid_probe_sprintf_uuid(pr, (unsigned char *)
 
146
                                                         &nvu_value,
 
147
                                                         sizeof(nvu_value),
 
148
                                                         "%"PRIu64, nvu_value);
 
149
                        found++;
 
150
                }
 
151
                if (left > nvp->nvp_size)
 
152
                        left -= nvp->nvp_size;
 
153
                else
 
154
                        left = 0;
 
155
                nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
 
156
        }
 
157
}
 
158
 
 
159
#define zdebug(fmt, ...)        do {} while(0)
 
160
/*#define zdebug(fmt, a...)     printf(fmt, ##a)*/
 
161
 
 
162
/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start
 
163
 * of the disk, and 2 areas at the end of the disk.  Check only some of them...
 
164
 * #4 (@ 132kB) is the first one written on a new filesystem. */
 
165
static int probe_zfs(blkid_probe pr,
 
166
                const struct blkid_idmag *mag __attribute__((__unused__)))
 
167
{
 
168
        uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
 
169
        struct zfs_uberblock *ub;
 
170
        int swab_endian;
 
171
        loff_t offset;
 
172
        int tried;
 
173
        int found;
 
174
 
 
175
        zdebug("probe_zfs\n");
 
176
        /* Look for at least 4 uberblocks to ensure a positive match */
 
177
        for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK;
 
178
             tried < ZFS_TRIES && found < ZFS_WANT;
 
179
             tried++, offset += 4096) {
 
180
                /* also try the second uberblock copy */
 
181
                if (tried == (ZFS_TRIES / 2))
 
182
                        offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK;
 
183
 
 
184
                ub = (struct zfs_uberblock *)
 
185
                        blkid_probe_get_buffer(pr, offset,
 
186
                                               sizeof(struct zfs_uberblock));
 
187
                if (ub == NULL)
 
188
                        return -1;
 
189
 
 
190
                if (ub->ub_magic == UBERBLOCK_MAGIC)
 
191
                        found++;
 
192
 
 
193
                if ((swab_endian = (ub->ub_magic == swab_magic)))
 
194
                        found++;
 
195
 
 
196
                zdebug("probe_zfs: found %s-endian uberblock at %llu\n",
 
197
                       swab_endian ? "big" : "little", offset >> 10);
 
198
        }
 
199
 
 
200
        if (found < 4)
 
201
                return -1;
 
202
 
 
203
        /* If we found the 4th uberblock, then we will have exited from the
 
204
         * scanning loop immediately, and ub will be a valid uberblock. */
 
205
        blkid_probe_sprintf_version(pr, "%" PRIu64, swab_endian ?
 
206
                                    swab64(ub->ub_version) : ub->ub_version);
 
207
 
 
208
        zfs_extract_guid_name(pr, offset);
 
209
 
 
210
        if (blkid_probe_set_magic(pr, offset,
 
211
                                sizeof(ub->ub_magic),
 
212
                                (unsigned char *) &ub->ub_magic))
 
213
                return -1;
 
214
 
 
215
        return 0;
 
216
}
 
217
 
 
218
const struct blkid_idinfo zfs_idinfo =
 
219
{
 
220
        .name           = "zfs_member",
 
221
        .usage          = BLKID_USAGE_RAID,
 
222
        .probefunc      = probe_zfs,
 
223
        .minsz          = 64 * 1024 * 1024,
 
224
        .magics         = BLKID_NONE_MAGIC
 
225
};
 
226