Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

stat: add %m to output the mount point for a file

* src/find-mount-point.c: A new file refactoring
find_mount_point() out from df.c
* src/find-mount-point.h: Likewise.
* src/df.c: Use the new find-mount-point module.
* src/stat.c (print_stat): Handle the new %m format.
(find_bind_mount): A new function to
return the bind mount for a file if any.
(out_mount_mount): Print the bind mount for a file, or else
the standard mount point given by the find-mount-point module.
(usage): Document the %m format directive.
* src/Makefile.am: Reference the refactored find-mount-point.c
* po/POTFILES.in: Add find_mount_point.c to the translation list
* doc/coreutils.texi (stat invocation): Document %m,
and how it may differ from the mount point that df outputs.
* test/misc/stat-mount: A new test to correlate mount points
output from df and stat.
* tests/Makefile.am: Reference the new test.
* NEWS: Mention the new feature
* THANKS: Add the author

Signed-off-by: Pádraig Brady <P@draigBrady.com>
  • Loading branch information...
commit ddf6fb8686b1ed6a26d5f1c76a642d0fb5fe7ba7 1 parent 872f6bb
Aaron Burgemeister authored pixelb committed
3  NEWS
@@ -24,6 +24,9 @@ GNU coreutils NEWS                                    -*- outline -*-
24 24
 
25 25
   sort now supports -d, -f, -i, -R, and -V in any combination.
26 26
 
  27
+  stat now accepts the %m format directive to output
  28
+  the mount point for a file.
  29
+
27 30
 ** Changes in behavior
28 31
 
29 32
   df now consistently prints the device name for a bind mounted file,
1  THANKS
@@ -8,6 +8,7 @@ the bug-report mailing list (as seen on last line of e.g., cp --help).
8 8
 
9 9
 ???                                 kytek@cybercomm.net
10 10
 A Costa                             agcosta@gis.net
  11
+Aaron Burgemeister                  dajoker@gmail.com
11 12
 Aaron Hawley                        ashawley@uvm.edu
12 13
 Achim Blumensath                    blume@corona.oche.de
13 14
 Adam Jimerson                       vendion@charter.net
18  doc/coreutils.texi
@@ -8487,6 +8487,7 @@ removal is requested.  Equivalent to @option{-I}.
8487 8487
 When removing a hierarchy recursively, skip any directory that is on a
8488 8488
 file system different from that of the corresponding command line argument.
8489 8489
 
  8490
+@cindex bind mount
8490 8491
 This option is useful when removing a build ``chroot'' hierarchy,
8491 8492
 which normally contains no valuable data.  However, it is not uncommon
8492 8493
 to bind-mount @file{/home} into such a hierarchy, to make it easier to
@@ -10685,6 +10686,7 @@ The valid @var{format} directives for files with @option{--format} and
10685 10686
 @item %G - Group name of owner
10686 10687
 @item %h - Number of hard links
10687 10688
 @item %i - Inode number
  10689
+@item %m - Mount point (See note below)
10688 10690
 @item %n - File name
10689 10691
 @item %N - Quoted file name with dereference if symbolic link
10690 10692
 @item %o - I/O block size
@@ -10701,6 +10703,22 @@ The valid @var{format} directives for files with @option{--format} and
10701 10703
 @item %Z - Time of last change as seconds since Epoch
10702 10704
 @end itemize
10703 10705
 
  10706
+The mount point printed by @samp{%m} is similar to that output
  10707
+by @command{df}, except that:
  10708
+@itemize @bullet
  10709
+@item
  10710
+stat does not dereference symlinks by default
  10711
+(unless @option{-L} is specified)
  10712
+@item
  10713
+stat does not search for specified device nodes in the
  10714
+file system list, instead operating on them directly
  10715
+@item
  10716
+@cindex bind mount
  10717
+stat outputs the alias for a bind mounted file,
  10718
+rather than its backing device.  One can recursively call stat
  10719
+until there is no change in output, to get the base mount point
  10720
+@end itemize
  10721
+
10704 10722
 When listing file system information (@option{--file-system} (@option{-f})),
10705 10723
 you must use a different set of @var{format} directives:
10706 10724
 
