drivers: power: report battery voltage in AOSP compatible format
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / block / uuid.c
CommitLineData
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
7static 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
24struct 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 */
43static 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
135static 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
145static 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 **/
166static 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
204int 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 */
286int 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
349void 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}
359EXPORT_SYMBOL_GPL(free_fs_info);
360
361struct 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}
492EXPORT_SYMBOL_GPL(fs_info_from_block_dev);
493
494static 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);