Commit | Line | Data |
---|---|---|
6fa3eb70 S |
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> | |
6 | ||
7 | static int debug_enabled; | |
8 | ||
9 | #define PRINTK(fmt, args...) do { \ | |
10 | if (debug_enabled) \ | |
11 | printk(KERN_DEBUG fmt, ## args); \ | |
12 | } while (0) | |
13 | ||
14 | #define PRINT_HEX_DUMP(v1, v2, v3, v4, v5, v6, v7, v8) \ | |
15 | do { \ | |
16 | if (debug_enabled) \ | |
17 | print_hex_dump(v1, v2, v3, v4, v5, v6, v7, v8); \ | |
18 | } while (0) | |
19 | ||
20 | /* | |
21 | * Simple UUID translation | |
22 | */ | |
23 | ||
24 | struct uuid_info { | |
25 | const char *key; | |
26 | const char *name; | |
27 | long bkoff; | |
28 | unsigned sboff; | |
29 | unsigned sig_len; | |
30 | const char *magic; | |
31 | int uuid_offset; | |
32 | int last_mount_offset; | |
33 | int last_mount_size; | |
34 | }; | |
35 | ||
36 | /* | |
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. | |
40 | * | |
41 | * Rearranged by page of fs signature for optimisation. | |
42 | */ | |
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} | |
133 | }; | |
134 | ||
135 | static int null_uuid(const char *uuid) | |
136 | { | |
137 | int i; | |
138 | ||
139 | for (i = 0; i < 16 && !uuid[i]; i++); | |
140 | ||
141 | return (i == 16); | |
142 | } | |
143 | ||
144 | ||
145 | static void uuid_end_bio(struct bio *bio, int err) | |
146 | { | |
147 | struct page *page = bio->bi_io_vec[0].bv_page; | |
148 | ||
149 | if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) | |
150 | SetPageError(page); | |
151 | ||
152 | unlock_page(page); | |
153 | bio_put(bio); | |
154 | } | |
155 | ||
156 | ||
157 | /** | |
158 | * submit - submit BIO request | |
159 | * @dev: The block device we're using. | |
160 | * @page_num: The page we're reading. | |
161 | * | |
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." | |
165 | **/ | |
166 | static struct page *read_bdev_page(struct block_device *dev, int page_num) | |
167 | { | |
168 | struct bio *bio = NULL; | |
169 | struct page *page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); | |
170 | ||
171 | if (!page) { | |
172 | printk(KERN_ERR "Failed to allocate a page for reading data " "in UUID checks."); | |
173 | return NULL; | |
174 | } | |
175 | ||
176 | bio = bio_alloc(GFP_NOFS, 1); | |
177 | bio->bi_bdev = dev; | |
178 | bio->bi_sector = page_num << 3; | |
179 | bio->bi_end_io = uuid_end_bio; | |
180 | bio->bi_flags |= (1 << BIO_TOI); | |
181 | ||
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); | |
184 | ||
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); | |
187 | bio_put(bio); | |
188 | __free_page(page); | |
189 | printk(KERN_DEBUG "read_bdev_page freed page %p (in error " "path).\n", page); | |
190 | return NULL; | |
191 | } | |
192 | ||
193 | lock_page(page); | |
194 | submit_bio(READ | REQ_SYNC, bio); | |
195 | ||
196 | wait_on_page_locked(page); | |
197 | if (PageError(page)) { | |
198 | __free_page(page); | |
199 | page = NULL; | |
200 | } | |
201 | return page; | |
202 | } | |
203 | ||
204 | int bdev_matches_key(struct block_device *bdev, const char *key) | |
205 | { | |
206 | unsigned char *data = NULL; | |
207 | struct page *data_page = NULL; | |
208 | ||
209 | int dev_offset, pg_num, pg_off, i; | |
210 | int last_pg_num = -1; | |
211 | int result = 0; | |
212 | char buf[50]; | |
213 | ||
214 | if (null_uuid(key)) { | |
215 | PRINTK("Refusing to find a NULL key.\n"); | |
216 | return 0; | |
217 | } | |
218 | ||
219 | if (!bdev->bd_disk) { | |
220 | bdevname(bdev, buf); | |
221 | PRINTK("bdev %s has no bd_disk.\n", buf); | |
222 | return 0; | |
223 | } | |
224 | ||
225 | if (!bdev->bd_disk->queue) { | |
226 | bdevname(bdev, buf); | |
227 | PRINTK("bdev %s has no queue.\n", buf); | |
228 | return 0; | |
229 | } | |
230 | ||
231 | for (i = 0; uuid_list[i].name; i++) { | |
232 | struct uuid_info *dat = &uuid_list[i]; | |
233 | ||
234 | if (!dat->key || strcmp(dat->key, key)) | |
235 | continue; | |
236 | ||
237 | dev_offset = (dat->bkoff << 10) + dat->sboff; | |
238 | pg_num = dev_offset >> 12; | |
239 | pg_off = dev_offset & 0xfff; | |
240 | ||
241 | if ((((pg_num + 1) << 3) - 1) > bdev->bd_part->nr_sects >> 1) | |
242 | continue; | |
243 | ||
244 | if (pg_num != last_pg_num) { | |
245 | if (data_page) { | |
246 | kunmap(data_page); | |
247 | __free_page(data_page); | |
248 | } | |
249 | data_page = read_bdev_page(bdev, pg_num); | |
250 | if (!data_page) | |
251 | continue; | |
252 | data = kmap(data_page); | |
253 | } | |
254 | ||
255 | last_pg_num = pg_num; | |
256 | ||
257 | if (strncmp(&data[pg_off], dat->magic, dat->sig_len)) | |
258 | continue; | |
259 | ||
260 | result = 1; | |
261 | break; | |
262 | } | |
263 | ||
264 | if (data_page) { | |
265 | kunmap(data_page); | |
266 | __free_page(data_page); | |
267 | } | |
268 | ||
269 | return result; | |
270 | } | |
271 | ||
272 | /* | |
273 | * part_matches_fs_info - Does the given partition match the details given? | |
274 | * | |
275 | * Returns a score saying how good the match is. | |
276 | * 0 = no UUID match. | |
277 | * 1 = UUID but last mount time differs. | |
278 | * 2 = UUID, last mount time but not dev_t | |
279 | * 3 = perfect match | |
280 | * | |
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 | |
284 | * correct partition. | |
285 | */ | |
286 | int part_matches_fs_info(struct hd_struct *part, struct fs_info *seek) | |
287 | { | |
288 | struct block_device *bdev; | |
289 | struct fs_info *got; | |
290 | int result = 0; | |
291 | char buf[50]; | |
292 | ||
293 | if (null_uuid((char *)&seek->uuid)) { | |
294 | PRINTK("Refusing to find a NULL uuid.\n"); | |
295 | return 0; | |
296 | } | |
297 | ||
298 | bdev = bdget(part_devt(part)); | |
299 | ||
300 | PRINTK("part_matches fs info considering %x.\n", part_devt(part)); | |
301 | ||
302 | if (blkdev_get(bdev, FMODE_READ, 0)) { | |
303 | PRINTK("blkdev_get failed.\n"); | |
304 | return 0; | |
305 | } | |
306 | ||
307 | if (!bdev->bd_disk) { | |
308 | bdevname(bdev, buf); | |
309 | PRINTK("bdev %s has no bd_disk.\n", buf); | |
310 | goto out; | |
311 | } | |
312 | ||
313 | if (!bdev->bd_disk->queue) { | |
314 | bdevname(bdev, buf); | |
315 | PRINTK("bdev %s has no queue.\n", buf); | |
316 | goto out; | |
317 | } | |
318 | ||
319 | got = fs_info_from_block_dev(bdev); | |
320 | ||
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); | |
325 | result = 1; | |
326 | ||
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)) { | |
330 | result = 2; | |
331 | ||
332 | PRINTK(" Matching last mount time.\n"); | |
333 | ||
334 | if (part_devt(part) == seek->dev_t) { | |
335 | result = 3; | |
336 | PRINTK(" Matching dev_t.\n"); | |
337 | } else | |
338 | PRINTK("Dev_ts differ (%x vs %x).\n", part_devt(part), seek->dev_t); | |
339 | } | |
340 | } | |
341 | ||
342 | PRINTK(" Score for %x is %d.\n", part_devt(part), result); | |
343 | free_fs_info(got); | |
344 | out: | |
345 | blkdev_put(bdev, FMODE_READ); | |
346 | return result; | |
347 | } | |
348 | ||
349 | void free_fs_info(struct fs_info *fs_info) | |
350 | { | |
351 | if (!fs_info || IS_ERR(fs_info)) | |
352 | return; | |
353 | ||
354 | if (fs_info->last_mount) | |
355 | kfree(fs_info->last_mount); | |
356 | ||
357 | kfree(fs_info); | |
358 | } | |
359 | EXPORT_SYMBOL_GPL(free_fs_info); | |
360 | ||
361 | struct fs_info *fs_info_from_block_dev(struct block_device *bdev) | |
362 | { | |
363 | unsigned char *data = NULL; | |
364 | struct page *data_page = NULL; | |
365 | ||
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; | |
370 | ||
371 | int last_pg_num = -1, last_uuid_pg_num = 0; | |
372 | char buf[50]; | |
373 | struct fs_info *fs_info = NULL; | |
374 | ||
375 | bdevname(bdev, buf); | |
376 | ||
377 | PRINTK("uuid_from_block_dev looking for partition type of %s.\n", buf); | |
378 | ||
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; | |
386 | ||
387 | if ((((pg_num + 1) << 3) - 1) > bdev->bd_part->nr_sects >> 1) | |
388 | continue; | |
389 | ||
390 | /* Ignore partition types with no UUID offset */ | |
391 | if (!dat->uuid_offset) | |
392 | continue; | |
393 | ||
394 | if (pg_num != last_pg_num) { | |
395 | if (data_page) { | |
396 | kunmap(data_page); | |
397 | __free_page(data_page); | |
398 | } | |
399 | data_page = read_bdev_page(bdev, pg_num); | |
400 | if (!data_page) | |
401 | continue; | |
402 | data = kmap(data_page); | |
403 | } | |
404 | ||
405 | last_pg_num = pg_num; | |
406 | ||
407 | if (strncmp(&data[pg_off], dat->magic, dat->sig_len)) | |
408 | continue; | |
409 | ||
410 | PRINTK("This partition looks like %s.\n", dat->name); | |
411 | ||
412 | fs_info = kzalloc(sizeof(struct fs_info), GFP_KERNEL); | |
413 | ||
414 | if (!fs_info) { | |
415 | PRINTK("Failed to allocate fs_info struct."); | |
416 | fs_info = ERR_PTR(-ENOMEM); | |
417 | break; | |
418 | } | |
419 | ||
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) | |
422 | goto no_uuid; | |
423 | ||
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); | |
428 | else { | |
429 | if (uuid_data_page) { | |
430 | kunmap(uuid_data_page); | |
431 | __free_page(uuid_data_page); | |
432 | } | |
433 | uuid_data_page = read_bdev_page(bdev, uuid_pg_num); | |
434 | if (!uuid_data_page) | |
435 | continue; | |
436 | uuid_data = kmap(uuid_data_page); | |
437 | } | |
438 | } | |
439 | ||
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; | |
443 | ||
444 | no_uuid: | |
445 | PRINT_HEX_DUMP(KERN_EMERG, "fs_info_from_block_dev " | |
446 | "returning uuid ", DUMP_PREFIX_NONE, 16, 1, fs_info->uuid, 16, 0); | |
447 | ||
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; | |
453 | char *ptr; | |
454 | ||
455 | if (!last_mount) { | |
456 | fs_info = ERR_PTR(-ENOMEM); | |
457 | break; | |
458 | } | |
459 | last_mount_data = kmap(last_mount); | |
460 | sz = dat->last_mount_size; | |
461 | ptr = kmalloc(sz, GFP_KERNEL); | |
462 | ||
463 | if (!ptr) { | |
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); | |
468 | } else { | |
469 | fs_info->last_mount = ptr; | |
470 | fs_info->last_mount_size = sz; | |
471 | memcpy(ptr, &last_mount_data[off], sz); | |
472 | } | |
473 | ||
474 | kunmap(last_mount); | |
475 | __free_page(last_mount); | |
476 | } | |
477 | break; | |
478 | } | |
479 | ||
480 | if (data_page) { | |
481 | kunmap(data_page); | |
482 | __free_page(data_page); | |
483 | } | |
484 | ||
485 | if (uuid_data_page) { | |
486 | kunmap(uuid_data_page); | |
487 | __free_page(uuid_data_page); | |
488 | } | |
489 | ||
490 | return fs_info; | |
491 | } | |
492 | EXPORT_SYMBOL_GPL(fs_info_from_block_dev); | |
493 | ||
494 | static int __init uuid_debug_setup(char *str) | |
495 | { | |
496 | int value; | |
497 | ||
498 | if (sscanf(str, "=%d", &value)) | |
499 | debug_enabled = value; | |
500 | ||
501 | return 1; | |
502 | } | |
503 | ||
504 | __setup("uuid_debug", uuid_debug_setup); |