]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Handle Photon's "/dev/root" mount point when doing disk device mapping
authorOliver Kurth <okurth@vmware.com>
Wed, 4 Mar 2020 20:07:11 +0000 (12:07 -0800)
committerOliver Kurth <okurth@vmware.com>
Wed, 4 Mar 2020 20:07:11 +0000 (12:07 -0800)
Photon EFI boot VMs have the root disc mounted long before the various
PCI controllers are initialized and the attached devices enumerated.
When looking through the /proc/mounts, the root filesystem mounted
at '/' is displayed as if on device /dev/root/  But there is no "root"
device in /dev, nor in the PCI device tree at /sys/class/block.  Later
Photon builds fabricate a block device at "/dev/root" but again nothing
in the /sys/class/block PCI tree.

For this situation, it is necessary to use the pseudo device's major and
minor number to access the PCI device tree through /sys/dev/block.
The major and minor device number can be extracted from the contents of
/proc/self/mountinfo.

19 0 8:2 / / rw,relatime shared:1 - ext4 /dev/root rw

The 3rd field is the major:minor number and the 9th or 10th, depending
on Linux release, is the pseudo device in this case.

open-vm-tools/services/plugins/guestInfo/diskInfo.c

index 032c09285113cf0cfded609a0c2fbc9cf7951912..c203c159984e3af0bec6e9f4710ce22c8617c6fe 100644 (file)
@@ -1,5 +1,5 @@
 /*********************************************************
- * Copyright (C) 2014-2019 VMware, Inc. All rights reserved.
+ * Copyright (C) 2014-2020 VMware, Inc. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published
@@ -22,6 +22,7 @@
  *      Get disk information.
  */
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "vm_assert.h"
@@ -68,7 +69,9 @@
    # define PATH_MAX 1024
 #endif
 
-#define LINUX_SYS_BLOCK_DIR "/sys/class/block"
+#define LINUX_SYS_CLASS_BLOCK_DIR "/sys/class/block"
+#define LINUX_SYS_DEV_BLOCK_DIR "/sys/dev/block"
+#define LINUX_PROC_SELF_MOUNTINFO "/proc/self/mountinfo"
 
 #define PCI_IDE         0x010100
 #define PCI_SATA_AHCI_1 0x010601
@@ -577,11 +580,12 @@ finished:
  * @param[in]     devNum     The number of the DiskDevName to be updated.
  *                           The diskDevNames is numbered  1, 2, ... as opposed
  *                           to array indexes which start at 0.
+ * @return TRUE if the device name for the disk has been determined.
  *
  ******************************************************************************
  */
 
-static void
+static Bool
 GuestInfoLinuxBlockDevice(const char *startPath,
                           PartitionEntryInt *partEntry,
                           int devNum)
@@ -596,6 +600,7 @@ GuestInfoLinuxBlockDevice(const char *startPath,
    char *unit = NULL;
    unsigned int devClass;
    char devName[DISK_DEVICE_NAME_SIZE];
+   Bool devNameSet;
 
    ASSERT(startPath);
    ASSERT(partEntry);
@@ -690,8 +695,10 @@ finished:
    g_clear_error(&gErr);
    g_free(unit);
    free(realPath);
+   devNameSet = devName[0] != '\0';
    g_debug("%s: Filesystem of interest found on device \"%s\"\n",
-          __FUNCTION__, devName[0] == '\0' ? "** unknown **" : devName);
+           __FUNCTION__, devNameSet ? devName : "** unknown **");
+   return devNameSet;
 }
 
 
