When starting a Fedora virtual machine using QEMU, if the device corresponding to the root directory is the entire disk or a disk partition, the device recorded in /proc/self/mounts will be /dev/root instead of the true device. This can lead to the failure of executing commands such as xfs_growfs/xfs_info. $ cat /proc/self/mounts /dev/root / xfs rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0 devtmpfs /dev devtmpfs rw,seclabel,relatime,size=4065432k,nr_inodes=1016358,mode=755 0 0 ... $ mount /dev/sda3 on / type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) devtmpfs on /dev type devtmpfs (rw,relatime,seclabel,size=4065432k,nr_inodes=1016358,mode=755) ... $ xfs_growfs / xfs_growfs: / is not a mounted XFS filesystem $ xfs_growfs /dev/sda3 xfs_growfs: /dev/sda3 is not a mounted XFS filesystem $ xfs_info / /: cannot find mount point.# So, if the root device is found to be /dev/root, we need to obtain the corresponding real device first. Signed-off-by: Wu Guanghao <wuguanghao3@xxxxxxxxxx> --- libfrog/paths.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/libfrog/paths.c b/libfrog/paths.c index a5dfab48..6ff649b8 100644 --- a/libfrog/paths.c +++ b/libfrog/paths.c @@ -352,6 +352,38 @@ out_nomem: return ENOMEM; } +static char *get_real_root_device(struct mntent *mnt) +{ + struct stat st; + char path[PATH_MAX], linkpath[PATH_MAX]; + ssize_t len; + char *fake_root_device = "/dev/root"; + /* + * The /dev/root does not point to a real root device, we need to obtain the + * real device. + */ + if (strlen(mnt->mnt_fsname) != strlen(fake_root_device) || + strcmp(mnt->mnt_fsname, fake_root_device)) + return NULL; + + if (stat(mnt->mnt_dir, &st) < 0) + return NULL; + + snprintf(path, sizeof(path), "/sys/dev/block/%d:%d", major(st.st_dev), minor(st.st_dev)); + len = readlink(path, linkpath, sizeof(linkpath) - 1); + if (len < 0 || len >= PATH_MAX) + return NULL; + + linkpath[len] = '\0'; + + char *p = strrchr(linkpath, '/'); + if (!p || strlen(p) <= 1) + return NULL; + + snprintf(path, sizeof(path), "/dev/%s", p + 1); + return strdup(path); +} + /* * If *path is NULL, initialize the fs table with all xfs mount points in mtab * If *path is specified, search for that path in mtab @@ -359,6 +391,7 @@ out_nomem: * Everything - path, devices, and mountpoints - are boiled down to realpath() * for comparison, but fs_table is populated with what comes from getmntent. */ + static int fs_table_initialise_mounts( char *path) @@ -368,6 +401,8 @@ fs_table_initialise_mounts( char *fslog, *fsrt; int error, found; char rpath[PATH_MAX], rmnt_fsname[PATH_MAX], rmnt_dir[PATH_MAX]; + bool change_device = false; + char *fsname = NULL; error = found = 0; fslog = fsrt = NULL; @@ -391,6 +426,13 @@ fs_table_initialise_mounts( continue; if (!realpath(mnt->mnt_dir, rmnt_dir)) continue; + + fsname = get_real_root_device(mnt); + if (fsname) { + change_device = true; + mnt->mnt_fsname = fsname; + } + if (!realpath(mnt->mnt_fsname, rmnt_fsname)) continue; @@ -402,6 +444,12 @@ fs_table_initialise_mounts( continue; (void) fs_table_insert(mnt->mnt_dir, 0, FS_MOUNT_POINT, mnt->mnt_fsname, fslog, fsrt); + + if (change_device) { + free(fsname); + change_device = false; + } + if (path) { found = 1; break; -- 2.51.0