rootdev: Strip partitions less aggressively

In device names like dm-N or loopN, the N is not a partition number and
should not be stripped off.  These can arise for example if run inside a
chroot mounted on a loopback device.

BUG=chromium:748665, chromium:730144
TEST=New unit tests.

Change-Id: If7ecacbfcf00690376cb4ffc75baa45422579085
Reviewed-on: https://chromium-review.googlesource.com/585652
Commit-Ready: Benjamin Gordon <bmgordon@chromium.org>
Tested-by: Benjamin Gordon <bmgordon@chromium.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/rootdev.c b/rootdev.c
index 6340ddd..3cad4f4 100644
--- a/rootdev.c
+++ b/rootdev.c
@@ -218,10 +218,18 @@
   char *part = (char *)rootdev_get_partition(dst, len);
   if (!part)
     return;
+
+  /* For devices matching dm-NN, the digits are not a partition. */
+  if (*(part - 1) == '-')
+    return;
+
   /* For devices that end with a digit, the kernel uses a 'p'
-   * as a separator. E.g., mmcblk1p2. */
-  if (*(part - 1) == 'p')
+   * as a separator. E.g., mmcblk1p2 and loop0p1, but not loop0. */
+  if (*(part - 1) == 'p') {
     part--;
+    if (strncmp(dst, "loop", 4) == 0 && dst + 3 == part)
+      return;
+  }
   *part = '\0';
 }
 
diff --git a/rootdev_test.sh b/rootdev_test.sh
index d236b2b..74d45eb 100755
--- a/rootdev_test.sh
+++ b/rootdev_test.sh
@@ -279,6 +279,72 @@
 }
 run_test t12_sda_strip
 
+# Set up dev nodes and minimal /sys/block entries for a loopback device with a
+# partition on it.
+#
+# Using the same major for the partition doesn't accurately reflect how a real
+# partitioned loopback device would be created, but rootdev doesn't currently
+# support partitions with a different major number than their containing device.
+# This setup can only be used to test that the partition stripping code
+# correctly handles both loop0 and loop0p1.
+h03_setup_loop_tree() {
+  local block=$1
+  local dev=$2
+  mkdir -p $block/loop0/loop0p1
+  mkdir -p $dev
+  echo "7:0" > $block/loop0/dev
+  echo "7:10" > $block/loop0/loop0p1/dev
+  mknod $dev/loop0 b 7 0
+  mknod $dev/loop0p1 b 7 10
+}
+
+# Verify that the pN doesn't get stripped from loopN.
+t13_loop_nostrip_device() {
+  local block=$WORKDIR/sys/block
+  local dev=$WORKDIR/dev
+  h03_setup_loop_tree $block $dev
+  out=$("${ROOTDEV}" -d --dev $dev --block $block --major 7 --minor 0 \
+        2>/dev/null)
+  expect "$? -eq 0" || return 1
+  expect "'$dev/loop0' = '$out'" || return 1
+}
+run_test t13_loop_nostrip_device
+
+# Verify that the pM does get stripped from loopNpM.
+t14_loop_strip_partition() {
+  local block=$WORKDIR/sys/block
+  local dev=$WORKDIR/dev
+  h03_setup_loop_tree $block $dev
+  out=$("${ROOTDEV}" -d --dev $dev --block $block --major 7 --minor 10 \
+        2>/dev/null)
+  expect "$? -eq 0" || return 1
+  expect "'$dev/loop0' = '$out'" || return 1
+}
+run_test t14_loop_strip_partition
+
+# Set up dev nodes and minimal /sys/block entries for a device mapper device.
+h04_setup_dm_tree() {
+  local block=$1
+  local dev=$2
+  mkdir -p $block
+  mkdir -p $dev
+  mkdir -p $block/dm-3
+  echo "252:3" > $block/dm-3/dev
+  mknod $dev/dm-3 b 252 3
+}
+
+# Verify that the -NN doesn't get stripped from dm-NN.
+t15_dm_strip() {
+  local block=$WORKDIR/sys/block
+  local dev=$WORKDIR/dev
+  h04_setup_dm_tree $block $dev
+  out=$("${ROOTDEV}" -d --dev $dev --block $block --major 252 --minor 3 \
+        2>/dev/null)
+  expect "$? -eq 0" || return 1
+  expect "'$dev/dm-3' = '$out'" || return 1
+}
+run_test t15_dm_strip
+
 # TODO(wad) add node creation tests
 
 TEST_COUNT=$((PASS_COUNT + FAIL_COUNT))