>From 840e7afb6d7d499d52aaac0f9d34cb90208b2183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Thu, 10 Jul 2014 23:59:35 +0100 Subject: [PATCH] df: give precedence to real device nodes This is significant when /etc/mtab is a real file rather than a symlink to /proc/mounts, in which case the bind mounted dir will be listed as the source "device" rather than the real base device. Therefore we stat the device node, which should be local and a relatively cheap operation, and give precedence to real device nodes. For example given this setup: truncate -s10M bind.img mkfs -F bind.img mount bind.img /var/archive/ mkdir -p /var/archive/home mkdir -p /t/home mount --bind /var/archive/home/ /t/home/ on an older system without /proc/mounts we would have displayed: Filesystem Mounted on /var/archive/home/ /t/home but we now display: Filesystem Mounted on /dev/loop0 /var/archive Note on a newer system using /proc/mounts we display: Filesystem Mounted on /dev/loop0 /t/home * src/df.c (filter_mount_list): stat() the device node for each mount entry and use that to give precedence to real device nodes. * NEWS: Mention the bug fix. Fixes http://bugs.gnu.org/17833 --- NEWS | 5 +++++ src/df.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 4e90b79..e942fe8 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,11 @@ GNU coreutils NEWS -*- outline -*- than error messages or values for the wrong file system. [These bugs were present in "the beginning".] + df now give precedence to displaying real device nodes in the presence of + bind mounts, on systems where the base device is not listed for all entries + in the mount list. + [bug introduced in coreutils-8.21] + du now silently ignores directory cycles introduced with bind mounts. Previously it would issue a warning and exit with a failure status. [bug introduced in coreutils-8.1] diff --git a/src/df.c b/src/df.c index 063cabf..46aeef9 100644 --- a/src/df.c +++ b/src/df.c @@ -48,6 +48,7 @@ static struct devlist { dev_t dev_num; + bool real_dev; struct mount_entry *me; struct devlist *next; } *device_list; @@ -618,35 +619,46 @@ filter_mount_list (bool devices_only) /* Sort all 'wanted' entries into the list device_list. */ for (me = mount_list; me;) { - struct stat buf; + struct stat mnt_buf; struct devlist *devlist; struct mount_entry *discard_me = NULL; + bool real_dev = false; /* TODO: On Linux we might avoid this stat() and another in get_dev() by using the device IDs available from /proc/self/mountinfo. read_file_system_list() could populate me_dev from those for efficiency and accuracy. */ - if (-1 == stat (me->me_mountdir, &buf)) + if (-1 == stat (me->me_mountdir, &mnt_buf)) { /* Stat failed - add ME to be able to complain about it later. */ - buf.st_dev = me->me_dev; + mnt_buf.st_dev = me->me_dev; } else { + /* when /etc/mtab is not linked to /proc/mounts, + then in the presence of bind mounts, the source will + be the bind mounted directory, rather than the base device. + In this case we want to give precedence to the base device. */ + struct stat sb; + real_dev = (! me->me_dummy + && stat (me->me_devname, &sb) == 0 + && (S_ISBLK (sb.st_mode) || S_ISCHR (sb.st_mode))); + /* If we've already seen this device... */ for (devlist = device_list; devlist; devlist = devlist->next) - if (devlist->dev_num == buf.st_dev) + if (devlist->dev_num == mnt_buf.st_dev) break; if (devlist) { /* ...let the shorter mountdir win. */ - if ((strchr (me->me_devname, '/') - && ! strchr (devlist->me->me_devname, '/')) - || (strlen (devlist->me->me_mountdir) - > strlen (me->me_mountdir)) - /* or one overmounted on a different device. */ - || ! STREQ (devlist->me->me_devname, me->me_devname)) + if (real_dev >= devlist->real_dev + && ((strchr (me->me_devname, '/') + && ! strchr (devlist->me->me_devname, '/')) + || (strlen (devlist->me->me_mountdir) + > strlen (me->me_mountdir)) + /* or one overmounted on a different device. */ + || ! STREQ (devlist->me->me_devname, me->me_devname))) { /* Discard mount entry for existing device. */ discard_me = devlist->me; @@ -672,7 +684,8 @@ filter_mount_list (bool devices_only) /* Add the device number to the global list devlist. */ devlist = xmalloc (sizeof *devlist); devlist->me = me; - devlist->dev_num = buf.st_dev; + devlist->dev_num = mnt_buf.st_dev; + devlist->real_dev = real_dev; devlist->next = device_list; device_list = devlist; -- 1.7.7.6