1  po/POTFILES.in
@@ -61,6 +61,7 @@ src/expand.c
61 61
 src/expr.c
62 62
 src/factor.c
63 63
 src/false.c
  64
+src/find-mount-point.c
64 65
 src/fmt.c
65 66
 src/fold.c
66 67
 src/getlimits.c
4  src/Makefile.am
@@ -145,6 +145,7 @@ noinst_HEADERS =	\
145 145
   copy.h		\
146 146
   cp-hash.h		\
147 147
   dircolors.h		\
  148
+  find-mount-point.h	\
148 149
   fs.h			\
149 150
   group-list.h		\
150 151
   ls.h			\
@@ -478,6 +479,9 @@ rm_SOURCES = rm.c remove.c
478 479
 mkdir_SOURCES = mkdir.c prog-fprintf.c
479 480
 rmdir_SOURCES = rmdir.c prog-fprintf.c
480 481
 
  482
+df_SOURCES = df.c find-mount-point.c
  483
+stat_SOURCES = stat.c find-mount-point.c
  484
+
481 485
 uname_SOURCES = uname.c uname-uname.c
482 486
 arch_SOURCES = uname.c uname-arch.c
483 487
 nproc_SOURCES = nproc.c
91  src/df.c
@@ -29,8 +29,7 @@
29 29
 #include "human.h"
30 30
 #include "mountlist.h"
31 31
 #include "quote.h"
32  
-#include "save-cwd.h"
33  
-#include "xgetcwd.h"
  32
+#include "find-mount-point.h"
34 33
 
35 34
 /* The official name of this program (e.g., no `g' prefix).  */
36 35
 #define PROGRAM_NAME "df"
@@ -522,94 +521,6 @@ show_dev (char const *disk, char const *mount_point,
522 521
   putchar ('\n');
523 522
 }
524 523
 
525  
-/* Return the root mountpoint of the file system on which FILE exists, in
526  
-   malloced storage.  FILE_STAT should be the result of stating FILE.
527  
-   Give a diagnostic and return NULL if unable to determine the mount point.
528  
-   Exit if unable to restore current working directory.  */
529  
-static char *
530  
-find_mount_point (const char *file, const struct stat *file_stat)
531  
-{
532  
-  struct saved_cwd cwd;
533  
-  struct stat last_stat;
534  
-  char *mp = NULL;		/* The malloced mount point.  */
535  
-
536  
-  if (save_cwd (&cwd) != 0)
537  
-    {
538  
-      error (0, errno, _("cannot get current directory"));
539  
-      return NULL;
540  
-    }
541  
-
542  
-  if (S_ISDIR (file_stat->st_mode))
543  
-    /* FILE is a directory, so just chdir there directly.  */
544  
-    {
545  
-      last_stat = *file_stat;
546  
-      if (chdir (file) < 0)
547  
-        {
548  
-          error (0, errno, _("cannot change to directory %s"), quote (file));
549  
-          return NULL;
550  
-        }
551  
-    }
552  
-  else
553  
-    /* FILE is some other kind of file; use its directory.  */
554  
-    {
555  
-      char *xdir = dir_name (file);
556  
-      char *dir;
557  
-      ASSIGN_STRDUPA (dir, xdir);
558  
-      free (xdir);
559  
-
560  
-      if (chdir (dir) < 0)
561  
-        {
562  
-          error (0, errno, _("cannot change to directory %s"), quote (dir));
563  
-          return NULL;
564  
-        }
565  
-
566  
-      if (stat (".", &last_stat) < 0)
567  
-        {
568  
-          error (0, errno, _("cannot stat current directory (now %s)"),
569  
-                 quote (dir));
570  
-          goto done;
571  
-        }
572  
-    }
573  
-
574  
-  /* Now walk up FILE's parents until we find another file system or /,
575  
-     chdiring as we go.  LAST_STAT holds stat information for the last place
576  
-     we visited.  */
577  
-  while (true)
578  
-    {
579  
-      struct stat st;
580  
-      if (stat ("..", &st) < 0)
581  
-        {
582  
-          error (0, errno, _("cannot stat %s"), quote (".."));
583  
-          goto done;
584  
-        }
585  
-      if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
586  
-        /* cwd is the mount point.  */
587  
-        break;
588  
-      if (chdir ("..") < 0)
589  
-        {
590  
-          error (0, errno, _("cannot change to directory %s"), quote (".."));
591  
-          goto done;
592  
-        }
593  
-      last_stat = st;
594  
-    }
595  
-
596  
-  /* Finally reached a mount point, see what it's called.  */
597  
-  mp = xgetcwd ();
598  
-
599  
-done:
600  
-  /* Restore the original cwd.  */
601  
-  {
602  
-    int save_errno = errno;
603  
-    if (restore_cwd (&cwd) != 0)
604  
-      error (EXIT_FAILURE, errno,
605  
-             _("failed to return to initial working directory"));
606  
-    free_cwd (&cwd);
607  
-    errno = save_errno;
608  
-  }
609  
-
610  
-  return mp;
611  
-}
612  
-
613 524
 /* If DISK corresponds to a mount point, show its usage
614 525
    and return true.  Otherwise, return false.  */
