1 #include <linux/blkdev.h>
2 #include <linux/ctype.h>
3 #include <linux/fs_uuid.h>
4 #include <linux/slab.h>
5 #include <linux/export.h>
7 static int debug_enabled
;
9 #define PRINTK(fmt, args...) do { \
11 printk(KERN_DEBUG fmt, ## args); \
14 #define PRINT_HEX_DUMP(v1, v2, v3, v4, v5, v6, v7, v8) \
17 print_hex_dump(v1, v2, v3, v4, v5, v6, v7, v8); \
21 * Simple UUID translation
32 int last_mount_offset
;
37 * Based on libuuid's blkid_magic array. Note that I don't
38 * have uuid offsets for all of these yet - mssing ones are 0x0.
39 * Further information welcome.
41 * Rearranged by page of fs signature for optimisation.
43 static struct uuid_info uuid_list
[] = {
44 {NULL
, "oracleasm", 0, 32, 8, "ORCLDISK", 0x0, 0, 0},
45 {"ntfs", "ntfs", 0, 3, 8, "NTFS ", 0x0, 0, 0},
46 {"vfat", "vfat", 0, 0x52, 5, "MSWIN", 0x0, 0, 0},
47 {"vfat", "vfat", 0, 0x52, 8, "FAT32 ", 0x0, 0, 0},
48 {"vfat", "vfat", 0, 0x36, 5, "MSDOS", 0x0, 0, 0},
49 {"vfat", "vfat", 0, 0x36, 8, "FAT16 ", 0x0, 0, 0},
50 {"vfat", "vfat", 0, 0x36, 8, "FAT12 ", 0x0, 0, 0},
51 {"vfat", "vfat", 0, 0, 1, "\353", 0x0, 0, 0},
52 {"vfat", "vfat", 0, 0, 1, "\351", 0x0, 0, 0},
53 {"vfat", "vfat", 0, 0x1fe, 2, "\125\252", 0x0, 0, 0},
54 {"xfs", "xfs", 0, 0, 4, "XFSB", 0x20, 0, 0},
55 {"romfs", "romfs", 0, 0, 8, "-rom1fs-", 0x0, 0, 0},
56 {"bfs", "bfs", 0, 0, 4, "\316\372\173\033", 0, 0, 0},
57 {"cramfs", "cramfs", 0, 0, 4, "E=\315\050", 0x0, 0, 0},
58 {"qnx4", "qnx4", 0, 4, 6, "QNX4FS", 0, 0, 0},
59 {NULL
, "crypt_LUKS", 0, 0, 6, "LUKS\xba\xbe", 0x0, 0, 0},
60 {"squashfs", "squashfs", 0, 0, 4, "sqsh", 0, 0, 0},
61 {"squashfs", "squashfs", 0, 0, 4, "hsqs", 0, 0, 0},
62 {"ocfs", "ocfs", 0, 8, 9, "OracleCFS", 0x0, 0, 0},
63 {"lvm2pv", "lvm2pv", 0, 0x018, 8, "LVM2 001", 0x0, 0, 0},
64 {"sysv", "sysv", 0, 0x3f8, 4, "\020~\030\375", 0, 0, 0},
65 {"ext", "ext", 1, 0x38, 2, "\123\357", 0x468, 0x42c, 4},
66 {"minix", "minix", 1, 0x10, 2, "\177\023", 0, 0, 0},
67 {"minix", "minix", 1, 0x10, 2, "\217\023", 0, 0, 0},
68 {"minix", "minix", 1, 0x10, 2, "\150\044", 0, 0, 0},
69 {"minix", "minix", 1, 0x10, 2, "\170\044", 0, 0, 0},
70 {"lvm2pv", "lvm2pv", 1, 0x018, 8, "LVM2 001", 0x0, 0, 0},
71 {"vxfs", "vxfs", 1, 0, 4, "\365\374\001\245", 0, 0, 0},
72 {"hfsplus", "hfsplus", 1, 0, 2, "BD", 0x0, 0, 0},
73 {"hfsplus", "hfsplus", 1, 0, 2, "H+", 0x0, 0, 0},
74 {"hfsplus", "hfsplus", 1, 0, 2, "HX", 0x0, 0, 0},
75 {"hfs", "hfs", 1, 0, 2, "BD", 0x0, 0, 0},
76 {"ocfs2", "ocfs2", 1, 0, 6, "OCFSV2", 0x0, 0, 0},
77 {"lvm2pv", "lvm2pv", 0, 0x218, 8, "LVM2 001", 0x0, 0, 0},
78 {"lvm2pv", "lvm2pv", 1, 0x218, 8, "LVM2 001", 0x0, 0, 0},
79 {"ocfs2", "ocfs2", 2, 0, 6, "OCFSV2", 0x0, 0, 0},
80 {"swap", "swap", 0, 0xff6, 10, "SWAP-SPACE", 0x40c, 0, 0},
81 {"swap", "swap", 0, 0xff6, 10, "SWAPSPACE2", 0x40c, 0, 0},
82 {"swap", "swsuspend", 0, 0xff6, 9, "S1SUSPEND", 0x40c, 0, 0},
83 {"swap", "swsuspend", 0, 0xff6, 9, "S2SUSPEND", 0x40c, 0, 0},
84 {"swap", "swsuspend", 0, 0xff6, 9, "ULSUSPEND", 0x40c, 0, 0},
85 {"ocfs2", "ocfs2", 4, 0, 6, "OCFSV2", 0x0, 0, 0},
86 {"ocfs2", "ocfs2", 8, 0, 6, "OCFSV2", 0x0, 0, 0},
87 {"hpfs", "hpfs", 8, 0, 4, "I\350\225\371", 0, 0, 0},
88 {"reiserfs", "reiserfs", 8, 0x34, 8, "ReIsErFs", 0x10054, 0, 0},
89 {"reiserfs", "reiserfs", 8, 20, 8, "ReIsErFs", 0x10054, 0, 0},
90 {"zfs", "zfs", 8, 0, 8, "\0\0\x02\xf5\xb0\x07\xb1\x0c", 0x0, 0, 0},
91 {"zfs", "zfs", 8, 0, 8, "\x0c\xb1\x07\xb0\xf5\x02\0\0", 0x0, 0, 0},
92 {"ufs", "ufs", 8, 0x55c, 4, "T\031\001\000", 0, 0, 0},
93 {"swap", "swap", 0, 0x1ff6, 10, "SWAP-SPACE", 0x40c, 0, 0},
94 {"swap", "swap", 0, 0x1ff6, 10, "SWAPSPACE2", 0x40c, 0, 0},
95 {"swap", "swsuspend", 0, 0x1ff6, 9, "S1SUSPEND", 0x40c, 0, 0},
96 {"swap", "swsuspend", 0, 0x1ff6, 9, "S2SUSPEND", 0x40c, 0, 0},
97 {"swap", "swsuspend", 0, 0x1ff6, 9, "ULSUSPEND", 0x40c, 0, 0},
98 {"reiserfs", "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", 0x10054, 0, 0},
99 {"reiserfs", "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", 0x10054, 0, 0},
100 {"reiserfs", "reiserfs", 64, 0x34, 8, "ReIsErFs", 0x10054, 0, 0},
101 {"reiser4", "reiser4", 64, 0, 7, "ReIsEr4", 0x100544, 0, 0},
102 {"gfs2", "gfs2", 64, 0, 4, "\x01\x16\x19\x70", 0x0, 0, 0},
103 {"gfs", "gfs", 64, 0, 4, "\x01\x16\x19\x70", 0x0, 0, 0},
104 {"btrfs", "btrfs", 64, 0x40, 8, "_BHRfS_M", 0x0, 0, 0},
105 {"swap", "swap", 0, 0x3ff6, 10, "SWAP-SPACE", 0x40c, 0, 0},
106 {"swap", "swap", 0, 0x3ff6, 10, "SWAPSPACE2", 0x40c, 0, 0},
107 {"swap", "swsuspend", 0, 0x3ff6, 9, "S1SUSPEND", 0x40c, 0, 0},
108 {"swap", "swsuspend", 0, 0x3ff6, 9, "S2SUSPEND", 0x40c, 0, 0},
109 {"swap", "swsuspend", 0, 0x3ff6, 9, "ULSUSPEND", 0x40c, 0, 0},
110 {"udf", "udf", 32, 1, 5, "BEA01", 0x0, 0, 0},
111 {"udf", "udf", 32, 1, 5, "BOOT2", 0x0, 0, 0},
112 {"udf", "udf", 32, 1, 5, "CD001", 0x0, 0, 0},
113 {"udf", "udf", 32, 1, 5, "CDW02", 0x0, 0, 0},
114 {"udf", "udf", 32, 1, 5, "NSR02", 0x0, 0, 0},
115 {"udf", "udf", 32, 1, 5, "NSR03", 0x0, 0, 0},
116 {"udf", "udf", 32, 1, 5, "TEA01", 0x0, 0, 0},
117 {"iso9660", "iso9660", 32, 1, 5, "CD001", 0x0, 0, 0},
118 {"iso9660", "iso9660", 32, 9, 5, "CDROM", 0x0, 0, 0},
119 {"jfs", "jfs", 32, 0, 4, "JFS1", 0x88, 0, 0},
120 {"swap", "swap", 0, 0x7ff6, 10, "SWAP-SPACE", 0x40c, 0, 0},
121 {"swap", "swap", 0, 0x7ff6, 10, "SWAPSPACE2", 0x40c, 0, 0},
122 {"swap", "swsuspend", 0, 0x7ff6, 9, "S1SUSPEND", 0x40c, 0, 0},
123 {"swap", "swsuspend", 0, 0x7ff6, 9, "S2SUSPEND", 0x40c, 0, 0},
124 {"swap", "swsuspend", 0, 0x7ff6, 9, "ULSUSPEND", 0x40c, 0, 0},
125 {"swap", "swap", 0, 0xfff6, 10, "SWAP-SPACE", 0x40c, 0, 0},
126 {"swap", "swap", 0, 0xfff6, 10, "SWAPSPACE2", 0x40c, 0, 0},
127 {"swap", "swsuspend", 0, 0xfff6, 9, "S1SUSPEND", 0x40c, 0, 0},
128 {"swap", "swsuspend", 0, 0xfff6, 9, "S2SUSPEND", 0x40c, 0, 0},
129 {"swap", "swsuspend", 0, 0xfff6, 9, "ULSUSPEND", 0x40c, 0, 0},
130 {"zfs", "zfs", 264, 0, 8, "\0\0\x02\xf5\xb0\x07\xb1\x0c", 0x0, 0, 0},
131 {"zfs", "zfs", 264, 0, 8, "\x0c\xb1\x07\xb0\xf5\x02\0\0", 0x0, 0, 0},
132 {NULL
, NULL
, 0, 0, 0, NULL
, 0x0, 0, 0}
135 static int null_uuid(const char *uuid
)
139 for (i
= 0; i
< 16 && !uuid
[i
]; i
++);
145 static void uuid_end_bio(struct bio
*bio
, int err
)
147 struct page
*page
= bio
->bi_io_vec
[0].bv_page
;
149 if (!test_bit(BIO_UPTODATE
, &bio
->bi_flags
))
158 * submit - submit BIO request
159 * @dev: The block device we're using.
160 * @page_num: The page we're reading.
162 * Based on Patrick Mochell's pmdisk code from long ago: "Straight from the
163 * textbook - allocate and initialize the bio. If we're writing, make sure
164 * the page is marked as dirty. Then submit it and carry on."
166 static struct page
*read_bdev_page(struct block_device
*dev
, int page_num
)
168 struct bio
*bio
= NULL
;
169 struct page
*page
= alloc_page(GFP_NOFS
| __GFP_HIGHMEM
);
172 printk(KERN_ERR
"Failed to allocate a page for reading data " "in UUID checks.");
176 bio
= bio_alloc(GFP_NOFS
, 1);
178 bio
->bi_sector
= page_num
<< 3;
179 bio
->bi_end_io
= uuid_end_bio
;
180 bio
->bi_flags
|= (1 << BIO_TOI
);
182 PRINTK("Submitting bio on device %lx, page %d using bio %p and page %p.\n",
183 (unsigned long)dev
->bd_dev
, page_num
, bio
, page
);
185 if (bio_add_page(bio
, page
, PAGE_SIZE
, 0) < PAGE_SIZE
) {
186 printk(KERN_DEBUG
"ERROR: adding page to bio at %d\n", page_num
);
189 printk(KERN_DEBUG
"read_bdev_page freed page %p (in error " "path).\n", page
);
194 submit_bio(READ
| REQ_SYNC
, bio
);
196 wait_on_page_locked(page
);
197 if (PageError(page
)) {
204 int bdev_matches_key(struct block_device
*bdev
, const char *key
)
206 unsigned char *data
= NULL
;
207 struct page
*data_page
= NULL
;
209 int dev_offset
, pg_num
, pg_off
, i
;
210 int last_pg_num
= -1;
214 if (null_uuid(key
)) {
215 PRINTK("Refusing to find a NULL key.\n");
219 if (!bdev
->bd_disk
) {
221 PRINTK("bdev %s has no bd_disk.\n", buf
);
225 if (!bdev
->bd_disk
->queue
) {
227 PRINTK("bdev %s has no queue.\n", buf
);
231 for (i
= 0; uuid_list
[i
].name
; i
++) {
232 struct uuid_info
*dat
= &uuid_list
[i
];
234 if (!dat
->key
|| strcmp(dat
->key
, key
))
237 dev_offset
= (dat
->bkoff
<< 10) + dat
->sboff
;
238 pg_num
= dev_offset
>> 12;
239 pg_off
= dev_offset
& 0xfff;
241 if ((((pg_num
+ 1) << 3) - 1) > bdev
->bd_part
->nr_sects
>> 1)
244 if (pg_num
!= last_pg_num
) {
247 __free_page(data_page
);
249 data_page
= read_bdev_page(bdev
, pg_num
);
252 data
= kmap(data_page
);
255 last_pg_num
= pg_num
;
257 if (strncmp(&data
[pg_off
], dat
->magic
, dat
->sig_len
))
266 __free_page(data_page
);
273 * part_matches_fs_info - Does the given partition match the details given?
275 * Returns a score saying how good the match is.
277 * 1 = UUID but last mount time differs.
278 * 2 = UUID, last mount time but not dev_t
281 * This lets us cope elegantly with probing resulting in dev_ts changing
282 * from boot to boot, and with the case where a user copies a partition
283 * (UUID is non unique), and we need to check the last mount time of the
286 int part_matches_fs_info(struct hd_struct
*part
, struct fs_info
*seek
)
288 struct block_device
*bdev
;
293 if (null_uuid((char *)&seek
->uuid
)) {
294 PRINTK("Refusing to find a NULL uuid.\n");
298 bdev
= bdget(part_devt(part
));
300 PRINTK("part_matches fs info considering %x.\n", part_devt(part
));
302 if (blkdev_get(bdev
, FMODE_READ
, 0)) {
303 PRINTK("blkdev_get failed.\n");
307 if (!bdev
->bd_disk
) {
309 PRINTK("bdev %s has no bd_disk.\n", buf
);
313 if (!bdev
->bd_disk
->queue
) {
315 PRINTK("bdev %s has no queue.\n", buf
);
319 got
= fs_info_from_block_dev(bdev
);
321 if (got
&& !memcmp(got
->uuid
, seek
->uuid
, 16)) {
322 PRINTK(" Have matching UUID.\n");
323 PRINTK(" Got: LMS %d, LM %p.\n", got
->last_mount_size
, got
->last_mount
);
324 PRINTK(" Seek: LMS %d, LM %p.\n", seek
->last_mount_size
, seek
->last_mount
);
327 if (got
->last_mount_size
== seek
->last_mount_size
&&
328 got
->last_mount
&& seek
->last_mount
&&
329 !memcmp(got
->last_mount
, seek
->last_mount
, got
->last_mount_size
)) {
332 PRINTK(" Matching last mount time.\n");
334 if (part_devt(part
) == seek
->dev_t
) {
336 PRINTK(" Matching dev_t.\n");
338 PRINTK("Dev_ts differ (%x vs %x).\n", part_devt(part
), seek
->dev_t
);
342 PRINTK(" Score for %x is %d.\n", part_devt(part
), result
);
345 blkdev_put(bdev
, FMODE_READ
);
349 void free_fs_info(struct fs_info
*fs_info
)
351 if (!fs_info
|| IS_ERR(fs_info
))
354 if (fs_info
->last_mount
)
355 kfree(fs_info
->last_mount
);
359 EXPORT_SYMBOL_GPL(free_fs_info
);
361 struct fs_info
*fs_info_from_block_dev(struct block_device
*bdev
)
363 unsigned char *data
= NULL
;
364 struct page
*data_page
= NULL
;
366 int dev_offset
, pg_num
, pg_off
;
367 int uuid_pg_num
, uuid_pg_off
, i
;
368 unsigned char *uuid_data
= NULL
;
369 struct page
*uuid_data_page
= NULL
;
371 int last_pg_num
= -1, last_uuid_pg_num
= 0;
373 struct fs_info
*fs_info
= NULL
;
377 PRINTK("uuid_from_block_dev looking for partition type of %s.\n", buf
);
379 for (i
= 0; uuid_list
[i
].name
; i
++) {
380 struct uuid_info
*dat
= &uuid_list
[i
];
381 dev_offset
= (dat
->bkoff
<< 10) + dat
->sboff
;
382 pg_num
= dev_offset
>> 12;
383 pg_off
= dev_offset
& 0xfff;
384 uuid_pg_num
= dat
->uuid_offset
>> 12;
385 uuid_pg_off
= dat
->uuid_offset
& 0xfff;
387 if ((((pg_num
+ 1) << 3) - 1) > bdev
->bd_part
->nr_sects
>> 1)
390 /* Ignore partition types with no UUID offset */
391 if (!dat
->uuid_offset
)
394 if (pg_num
!= last_pg_num
) {
397 __free_page(data_page
);
399 data_page
= read_bdev_page(bdev
, pg_num
);
402 data
= kmap(data_page
);
405 last_pg_num
= pg_num
;
407 if (strncmp(&data
[pg_off
], dat
->magic
, dat
->sig_len
))
410 PRINTK("This partition looks like %s.\n", dat
->name
);
412 fs_info
= kzalloc(sizeof(struct fs_info
), GFP_KERNEL
);
415 PRINTK("Failed to allocate fs_info struct.");
416 fs_info
= ERR_PTR(-ENOMEM
);
420 /* UUID can't be off the end of the disk */
421 if ((uuid_pg_num
> bdev
->bd_part
->nr_sects
>> 3) || !dat
->uuid_offset
)
424 if (!uuid_data
|| uuid_pg_num
!= last_uuid_pg_num
) {
425 /* No need to reread the page from above */
426 if (uuid_pg_num
== pg_num
&& uuid_data
)
427 memcpy(uuid_data
, data
, PAGE_SIZE
);
429 if (uuid_data_page
) {
430 kunmap(uuid_data_page
);
431 __free_page(uuid_data_page
);
433 uuid_data_page
= read_bdev_page(bdev
, uuid_pg_num
);
436 uuid_data
= kmap(uuid_data_page
);
440 last_uuid_pg_num
= uuid_pg_num
;
441 memcpy(&fs_info
->uuid
, &uuid_data
[uuid_pg_off
], 16);
442 fs_info
->dev_t
= bdev
->bd_dev
;
445 PRINT_HEX_DUMP(KERN_EMERG
, "fs_info_from_block_dev "
446 "returning uuid ", DUMP_PREFIX_NONE
, 16, 1, fs_info
->uuid
, 16, 0);
448 if (dat
->last_mount_size
) {
449 int pg
= dat
->last_mount_offset
>> 12, sz
;
450 int off
= dat
->last_mount_offset
& 0xfff;
451 struct page
*last_mount
= read_bdev_page(bdev
, pg
);
452 unsigned char *last_mount_data
;
456 fs_info
= ERR_PTR(-ENOMEM
);
459 last_mount_data
= kmap(last_mount
);
460 sz
= dat
->last_mount_size
;
461 ptr
= kmalloc(sz
, GFP_KERNEL
);
464 printk(KERN_EMERG
"fs_info_from_block_dev "
465 "failed to get memory for last mount " "timestamp.");
466 free_fs_info(fs_info
);
467 fs_info
= ERR_PTR(-ENOMEM
);
469 fs_info
->last_mount
= ptr
;
470 fs_info
->last_mount_size
= sz
;
471 memcpy(ptr
, &last_mount_data
[off
], sz
);
475 __free_page(last_mount
);
482 __free_page(data_page
);
485 if (uuid_data_page
) {
486 kunmap(uuid_data_page
);
487 __free_page(uuid_data_page
);
492 EXPORT_SYMBOL_GPL(fs_info_from_block_dev
);
494 static int __init
uuid_debug_setup(char *str
)
498 if (sscanf(str
, "=%d", &value
))
499 debug_enabled
= value
;
504 __setup("uuid_debug", uuid_debug_setup
);