@@ -736,7 +743,7 @@ GuestInfoIsLinuxLvmDevice(const char *fsName,
    if ((realPath = Posix_RealPath(fsName)) == NULL) {
       return FALSE;
    }
-   Str_Snprintf(slavesPath, PATH_MAX, "%s/%s/slaves", LINUX_SYS_BLOCK_DIR,
+   Str_Snprintf(slavesPath, PATH_MAX, "%s/%s/slaves", LINUX_SYS_CLASS_BLOCK_DIR,
                 strrchr(realPath, '/') + 1);
    free(realPath);
    if (!File_IsDirectory(slavesPath)) {
@@ -778,6 +785,70 @@ GuestInfoIsLinuxLvmDevice(const char *fsName,
    return TRUE;
 }
 
+
+/*
+ ******************************************************************************
+ * GuestInfoCheckDevRoot --
+ *
+ * Disks mounted on /dev/root are setup at boot time before the OS enumerates
+ * the PCI devices in /sys/class/block.  The device for /dev/root does not
+ * appear there.   Instead the PCI device info needs to be accessed through
+ * /sys/dev/block and the major and minor device number.  That can be extracted
+ * from the contents of /proc/self/mountinfo.
+ *
+ * The format of the lines in /proc/self/mountinfo can vary between Linux
+ * versions.
+ *
+ *    Field
+ *    -----
+ *      3     The major and minor number of the device in "m:n" format.
+ *   9 or 10  The fsName for this specific entry.
+ *
+ * @param[in]  fsName       Name of the filesystem device - i.e. /dev/root
+ * @param[in]  pathLen      Maximum size including terminating NUL character
+ *                          for the starting search path.
+ * @param[out] blockDevPath Starting path to begin the search for the "device"
+ *                          file if the major and minor number can be found.
+ * @return TRUE if the device major and minor numbers have been located and
+ *         blockDevPath has been filled in.
+ *
+ ******************************************************************************
+ */
+
+static Bool
+GuestInfoCheckDevRoot(const char *fsName,
+                      int pathLen,
+                      char *blockDevPath)
+{
+   FILE *mountinfo;
+   char buffer[BUFSIZ];
+   char pattern[128];
+
+   if ((mountinfo = fopen(LINUX_PROC_SELF_MOUNTINFO, "r")) == NULL) {
+      g_debug("%s: unable to open \"" LINUX_PROC_SELF_MOUNTINFO
+              "\": (%d) %s\n", __FUNCTION__, errno, strerror(errno));
+      return FALSE;
+   }
+   snprintf(pattern, sizeof pattern, " %s ", fsName);
+   /* Locate the "/dev/root" entry in /proc/self/mountinfo. */
+   while ((fgets(buffer, sizeof buffer, mountinfo)) != NULL) {
+      if (strstr(buffer, pattern) != NULL) {
+         char *savedPtr = NULL;
+         const char *majMinNo;
+
+         (void) strtok_r(buffer, " ", &savedPtr);
+         (void) strtok_r(NULL, " ", &savedPtr);
+         majMinNo = strtok_r(NULL, " ", &savedPtr);
+         Str_Snprintf(blockDevPath, pathLen, "%s/%s", LINUX_SYS_DEV_BLOCK_DIR,
+                      majMinNo);
+         fclose(mountinfo);
+         return TRUE;
+      }
+   }
+   fclose(mountinfo);
+   return FALSE;
+}
+
 #endif /* __linux__ */
 
 /*
@@ -833,10 +904,24 @@ GuestInfoGetDiskDevice(const char *fsName,
        * lookup; avoid at this time.
        */
       if (baseDevName != NULL && strcmp(partEntry->fsType, "zfs") != 0) {
-         Str_Snprintf(blockDevPath, PATH_MAX,  "%s/%s", LINUX_SYS_BLOCK_DIR,
-                      baseDevName + 1);
-         /* First and only disk device. */
-         GuestInfoLinuxBlockDevice(blockDevPath, partEntry, 1);
+         /*
+          * Have a single disk device associated with this mount point.  The
+          * majority of these will be handled by the basic Linux block device
+          * lookup.
+          */
+         Str_Snprintf(blockDevPath, sizeof blockDevPath,  "%s/%s",
+                      LINUX_SYS_CLASS_BLOCK_DIR, baseDevName + 1);
+         if (!GuestInfoLinuxBlockDevice(blockDevPath, partEntry, 1)) {
+            /*
+             * No device name was located.  This may be a pseudo device
+             * such as Photon's /dev/root.  Try a lookup based on the device
+             * major and minor number.
+             */
+            if (GuestInfoCheckDevRoot(fsName, sizeof blockDevPath,
+                                      blockDevPath)) {
+               GuestInfoLinuxBlockDevice(blockDevPath, partEntry, 1);
+            }
+         }
       }
    }