615 526
 static bool
113  src/find-mount-point.c
... ...
@@ -0,0 +1,113 @@
  1
+/* find-mount-point.c -- find the root mount point for a file.
  2
+   Copyright (C) 2010 Free Software Foundation, Inc.
  3
+
  4
+   This program is free software: you can redistribute it and/or modify
  5
+   it under the terms of the GNU General Public License as published by
  6
+   the Free Software Foundation, either version 3 of the License, or
  7
+   (at your option) any later version.
  8
+
  9
+   This program is distributed in the hope that it will be useful,
  10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12
+   GNU General Public License for more details.
  13
+
  14
+   You should have received a copy of the GNU General Public License
  15
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
  16
+
  17
+#include <config.h>
  18
+#include <sys/types.h>
  19
+
  20
+#include "system.h"
  21
+#include "error.h"
  22
+#include "quote.h"
  23
+#include "save-cwd.h"
  24
+#include "xgetcwd.h"
  25
+#include "find-mount-point.h"
  26
+
  27
+/* Return the root mountpoint of the file system on which FILE exists, in
  28
+   malloced storage.  FILE_STAT should be the result of stating FILE.
  29
+   Give a diagnostic and return NULL if unable to determine the mount point.
  30
+   Exit if unable to restore current working directory.  */
  31
+extern char *
  32
+find_mount_point (const char *file, const struct stat *file_stat)
  33
+{
  34
+  struct saved_cwd cwd;
  35
+  struct stat last_stat;
  36
+  char *mp = NULL;		/* The malloced mount point.  */
  37
+
  38
+  if (save_cwd (&cwd) != 0)
  39
+    {
  40
+      error (0, errno, _("cannot get current directory"));
  41
+      return NULL;
  42
+    }
  43
+
  44
+  if (S_ISDIR (file_stat->st_mode))
  45
+    /* FILE is a directory, so just chdir there directly.  */
  46
+    {
  47
+      last_stat = *file_stat;
  48
+      if (chdir (file) < 0)
  49
+        {
  50
+          error (0, errno, _("cannot change to directory %s"), quote (file));
  51
+          return NULL;
  52
+        }
  53
+    }
  54
+  else
  55
+    /* FILE is some other kind of file; use its directory.  */
  56
+    {
  57
+      char *xdir = dir_name (file);
  58
+      char *dir;
  59
+      ASSIGN_STRDUPA (dir, xdir);
  60
+      free (xdir);
  61
+
  62
+      if (chdir (dir) < 0)
  63
+        {
  64
+          error (0, errno, _("cannot change to directory %s"), quote (dir));
  65
+          return NULL;
  66
+        }
  67
+
  68
+      if (stat (".", &last_stat) < 0)
  69
+        {
  70
+          error (0, errno, _("cannot stat current directory (now %s)"),
  71
+                 quote (dir));
  72
+          goto done;
  73
+        }
  74
+    }
  75
+
  76
+  /* Now walk up FILE's parents until we find another file system or /,
  77
+     chdiring as we go.  LAST_STAT holds stat information for the last place
  78
+     we visited.  */
  79
+  while (true)
  80
+    {
  81
+      struct stat st;
  82
+      if (stat ("..", &st) < 0)
  83
+        {
  84
+          error (0, errno, _("cannot stat %s"), quote (".."));
  85
+          goto done;
  86
+        }
  87
+      if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
  88
+        /* cwd is the mount point.  */
  89
+        break;
  90
+      if (chdir ("..") < 0)
  91
+        {
  92
+          error (0, errno, _("cannot change to directory %s"), quote (".."));
  93
+          goto done;
  94
+        }
  95
+      last_stat = st;
  96
+    }
  97
+
  98
+  /* Finally reached a mount point, see what it's called.  */
  99
+  mp = xgetcwd ();
  100
+
  101
+done:
  102
+  /* Restore the original cwd.  */
  103
+  {
  104
+    int save_errno = errno;
  105
+    if (restore_cwd (&cwd) != 0)
  106
+      error (EXIT_FAILURE, errno,
  107
+             _("failed to return to initial working directory"));
  108
+    free_cwd (&cwd);
  109
+    errno = save_errno;
  110
+  }
  111
+
  112
+  return mp;
  113
+}
17  src/find-mount-point.h
... ...
@@ -0,0 +1,17 @@
  1
