mirror of
https://https.git.savannah.gnu.org/git/coreutils.git
synced 2026-01-26 15:29:07 +00:00
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>
This commit is contained in:
parent
872f6bb2ba
commit
ddf6fb8686
3
NEWS
3
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,
|
||||
|
||||
1
THANKS
1
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
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
91
src/df.c
91
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
|
||||
|
||||
113
src/find-mount-point.c
Normal file
113
src/find-mount-point.c
Normal file
@ -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;
|
||||
}
|
||||
17
src/find-mount-point.h
Normal file
17
src/find-mount-point.h
Normal file
@ -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 *);
|
||||
94
src/stat.c
94
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\
|
||||
|
||||
@ -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 \
|
||||
|
||||
31
tests/misc/stat-mount
Executable file
31
tests/misc/stat-mount
Executable file
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user