summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--THANKS1
-rw-r--r--doc/coreutils.texi18
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/Makefile.am4
-rw-r--r--src/df.c91
-rw-r--r--src/find-mount-point.c113
-rw-r--r--src/find-mount-point.h17
-rw-r--r--src/stat.c94
-rw-r--r--tests/Makefile.am1
-rwxr-xr-xtests/misc/stat-mount31
11 files changed, 284 insertions, 90 deletions
diff --git a/NEWS b/NEWS
index 85f55a261..93a1f96b4 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,9 @@ GNU coreutils NEWS -*- outline -*-
sort now supports -d, -f, -i, -R, and -V in any combination.
+ stat now accepts the %m format directive to output
+ the mount point for a file.
+
** Changes in behavior
df now consistently prints the device name for a bind mounted file,
diff --git a/THANKS b/THANKS
index 34b95f10f..c368a2c1b 100644
--- a/THANKS
+++ b/THANKS
@@ -8,6 +8,7 @@ the bug-report mailing list (as seen on last line of e.g., cp --help).
??? kytek@cybercomm.net
A Costa agcosta@gis.net
+Aaron Burgemeister dajoker@gmail.com
Aaron Hawley ashawley@uvm.edu
Achim Blumensath blume@corona.oche.de
Adam Jimerson vendion@charter.net
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 7eedec75a..6771da949 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8487,6 +8487,7 @@ removal is requested. Equivalent to @option{-I}.
When removing a hierarchy recursively, skip any directory that is on a
file system different from that of the corresponding command line argument.
+@cindex bind mount
This option is useful when removing a build ``chroot'' hierarchy,
which normally contains no valuable data. However, it is not uncommon
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
@item %G - Group name of owner
@item %h - Number of hard links
@item %i - Inode number
+@item %m - Mount point (See note below)
@item %n - File name
@item %N - Quoted file name with dereference if symbolic link
@item %o - I/O block size
@@ -10701,6 +10703,22 @@ The valid @var{format} directives for files with @option{--format} and
@item %Z - Time of last change as seconds since Epoch
@end itemize
+The mount point printed by @samp{%m} is similar to that output
+by @command{df}, except that:
+@itemize @bullet
+@item
+stat does not dereference symlinks by default
+(unless @option{-L} is specified)
+@item
+stat does not search for specified device nodes in the
+file system list, instead operating on them directly
+@item
+@cindex bind mount
+stat outputs the alias for a bind mounted file,
+rather than its backing device. One can recursively call stat
+until there is no change in output, to get the base mount point
+@end itemize
+
When listing file system information (@option{--file-system} (@option{-f})),
you must use a different set of @var{format} directives:
diff --git a/po/POTFILES.in b/po/POTFILES.in
index be20f4c28..673a7d9e6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -61,6 +61,7 @@ src/expand.c
src/expr.c
src/factor.c
src/false.c
+src/find-mount-point.c
src/fmt.c
src/fold.c
src/getlimits.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 1a19fa611..00c7ff713 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -145,6 +145,7 @@ noinst_HEADERS = \
copy.h \
cp-hash.h \
dircolors.h \
+ find-mount-point.h \
fs.h \
group-list.h \
ls.h \
@@ -478,6 +479,9 @@ rm_SOURCES = rm.c remove.c
mkdir_SOURCES = mkdir.c prog-fprintf.c
rmdir_SOURCES = rmdir.c prog-fprintf.c
+df_SOURCES = df.c find-mount-point.c
+stat_SOURCES = stat.c find-mount-point.c
+
uname_SOURCES = uname.c uname-uname.c
arch_SOURCES = uname.c uname-arch.c
nproc_SOURCES = nproc.c
diff --git a/src/df.c b/src/df.c
index 24877ab13..749ce1a12 100644
--- a/src/df.c
+++ b/src/df.c
@@ -29,8 +29,7 @@
#include "human.h"
#include "mountlist.h"
#include "quote.h"
-#include "save-cwd.h"
-#include "xgetcwd.h"
+#include "find-mount-point.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "df"
@@ -522,94 +521,6 @@ show_dev (char const *disk, char const *mount_point,
putchar ('\n');
}
-/* Return the root mountpoint of the file system on which FILE exists, in
- malloced storage. FILE_STAT should be the result of stating FILE.
- Give a diagnostic and return NULL if unable to determine the mount point.
- Exit if unable to restore current working directory. */
-static char *
-find_mount_point (const char *file, const struct stat *file_stat)
-{
- struct saved_cwd cwd;
- struct stat last_stat;
- char *mp = NULL; /* The malloced mount point. */
-
- if (save_cwd (&cwd) != 0)
- {
- error (0, errno, _("cannot get current directory"));
- return NULL;
- }
-
- if (S_ISDIR (file_stat->st_mode))
- /* FILE is a directory, so just chdir there directly. */
- {
- last_stat = *file_stat;
- if (chdir (file) < 0)
- {
- error (0, errno, _("cannot change to directory %s"), quote (file));
- return NULL;
- }
- }
- else
- /* FILE is some other kind of file; use its directory. */
- {
- char *xdir = dir_name (file);
- char *dir;
- ASSIGN_STRDUPA (dir, xdir);
- free (xdir);
-
- if (chdir (dir) < 0)
- {
- error (0, errno, _("cannot change to directory %s"), quote (dir));
- return NULL;
- }
-
- if (stat (".", &last_stat) < 0)
- {
- error (0, errno, _("cannot stat current directory (now %s)"),
- quote (dir));
- goto done;
- }
- }
-
- /* Now walk up FILE's parents until we find another file system or /,
- chdiring as we go. LAST_STAT holds stat information for the last place
- we visited. */
- while (true)
- {
- struct stat st;
- if (stat ("..", &st) < 0)
- {
- error (0, errno, _("cannot stat %s"), quote (".."));
- goto done;
- }
- if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
- /* cwd is the mount point. */
- break;
- if (chdir ("..") < 0)
- {
- error (0, errno, _("cannot change to directory %s"), quote (".."));
- goto done;
- }
- last_stat = st;
- }
-
- /* Finally reached a mount point, see what it's called. */
- mp = xgetcwd ();
-
-done:
- /* Restore the original cwd. */
- {
- int save_errno = errno;
- if (restore_cwd (&cwd) != 0)
- error (EXIT_FAILURE, errno,
- _("failed to return to initial working directory"));
- free_cwd (&cwd);
- errno = save_errno;
- }
-
- return mp;
-}
-
/* If DISK corresponds to a mount point, show its usage
and return true. Otherwise, return false. */
static bool
diff --git a/src/find-mount-point.c b/src/find-mount-point.c
new file mode 100644
index 000000000..f3a20744d
--- /dev/null
+++ b/src/find-mount-point.c
@@ -0,0 +1,113 @@
+/* find-mount-point.c -- find the root mount point for a file.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <sys/types.h>
+
+#include "system.h"
+#include "error.h"
+#include "quote.h"
+#include "save-cwd.h"
+#include "xgetcwd.h"
+#include "find-mount-point.h"
+
+/* Return the root mountpoint of the file system on which FILE exists, in
+ malloced storage. FILE_STAT should be the result of stating FILE.
+ Give a diagnostic and return NULL if unable to determine the mount point.
+ Exit if unable to restore current working directory. */
+extern char *
+find_mount_point (const char *file, const struct stat *file_stat)
+{
+ struct saved_cwd cwd;
+ struct stat last_stat;
+ char *mp = NULL; /* The malloced mount point. */
+
+ if (save_cwd (&cwd) != 0)
+ {
+ error (0, errno, _("cannot get current directory"));
+ return NULL;
+ }
+
+ if (S_ISDIR (file_stat->st_mode))
+ /* FILE is a directory, so just chdir there directly. */
+ {
+ last_stat = *file_stat;
+ if (chdir (file) < 0)
+ {
+ error (0, errno, _("cannot change to directory %s"), quote (file));
+ return NULL;
+ }
+ }
+ else
+ /* FILE is some other kind of file; use its directory. */
+ {
+ char *xdir = dir_name (file);
+ char *dir;
+ ASSIGN_STRDUPA (dir, xdir);
+ free (xdir);
+
+ if (chdir (dir) < 0)
+ {
+ error (0, errno, _("cannot change to directory %s"), quote (dir));
+ return NULL;
+ }
+
+ if (stat (".", &last_stat) < 0)
+ {
+ error (0, errno, _("cannot stat current directory (now %s)"),
+ quote (dir));
+ goto done;
+ }
+ }
+
+ /* Now walk up FILE's parents until we find another file system or /,
+ chdiring as we go. LAST_STAT holds stat information for the last place
+ we visited. */
+ while (true)
+ {
+ struct stat st;
+ if (stat ("..", &st) < 0)
+ {
+ error (0, errno, _("cannot stat %s"), quote (".."));
+ goto done;
+ }
+ if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
+ /* cwd is the mount point. */
+ break;
+ if (chdir ("..") < 0)
+ {
+ error (0, errno, _("cannot change to directory %s"), quote (".."));
+ goto done;
+ }
+ last_stat = st;
+ }
+
+ /* Finally reached a mount point, see what it's called. */
+ mp = xgetcwd ();
+
+done:
+ /* Restore the original cwd. */
+ {
+ int save_errno = errno;
+ if (restore_cwd (&cwd) != 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to return to initial working directory"));
+ free_cwd (&cwd);
+ errno = save_errno;
+ }
+
+ return mp;
+}
diff --git a/src/find-mount-point.h b/src/find-mount-point.h
new file mode 100644
index 000000000..49be9bab8
--- /dev/null
+++ b/src/find-mount-point.h
@@ -0,0 +1,17 @@
+/* find-mount-point.h -- find the root mount point for a file.
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+extern char* find_mount_point (const char *, const struct stat *);
diff --git a/src/stat.c b/src/stat.c
index 18a746f97..f98597885 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -64,10 +64,12 @@
#include "filemode.h"
#include "fs.h"
#include "getopt.h"
+#include "mountlist.h"
#include "quote.h"
#include "quotearg.h"
#include "stat-time.h"
#include "strftime.h"
+#include "find-mount-point.h"
#if USE_STATVFS
# define STRUCT_STATVFS struct statvfs
@@ -604,6 +606,94 @@ print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
return fail;
}
+/* Return any bind mounted source for a path.
+ The caller should not free the returned buffer.
+ Return NULL if no bind mount found. */
+static char const * ATTRIBUTE_WARN_UNUSED_RESULT
+find_bind_mount (char const * name)
+{
+ char const * bind_mount = NULL;
+
+ static struct mount_entry *mount_list;
+ static bool tried_mount_list = false;
+ if (!tried_mount_list) /* attempt/warn once per process. */
+ {
+ if (!(mount_list = read_file_system_list (false)))
+ error (0, errno, "%s", _("cannot read table of mounted file systems"));
+ tried_mount_list = true;
+ }
+
+ struct mount_entry *me;
+ for (me = mount_list; me; me = me->me_next)
+ {
+ if (me->me_dummy && me->me_devname[0] == '/'
+ && STREQ (me->me_mountdir, name))
+ {
+ struct stat name_stats;
+ struct stat dev_stats;
+
+ if (stat (name, &name_stats) == 0
+ && stat (me->me_devname, &dev_stats) == 0
+ && SAME_INODE (name_stats, dev_stats))
+ {
+ bind_mount = me->me_devname;
+ break;
+ }
+ }
+ }
+
+ return bind_mount;
+}
+
+/* Print mount point. Return zero upon success, nonzero upon failure. */
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+out_mount_point (char const *filename, char *pformat, size_t prefix_len,
+ const struct stat *statp)
+{
+
+ char const *np = "?", *bp = NULL;
+ char *mp = NULL;
+ bool fail = true;
+
+ /* Look for bind mounts first. Note we output the immediate alias,
+ rather than further resolving to a base device mount point. */
+ if (follow_links || !S_ISLNK (statp->st_mode))
+ {
+ char *resolved = canonicalize_file_name (filename);
+ if (!resolved)
+ {
+ error (0, errno, _("failed to canonicalize %s"), quote (filename));
+ goto print_mount_point;
+ }
+ bp = find_bind_mount (resolved);
+ free (resolved);
+ if (bp)
+ {
+ fail = false;
+ goto print_mount_point;
+ }
+ }
+
+ /* If there is no direct bind mount, then navigate
+ back up the tree looking for a device change.
+ Note we don't detect if any of the directory components
+ are bind mounted to the same device, but that's OK
+ since we've not directly queried them. */
+ if ((mp = find_mount_point (filename, statp)))
+ {
+ /* This dir might be bind mounted to another device,
+ so we resolve the bound source in that case also. */
+ bp = find_bind_mount (mp);
+ fail = false;
+ }
+
+print_mount_point:
+
+ out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
+ free (mp);
+ return fail;
+}
+
/* Print stat info. Return zero upon success, nonzero upon failure. */
static bool
print_stat (char *pformat, size_t prefix_len, char m,
@@ -680,6 +770,9 @@ print_stat (char *pformat, size_t prefix_len, char m,
case 't':
out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
break;
+ case 'm':
+ fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
+ break;
case 'T':
out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
break;
@@ -1026,6 +1119,7 @@ The valid format sequences for files (without --file-system):\n\
fputs (_("\
%h Number of hard links\n\
%i Inode number\n\
+ %m Mount point\n\
%n File name\n\
%N Quoted file name with dereference if symbolic link\n\
%o I/O block size\n\
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4aa93bf9e..5619d0b76 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -241,6 +241,7 @@ TESTS = \
misc/split-l \
misc/stat-fmt \
misc/stat-hyphen \
+ misc/stat-mount \
misc/stat-printf \
misc/stat-slash \
misc/stdbuf \
diff --git a/tests/misc/stat-mount b/tests/misc/stat-mount
new file mode 100755
index 000000000..c1c43d49c
--- /dev/null
+++ b/tests/misc/stat-mount
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Test stat -c%m
+
+# Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+# Note we assume that the current directory is not bind mounted
+# (as then, stat and df may have different results).
+# This should be the case given the directory is temporary
+# for the duration of the test.
+
+df_mnt=$(df -P . | sed -n '2s/.* \([^ ]*$\)/\1/p')
+stat_mnt=$(stat -c%m .)
+
+test "$df_mnt" = "$stat_mnt" || fail=1
+
+Exit $fail