From f77361fd6553ba9453cbcce93411cf776f7da84c Mon Sep 17 00:00:00 2001 From: Leslie P. Polzer Date: Mon, 24 Mar 2008 01:46:03 +0000 Subject: [PATCH] Merge Leslie Polzer's changes implementing -xattr and -ext2attr. To: address@hidden 2008-03-24 Leslie P. Polzer Merge Leslie Polzer's changes implementing the -xattr and -ext2attr predicates in find. These enhancements were made as part of the Google Summer of Code 2007. * configure.ac: check for header files attr/attributes.h attr/xattr.h and e3p.h and libraries -lattr, -le2p. * doc/find.texi (Attributes): New section. * find/Makefile.am (LDADD): Link against -le2p and -lattr if available. * find/defs.h (struct predicate): Added struct nv_pair, representing a name/value pair, to the predicate arguments, and a mask (for -ext2attr). Declare pred_ext2attr and pred_xattr. * find/find.1: docuemnt -ext2attr and -xattr. * find/parser.c: Include attr/attributes.h and e2p/e2p.h. Declare pred_ext2attr and pred_xattr. Also add these functions to parse_table[]. (build_mask): Convert a sequence of ext2 flag characters to a mask of bits. (get_mask): Likewise, for just one character. (parse_ext2attr): New function for parsing -ext2attr. (parse_xattr): New function for parsing -xattr. * find/pred.c: Include attr/attributes.h and e2p/e2p.h. (pred_xattr): New function, for -xattr. (pred_ext2attr): New function, for -ext2attr. * find/tree.c (costlookup): Add entries fpr pred_ext2attr and pred_xattr. * import-gnulib.config (modules): Added strsep. * NEWS: Mention these changes. --- NEWS | 5 ++ configure.ac | 11 ++++ doc/find.texi | 35 +++++++++++- find/Makefile.am | 3 +- find/defs.h | 14 ++++- find/find.1 | 8 +++ find/parser.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++ find/pred.c | 66 +++++++++++++++++++++ find/tree.c | 6 ++ import-gnulib.config | 2 + 10 files changed, 302 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 3994c59..bbfb4c3 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,11 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) * Major changes in release 4.5.0-CVS +** Enhancements + +Two new predicates for find, -xattr and -ext2attr, for querying +extended file attributes. + ** Performance changes The default optimisation level for find is now -O2 instead of -O0, diff --git a/configure.ac b/configure.ac index 7074fe3..e791194 100644 --- a/configure.ac +++ b/configure.ac @@ -122,6 +122,17 @@ AC_CHECK_HEADERS(unistd.h sys/types.h inttypes.h fcntl.h locale.h stdint.h) AC_CHECK_HEADERS(sys/param.h mntent.h sys/mnttab.h sys/mntio.h sys/mkdev.h) dnl find.c needs sys/utsname.h because it calls uname(2). AC_CHECK_HEADERS(sys/utsname.h) + +AC_SUBST(XATTR_LDADD)dnl +AC_CHECK_HEADERS([attr/attributes.h attr/xattr.h], + [AC_CHECK_LIB(attr, [attr_get], + [AC_SUBST(XATTR_LDADD,[-lattr])])]) + +AC_SUBST(E2P_LDADD)dnl +AC_CHECK_HEADERS([e2p/e2p.h], + [AC_CHECK_LIB(e2p, [fgetflags], + [AC_SUBST(E2P_LDADD,[-le2p])])]) + AC_HEADER_MAJOR AC_HEADER_DIRENT AC_HEADER_STAT diff --git a/doc/find.texi b/doc/find.texi index aa95456..b2a9061 100644 --- a/doc/find.texi +++ b/doc/find.texi @@ -349,6 +349,7 @@ more information about the matching files. * Type:: * Owner:: * Mode Bits:: +* Attributes:: * Contents:: * Directories:: * Filesystems:: @@ -1242,6 +1243,37 @@ situation. @end deffn + address@hidden Attributes address@hidden Attributes + +Some operating and file systems support the annotation +of files with auxiliary information. This information +may be of an arbitrary nature with user-defined semantics +for names and value (POSIX extended attributes) or +effect certain behaviour for operations on the file in +question (ext2 attributes). + +The following two tests for extended attributes are +currently supported. + address@hidden Test -xattr name=value +True if a POSIX extended attribute @samp{name} exists +in the user namespace and has a value of @samp{value}. address@hidden deffn + address@hidden Test -ext2attr attrlist +True if the specified ext2fs attribute flags are set. +See chattr(1), section @var{Attributes} for a list of +valid flags. @var{attrlist} consists of a string of +single characters, each denoting the existence of +an attribute. For example, the expression address@hidden iac} will match only files with the address@hidden, address@hidden and @var{compression} attributes all set. address@hidden deffn + + @node Contents @section Contents @@ -2395,7 +2427,8 @@ unprintable characters is harmonised for @samp{-ls}, @samp{-fls}, the command each time it executes it. By default, it uses up to @code{ARG_MAX} - 2k, or 128k, whichever is smaller, characters per command. It uses as many lines and arguments as fit within that -limit. The following options modify those values. +limit. If the limit doesn't work, it is adjusted gradually until +it does. The following options modify the limit. @table @code @item --no-run-if-empty diff --git a/find/Makefile.am b/find/Makefile.am index b001509..0db1fa1 100644 --- a/find/Makefile.am +++ b/find/Makefile.am @@ -26,7 +26,8 @@ endif EXTRA_DIST = defs.h $(man_MANS) INCLUDES = -I../gnulib/lib -I$(top_srcdir)/lib -I$(top_srcdir)/gnulib/lib -I../intl -DLOCALEDIR=\"$(localedir)\" -LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a @INTLLIBS@ @LIB_CLOCK_GETTIME@ @FINDLIBS@ +LDADD = ./libfindtools.a ../lib/libfind.a ../gnulib/lib/libgnulib.a \ + @INTLLIBS@ @LIB_CLOCK_GETTIME@ @XATTR_LDADD@ @FINDLIBS@ @E2P_LDADD@ man_MANS = find.1 SUBDIRS = . testsuite diff --git a/find/defs.h b/find/defs.h index 1708d83..726ea12 100644 --- a/find/defs.h +++ b/find/defs.h @@ -174,6 +174,12 @@ struct size_val uintmax_t size; }; +struct nv_pair +{ + char* name; + char* value; +}; + enum xval { @@ -303,7 +309,8 @@ struct predicate Next to each member are listed the predicates that use it. */ union { - const char *str; /* fstype [i]lname [i]name [i]path */ + char *str; /* fstype [i]lname [i]name [i]path */ + struct nv_pair nv; /* xattr */ struct re_pattern_buffer *regex; /* regex */ struct exec_val exec_vec; /* exec ok */ struct long_val numinfo; /* gid inum links uid */ @@ -315,6 +322,7 @@ struct predicate struct samefile_file_id samefileid; /* samefile */ mode_t type; /* type */ struct format_val printf_vec; /* printf fprintf fprint ls fls print0 fprint0 print */ + unsigned long mask; /* ext2attr */ } args; /* The next predicate in the user input sequence, @@ -415,6 +423,9 @@ PREDICATEFUNCTION pred_empty; PREDICATEFUNCTION pred_exec; PREDICATEFUNCTION pred_execdir; PREDICATEFUNCTION pred_executable; +#if defined(HAVE_E2P_E2P_H) +PREDICATEFUNCTION pred_ext2attr; +#endif PREDICATEFUNCTION pred_false; PREDICATEFUNCTION pred_fls; PREDICATEFUNCTION pred_fprint; @@ -458,6 +469,7 @@ PREDICATEFUNCTION pred_uid; PREDICATEFUNCTION pred_used; PREDICATEFUNCTION pred_user; PREDICATEFUNCTION pred_writable; +PREDICATEFUNCTION pred_xattr; PREDICATEFUNCTION pred_xtype; diff --git a/find/find.1 b/find/find.1 index d7f40cf..774bd04 100644 --- a/find/find.1 +++ b/find/find.1 @@ -550,6 +550,10 @@ the result of the system call, there is no guarantee that a file for which this test succeeds can actually be executed. +.IP "\-ext2attr \fIattrlist\fB" +True if the ext2fs attribute flags in \fIattrlist\fR are set. +See chattr(5) for more information. + .IP \-false Always false. @@ -917,6 +921,10 @@ mapping (or root-squashing), since many systems implement in the client's kernel and so cannot make use of the UID mapping information held on the server. +.IP "\-xattr \fIname=value\fR" +True if a POSIX extended attribute \fIname\fR exists +in the user namespace and has a value of \fIvalue\fR. + .IP "\-xtype \fIc\fR" The same as .B \-type diff --git a/find/parser.c b/find/parser.c index 246c4ce..569a7ce 100644 --- a/find/parser.c +++ b/find/parser.c @@ -26,6 +26,12 @@ #include #include #include +#if defined(HAVE_ATTR_ATTRIBUTES_H) +#include +#endif +#if defined(HAVE_E2P_E2P_H) +#include +#endif #include "modechange.h" #include "modetype.h" #include "xstrtol.h" @@ -97,6 +103,9 @@ static boolean parse_depth PARAMS((const struct parser_table*, char *arg static boolean parse_empty PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); static boolean parse_exec PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); static boolean parse_execdir PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); +#if defined(HAVE_E2P_E2P_H) +static boolean parse_ext2attr PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); +#endif static boolean parse_false PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); static boolean parse_fls PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); static boolean parse_fprintf PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); @@ -154,6 +163,9 @@ static boolean parse_xdev PARAMS((const struct parser_table*, char *arg static boolean parse_ignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); static boolean parse_noignore_race PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); static boolean parse_warn PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); +#if defined(HAVE_ATTR_ATTRIBUTES_H) || defined(HAVE_ATTR_XATTR_H) +static boolean parse_xattr PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); +#endif static boolean parse_xtype PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); static boolean parse_quit PARAMS((const struct parser_table*, char *argv[], int *arg_ptr)); @@ -260,6 +272,9 @@ static struct parser_table const parse_table[] = {ARG_ACTION, "exec", parse_exec, pred_exec}, /* POSIX */ {ARG_TEST, "executable", parse_accesscheck, pred_executable}, /* GNU, 4.3.0+ */ PARSE_ACTION ("execdir", execdir), /* *BSD, GNU */ +#if defined(HAVE_E2P_E2P_H) + PARSE_TEST ("ext2attr", ext2attr), /* GNU */ +#endif PARSE_ACTION ("fls", fls), /* GNU */ PARSE_POSOPT ("follow", follow), /* GNU, Unix */ PARSE_ACTION ("fprint", fprint), /* GNU */ @@ -321,6 +336,9 @@ static struct parser_table const parse_table[] = PARSE_TEST_NP ("wholename", wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */ {ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */ PARSE_OPTION ("xdev", xdev), /* POSIX */ +#if defined(HAVE_ATTR_ATTRIBUTES_H) || defined(HAVE_ATTR_XATTR_H) + PARSE_TEST ("xattr", xattr), /* GNU */ +#endif PARSE_TEST ("xtype", xtype), /* GNU */ #ifdef UNIMPLEMENTED_UNIX /* It's pretty ugly for find to know about archive formats. @@ -2554,6 +2572,143 @@ parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr) return parse_noop(entry, argv, arg_ptr); } + +#if defined(HAVE_E2P_E2P_H) + + +/* translate single flag char to bit */ +static unsigned long +get_mask (const char flag) +{ + struct flags_name + { + unsigned long flag; + const char *short_name; + const char *long_name; + }; + + static struct flags_name flags_array[] = + { + { EXT2_SECRM_FL, "s", "Secure_Deletion" }, + { EXT2_UNRM_FL, "u" , "Undelete" }, + { EXT2_SYNC_FL, "S", "Synchronous_Updates" }, + { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" }, + { EXT2_IMMUTABLE_FL, "i", "Immutable" }, + { EXT2_APPEND_FL, "a", "Append_Only" }, + { EXT2_NODUMP_FL, "d", "No_Dump" }, + { EXT2_NOATIME_FL, "A", "No_Atime" }, + { EXT2_COMPR_FL, "c", "Compression_Requested" }, +#ifdef ENABLE_COMPRESSION + { EXT2_COMPRBLK_FL, "B", "Compressed_File" }, + { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" }, + { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" }, + { EXT2_ECOMPR_FL, "E", "Compression_Error" }, +#endif + { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" }, + { EXT2_INDEX_FL, "I", "Indexed_direcctory" }, + { EXT2_NOTAIL_FL, "t", "No_Tailmerging" }, + { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" }, + { 0, NULL, NULL } + }; + + int i; + + for (i=0; flags_array[i].flag; ++i) + { + if (*(flags_array[i].short_name) == flag) + return flags_array[i].flag; + } + + return -1; +} + +/* translate flag specification from char to bit array */ +static unsigned long +build_mask (const char* flagspec) +{ + unsigned long flags = 0L; + char f; + unsigned long mask; + + + while (f = *(flagspec++)) + { + mask = get_mask (f); + + if (mask == -1) + error(1, 0, "unknown ext2 attribute specifier '%c'.", f); + + flags |= mask; + } + + return flags; +} + +static boolean +parse_ext2attr (const struct parser_table* entry, char **argv, int *arg_ptr) +{ + struct predicate *our_pred; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return false; + if (!check_name_arg("-ext2attr", argv[*arg_ptr])) + return false; + + if (argv[*arg_ptr] == NULL || *arg_ptr == '\0') + error (1, 0, "missing flag specifier "); + + + our_pred = insert_primary (entry); + + our_pred->args.mask = build_mask (argv[*arg_ptr]); + our_pred->need_stat = our_pred->need_type = false; + our_pred->est_success_rate = 0.001; /* Conservative choice. This heavily + depends on the attributes specified and the + production environment. */ + (*arg_ptr)++; + + return true; +} +#endif + + +#if defined(HAVE_ATTR_ATTRIBUTES_H) || defined(HAVE_ATTR_XATTR_H) +static boolean +parse_xattr (const struct parser_table* entry, char **argv, int *arg_ptr) +{ + struct predicate *our_pred; + char *name, *value; + + if ((argv == NULL) || (argv[*arg_ptr] == NULL)) + return false; + if (!check_name_arg("-xattr", argv[*arg_ptr])) + return false; + + fnmatch_sanitycheck(); + + name = strsep(&(argv[*arg_ptr]), "="); + + value = argv[*arg_ptr]; + + if (name == NULL || *name == '\0') + error (1, 0, "missing attribute name (format: ATTR=VALUE)"); + + if (value == NULL) + error (1, 0, "missing attribute value (format: ATTR=VALUE)"); + + our_pred = insert_primary (entry); + + our_pred->args.nv.name = name; + our_pred->args.nv.value = value; + our_pred->need_stat = our_pred->need_type = false; + our_pred->est_success_rate = 0.001; /* Again, a conservative choice. + See comment above. */ + (*arg_ptr)++; + + return true; +} +#endif + static boolean parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr) { diff --git a/find/pred.c b/find/pred.c index d758276..3da13b8 100644 --- a/find/pred.c +++ b/find/pred.c @@ -31,6 +31,15 @@ #include #include #include +#if defined(HAVE_ATTR_ATTRIBUTES_H) +#include +#endif +#if defined(HAVE_ATTR_XATTR_H) +#include +#endif +#if defined(HAVE_E2P_E2P_H) +#include +#endif #include #include "xalloc.h" #include "dirname.h" @@ -186,6 +195,9 @@ struct pred_assoc pred_table[] = {pred_exec, "exec "}, {pred_execdir, "execdir "}, {pred_executable, "executable "}, +#if defined(HAVE_E2P_E2P_H) + {pred_ext2attr, "ext2attr "}, +#endif {pred_false, "false "}, {pred_fprint, "fprint "}, {pred_fprint0, "fprint0 "}, @@ -228,6 +240,9 @@ struct pred_assoc pred_table[] = {pred_used, "used "}, {pred_user, "user "}, {pred_writable, "writable "}, +#if defined(HAVE_ATTR_ATTRIBUTES_H) || defined(HAVE_ATTR_XATTR_H) + {pred_xattr, "xattr "}, +#endif {pred_xtype, "xtype "}, {0, "none "} }; @@ -1804,6 +1819,57 @@ pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_p return (false); } + +#if defined(HAVE_E2P_E2P_H) +boolean +pred_ext2attr (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +{ + unsigned long flags; + + if (fgetflags (state.rel_pathname, &flags) == -1 && (ENOENT != errno)) + { + error (0, errno, "failed to get ext2 attributes for '%s'", pathname); + return false; + } + + return ((flags & pred_ptr->args.mask) != 0); +} +#endif + + +#if defined(HAVE_ATTR_ATTRIBUTES_H) || defined(HAVE_ATTR_XATTR_H) +boolean +pred_xattr (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +{ + char* attrvalue = xmalloc (ATTR_MAX_VALUELEN+1); + int attrlen = ATTR_MAX_VALUELEN; + bool matched = false; + + (void) stat_buf; + + + /* XXX_SOC: support root namespace, perhaps by an optional namespace specifier */ + if (attr_get (state.rel_pathname, pred_ptr->args.nv.name, attrvalue, &attrlen, 0) + == -1) + { + if ((ENOATTR != errno) && (ENOENT != errno)) + { + error (0, errno, "failed to get extended attribute for %s", pathname); + } + return false; + } + + if (fnmatch (pred_ptr->args.nv.value, attrvalue, 0) == 0) + { + matched = true; + } + + free (attrvalue); + return matched; +} +#endif + + boolean pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { diff --git a/find/tree.c b/find/tree.c index 7420c60..37b53be 100644 --- a/find/tree.c +++ b/find/tree.c @@ -910,6 +910,9 @@ static struct pred_cost_lookup costlookup[] = { pred_exec , NeedsEventualExec }, { pred_execdir , NeedsEventualExec }, { pred_executable, NeedsAccessInfo }, +#if defined(HAVE_E2P_E2P_H) + { pred_ext2attr , NeedsNothing }, /* SOC_XXX */ +#endif { pred_false , NeedsNothing }, { pred_fprint , NeedsNothing }, { pred_fprint0 , NeedsNothing }, @@ -953,6 +956,9 @@ static struct pred_cost_lookup costlookup[] = { pred_used , NeedsStatInfo }, { pred_user , NeedsStatInfo }, { pred_writable , NeedsAccessInfo }, +#if defined(HAVE_ATTR_ATTRIBUTES_H) || defined(HAVE_ATTR_XATTR_H) + { pred_xattr , NeedsNothing }, /* SOC_XXX */ +#endif { pred_xtype , NeedsType } /* roughly correct unless most files are symlinks */ }; static int pred_table_sorted = 0; diff --git a/import-gnulib.config b/import-gnulib.config index 287c2d8..76cb251 100644 --- a/import-gnulib.config +++ b/import-gnulib.config @@ -74,6 +74,7 @@ stpcpy strcasestr strdup strftime +strsep strtol strtoul strtoull @@ -92,3 +93,4 @@ xstrtol xstrtoumax yesno ' + -- 1.5.3.8