1/* See LICENSE file for copyright and license details. */
2#include "util.h"
3#include "diskutil.h"
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9#include <fcntl.h>
10
11struct FsType {
12 const char *name;
13 size_t magic_offset;
14 size_t magic_len;
15 uint64_t magic1;
16 uint64_t magic2;
17 size_t uuid_offset;
18 size_t uuid_len;
19 size_t label_offset;
20 size_t label_len;
21};
22
23static const struct FsType fstypes[] = {
24 /* ext2/3/4 */
25 { "ext", 1080, 2, 0xEF53, 0x53EF, 1128, 16, 1144, 16 },
26 /* vfat / fat32 */
27 { "vfat", 82, 5, 0x3233544146ULL, 0, 67, 4, 71, 11 },
28 /* vfat / fat12/16 */
29 { "vfat", 54, 4, 0x31544146, 0, 39, 4, 43, 11 },
30 /* ntfs */
31 { "ntfs", 3, 4, 0x5346544e, 0, 72, 8, 0, 0 },
32 /* xfs */
33 { "xfs", 0, 4, 0x42534658, 0x58465342, 32, 16, 108, 12 },
34 /* btrfs */
35 { "btrfs", 65600, 8, 0x4D5F53665248425FULL, 0, 65803, 16, 65819, 256 },
36 /* f2fs */
37 { "f2fs", 1024, 4, 0xF2F52010, 0x1020F5F2, 1132, 16, 1148, 512 },
38 /* ufs1 */
39 { "ufs1", 9564, 4, 0x011954, 0x54190100, 0, 0, 0, 0 },
40 /* ufs2 */
41 { "ufs2", 66908, 4, 0x19540119, 0x19015419, 67056, 16, 67072, 32 },
42 /* fossil */
43 { "fossil", 131200, 4, 0x2340A3B1, 0xB1A34023, 0, 0, 131072, 127 },
44 /* cwfs */
45 { "cwfs", 0, 4, 0xc1a551f5, 0xf551a5c1, 0, 0, 0, 0 },
46};
47
48static char *oflag = "full";
49static char *sflag = NULL;
50static int Uflag = 0;
51static int Lflag = 0;
52
53static void
54usage(void)
55{
56 eprintf("usage: %s [-o format] [-s tag] [-U] [-L] [device ...]\n", argv0);
57}
58
59static void
60format_uuid(const unsigned char *buf, size_t len, const char *type, char *out, size_t out_len)
61{
62 size_t i;
63 char *p = out;
64
65 if (strcmp(type, "vfat") == 0) {
66 snprintf(out, out_len, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]);
67 return;
68 }
69
70 if (strcmp(type, "ntfs") == 0) {
71 for (i = 8; i > 0; i--) {
72 snprintf(p, out_len - (p - out), "%02X", buf[i - 1]);
73 p += 2;
74 }
75 return;
76 }
77
78 for (i = 0; i < len; i++) {
79 if (i == 4 || i == 6 || i == 8 || i == 10) {
80 *p++ = '-';
81 }
82 snprintf(p, out_len - (p - out), "%02x", buf[i]);
83 p += 2;
84 }
85 *p = '\0';
86}
87
88static void
89print_tag(const char *devname, const char *tag, const char *value)
90{
91 if (sflag && strcasecmp(sflag, tag) != 0)
92 return;
93
94 if (strcmp(oflag, "value") == 0) {
95 printf("%s\n", value);
96 } else if (strcmp(oflag, "export") == 0) {
97 printf("%s=%s\n", tag, value);
98 } else {
99 printf(" %s=\"%s\"", tag, value);
100 }
101}
102
103static int
104detect_zfs(const unsigned char *buf, size_t len)
105{
106 size_t offset;
107 uint64_t magic;
108
109 for (offset = 131072; offset + 8 <= len; offset += 1024) {
110 magic = ((uint64_t)buf[offset + 7] << 56) |
111 ((uint64_t)buf[offset + 6] << 48) |
112 ((uint64_t)buf[offset + 5] << 40) |
113 ((uint64_t)buf[offset + 4] << 32) |
114 ((uint64_t)buf[offset + 3] << 24) |
115 ((uint64_t)buf[offset + 2] << 16) |
116 ((uint64_t)buf[offset + 1] << 8) |
117 (uint64_t)buf[offset];
118
119 if (magic == 0x00bab10caULL || magic == 0x0cb1ba0000000000ULL ||
120 magic == 0x00bab10cULL || magic == 0x0cb1ba00ULL)
121 return 1;
122 }
123 return 0;
124}
125
126static int
127detect_plan9_other(const unsigned char *buf, size_t len, const char **name)
128{
129 if (len >= 4) {
130 uint32_t magic = ((uint32_t)buf[3] << 24) | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[1] << 8) | (uint32_t)buf[0];
131 if (magic == 0x686a6673 || magic == 0x73666a68) {
132 *name = "hjfs";
133 return 1;
134 }
135 if (magic == 0x67656673 || magic == 0x73666567) {
136 *name = "gefs";
137 return 1;
138 }
139 }
140 if (len >= 516) {
141 uint32_t magic = ((uint32_t)buf[515] << 24) | ((uint32_t)buf[514] << 16) | ((uint32_t)buf[513] << 8) | (uint32_t)buf[512];
142 if (magic == 0x686a6673 || magic == 0x73666a68) {
143 *name = "hjfs";
144 return 1;
145 }
146 if (magic == 0x67656673 || magic == 0x73666567) {
147 *name = "gefs";
148 return 1;
149 }
150 }
151 return 0;
152}
153
154static int
155do_blkid(const char *path)
156{
157 struct BlockDev dev;
158 unsigned char *buf;
159 size_t read_size = 262144; /* 256kb */
160 size_t i, j;
161 int found = 0;
162
163 if (blockdev_open(&dev, path, 0) < 0)
164 return -1;
165
166 if (dev.size < read_size)
167 read_size = dev.size;
168
169 buf = emalloc(read_size);
170 /* read sectors */
171 size_t sectors_to_read = (read_size + dev.sec_size - 1) / dev.sec_size;
172 if (blockdev_read(&dev, 0, buf, sectors_to_read) < 0) {
173 free(buf);
174 blockdev_close(&dev);
175 return -1;
176 }
177
178 for (i = 0; i < LEN(fstypes); i++) {
179 const struct FsType *fs = &fstypes[i];
180 if (fs->magic_offset + fs->magic_len > read_size)
181 continue;
182
183 uint64_t val = 0;
184 for (j = 0; j < fs->magic_len; j++) {
185 val |= ((uint64_t)buf[fs->magic_offset + j]) << (8 * j);
186 }
187
188 if (val == fs->magic1 || (fs->magic2 && val == fs->magic2)) {
189 const char *type_name = fs->name;
190 if (strcmp(fs->name, "ext") == 0) {
191 if (buf[1116] & 4)
192 type_name = "ext3";
193 else if (buf[1120] & 64)
194 type_name = "ext4";
195 else
196 type_name = "ext2";
197 }
198
199 if (Uflag || Lflag) {
200 char label[256] = {0};
201 char uuid[256] = {0};
202 if (fs->label_len && fs->label_offset + fs->label_len <= read_size) {
203 snprintf(label, sizeof(label), "%.*s", (int)fs->label_len, buf + fs->label_offset);
204 }
205 if (fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
206 format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, sizeof(uuid));
207 }
208 if (Uflag) {
209 printf("%s\n", uuid);
210 } else {
211 printf("%s\n", label);
212 }
213 found = 1;
214 break;
215 }
216
217 if (strcmp(oflag, "export") == 0) {
218 printf("DEVNAME=%s\n", path);
219 } else if (strcmp(oflag, "value") != 0) {
220 printf("%s:", path);
221 }
222
223 if (fs->label_len && fs->label_offset + fs->label_len <= read_size) {
224 char label[256];
225 snprintf(label, sizeof(label), "%.*s", (int)fs->label_len, buf + fs->label_offset);
226 /* strip trailing spaces */
227 char *end = label + strlen(label) - 1;
228 while (end >= label && *end == ' ') {
229 *end = '\0';
230 end--;
231 }
232 if (label[0])
233 print_tag(path, "LABEL", label);
234 }
235
236 if (fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
237 char uuid[256];
238 format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, sizeof(uuid));
239 print_tag(path, "UUID", uuid);
240 }
241
242 print_tag(path, "TYPE", type_name);
243 if (strcmp(oflag, "full") == 0)
244 printf("\n");
245
246 found = 1;
247 break;
248 }
249 }
250
251 if (!found && detect_zfs(buf, read_size)) {
252 if (Uflag || Lflag) {
253 printf("\n");
254 } else {
255 if (strcmp(oflag, "export") == 0) {
256 printf("DEVNAME=%s\n", path);
257 } else if (strcmp(oflag, "value") != 0) {
258 printf("%s:", path);
259 }
260 print_tag(path, "TYPE", "zfs_member");
261 if (strcmp(oflag, "full") == 0)
262 printf("\n");
263 }
264 found = 1;
265 }
266
267 if (!found) {
268 const char *p9_name = NULL;
269 if (detect_plan9_other(buf, read_size, &p9_name)) {
270 if (Uflag || Lflag) {
271 printf("\n");
272 } else {
273 if (strcmp(oflag, "export") == 0) {
274 printf("DEVNAME=%s\n", path);
275 } else if (strcmp(oflag, "value") != 0) {
276 printf("%s:", path);
277 }
278 print_tag(path, "TYPE", p9_name);
279 if (strcmp(oflag, "full") == 0)
280 printf("\n");
281 }
282 found = 1;
283 }
284 }
285
286 free(buf);
287 blockdev_close(&dev);
288 return found ? 0 : -2;
289}
290
291// ?man blkid: print block device attributes
292// ?man arguments: [device ...]
293// ?man blkid locates and prints attributes (such as uuid, volume label, and filesystem type)
294// ?man of block devices or partition images
295int
296main(int argc, char *argv[])
297{
298 int ret = 0;
299
300 ARGBEGIN {
301 // ?man -o:specify output format (full, value, export)
302 case 'o':
303 oflag = EARGF(usage());
304 break;
305 // ?man -s:only show specified tag (e.g. UUID, LABEL, TYPE)
306 case 's':
307 sflag = EARGF(usage());
308 break;
309 // ?man -U:print UUID only
310 case 'U':
311 Uflag = 1;
312 break;
313 // ?man -L:print volume label only
314 case 'L':
315 Lflag = 1;
316 break;
317 default:
318 usage();
319 } ARGEND
320
321 if (argc > 0) {
322 for (; *argv; argv++) {
323 if (do_blkid(*argv) < 0)
324 ret = 1;
325 }
326 } else {
327 struct BlockDevInfo *list = blockdev_get_list();
328 struct BlockDevInfo *curr;
329 if (!list) {
330 return 1;
331 }
332 for (curr = list; curr; curr = curr->next) {
333 char devpath[128];
334 snprintf(devpath, sizeof(devpath), "/dev/%s", curr->name);
335 do_blkid(devpath);
336 struct BlockDevInfo *part;
337 for (part = curr->parts; part; part = part->next) {
338 snprintf(devpath, sizeof(devpath), "/dev/%s", part->name);
339 do_blkid(devpath);
340 }
341 }
342 blockdev_free_list(list);
343 }
344
345 return ret;
346}