+/* find-mount-point.h -- find the root mount point for a file.
  2
+   Copyright (C) 2010 Free Software Foundation, Inc.
  3
+
  4
+   This program is free software: you can redistribute it and/or modify
  5
+   it under the terms of the GNU General Public License as published by
  6
+   the Free Software Foundation, either version 3 of the License, or
  7
+   (at your option) any later version.
  8
+
  9
+   This program is distributed in the hope that it will be useful,
  10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12
+   GNU General Public License for more details.
  13
+
  14
+   You should have received a copy of the GNU General Public License
  15
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
  16
+
  17
+extern char* find_mount_point (const char *, const struct stat *);
94  src/stat.c
@@ -64,10 +64,12 @@
64 64
 #include "filemode.h"
65 65
 #include "fs.h"
66 66
 #include "getopt.h"
  67
+#include "mountlist.h"
67 68
 #include "quote.h"
68 69
 #include "quotearg.h"
69 70
 #include "stat-time.h"
70 71
 #include "strftime.h"
  72
+#include "find-mount-point.h"
71 73
 
72 74
 #if USE_STATVFS
73 75
 # define STRUCT_STATVFS struct statvfs
@@ -604,6 +606,94 @@ print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
604 606
   return fail;
605 607
 }
606 608
 
  609
+/* Return any bind mounted source for a path.
  610
+   The caller should not free the returned buffer.
  611
+   Return NULL if no bind mount found.  */
  612
+static char const * ATTRIBUTE_WARN_UNUSED_RESULT
  613
+find_bind_mount (char const * name)
  614
+{
  615
+  char const * bind_mount = NULL;
  616
+
  617
+  static struct mount_entry *mount_list;
  618
+  static bool tried_mount_list = false;
  619
+  if (!tried_mount_list) /* attempt/warn once per process.  */
  620
+    {
  621
+      if (!(mount_list = read_file_system_list (false)))
  622
+        error (0, errno, "%s", _("cannot read table of mounted file systems"));
  623
+      tried_mount_list = true;
  624
+    }
  625
+
  626
+  struct mount_entry *me;
  627
+  for (me = mount_list; me; me = me->me_next)
  628
+    {
  629
+      if (me->me_dummy && me->me_devname[0] == '/'
  630
+          && STREQ (me->me_mountdir, name))
  631
+        {
  632
+          struct stat name_stats;
  633
+          struct stat dev_stats;
  634
+
  635
+          if (stat (name, &name_stats) == 0
  636
+              && stat (me->me_devname, &dev_stats) == 0
  637
+              && SAME_INODE (name_stats, dev_stats))
  638
+            {
  639
+              bind_mount = me->me_devname;
  640
+              break;
  641
+            }
  642
+        }
  643
+    }
  644
+
  645
+  return bind_mount;
  646
+}
  647
+
  648
+/* Print mount point.  Return zero upon success, nonzero upon failure.  */
  649
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
  650
+out_mount_point (char const *filename, char *pformat, size_t prefix_len,
  651
+                 const struct stat *statp)
  652
