disk: get_device_and_partition() "auto" partition and cleanup
authorStephen Warren <swarren@nvidia.com>
Fri, 21 Sep 2012 09:50:57 +0000 (09:50 +0000)
committerTom Rini <trini@ti.com>
Tue, 25 Sep 2012 21:58:48 +0000 (14:58 -0700)
Rework get_device_and_partition() to:
a) Implement a new partition ID of "auto", which requests that U-Boot
   search for the first "bootable" partition, and fall back to the first
   valid partition if none is found. This way, users don't need to
   specify an explicit partition in their commands.
b) Make use of get_device().
c) Add parameter to indicate whether returning a whole device is
   acceptable, or whether a partition is mandatory.
d) Make error-checking of the user's device-/partition-specification
   more complete. In particular, if strtoul() doesn't convert all
   characters, it's an error rather than just ignored.

The resultant device/partition returned by the function will be as
follows, based on whether the disk has a partition table (ptable) or not,
and whether the calling command allows the whole device to be returned
or not.

(D and P are integers, P >= 1)

D
D:
  No ptable:
    !allow_whole_dev: error
    allow_whole_dev: device D
  ptable:
    device D partition 1
D:0
  !allow_whole_dev: error
  allow_whole_dev: device D
D:P
  No ptable: error
  ptable: device D partition P
D:auto
  No ptable:
    !allow_whole_dev: error
    allow_whole_dev: device D
  ptable:
    first partition in device D with bootable flag set.
    If none, first valid paratition in device D.

Note: In order to review this patch, it's probably easiest to simply
look at the file contents post-application, rather than reading the
patch itself.

Signed-off-by: Rob Herring <rob.herring@calxeda.com>
[swarren: Rob implemented scanning for bootable partitions. I fixed a
couple of issues there, switched the syntax to ":auto", added the
error-checking rework, and ":0" syntax for the whole device]
Signed-off-by: Stephen Warren <swarren@nvidia.com>
common/cmd_disk.c
common/cmd_ext4.c
common/cmd_ext_common.c
common/cmd_fat.c
common/cmd_reiser.c
common/cmd_zfs.c
disk/part.c
include/part.h

index 8df93444df03119cb59885dab52376cb92f20d9a..ee4e21551b172000b4cdb6c2f191245529163e4e 100644 (file)
@@ -54,7 +54,7 @@ int common_diskboot(cmd_tbl_t *cmdtp, const char *intf, int argc,
        bootstage_mark(BOOTSTAGE_ID_IDE_BOOT_DEVICE);
 
        part = get_device_and_partition(intf, (argc == 3) ? argv[2] : NULL,
-                                       &dev_desc, &info);
+                                       &dev_desc, &info, 1);
        if (part < 0) {
                bootstage_error(BOOTSTAGE_ID_IDE_TYPE);
                return 1;
index 48f9ba314cca44a771ad6c35ed95f9e21e2b5cd5..ca4656184cb494414613088d42a9903e7b9d08da 100644 (file)
@@ -87,7 +87,7 @@ int do_ext4_write(cmd_tbl_t *cmdtp, int flag, int argc,
        if (argc < 6)
                return cmd_usage(cmdtp);
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
index 7d26944ead2c77a71aa45529c91d4c71133c25a2..1952f4d1e14d823c72d662cd009f16233b564dd1 100644 (file)
@@ -108,7 +108,7 @@ int do_ext_load(cmd_tbl_t *cmdtp, int flag, int argc,
                return 1;
        }
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
@@ -166,7 +166,7 @@ int do_ext_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
        if (argc < 2)
                return cmd_usage(cmdtp);
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
index 90412d6233a867446fc21499a37341c2eec12ab4..01e02f5b2bb653caf2115091ae620587e3edf247 100644 (file)
@@ -49,7 +49,7 @@ int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                return 1;
        }
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
@@ -101,7 +101,7 @@ int do_fat_ls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                return 0;
        }
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
@@ -139,7 +139,7 @@ int do_fat_fsinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                return 0;
        }
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
@@ -175,7 +175,7 @@ static int do_fat_fswrite(cmd_tbl_t *cmdtp, int flag,
        if (argc < 5)
                return cmd_usage(cmdtp);
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
index 2865014463d352ab951eb120a82e1f9c2c4af341..e658618c6d0c767c0bb32a81e65b5420d3b91738 100644 (file)
@@ -57,7 +57,7 @@ int do_reiserls (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
        if (argc < 3)
                return CMD_RET_USAGE;
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
@@ -140,7 +140,7 @@ int do_reiserload (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                return 1;
        }
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
index 27f88563d7239c4eab563768b68ac09408ba6546..d580f7bca064f9b14b5bdea54353584252d4a0c4 100644 (file)
@@ -94,7 +94,7 @@ static int do_zfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
                return 1;
        }
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
@@ -160,7 +160,7 @@ static int do_zfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        if (argc == 4)
                filename = argv[3];
 
-       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info);
+       part = get_device_and_partition(argv[1], argv[2], &dev_desc, &info, 1);
        if (part < 0)
                return 1;
 
