From 982d5b1bffb0b97946fa5d17fcbe866f8a8df649 Mon Sep 17 00:00:00 2001 From: jay Date: Mon, 3 Jan 2005 00:41:29 +0000 Subject: [PATCH] Implemented the -samefile test for find --- NEWS | 1 + doc/find.texi | 45 ++++++++++++++++++++++++++++++++++++--------- find/defs.h | 13 +++++++++++++ find/find.1 | 7 ++++++- find/find.c | 5 ----- find/parser.c | 23 +++++++++++++++++++++++ find/pred.c | 16 ++++++++++++++++ 7 files changed, 95 insertions(+), 15 deletions(-) diff --git a/NEWS b/NEWS index 23ba685..5029af1 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) a more helpful error message. *** locate now supports a --statistics (or -S) option, which prints some statistics about the locate databases. +*** Implemented the -samefile option. ** Documentation improvements *** New chapter in the manual, "Security Considerations". *** Better documentation for -prune (Mainly thanks to Stepan Kasal) diff --git a/doc/find.texi b/doc/find.texi index 892908c..403971b 100644 --- a/doc/find.texi +++ b/doc/find.texi @@ -618,15 +618,40 @@ find . -lname '*sysdep.c' @node Hard Links @subsection Hard Links -To find hard links, first get the inode number of the file whose links -you want to find. You can learn a file's inode number and the number of -links to it by running @samp{ls -i} or @samp{find -ls}. If the file has -more than one link, you can search for the other links by passing that -inode number to @samp{-inum}. Add the @samp{-xdev} option if you are -starting the search at a directory that has other filesystems mounted on -it, such as @file{/usr} on many systems. Doing this saves needless -searching, since hard links to a file must be on the same filesystem. -@xref{Filesystems}. +Hard links allow more than one name to refer to the same file. To +find all the names which refer to the same file as NAME, use +@samp{-samefile NAME}. If you are not using the @samp{-L} option, you +can confine your search to one filesystem using the @samp{-xdev} +option. This is useful because hard links cannot point outside a +single filesystem, so this can cut down on needless searching. + +If the @samp{-L} option is in effect, and NAME is in fact a symbolic +link, the symbolic link will be dereferenced. Hence you are searching +for other links (hard or symbolic) to the file pointed to by NAME. If +@samp{-L} is in effect but NAME is not itself a symbolic link, other +symbolic links to the file NAME will be matched. + +You can also search for files by inode number. This can occasionally +be useful in diagnosing problems with filesystems for example, because +@code{fsck} tends to print inode numbers. Inode numbers also +occasionally turn up in log messages for some types of software, and +are used to support the @code{ftok()} library function. + +You can learn a file's inode number and the number of links to it by +running @samp{ls -li} or @samp{find -ls}. + +You can search for hard links to inode number NUM by using @samp{-inum +NUM}. If there are any file system mount points below the directory +where you are starting the search, use the @samp{-xdev} option unless +you are also using the @samp{-L} option. Using @samp{-xdev} this +saves needless searching, since hard links to a file must be on the +same filesystem. @xref{Filesystems}. + +@deffn Test -samefile NAME +File is a hard link to the same inode as NAME. If the @samp{-L} +option is in effect, symbolic links to the same file as NAME points to +are also matched. +@end deffn @deffn Test -inum n File has inode number @var{n}. The @samp{+} and @samp{-} qualifiers @@ -637,6 +662,8 @@ You can also search for files that have a certain number of links, with @samp{-links}. Directories normally have at least two hard links; their @file{.} entry is the second one. If they have subdirectories, each of those also has a hard link called @file{..} to its parent directory. +The @file{.} and @file{..} directory entries are not normally searched +unless they are mentioned on the @code{find} command line. @deffn Test -links n File has @var{n} hard links. diff --git a/find/defs.h b/find/defs.h index 23ef18d..d93c0c9 100644 --- a/find/defs.h +++ b/find/defs.h @@ -191,6 +191,15 @@ struct perm_val mode_t val; }; +/* dir_id is used to support loop detection in find.c and + * also to support the -samefile test. + */ +struct dir_id +{ + ino_t ino; + dev_t dev; +}; + struct size_val { enum comparison_type kind; @@ -262,6 +271,8 @@ struct predicate /* True if this predicate node requires a stat system call to execute. */ boolean need_stat; + + /* Information needed by the predicate processor. Next to each member are listed the predicates that use it. */ union @@ -276,6 +287,7 @@ struct predicate gid_t gid; /* group */ time_t time; /* newer */ struct perm_val perm; /* perm */ + struct dir_id fileid; /* samefile */ mode_t type; /* type */ FILE *stream; /* fprint fprint0 */ struct format_val printf_vec; /* printf fprintf */ @@ -392,6 +404,7 @@ boolean pred_print0 PARAMS((char *pathname, struct stat *stat_buf, struct predic boolean pred_prune PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_quit PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_regex PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); +boolean pred_samefile PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_size PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_true PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); boolean pred_type PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)); diff --git a/find/find.1 b/find/find.1 index ab2ca59..8efd46f 100644 --- a/find/find.1 +++ b/find/find.1 @@ -269,7 +269,9 @@ shell, an initial '.' can be matched by '*'. That is, will match the file `.foobar'. .IP "\-inum \fIn\fR" -File has inode number \fIn\fR. +File has inode number \fIn\fR. It is normally easier to use the +.B \-samefile +test instead. .IP "\-ipath \fIpattern\fR" Behaves in the same way as \-iwholename. This option is deprecated, so please do not use it. @@ -351,6 +353,9 @@ system library function where this is present (i.e. on systems using the GNU C Library). On other systems, the implementation within Gnulib is used; by default, Gnulib provides ``basic'' regular expressions. +.IP "\-samefile \fIname\fR" +File refers to the same inode as \fIname\fR. When -L is in effect, +this can include symbolic links. .IP "\-size \fIn\fR[cwbkMG]" File uses \fIn\fP units of space. The following suffixes can be used: diff --git a/find/find.c b/find/find.c index be5f77a..9dfcebc 100644 --- a/find/find.c +++ b/find/find.c @@ -952,11 +952,6 @@ process_top_path (char *pathname) /* Info on each directory in the current tree branch, to avoid getting stuck in symbolic link loops. */ -struct dir_id -{ - ino_t ino; - dev_t dev; -}; static struct dir_id *dir_ids = NULL; /* Entries allocated in `dir_ids'. */ static int dir_alloc = 0; diff --git a/find/parser.c b/find/parser.c index a15f4df..1790b03 100644 --- a/find/parser.c +++ b/find/parser.c @@ -114,6 +114,7 @@ static boolean parse_printf PARAMS((char *argv[], int *arg_ptr)); static boolean parse_prune PARAMS((char *argv[], int *arg_ptr)); static boolean parse_regex PARAMS((char *argv[], int *arg_ptr)); static boolean insert_regex PARAMS((char *argv[], int *arg_ptr, boolean ignore_case)); +static boolean parse_samefile PARAMS((char *argv[], int *arg_ptr)); static boolean parse_size PARAMS((char *argv[], int *arg_ptr)); static boolean parse_true PARAMS((char *argv[], int *arg_ptr)); static boolean parse_type PARAMS((char *argv[], int *arg_ptr)); @@ -240,6 +241,7 @@ static struct parser_table const parse_table[] = {ARG_TEST, "prune", parse_prune}, {ARG_ACTION, "quit", parse_quit}, /* GNU */ {ARG_TEST, "regex", parse_regex}, /* GNU */ + {ARG_TEST, "samefile", parse_samefile}, /* GNU */ {ARG_TEST, "size", parse_size}, {ARG_TEST, "true", parse_true}, /* GNU */ {ARG_TEST, "type", parse_type}, @@ -1398,6 +1400,27 @@ parse_size (char **argv, int *arg_ptr) return (true); } + +static boolean +parse_samefile (char **argv, int *arg_ptr) +{ + struct predicate *our_pred; + struct stat st; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return (false); + if ((*options.xstat) (argv[*arg_ptr], &st)) + error (1, errno, "%s", argv[*arg_ptr]); + + our_pred = insert_primary (pred_samefile); + our_pred->args.fileid.ino = st.st_ino; + our_pred->args.fileid.dev = st.st_dev; + our_pred->need_stat = true; + (*arg_ptr)++; + return (true); +} + + static boolean parse_true (char **argv, int *arg_ptr) { diff --git a/find/pred.c b/find/pred.c index ac86670..254f16a 100644 --- a/find/pred.c +++ b/find/pred.c @@ -194,6 +194,7 @@ struct pred_assoc pred_table[] = {pred_print0, "print0 "}, {pred_prune, "prune "}, {pred_regex, "regex "}, + {pred_samefile,"samefile "}, {pred_size, "size "}, {pred_true, "true "}, {pred_type, "type "}, @@ -1227,6 +1228,21 @@ pred_size (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) } boolean +pred_samefile (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +{ + /* Potential optimisation: because of the loop protection, we + * always know the device of the current directory, hence the + * device number of the current filesystem. If -L is not in + * effect, and the device number of the file we're looking for + * is not the same as the device number of the current directory, + * this predicate cannot return true. Hence there would be no + * need to stat the file. + */ + return stat_buf->st_ino == pred_ptr->args.fileid.ino + && stat_buf->st_dev == pred_ptr->args.fileid.dev; +} + +boolean pred_true (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { (void) pathname; -- 2.11.4.GIT