* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
#endif /* not lint */
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.58 2006/05/27 18:27:41 krion Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.71 2011/06/13 05:22:07 avatar Exp $");
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/acl.h>
#include <sys/wait.h>
#include <sys/mount.h>
-#include <sys/timeb.h>
#include <dirent.h>
#include <err.h>
#include <unistd.h>
#include <ctype.h>
-#include "find.h"
-
#ifdef __APPLE__
#include <sys/sysctl.h>
+#include <sys/xattr.h>
+#include <libgen.h>
#include <get_compat.h>
#else
#define COMPAT_MODE(func, mode) 1
#endif
+#include "find.h"
+
static PLAN *palloc(OPTION *);
static long long find_parsenum(PLAN *, const char *, char *, char *);
static long long find_parsetime(PLAN *, const char *, char *);
extern char **environ;
static PLAN *lastexecplus = NULL;
+int execplus_error;
#define COMPARE(a, b) do { \
switch (plan->flags & F_ELG_MASK) { \
else
xtime = entry->fts_statp->st_mtime;
- if (COMPAT_MODE("bin/find", "unix2003") || plan->flags & F_EXACTTIME)
- COMPARE((now - xtime) / 86400, plan->t_data);
+ if (plan->flags & F_EXACTTIME)
+ COMPARE(now - xtime, plan->t_data);
else
- COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data);
+ COMPARE((now - xtime + (COMPAT_MODE("bin/find", "unix2003") ? 0 : 86400 - 1)) / 86400, plan->t_data);
}
PLAN *
new = palloc(option);
new->t_data = find_parsetime(new, option->name, value);
- if (!(new->flags & F_EXACTTIME))
+ if (!(new->flags & F_EXACTTIME) && !COMPAT_MODE("bin/find", "unix2003"))
TIME_CORRECT(new);
return new;
}
return new;
}
-#ifndef __APPLE__
/*
* -acl function --
*
* Show files with EXTENDED ACL attributes.
*/
+#ifdef __APPLE__
int
f_acl(PLAN *plan __unused, FTSENT *entry)
{
- int match, entries;
+ acl_t facl;
+ int match;
acl_entry_t ae;
+
+ match = 0;
+ if ((facl = acl_get_link_np(entry->fts_accpath, ACL_TYPE_EXTENDED)) != NULL) {
+ if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 0) {
+ match = 1;
+ }
+ acl_free(facl);
+ }
+ return match;
+}
+#else /* !__APPLE__ */
+int
+f_acl(PLAN *plan __unused, FTSENT *entry)
+{
acl_t facl;
+ acl_type_t acl_type;
+ int acl_supported = 0, ret, trivial;
if (S_ISLNK(entry->fts_statp->st_mode))
return 0;
- if ((match = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED)) <= 0) {
- if (match < 0 && errno != EINVAL)
- warn("%s", entry->fts_accpath);
- else
- return 0;
+ ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_NFS4;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("%s", entry->fts_accpath);
+ return (0);
}
- match = 0;
- if ((facl = acl_get_file(entry->fts_accpath,ACL_TYPE_ACCESS)) != NULL) {
- if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 1) {
- /*
- * POSIX.1e requires that ACLs of type ACL_TYPE_ACCESS
- * must have at least three entries (owner, group,
- * other).
- */
- entries = 1;
- while (acl_get_entry(facl, ACL_NEXT_ENTRY, &ae) == 1) {
- if (++entries > 3) {
- match = 1;
- break;
- }
- }
+ if (acl_supported == 0) {
+ ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_ACCESS;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("%s", entry->fts_accpath);
+ return (0);
}
- acl_free(facl);
- } else
+ }
+ if (acl_supported == 0)
+ return (0);
+
+ facl = acl_get_file(entry->fts_accpath, acl_type);
+ if (facl == NULL) {
warn("%s", entry->fts_accpath);
- return match;
+ return (0);
+ }
+ ret = acl_is_trivial_np(facl, &trivial);
+ acl_free(facl);
+ if (ret) {
+ warn("%s", entry->fts_accpath);
+ acl_free(facl);
+ return (0);
+ }
+ if (trivial)
+ return (0);
+ return (1);
}
+#endif /* __APPLE__ */
PLAN *
c_acl(OPTION *option, char ***argvp __unused)
{
+#ifndef __APPLE__
ftsoptions &= ~FTS_NOSTAT;
+#endif /* !__APPLE__ */
return (palloc(option));
}
-#endif /* !__APPLE__ */
+
+#ifdef __APPLE__
+int
+f_xattr(PLAN *plan __unused, FTSENT *entry)
+{
+ ssize_t xattr;
+ int match;
+
+ match = 0;
+ xattr = listxattr(entry->fts_accpath, NULL, 0, XATTR_NOFOLLOW);
+ if (xattr > 0) {
+ match = 1;
+ }
+ return match;
+}
+
+int
+f_xattrname(PLAN *plan, FTSENT *entry)
+{
+ ssize_t xattr;
+ int match;
+
+ match = 0;
+ xattr = getxattr(entry->fts_accpath, plan->c_data, NULL, 0, 0, XATTR_NOFOLLOW);
+ if (xattr > 0) {
+ match = 1;
+ }
+ return match;
+}
+#endif /* __APPLE__ */
/*
* -delete functions --
/* sanity check */
if (isdepth == 0 || /* depth off */
- (ftsoptions & FTS_NOSTAT) || /* not stat()ing */
- !(ftsoptions & FTS_PHYSICAL) || /* physical off */
- (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
+ (ftsoptions & FTS_NOSTAT)) /* not stat()ing */
errx(1, "-delete: insecure options got turned on");
+ if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */
+ (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
+ errx(1, "-delete: forbidden when symlinks are followed");
+
/* Potentially unsafe - do not accept relative paths whatsoever */
if (strchr(entry->fts_accpath, '/') != NULL)
errx(1, "-delete: %s: relative path potentially not safe",
if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
!(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
geteuid() == 0)
- chflags(entry->fts_accpath,
+ lchflags(entry->fts_accpath,
entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
/* rmdir directories, unlink everything else */
{
ftsoptions &= ~FTS_NOSTAT; /* no optimise */
- ftsoptions |= FTS_PHYSICAL; /* disable -follow */
- ftsoptions &= ~FTS_LOGICAL; /* disable -follow */
isoutput = 1; /* possible output */
isdepth = 1; /* -depth implied */
/*
* always_true --
*
- * Always true, used for -maxdepth, -mindepth, -xdev and -follow
+ * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true
*/
int
f_always_true(PLAN *plan __unused, FTSENT *entry __unused)
empty = 1;
dir = opendir(entry->fts_accpath);
if (dir == NULL)
- err(1, "%s", entry->fts_accpath);
+ return 0;
for (dp = readdir(dir); dp; dp = readdir(dir))
if (dp->d_name[0] != '.' ||
(dp->d_name[1] != '\0' &&
plan->e_psize = plan->e_pbsize;
}
pid = waitpid(pid, &status, 0);
- if (plan->flags & F_EXECPLUS && WIFEXITED(status) && WEXITSTATUS(status))
- _exit(WEXITSTATUS(status));
+ if (plan->flags & F_EXECPLUS && WIFEXITED(status) && WEXITSTATUS(status) && !execplus_error) {
+ /* Test 140 (8907531, 10656525) */
+ execplus_error = WEXITSTATUS(status);
+ }
return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
}
/* Finish any pending -exec ... {} + functions. */
void
-finish_execplus()
+finish_execplus(void)
{
PLAN *p;
static dev_t curdev; /* need a guaranteed illegal dev value */
static int first = 1;
struct statfs sb;
- static int val_type, val_flags;
+ static int val_flags;
+ static char fstype[sizeof(sb.f_fstypename)];
char *p, save[2] = {0,0};
if ((plan->flags & F_MTMASK) == F_MTUNKNOWN)
* always copy both of them.
*/
val_flags = sb.f_flags;
- val_type = sb.f_type;
+ strlcpy(fstype, sb.f_fstypename, sizeof(fstype));
}
switch (plan->flags & F_MTMASK) {
case F_MTFLAG:
return val_flags & plan->mt_data;
case F_MTTYPE:
- return val_type == plan->mt_data;
+ return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0);
default:
abort();
}
{
char *fsname;
PLAN *new;
- struct vfsconf vfc;
fsname = nextarg(option, argvp);
ftsoptions &= ~FTS_NOSTAT;
new = palloc(option);
-
- /*
- * Check first for a filesystem name.
- */
- if (getvfsbyname(fsname, &vfc) == 0) {
- new->flags |= F_MTTYPE;
- new->mt_data = vfc.vfc_typenum;
- return new;
- }
-
switch (*fsname) {
case 'l':
if (!strcmp(fsname, "local")) {
break;
}
- /*
- * We need to make filesystem checks for filesystems
- * that exists but aren't in the kernel work.
- */
- fprintf(stderr, "Warning: Unknown filesystem type %s\n", fsname);
- new->flags |= F_MTUNKNOWN;
+ new->flags |= F_MTTYPE;
+ new->c_data = fsname;
return new;
}
g = getgrnam(gname);
if (g == NULL) {
char* cp = gname;
- if( gname[0] == '-' || gname[0] == '+' )
+ if (gname[0] == '-' || gname[0] == '+')
gname++;
gid = atoi(gname);
if (gid == 0 && gname[0] != '0')
return new;
}
+/*
+ * -samefile FN
+ *
+ * True if the file has the same inode (eg hard link) FN
+ */
+
+/* f_samefile is just f_inum */
+PLAN *
+c_samefile(OPTION *option, char ***argvp)
+{
+ char *fn;
+ PLAN *new;
+ struct stat sb;
+
+ fn = nextarg(option, argvp);
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(option);
+ if (stat(fn, &sb))
+ err(1, "%s", fn);
+ new->i_data = sb.st_ino;
+ return new;
+}
+
/*
* -links n functions --
*
int
f_name(PLAN *plan, FTSENT *entry)
{
- return !fnmatch(plan->c_data, entry->fts_name,
+ char fn[PATH_MAX];
+ const char *name;
+ ssize_t len;
+
+ if (plan->flags & F_LINK) {
+ /*
+ * The below test both avoids obviously useless readlink()
+ * calls and ensures that symlinks with existent target do
+ * not match if symlinks are being followed.
+ * Assumption: fts will stat all symlinks that are to be
+ * followed and will return the stat information.
+ */
+ if (entry->fts_info != FTS_NSOK && entry->fts_info != FTS_SL &&
+ entry->fts_info != FTS_SLNONE)
+ return 0;
+ len = readlink(entry->fts_accpath, fn, sizeof(fn) - 1);
+ if (len == -1)
+ return 0;
+ fn[len] = '\0';
+ name = fn;
+ } else if (entry->fts_namelen == 0) {
+ name = basename(entry->fts_path);
+ } else
+ name = entry->fts_name;
+ return !fnmatch(plan->c_data, name,
plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0);
}
new = palloc(option);
/* compare against what */
if (option->flags & F_TIME2_T) {
- new->t_data = get_date(fn_or_tspec, (struct timeb *) 0);
+ new->t_data = get_date(fn_or_tspec);
if (new->t_data == (time_t) -1)
errx(1, "Can't parse date/time: %s", fn_or_tspec);
} else {
new->t_data = sb.st_ctime;
else if (option->flags & F_TIME2_A)
new->t_data = sb.st_atime;
+ else if (option->flags & F_TIME2_B)
+ new->t_data = sb.st_birthtime;
else
new->t_data = sb.st_mtime;
}
return new;
}
-/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or */
+/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */
PLAN *
c_simple(OPTION *option, char ***argvp __unused)
}
/* c_or == c_simple */
+
+/*
+ * -false
+ *
+ * Always false.
+ */
+int
+f_false(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ return 0;
+}
+
+/* c_false == c_simple */
+
+/*
+ * -quit
+ *
+ * Exits the program
+ */
+int
+f_quit(PLAN *plan __unused, FTSENT *entry __unused)
+{
+ exit(0);
+}
+
+/* c_quit == c_simple */