master xplshn/aruu / cmd / extra / blkid.c
  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}