Merge branch 'master' into for-next
[pandora-kernel.git] / fs / partitions / check.c
index e238ab2..5dcd4b0 100644 (file)
@@ -45,7 +45,7 @@ extern void md_autodetect_dev(dev_t dev);
 
 int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
 
-static int (*check_part[])(struct parsed_partitions *, struct block_device *) = {
+static int (*check_part[])(struct parsed_partitions *) = {
        /*
         * Probe partition formats with tables at disk address 0
         * that also have an ADFS boot block at 0xdc0.
@@ -161,10 +161,11 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
        struct parsed_partitions *state;
        int i, res, err;
 
-       state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
+       state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
        if (!state)
                return NULL;
 
+       state->bdev = bdev;
        disk_name(hd, 0, state->name);
        printk(KERN_INFO " %s:", state->name);
        if (isdigit(state->name[strlen(state->name)-1]))
@@ -174,7 +175,7 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
        i = res = err = 0;
        while (!res && check_part[i]) {
                memset(&state->parts, 0, sizeof(state->parts));
-               res = check_part[i++](state, bdev);
+               res = check_part[i++](state);
                if (res < 0) {
                        /* We have hit an I/O error which we don't report now.
                        * But record it, and let the others do their job.
@@ -186,6 +187,8 @@ check_partition(struct gendisk *hd, struct block_device *bdev)
        }
        if (res > 0)
                return state;
+       if (state->access_beyond_eod)
+               err = -ENOSPC;
        if (err)
        /* The partition is unrecognized. So report I/O errors if there were any */
                res = err;
@@ -538,12 +541,33 @@ exit:
        disk_part_iter_exit(&piter);
 }
 
+static bool disk_unlock_native_capacity(struct gendisk *disk)
+{
+       const struct block_device_operations *bdops = disk->fops;
+
+       if (bdops->unlock_native_capacity &&
+           !(disk->flags & GENHD_FL_NATIVE_CAPACITY)) {
+               printk(KERN_CONT "enabling native capacity\n");
+               bdops->unlock_native_capacity(disk);
+               disk->flags |= GENHD_FL_NATIVE_CAPACITY;
+               return true;
+       } else {
+               printk(KERN_CONT "truncated\n");
+               return false;
+       }
+}
+
 int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
 {
+       struct parsed_partitions *state = NULL;
        struct disk_part_iter piter;
        struct hd_struct *part;
-       struct parsed_partitions *state;
        int p, highest, res;
+rescan:
+       if (state && !IS_ERR(state)) {
+               kfree(state);
+               state = NULL;
+       }
 
        if (bdev->bd_part_count)
                return -EBUSY;
@@ -562,8 +586,32 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
        bdev->bd_invalidated = 0;
        if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
                return 0;
-       if (IS_ERR(state))      /* I/O error reading the partition table */
+       if (IS_ERR(state)) {
+               /*
+                * I/O error reading the partition table.  If any
+                * partition code tried to read beyond EOD, retry
+                * after unlocking native capacity.
+                */
+               if (PTR_ERR(state) == -ENOSPC) {
+                       printk(KERN_WARNING "%s: partition table beyond EOD, ",
+                              disk->disk_name);
+                       if (disk_unlock_native_capacity(disk))
+                               goto rescan;
+               }
                return -EIO;
+       }
+       /*
+        * If any partition code tried to read beyond EOD, try
+        * unlocking native capacity even if partition table is
+        * sucessfully read as we could be missing some partitions.
+        */
+       if (state->access_beyond_eod) {
+               printk(KERN_WARNING
+                      "%s: partition table partially beyond EOD, ",
+                      disk->disk_name);
+               if (disk_unlock_native_capacity(disk))
+                       goto rescan;
+       }
 
        /* tell userspace that the media / partition table may have changed */
        kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
@@ -581,7 +629,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
        /* add partitions */
        for (p = 1; p < state->limit; p++) {
                sector_t size, from;
-try_scan:
+
                size = state->parts[p].size;
                if (!size)
                        continue;
@@ -589,30 +637,21 @@ try_scan:
                from = state->parts[p].from;
                if (from >= get_capacity(disk)) {
                        printk(KERN_WARNING
-                              "%s: p%d ignored, start %llu is behind the end of the disk\n",
+                              "%s: p%d start %llu is beyond EOD, ",
                               disk->disk_name, p, (unsigned long long) from);
+                       if (disk_unlock_native_capacity(disk))
+                               goto rescan;
                        continue;
                }
 
                if (from + size > get_capacity(disk)) {
-                       const struct block_device_operations *bdops = disk->fops;
-                       unsigned long long capacity;
-
                        printk(KERN_WARNING
-                              "%s: p%d size %llu exceeds device capacity, ",
+                              "%s: p%d size %llu extends beyond EOD, ",
                               disk->disk_name, p, (unsigned long long) size);
 
-                       if (bdops->set_capacity &&
-                           (disk->flags & GENHD_FL_NATIVE_CAPACITY) == 0) {
-                               printk(KERN_CONT "enabling native capacity\n");
-                               capacity = bdops->set_capacity(disk, ~0ULL);
-                               disk->flags |= GENHD_FL_NATIVE_CAPACITY;
-                               if (capacity > get_capacity(disk)) {
-                                       set_capacity(disk, capacity);
-                                       check_disk_size_change(disk, bdev);
-                                       bdev->bd_invalidated = 0;
-                               }
-                               goto try_scan;
+                       if (disk_unlock_native_capacity(disk)) {
+                               /* free state and restart */
+                               goto rescan;
                        } else {
                                /*
                                 * we can not ignore partitions of broken tables
@@ -620,7 +659,6 @@ try_scan:
                                 * we limit them to the end of the disk to avoid
                                 * creating invalid block devices
                                 */
-                               printk(KERN_CONT "limited to end of disk\n");
                                size = get_capacity(disk) - from;
                        }
                }