+{
  653
+
  654
+  char const *np = "?", *bp = NULL;
  655
+  char *mp = NULL;
  656
+  bool fail = true;
  657
+
  658
+  /* Look for bind mounts first.  Note we output the immediate alias,
  659
+     rather than further resolving to a base device mount point.  */
  660
+  if (follow_links || !S_ISLNK (statp->st_mode))
  661
+    {
  662
+      char *resolved = canonicalize_file_name (filename);
  663
+      if (!resolved)
  664
+        {
  665
+          error (0, errno, _("failed to canonicalize %s"), quote (filename));
  666
+          goto print_mount_point;
  667
+        }
  668
+      bp = find_bind_mount (resolved);
  669
+      free (resolved);
  670
+      if (bp)
  671
+        {
  672
+          fail = false;
  673
+          goto print_mount_point;
  674
+        }
  675
+    }
  676
+
  677
+  /* If there is no direct bind mount, then navigate
  678
+     back up the tree looking for a device change.
  679
+     Note we don't detect if any of the directory components
  680
+     are bind mounted to the same device, but that's OK
  681
+     since we've not directly queried them.  */
  682
+  if ((mp = find_mount_point (filename, statp)))
  683
+    {
  684
+      /* This dir might be bind mounted to another device,
  685
+         so we resolve the bound source in that case also.  */
  686
+      bp = find_bind_mount (mp);
  687
+      fail = false;
  688
+    }
  689
+
  690
+print_mount_point:
  691
+
  692
+  out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
  693
+  free (mp);
  694
+  return fail;
  695
+}
  696
+
607 697
 /* Print stat info.  Return zero upon success, nonzero upon failure.  */
608 698
 static bool
609 699
 print_stat (char *pformat, size_t prefix_len, char m,
@@ -680,6 +770,9 @@ print_stat (char *pformat, size_t prefix_len, char m,
680 770
     case 't':
681 771
       out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
682 772
       break;
  773
+    case 'm':
  774
+      fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
  775
+      break;
683 776
     case 'T':
684 777
       out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
685 778
       break;
@@ -1026,6 +1119,7 @@ The valid format sequences for files (without --file-system):\n\
1026 1119
       fputs (_("\
1027 1120
   %h   Number of hard links\n\
1028 1121
   %i   Inode number\n\
  1122
+  %m   Mount point\n\
1029 1123
   %n   File name\n\
1030 1124
   %N   Quoted file name with dereference if symbolic link\n\
1031 1125
   %o   I/O block size\n\
1  tests/Makefile.am
@@ -241,6 +241,7 @@ TESTS =						\
241 241
   misc/split-l					\
242 242
   misc/stat-fmt					\
243 243
   misc/stat-hyphen				\
  244
+  misc/stat-mount				\
244 245
   misc/stat-printf				\
245 246
   misc/stat-slash				\
246 247
   misc/stdbuf					\
31  tests/misc/stat-mount
... ...
@@ -0,0 +1,31 @@
  1
+#!/bin/sh
  2
+# Test stat -c%m
  3
+
  4
+# Copyright (C) 2010 Free Software Foundation, Inc.
  5
+
  6
+# This program is free software: you can redistribute it and/or modify
  7
+# it under the terms of the GNU General Public License as published by
  8
+# the Free Software Foundation, either version 3 of the License, or
  9
+# (at your option) any later version.
  10
+
  11
+# This program is distributed in the hope that it will be useful,
  12
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
  13
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14
+# GNU General Public License for more details.
  15
+
  16
+# You should have received a copy of the GNU General Public License
  17
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18
+
  19
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
  20
+
  21
+# Note we assume that the current directory is not bind mounted
  22
+# (as then, stat and df may have different results).
  23
+# This should be the case given the directory is temporary
  24
+# for the duration of the test.
  25
+
  26
+df_mnt=$(df -P . | sed -n '2s/.* \([^ ]*$\)/\1/p')
  27
+stat_mnt=$(stat -c%m .)
  28
+
  29
+test "$df_mnt" = "$stat_mnt" || fail=1
  30
+
  31
+Exit $fail

0 notes on commit ddf6fb8

Please sign in to comment.
Something went wrong with that request. Please try again.