diff options
Diffstat (limited to 'fs/partitions')
-rw-r--r-- | fs/partitions/check.c | 69 | ||||
-rw-r--r-- | fs/partitions/check.h | 5 |
2 files changed, 60 insertions, 14 deletions
diff --git a/fs/partitions/check.c b/fs/partitions/check.c index a19995c6f6af..5dcd4b0c5533 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c | |||
@@ -161,7 +161,7 @@ check_partition(struct gendisk *hd, struct block_device *bdev) | |||
161 | struct parsed_partitions *state; | 161 | struct parsed_partitions *state; |
162 | int i, res, err; | 162 | int i, res, err; |
163 | 163 | ||
164 | state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL); | 164 | state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL); |
165 | if (!state) | 165 | if (!state) |
166 | return NULL; | 166 | return NULL; |
167 | 167 | ||
@@ -187,6 +187,8 @@ check_partition(struct gendisk *hd, struct block_device *bdev) | |||
187 | } | 187 | } |
188 | if (res > 0) | 188 | if (res > 0) |
189 | return state; | 189 | return state; |
190 | if (state->access_beyond_eod) | ||
191 | err = -ENOSPC; | ||
190 | if (err) | 192 | if (err) |
191 | /* The partition is unrecognized. So report I/O errors if there were any */ | 193 | /* The partition is unrecognized. So report I/O errors if there were any */ |
192 | res = err; | 194 | res = err; |
@@ -539,13 +541,34 @@ exit: | |||
539 | disk_part_iter_exit(&piter); | 541 | disk_part_iter_exit(&piter); |
540 | } | 542 | } |
541 | 543 | ||
544 | static bool disk_unlock_native_capacity(struct gendisk *disk) | ||
545 | { | ||
546 | const struct block_device_operations *bdops = disk->fops; | ||
547 | |||
548 | if (bdops->unlock_native_capacity && | ||
549 | !(disk->flags & GENHD_FL_NATIVE_CAPACITY)) { | ||
550 | printk(KERN_CONT "enabling native capacity\n"); | ||
551 | bdops->unlock_native_capacity(disk); | ||
552 | disk->flags |= GENHD_FL_NATIVE_CAPACITY; | ||
553 | return true; | ||
554 | } else { | ||
555 | printk(KERN_CONT "truncated\n"); | ||
556 | return false; | ||
557 | } | ||
558 | } | ||
559 | |||
542 | int rescan_partitions(struct gendisk *disk, struct block_device *bdev) | 560 | int rescan_partitions(struct gendisk *disk, struct block_device *bdev) |
543 | { | 561 | { |
562 | struct parsed_partitions *state = NULL; | ||
544 | struct disk_part_iter piter; | 563 | struct disk_part_iter piter; |
545 | struct hd_struct *part; | 564 | struct hd_struct *part; |
546 | struct parsed_partitions *state; | ||
547 | int p, highest, res; | 565 | int p, highest, res; |
548 | rescan: | 566 | rescan: |
567 | if (state && !IS_ERR(state)) { | ||
568 | kfree(state); | ||
569 | state = NULL; | ||
570 | } | ||
571 | |||
549 | if (bdev->bd_part_count) | 572 | if (bdev->bd_part_count) |
550 | return -EBUSY; | 573 | return -EBUSY; |
551 | res = invalidate_partition(disk, 0); | 574 | res = invalidate_partition(disk, 0); |
@@ -563,8 +586,32 @@ rescan: | |||
563 | bdev->bd_invalidated = 0; | 586 | bdev->bd_invalidated = 0; |
564 | if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) | 587 | if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) |
565 | return 0; | 588 | return 0; |
566 | if (IS_ERR(state)) /* I/O error reading the partition table */ | 589 | if (IS_ERR(state)) { |
590 | /* | ||
591 | * I/O error reading the partition table. If any | ||
592 | * partition code tried to read beyond EOD, retry | ||
593 | * after unlocking native capacity. | ||
594 | */ | ||
595 | if (PTR_ERR(state) == -ENOSPC) { | ||
596 | printk(KERN_WARNING "%s: partition table beyond EOD, ", | ||
597 | disk->disk_name); | ||
598 | if (disk_unlock_native_capacity(disk)) | ||
599 | goto rescan; | ||
600 | } | ||
567 | return -EIO; | 601 | return -EIO; |
602 | } | ||
603 | /* | ||
604 | * If any partition code tried to read beyond EOD, try | ||
605 | * unlocking native capacity even if partition table is | ||
606 | * sucessfully read as we could be missing some partitions. | ||
607 | */ | ||
608 | if (state->access_beyond_eod) { | ||
609 | printk(KERN_WARNING | ||
610 | "%s: partition table partially beyond EOD, ", | ||
611 | disk->disk_name); | ||
612 | if (disk_unlock_native_capacity(disk)) | ||
613 | goto rescan; | ||
614 | } | ||
568 | 615 | ||
569 | /* tell userspace that the media / partition table may have changed */ | 616 | /* tell userspace that the media / partition table may have changed */ |
570 | kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); | 617 | kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); |
@@ -590,25 +637,20 @@ rescan: | |||
590 | from = state->parts[p].from; | 637 | from = state->parts[p].from; |
591 | if (from >= get_capacity(disk)) { | 638 | if (from >= get_capacity(disk)) { |
592 | printk(KERN_WARNING | 639 | printk(KERN_WARNING |
593 | "%s: p%d ignored, start %llu is behind the end of the disk\n", | 640 | "%s: p%d start %llu is beyond EOD, ", |
594 | disk->disk_name, p, (unsigned long long) from); | 641 | disk->disk_name, p, (unsigned long long) from); |
642 | if (disk_unlock_native_capacity(disk)) | ||
643 | goto rescan; | ||
595 | continue; | 644 | continue; |
596 | } | 645 | } |
597 | 646 | ||
598 | if (from + size > get_capacity(disk)) { | 647 | if (from + size > get_capacity(disk)) { |
599 | const struct block_device_operations *bdops = disk->fops; | ||
600 | |||
601 | printk(KERN_WARNING | 648 | printk(KERN_WARNING |
602 | "%s: p%d size %llu exceeds device capacity, ", | 649 | "%s: p%d size %llu extends beyond EOD, ", |
603 | disk->disk_name, p, (unsigned long long) size); | 650 | disk->disk_name, p, (unsigned long long) size); |
604 | 651 | ||
605 | if (bdops->unlock_native_capacity && | 652 | if (disk_unlock_native_capacity(disk)) { |
606 | (disk->flags & GENHD_FL_NATIVE_CAPACITY) == 0) { | ||
607 | printk(KERN_CONT "enabling native capacity\n"); | ||
608 | bdops->unlock_native_capacity(disk); | ||
609 | disk->flags |= GENHD_FL_NATIVE_CAPACITY; | ||
610 | /* free state and restart */ | 653 | /* free state and restart */ |
611 | kfree(state); | ||
612 | goto rescan; | 654 | goto rescan; |
613 | } else { | 655 | } else { |
614 | /* | 656 | /* |
@@ -617,7 +659,6 @@ rescan: | |||
617 | * we limit them to the end of the disk to avoid | 659 | * we limit them to the end of the disk to avoid |
618 | * creating invalid block devices | 660 | * creating invalid block devices |
619 | */ | 661 | */ |
620 | printk(KERN_CONT "limited to end of disk\n"); | ||
621 | size = get_capacity(disk) - from; | 662 | size = get_capacity(disk) - from; |
622 | } | 663 | } |
623 | } | 664 | } |
diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 4b31a97775be..52f8bd399396 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h | |||
@@ -15,11 +15,16 @@ struct parsed_partitions { | |||
15 | } parts[DISK_MAX_PARTS]; | 15 | } parts[DISK_MAX_PARTS]; |
16 | int next; | 16 | int next; |
17 | int limit; | 17 | int limit; |
18 | bool access_beyond_eod; | ||
18 | }; | 19 | }; |
19 | 20 | ||
20 | static inline void *read_part_sector(struct parsed_partitions *state, | 21 | static inline void *read_part_sector(struct parsed_partitions *state, |
21 | sector_t n, Sector *p) | 22 | sector_t n, Sector *p) |
22 | { | 23 | { |
24 | if (n >= get_capacity(state->bdev->bd_disk)) { | ||
25 | state->access_beyond_eod = true; | ||
26 | return NULL; | ||
27 | } | ||
23 | return read_dev_sector(state->bdev, n, p); | 28 | return read_dev_sector(state->bdev, n, p); |
24 | } | 29 | } |
25 | 30 | ||