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