index ed6bbe29bc878bc0073b32e8bd5a007c106748ff..0dd4b0f0c63a2a0ce118ffc21de383b5fd1f325e 100644 (file)
@@ -24,6 +24,7 @@
 #include <common.h>
 #include <command.h>
 #include <ide.h>
+#include <malloc.h>
 #include <part.h>
 
 #undef PART_DEBUG
@@ -465,66 +466,176 @@ int get_device(const char *ifname, const char *dev_str,
        return dev;
 }
 
-int get_device_and_partition(const char *ifname, const char *dev_str,
+#define PART_UNSPECIFIED -2
+#define PART_AUTO -1
+#define MAX_SEARCH_PARTITIONS 16
+int get_device_and_partition(const char *ifname, const char *dev_part_str,
                             block_dev_desc_t **dev_desc,
-                            disk_partition_t *info)
+                            disk_partition_t *info, int allow_whole_dev)
 {
-       int ret;
-       char *ep;
+       int ret = -1;
+       const char *part_str;
+       char *dup_str = NULL;
+       const char *dev_str;
        int dev;
-       block_dev_desc_t *desc;
-       int part = 0;
-       char *part_str;
-
-       if (dev_str)
-               dev = simple_strtoul(dev_str, &ep, 16);
-
-       if (!dev_str || (dev_str == ep)) {
-               dev_str = getenv("bootdevice");
-               if (dev_str)
-                       dev = simple_strtoul(dev_str, &ep, 16);
-               if (!dev_str || (dev_str == ep))
-                       goto err;
+       char *ep;
+       int p;
+       int part;
+       disk_partition_t tmpinfo;
+
+       /* If no dev_part_str, use bootdevice environment variable */
+       if (!dev_part_str)
+               dev_part_str = getenv("bootdevice");
+
+       /* If still no dev_part_str, it's an error */
+       if (!dev_part_str) {
+               printf("** No device specified **\n");
+               goto cleanup;
+       }
+
+       /* Separate device and partition ID specification */
+       part_str = strchr(dev_part_str, ':');
+       if (part_str) {
+               dup_str = strdup(dev_part_str);
+               dup_str[part_str - dev_part_str] = 0;
+               dev_str = dup_str;
+               part_str++;
+       } else {
+               dev_str = dev_part_str;
        }
 
-       desc = get_dev(ifname, dev);
-       if (!desc || (desc->type == DEV_TYPE_UNKNOWN))
-               goto err;
+       /* Look up the device */
+       dev = get_device(ifname, dev_str, dev_desc);
+       if (dev < 0)
+               goto cleanup;
+
+       /* Convert partition ID string to number */
+       if (!part_str || !*part_str) {
+               part = PART_UNSPECIFIED;
+       } else if (!strcmp(part_str, "auto")) {
+               part = PART_AUTO;
+       } else {
+               /* Something specified -> use exactly that */
+               part = (int)simple_strtoul(part_str, &ep, 16);
+               /*
+                * Less than whole string converted,
+                * or request for whole device, but caller requires partition.
+                */
+               if (*ep || (part == 0 && !allow_whole_dev)) {
+                       printf("** Bad partition specification %s %s **\n",
+                           ifname, dev_part_str);
+                       goto cleanup;
+               }
+       }
+
+       /*
+        * No partition table on device,
+        * or user requested partition 0 (entire device).
+        */
+       if (((*dev_desc)->part_type == PART_TYPE_UNKNOWN) ||
+           (part == 0)) {
+               if (!(*dev_desc)->lba) {
+                       printf("** Bad device size - %s %s **\n", ifname,
+                              dev_str);
+                       goto cleanup;
+               }
 
-       if (desc->part_type == PART_TYPE_UNKNOWN) {
-               /* disk doesn't use partition table */
-               if (!desc->lba) {
-                       printf("**Bad disk size - %s %d:0 **\n", ifname, dev);
-                       return -1;
+               /*
+                * If user specified a partition ID other than 0,
+                * or the calling command only accepts partitions,
+                * it's an error.
+                */
+               if ((part > 0) || (!allow_whole_dev)) {
+                       printf("** No partition table - %s %s **\n", ifname,
+                              dev_str);
+                       goto cleanup;
                }
+
                info->start = 0;
-               info->size = desc->lba;
-               info->blksz = desc->blksz;
+               info->size = (*dev_desc)->lba;
+               info->blksz = (*dev_desc)->blksz;
+               info->bootable = 0;
+#ifdef CONFIG_PARTITION_UUIDS
+               info->uuid[0] = 0;
+#endif
 
-               *dev_desc = desc;
-               return 0;
+               ret = 0;
+               goto cleanup;
        }
 
-       part_str = strchr(dev_str, ':');
-       if (part_str)
-               part = (int)simple_strtoul(++part_str, NULL, 16);
-
-       ret = get_partition_info(desc, part, info);
-       if (ret) {
-               printf("** Invalid partition %d, use `dev[:part]' **\n", part);
-               return -1;
+       /*
+        * Now there's known to be a partition table,
+        * not specifying a partition means to pick partition 1.
+        */
+       if (part == PART_UNSPECIFIED)
+               part = 1;
+
+       /*
+        * If user didn't specify a partition number, or did specify something
+        * other than "auto", use that partition number directly.
+        */
+       if (part != PART_AUTO) {
+               ret = get_partition_info(*dev_desc, part, info);
+               if (ret) {
+                       printf("** Invalid partition %d **\n", part);
+                       goto cleanup;
+               }
+       } else {
+               /*
+                * Find the first bootable partition.
+                * If none are bootable, fall back to the first valid partition.
+                */
+               part = 0;
+               for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
+                       ret = get_partition_info(*dev_desc, p, info);
+                       if (ret)
+                               continue;
+
+                       /*
+                        * First valid partition, or new better partition?
+                        * If so, save partition ID.
+                        */
+                       if (!part || info->bootable)
+                               part = p;
+
+                       /* Best possible partition? Stop searching. */
+                       if (info->bootable)
+                               break;
+
+                       /*
+                        * We now need to search further for best possible.
+                        * If we what we just queried was the best so far,
+                        * save the info since we over-write it next loop.
+                        */
+                       if (part == p)
+                               tmpinfo = *info;
+               }
+               /* If we found any acceptable partition */
+               if (part) {
+                       /*
+                        * If we searched all possible partition IDs,
+                        * return the first valid partition we found.
+                        */
+                       if (p == MAX_SEARCH_PARTITIONS + 1)
+                               *info = tmpinfo;
+                       ret = 0;
+               } else {
+                       printf("** No valid partitions found **\n");
+                       goto cleanup;
+               }
        }
        if (strncmp((char *)info->type, BOOT_PART_TYPE, sizeof(info->type)) != 0) {
                printf("** Invalid partition type \"%.32s\""
                        " (expect \"" BOOT_PART_TYPE "\")\n",
                        info->type);
-               return -1;
+               ret  = -1;
+               goto cleanup;
        }
 
-       *dev_desc = desc;
-       return part;
+       ret = part;
+       goto cleanup;
 
-err:
-       puts("** Invalid boot device, use `dev[:part]' **\n");
-       return -1;
+cleanup:
+       free(dup_str);
+       return ret;
 }
index 144df4e70be58952bc73ece3487d7c3b718e1391..3f780a1f92537e7ac003d746dffc0726ab25f23e 100644 (file)
@@ -114,9 +114,9 @@ void  init_part (block_dev_desc_t *dev_desc);
 void dev_print(block_dev_desc_t *dev_desc);
 int get_device(const char *ifname, const char *dev_str,
               block_dev_desc_t **dev_desc);
-int get_device_and_partition(const char *ifname, const char *dev_str,
+int get_device_and_partition(const char *ifname, const char *dev_part_str,
                             block_dev_desc_t **dev_desc,
-                            disk_partition_t *info);
+                            disk_partition_t *info, int allow_whole_dev);
 #else
 static inline block_dev_desc_t *get_dev(const char *ifname, int dev)
 { return NULL; }
@@ -137,9 +137,10 @@ static inline int get_device(const char *ifname, const char *dev_str,
               block_dev_desc_t **dev_desc)
 { return -1; }
 static inline int get_device_and_partition(const char *ifname,
-                                          const char *dev_str,
+                                          const char *dev_part_str,
                                           block_dev_desc_t **dev_desc,
-                                          disk_partition_t *info)
+                                          disk_partition_t *info,
+                                          int allow_whole_dev)
 { *dev_desc = NULL; return -1; }
 #endif