Author: emaste Date: Tue Apr 10 23:29:57 2018 New Revision: 332396 URL: https://svnweb.freebsd.org/changeset/base/332396
Log: setfacl: add recursive functionality Add a -R option to setfacl to operate recursively on directories, along with the accompanying flags -H, -L, and -P (whose behaviour mimics chmod). A patch was submitted with PR 155163, but this is a new implementation based on comments raised in the Phabricator review for that patch (review D9096). PR: 155163 Submitted by: Mitchell Horne <mhorne...@gmail.com> Reviewed by: jilles MFC after: 2 weeks Relnotes: Yes Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D14934 Modified: head/bin/setfacl/setfacl.1 head/bin/setfacl/setfacl.c head/bin/setfacl/setfacl.h head/bin/setfacl/util.c Modified: head/bin/setfacl/setfacl.1 ============================================================================== --- head/bin/setfacl/setfacl.1 Tue Apr 10 22:57:56 2018 (r332395) +++ head/bin/setfacl/setfacl.1 Tue Apr 10 23:29:57 2018 (r332396) @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 23, 2016 +.Dd April 10, 2018 .Dt SETFACL 1 .Os .Sh NAME @@ -34,6 +34,7 @@ .Nd set ACL information .Sh SYNOPSIS .Nm +.Op Fl R Op Fl H | L | P .Op Fl bdhkn .Op Fl a Ar position entries .Op Fl m Ar entries @@ -83,6 +84,12 @@ default ACL's. This option is not applicable to NFSv4 .It Fl h If the target of the operation is a symbolic link, perform the operation on the symbolic link itself, rather than following the link. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed +and hence unaffected by the command. +(Symbolic links encountered during tree traversal are not followed.) .It Fl k Delete any default ACL entries on the specified files. It @@ -91,6 +98,10 @@ any default ACL entries. An error will be reported if any of the specified files cannot have a default entry (i.e.\& non-directories). This option is not applicable to NFSv4 ACLs. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. .It Fl m Ar entries Modify the ACL on the specified file. New entries will be added, and existing entries will be modified @@ -116,6 +127,13 @@ the input is taken from stdin. Do not recalculate the permissions associated with the ACL mask entry. This option is not applicable to NFSv4 ACLs. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl R +Perform the action recursively on any specified directories. .It Fl x Ar entries | position If .Ar entries Modified: head/bin/setfacl/setfacl.c ============================================================================== --- head/bin/setfacl/setfacl.c Tue Apr 10 22:57:56 2018 (r332395) +++ head/bin/setfacl/setfacl.c Tue Apr 10 23:29:57 2018 (r332396) @@ -35,6 +35,9 @@ __FBSDID("$FreeBSD$"); #include <err.h> #include <errno.h> +#include <fts.h> +#include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -59,41 +62,20 @@ struct sf_entry { }; static TAILQ_HEAD(, sf_entry) entrylist; -/* TAILQ entry for files */ -struct sf_file { - const char *filename; - TAILQ_ENTRY(sf_file) next; -}; -static TAILQ_HEAD(, sf_file) filelist; - uint have_mask; uint need_mask; uint have_stdin; uint n_flag; -static void add_filename(const char *filename); static void usage(void); static void -add_filename(const char *filename) -{ - struct sf_file *file; - - if (strlen(filename) > PATH_MAX - 1) { - warn("illegal filename"); - return; - } - file = zmalloc(sizeof(struct sf_file)); - file->filename = filename; - TAILQ_INSERT_TAIL(&filelist, file, next); -} - -static void usage(void) { - fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] " - "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n"); + fprintf(stderr, "usage: setfacl [-R [-H | -L | -P]] [-bdhkn] " + "[-a position entries] [-m entries] [-M file] " + "[-x entries] [-X file] [file ...]\n"); exit(1); } @@ -104,23 +86,32 @@ main(int argc, char *argv[]) acl_type_t acl_type; acl_entry_t unused_entry; char filename[PATH_MAX]; - int local_error, carried_error, ch, i, entry_number, ret; - int h_flag; - struct sf_file *file; + int local_error, carried_error, ch, entry_number, ret, fts_options; + bool h_flag, H_flag, L_flag, R_flag, follow_symlink; + size_t fl_count, i; + FTS *ftsp; + FTSENT *file; + char **files_list; struct sf_entry *entry; - const char *fn_dup; char *end; - struct stat sb; acl_type = ACL_TYPE_ACCESS; - carried_error = local_error = 0; - h_flag = have_mask = have_stdin = n_flag = need_mask = 0; + carried_error = local_error = fts_options = 0; + have_mask = have_stdin = n_flag = need_mask = 0; + h_flag = H_flag = L_flag = R_flag = false; TAILQ_INIT(&entrylist); - TAILQ_INIT(&filelist); - while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1) + while ((ch = getopt(argc, argv, "HLM:PRX:a:bdhkm:nx:")) != -1) switch(ch) { + case 'H': + H_flag = true; + L_flag = false; + break; + case 'L': + L_flag = true; + H_flag = false; + break; case 'M': entry = zmalloc(sizeof(struct sf_entry)); entry->acl = get_acl_from_file(optarg); @@ -129,6 +120,12 @@ main(int argc, char *argv[]) entry->op = OP_MERGE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; + case 'P': + H_flag = L_flag = false; + break; + case 'R': + R_flag = true; + break; case 'X': entry = zmalloc(sizeof(struct sf_entry)); entry->acl = get_acl_from_file(optarg); @@ -213,43 +210,93 @@ main(int argc, char *argv[]) err(1, "cannot have more than one stdin"); have_stdin = 1; bzero(&filename, sizeof(filename)); + i = 0; + /* Start with an array size sufficient for basic cases. */ + fl_count = 1024; + files_list = zmalloc(fl_count * sizeof(char *)); while (fgets(filename, (int)sizeof(filename), stdin)) { /* remove the \n */ filename[strlen(filename) - 1] = '\0'; - fn_dup = strdup(filename); - if (fn_dup == NULL) + files_list[i] = strdup(filename); + if (files_list[i] == NULL) err(1, "strdup() failed"); - add_filename(fn_dup); + /* Grow array if necessary. */ + if (++i == fl_count) { + fl_count <<= 1; + if (fl_count > SIZE_MAX / sizeof(char *)) + errx(1, "Too many input files"); + files_list = zrealloc(files_list, + fl_count * sizeof(char *)); + } } + + /* fts_open() requires the last array element to be NULL. */ + files_list[i] = NULL; } else - for (i = 0; i < argc; i++) - add_filename(argv[i]); + files_list = argv; - /* cycle through each file */ - TAILQ_FOREACH(file, &filelist, next) { - local_error = 0; + if (R_flag) { + if (h_flag) + errx(1, "the -R and -h options may not be " + "specified together."); + if (L_flag) { + fts_options = FTS_LOGICAL; + } else { + fts_options = FTS_PHYSICAL; - if (stat(file->filename, &sb) == -1) { - warn("%s: stat() failed", file->filename); - carried_error++; + if (H_flag) { + fts_options |= FTS_COMFOLLOW; + } + } + } else if (h_flag) { + fts_options = FTS_PHYSICAL; + } else { + fts_options = FTS_LOGICAL; + } + + /* Open all files. */ + if ((ftsp = fts_open(files_list, fts_options | FTS_NOSTAT, 0)) == NULL) + err(1, "fts_open"); + while ((file = fts_read(ftsp)) != NULL) { + switch (file->fts_info) { + case FTS_D: + /* Do not recurse if -R not specified. */ + if (!R_flag) + fts_set(ftsp, file, FTS_SKIP); + break; + case FTS_DP: + /* Skip the second visit to a directory. */ continue; + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", file->fts_path, + strerror(file->fts_errno)); + continue; + default: + break; } - if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) { - warnx("%s: default ACL may only be set on a directory", - file->filename); + if (acl_type == ACL_TYPE_DEFAULT && file->fts_info != FTS_D) { + warnx("%s: default ACL may only be set on " + "a directory", file->fts_path); carried_error++; continue; } - if (h_flag) - ret = lpathconf(file->filename, _PC_ACL_NFS4); + local_error = 0; + + follow_symlink = ((fts_options & FTS_LOGICAL) || + ((fts_options & FTS_COMFOLLOW) && + file->fts_level == FTS_ROOTLEVEL)); + + if (follow_symlink) + ret = pathconf(file->fts_accpath, _PC_ACL_NFS4); else - ret = pathconf(file->filename, _PC_ACL_NFS4); + ret = lpathconf(file->fts_accpath, _PC_ACL_NFS4); if (ret > 0) { if (acl_type == ACL_TYPE_DEFAULT) { warnx("%s: there are no default entries " - "in NFSv4 ACLs", file->filename); + "in NFSv4 ACLs", file->fts_path); carried_error++; continue; } @@ -259,20 +306,20 @@ main(int argc, char *argv[]) acl_type = ACL_TYPE_ACCESS; } else if (ret < 0 && errno != EINVAL) { warn("%s: pathconf(..., _PC_ACL_NFS4) failed", - file->filename); + file->fts_path); } - if (h_flag) - acl = acl_get_link_np(file->filename, acl_type); + if (follow_symlink) + acl = acl_get_file(file->fts_accpath, acl_type); else - acl = acl_get_file(file->filename, acl_type); + acl = acl_get_link_np(file->fts_accpath, acl_type); if (acl == NULL) { - if (h_flag) - warn("%s: acl_get_link_np() failed", - file->filename); - else + if (follow_symlink) warn("%s: acl_get_file() failed", - file->filename); + file->fts_path); + else + warn("%s: acl_get_link_np() failed", + file->fts_path); carried_error++; continue; } @@ -285,11 +332,12 @@ main(int argc, char *argv[]) switch(entry->op) { case OP_ADD_ACL: local_error += add_acl(entry->acl, - entry->entry_number, &acl, file->filename); + entry->entry_number, + &acl, file->fts_path); break; case OP_MERGE_ACL: local_error += merge_acl(entry->acl, &acl, - file->filename); + file->fts_path); need_mask = 1; break; case OP_REMOVE_EXT: @@ -301,37 +349,37 @@ main(int argc, char *argv[]) acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { local_error += remove_default(&acl, - file->filename); + file->fts_path); break; } - remove_ext(&acl, file->filename); + remove_ext(&acl, file->fts_path); need_mask = 0; break; case OP_REMOVE_DEF: if (acl_type == ACL_TYPE_NFS4) { warnx("%s: there are no default entries in NFSv4 ACLs; " - "cannot remove", file->filename); + "cannot remove", file->fts_path); local_error++; break; } - if (acl_delete_def_file(file->filename) == -1) { + if (acl_delete_def_file(file->fts_accpath) == -1) { warn("%s: acl_delete_def_file() failed", - file->filename); + file->fts_path); local_error++; } if (acl_type == ACL_TYPE_DEFAULT) local_error += remove_default(&acl, - file->filename); + file->fts_path); need_mask = 0; break; case OP_REMOVE_ACL: local_error += remove_acl(entry->acl, &acl, - file->filename); + file->fts_path); need_mask = 1; break; case OP_REMOVE_BY_NUMBER: local_error += remove_by_number(entry->entry_number, - &acl, file->filename); + &acl, file->fts_path); need_mask = 1; break; } @@ -343,9 +391,9 @@ main(int argc, char *argv[]) */ if (acl_type == ACL_TYPE_DEFAULT && acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { - if (acl_delete_def_file(file->filename) == -1) { + if (acl_delete_def_file(file->fts_accpath) == -1) { warn("%s: acl_delete_def_file() failed", - file->filename); + file->fts_path); carried_error++; } continue; @@ -358,22 +406,22 @@ main(int argc, char *argv[]) } if (acl_type != ACL_TYPE_NFS4 && need_mask && - set_acl_mask(&acl, file->filename) == -1) { - warnx("%s: failed to set ACL mask", file->filename); + set_acl_mask(&acl, file->fts_path) == -1) { + warnx("%s: failed to set ACL mask", file->fts_path); carried_error++; - } else if (h_flag) { - if (acl_set_link_np(file->filename, acl_type, + } else if (follow_symlink) { + if (acl_set_file(file->fts_accpath, acl_type, acl) == -1) { carried_error++; - warn("%s: acl_set_link_np() failed", - file->filename); + warn("%s: acl_set_file() failed", + file->fts_path); } } else { - if (acl_set_file(file->filename, acl_type, + if (acl_set_link_np(file->fts_accpath, acl_type, acl) == -1) { carried_error++; - warn("%s: acl_set_file() failed", - file->filename); + warn("%s: acl_set_link_np() failed", + file->fts_path); } } Modified: head/bin/setfacl/setfacl.h ============================================================================== --- head/bin/setfacl/setfacl.h Tue Apr 10 22:57:56 2018 (r332395) +++ head/bin/setfacl/setfacl.h Tue Apr 10 23:29:57 2018 (r332396) @@ -47,6 +47,7 @@ void remove_ext(acl_t *prev_acl, const char *filenam int set_acl_mask(acl_t *prev_acl, const char *filename); /* util.c */ void *zmalloc(size_t size); +void *zrealloc(void *ptr, size_t size); const char *brand_name(int brand); int branding_mismatch(int brand1, int brand2); Modified: head/bin/setfacl/util.c ============================================================================== --- head/bin/setfacl/util.c Tue Apr 10 22:57:56 2018 (r332395) +++ head/bin/setfacl/util.c Tue Apr 10 23:29:57 2018 (r332396) @@ -44,6 +44,17 @@ zmalloc(size_t size) return (ptr); } +void * +zrealloc(void *ptr, size_t size) +{ + void *newptr; + + newptr = realloc(ptr, size); + if (newptr == NULL) + err(1, "realloc() failed"); + return (newptr); +} + const char * brand_name(int brand) { _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"