]> git.saurik.com Git - apple/shell_cmds.git/blobdiff - find/function.c
shell_cmds-216.60.1.tar.gz
[apple/shell_cmds.git] / find / function.c
index 0e6028e9b8cf046b5acf0c3e828f5abc16e818df..a673a025f1691d2518503c67ebfab6efd6e42d3b 100644 (file)
  * 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.
@@ -41,7 +37,7 @@ static const char sccsid[] = "@(#)function.c  8.10 (Berkeley) 5/4/95";
 #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>
@@ -50,7 +46,6 @@ __FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.58 2006/05/27 18:27:41 krion
 #include <sys/acl.h>
 #include <sys/wait.h>
 #include <sys/mount.h>
-#include <sys/timeb.h>
 
 #include <dirent.h>
 #include <err.h>
@@ -67,15 +62,17 @@ __FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.58 2006/05/27 18:27:41 krion
 #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 *);
@@ -84,6 +81,7 @@ static char *nextarg(OPTION *, char ***);
 extern char **environ;
 
 static PLAN *lastexecplus = NULL;
+int execplus_error;
 
 #define        COMPARE(a, b) do {                                              \
        switch (plan->flags & F_ELG_MASK) {                             \
@@ -320,10 +318,10 @@ f_Xtime(PLAN *plan, FTSENT *entry)
        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 *
@@ -337,7 +335,7 @@ c_Xtime(OPTION *option, char ***argvp)
 
        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;
 }
@@ -370,56 +368,115 @@ c_mXXdepth(OPTION *option, char ***argvp)
        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 --
@@ -436,11 +493,13 @@ f_delete(PLAN *plan __unused, FTSENT *entry)
 
        /* 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",
@@ -450,7 +509,7 @@ f_delete(PLAN *plan __unused, FTSENT *entry)
        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 */
@@ -471,8 +530,6 @@ c_delete(OPTION *option, char ***argvp __unused)
 {
 
        ftsoptions &= ~FTS_NOSTAT;      /* no optimise */
-       ftsoptions |= FTS_PHYSICAL;     /* disable -follow */
-       ftsoptions &= ~FTS_LOGICAL;     /* disable -follow */
        isoutput = 1;                   /* possible output */
        isdepth = 1;                    /* -depth implied */
 
@@ -483,7 +540,7 @@ c_delete(OPTION *option, char ***argvp __unused)
 /*
  * 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)
@@ -559,7 +616,7 @@ f_empty(PLAN *plan __unused, FTSENT *entry)
                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' &&
@@ -663,8 +720,10 @@ doexec:    if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv))
                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));
 }
 
@@ -771,7 +830,7 @@ done:       *argvp = argv + 1;
 
 /* Finish any pending -exec ... {} + functions. */
 void
-finish_execplus()
+finish_execplus(void)
 {
        PLAN *p;
 
@@ -852,7 +911,8 @@ f_fstype(PLAN *plan, FTSENT *entry)
        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)
@@ -894,13 +954,13 @@ f_fstype(PLAN *plan, FTSENT *entry)
                 * 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();
        }
@@ -911,22 +971,11 @@ c_fstype(OPTION *option, char ***argvp)
 {
        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")) {
@@ -944,12 +993,8 @@ c_fstype(OPTION *option, char ***argvp)
                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;
 }
 
@@ -981,7 +1026,7 @@ c_group(OPTION *option, char ***argvp)
        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')
@@ -1019,6 +1064,30 @@ c_inum(OPTION *option, char ***argvp)
        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 --
  *
@@ -1074,7 +1143,31 @@ c_ls(OPTION *option, char ***argvp __unused)
 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);
 }
 
@@ -1123,7 +1216,7 @@ c_newer(OPTION *option, char ***argvp)
        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 {
@@ -1133,6 +1226,8 @@ c_newer(OPTION *option, char ***argvp)
                        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;
        }
@@ -1367,7 +1462,7 @@ c_regex(OPTION *option, char ***argvp)
        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)
@@ -1649,3 +1744,29 @@ f_or(PLAN *plan, FTSENT *entry)
 }
 
 /* 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 */