1*7c0103adSwiz /* $NetBSD: setfacl.c,v 1.3 2020/06/18 19:44:01 wiz Exp $ */
29aa2a9c3Schristos
39aa2a9c3Schristos /*-
49aa2a9c3Schristos * Copyright (c) 2001 Chris D. Faulhaber
59aa2a9c3Schristos * All rights reserved.
69aa2a9c3Schristos *
79aa2a9c3Schristos * Redistribution and use in source and binary forms, with or without
89aa2a9c3Schristos * modification, are permitted provided that the following conditions
99aa2a9c3Schristos * are met:
109aa2a9c3Schristos * 1. Redistributions of source code must retain the above copyright
119aa2a9c3Schristos * notice, this list of conditions and the following disclaimer.
129aa2a9c3Schristos * 2. Redistributions in binary form must reproduce the above copyright
139aa2a9c3Schristos * notice, this list of conditions and the following disclaimer in the
149aa2a9c3Schristos * documentation and/or other materials provided with the distribution.
159aa2a9c3Schristos *
169aa2a9c3Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
179aa2a9c3Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
189aa2a9c3Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
199aa2a9c3Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
209aa2a9c3Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
219aa2a9c3Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
229aa2a9c3Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
239aa2a9c3Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
249aa2a9c3Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259aa2a9c3Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269aa2a9c3Schristos * SUCH DAMAGE.
279aa2a9c3Schristos */
289aa2a9c3Schristos
299aa2a9c3Schristos #include <sys/cdefs.h>
309aa2a9c3Schristos #if 0
319aa2a9c3Schristos __FBSDID("$FreeBSD: head/bin/setfacl/setfacl.c 339793 2018-10-26 21:17:06Z markj $");
329aa2a9c3Schristos #else
33*7c0103adSwiz __RCSID("$NetBSD: setfacl.c,v 1.3 2020/06/18 19:44:01 wiz Exp $");
349aa2a9c3Schristos #endif
359aa2a9c3Schristos
369aa2a9c3Schristos #include <sys/param.h>
379aa2a9c3Schristos #include <sys/acl.h>
389aa2a9c3Schristos #include <sys/queue.h>
399aa2a9c3Schristos
409aa2a9c3Schristos #include <err.h>
419aa2a9c3Schristos #include <errno.h>
429aa2a9c3Schristos #include <fts.h>
439aa2a9c3Schristos #include <stdbool.h>
449aa2a9c3Schristos #include <stdint.h>
459aa2a9c3Schristos #include <stdio.h>
469aa2a9c3Schristos #include <stdlib.h>
479aa2a9c3Schristos #include <string.h>
489aa2a9c3Schristos #include <unistd.h>
499aa2a9c3Schristos
509aa2a9c3Schristos #include "setfacl.h"
519aa2a9c3Schristos
529aa2a9c3Schristos /* file operations */
539aa2a9c3Schristos #define OP_MERGE_ACL 0x00 /* merge acl's (-mM) */
549aa2a9c3Schristos #define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */
559aa2a9c3Schristos #define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */
569aa2a9c3Schristos #define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */
579aa2a9c3Schristos #define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */
589aa2a9c3Schristos #define OP_ADD_ACL 0x05 /* add acls entries at a given position */
599aa2a9c3Schristos
609aa2a9c3Schristos /* TAILQ entry for acl operations */
619aa2a9c3Schristos struct sf_entry {
629aa2a9c3Schristos uint op;
639aa2a9c3Schristos acl_t acl;
649aa2a9c3Schristos uint entry_number;
659aa2a9c3Schristos TAILQ_ENTRY(sf_entry) next;
669aa2a9c3Schristos };
679aa2a9c3Schristos static TAILQ_HEAD(, sf_entry) entrylist;
689aa2a9c3Schristos
699aa2a9c3Schristos bool have_mask;
709aa2a9c3Schristos bool have_stdin;
719aa2a9c3Schristos bool n_flag;
729aa2a9c3Schristos static bool h_flag;
739aa2a9c3Schristos static bool H_flag;
749aa2a9c3Schristos static bool L_flag;
759aa2a9c3Schristos static bool R_flag;
769aa2a9c3Schristos static bool need_mask;
779aa2a9c3Schristos static acl_type_t acl_type = ACL_TYPE_ACCESS;
789aa2a9c3Schristos
799aa2a9c3Schristos static int handle_file(FTS *ftsp, FTSENT *file);
809aa2a9c3Schristos static acl_t clear_inheritance_flags(acl_t acl);
819aa2a9c3Schristos static char **stdin_files(void);
824fc30265Sjoerg static __dead void usage(void);
839aa2a9c3Schristos
849aa2a9c3Schristos static void
usage(void)859aa2a9c3Schristos usage(void)
869aa2a9c3Schristos {
879aa2a9c3Schristos
88*7c0103adSwiz fprintf(stderr, "usage: setfacl [-bdhkn] "
89*7c0103adSwiz "[-a position entries] [-M file] [-m entries] "
90*7c0103adSwiz "[-R [-H | -L | -P]] [-X file] [-x entries | position] [file ...]\n");
919aa2a9c3Schristos exit(1);
929aa2a9c3Schristos }
939aa2a9c3Schristos
949aa2a9c3Schristos static char **
stdin_files(void)959aa2a9c3Schristos stdin_files(void)
969aa2a9c3Schristos {
979aa2a9c3Schristos char **files_list;
989aa2a9c3Schristos char filename[PATH_MAX];
999aa2a9c3Schristos size_t fl_count, i;
1009aa2a9c3Schristos
1019aa2a9c3Schristos if (have_stdin)
1029aa2a9c3Schristos err(1, "cannot have more than one stdin");
1039aa2a9c3Schristos
1049aa2a9c3Schristos i = 0;
1059aa2a9c3Schristos have_stdin = true;
1069aa2a9c3Schristos bzero(&filename, sizeof(filename));
1079aa2a9c3Schristos /* Start with an array size sufficient for basic cases. */
1089aa2a9c3Schristos fl_count = 1024;
1099aa2a9c3Schristos files_list = zmalloc(fl_count * sizeof(char *));
1109aa2a9c3Schristos while (fgets(filename, (int)sizeof(filename), stdin)) {
1119aa2a9c3Schristos /* remove the \n */
1129aa2a9c3Schristos filename[strlen(filename) - 1] = '\0';
1139aa2a9c3Schristos files_list[i] = strdup(filename);
1149aa2a9c3Schristos if (files_list[i] == NULL)
1159aa2a9c3Schristos err(1, "strdup() failed");
1169aa2a9c3Schristos /* Grow array if necessary. */
1179aa2a9c3Schristos if (++i == fl_count) {
1189aa2a9c3Schristos fl_count <<= 1;
1199aa2a9c3Schristos if (fl_count > SIZE_MAX / sizeof(char *))
1209aa2a9c3Schristos errx(1, "Too many input files");
1219aa2a9c3Schristos files_list = zrealloc(files_list,
1229aa2a9c3Schristos fl_count * sizeof(char *));
1239aa2a9c3Schristos }
1249aa2a9c3Schristos }
1259aa2a9c3Schristos
1269aa2a9c3Schristos /* fts_open() requires the last array element to be NULL. */
1279aa2a9c3Schristos files_list[i] = NULL;
1289aa2a9c3Schristos
1299aa2a9c3Schristos return (files_list);
1309aa2a9c3Schristos }
1319aa2a9c3Schristos
1329aa2a9c3Schristos /*
1339aa2a9c3Schristos * Remove any inheritance flags from NFSv4 ACLs when running in recursive
1349aa2a9c3Schristos * mode. This is to avoid files being assigned identical ACLs to their
1359aa2a9c3Schristos * parent directory while also being set to inherit them.
1369aa2a9c3Schristos *
1379aa2a9c3Schristos * The acl argument is assumed to be valid.
1389aa2a9c3Schristos */
1399aa2a9c3Schristos static acl_t
clear_inheritance_flags(acl_t acl)1409aa2a9c3Schristos clear_inheritance_flags(acl_t acl)
1419aa2a9c3Schristos {
1429aa2a9c3Schristos acl_t nacl;
1439aa2a9c3Schristos acl_entry_t acl_entry;
1449aa2a9c3Schristos acl_flagset_t acl_flagset;
1459aa2a9c3Schristos int acl_brand, entry_id;
1469aa2a9c3Schristos
1479aa2a9c3Schristos (void)acl_get_brand_np(acl, &acl_brand);
1489aa2a9c3Schristos if (acl_brand != ACL_BRAND_NFS4)
1499aa2a9c3Schristos return (acl);
1509aa2a9c3Schristos
1519aa2a9c3Schristos nacl = acl_dup(acl);
1529aa2a9c3Schristos if (nacl == NULL) {
1539aa2a9c3Schristos warn("acl_dup() failed");
1549aa2a9c3Schristos return (acl);
1559aa2a9c3Schristos }
1569aa2a9c3Schristos
1579aa2a9c3Schristos entry_id = ACL_FIRST_ENTRY;
1589aa2a9c3Schristos while (acl_get_entry(nacl, entry_id, &acl_entry) == 1) {
1599aa2a9c3Schristos entry_id = ACL_NEXT_ENTRY;
1609aa2a9c3Schristos if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
1619aa2a9c3Schristos warn("acl_get_flagset_np() failed");
1629aa2a9c3Schristos continue;
1639aa2a9c3Schristos }
1649aa2a9c3Schristos if (acl_get_flag_np(acl_flagset, ACL_ENTRY_INHERIT_ONLY) == 1) {
1659aa2a9c3Schristos if (acl_delete_entry(nacl, acl_entry) != 0)
1669aa2a9c3Schristos warn("acl_delete_entry() failed");
1679aa2a9c3Schristos continue;
1689aa2a9c3Schristos }
1699aa2a9c3Schristos if (acl_delete_flag_np(acl_flagset,
1709aa2a9c3Schristos ACL_ENTRY_FILE_INHERIT |
1719aa2a9c3Schristos ACL_ENTRY_DIRECTORY_INHERIT |
1729aa2a9c3Schristos ACL_ENTRY_NO_PROPAGATE_INHERIT) != 0)
1739aa2a9c3Schristos warn("acl_delete_flag_np() failed");
1749aa2a9c3Schristos }
1759aa2a9c3Schristos
1769aa2a9c3Schristos return (nacl);
1779aa2a9c3Schristos }
1789aa2a9c3Schristos
1799aa2a9c3Schristos static int
handle_file(FTS * ftsp,FTSENT * file)1809aa2a9c3Schristos handle_file(FTS *ftsp, FTSENT *file)
1819aa2a9c3Schristos {
1829aa2a9c3Schristos acl_t acl, nacl;
1839aa2a9c3Schristos acl_entry_t unused_entry;
1849aa2a9c3Schristos int local_error, ret;
1859aa2a9c3Schristos struct sf_entry *entry;
1869aa2a9c3Schristos bool follow_symlink;
1879aa2a9c3Schristos
1889aa2a9c3Schristos local_error = 0;
1899aa2a9c3Schristos switch (file->fts_info) {
1909aa2a9c3Schristos case FTS_D:
1919aa2a9c3Schristos /* Do not recurse if -R not specified. */
1929aa2a9c3Schristos if (!R_flag)
1939aa2a9c3Schristos fts_set(ftsp, file, FTS_SKIP);
1949aa2a9c3Schristos break;
1959aa2a9c3Schristos case FTS_DP:
1969aa2a9c3Schristos /* Skip the second visit to a directory. */
1979aa2a9c3Schristos return (0);
1989aa2a9c3Schristos case FTS_DNR:
1999aa2a9c3Schristos case FTS_ERR:
2009aa2a9c3Schristos warnx("%s: %s", file->fts_path, strerror(file->fts_errno));
2019aa2a9c3Schristos return (0);
2029aa2a9c3Schristos default:
2039aa2a9c3Schristos break;
2049aa2a9c3Schristos }
2059aa2a9c3Schristos
2069aa2a9c3Schristos if (acl_type == ACL_TYPE_DEFAULT && file->fts_info != FTS_D) {
2079aa2a9c3Schristos warnx("%s: default ACL may only be set on a directory",
2089aa2a9c3Schristos file->fts_path);
2099aa2a9c3Schristos return (1);
2109aa2a9c3Schristos }
2119aa2a9c3Schristos
2129aa2a9c3Schristos follow_symlink = (!R_flag && !h_flag) || (R_flag && L_flag) ||
2139aa2a9c3Schristos (R_flag && H_flag && file->fts_level == FTS_ROOTLEVEL);
2149aa2a9c3Schristos
2159aa2a9c3Schristos if (follow_symlink)
2169aa2a9c3Schristos ret = pathconf(file->fts_accpath, _PC_ACL_NFS4);
2179aa2a9c3Schristos else
2189aa2a9c3Schristos ret = lpathconf(file->fts_accpath, _PC_ACL_NFS4);
2199aa2a9c3Schristos if (ret > 0) {
2209aa2a9c3Schristos if (acl_type == ACL_TYPE_DEFAULT) {
2219aa2a9c3Schristos warnx("%s: there are no default entries in NFSv4 ACLs",
2229aa2a9c3Schristos file->fts_path);
2239aa2a9c3Schristos return (1);
2249aa2a9c3Schristos }
2259aa2a9c3Schristos acl_type = ACL_TYPE_NFS4;
2269aa2a9c3Schristos } else if (ret == 0) {
2279aa2a9c3Schristos if (acl_type == ACL_TYPE_NFS4)
2289aa2a9c3Schristos acl_type = ACL_TYPE_ACCESS;
2299aa2a9c3Schristos } else if (ret < 0 && errno != EINVAL && errno != ENOENT) {
2309aa2a9c3Schristos warn("%s: pathconf(_PC_ACL_NFS4) failed",
2319aa2a9c3Schristos file->fts_path);
2329aa2a9c3Schristos }
2339aa2a9c3Schristos
2349aa2a9c3Schristos if (follow_symlink)
2359aa2a9c3Schristos acl = acl_get_file(file->fts_accpath, acl_type);
2369aa2a9c3Schristos else
2379aa2a9c3Schristos acl = acl_get_link_np(file->fts_accpath, acl_type);
2389aa2a9c3Schristos if (acl == NULL) {
2399aa2a9c3Schristos if (follow_symlink)
2409aa2a9c3Schristos warn("%s: acl_get_file() failed", file->fts_path);
2419aa2a9c3Schristos else
2429aa2a9c3Schristos warn("%s: acl_get_link_np() failed", file->fts_path);
2439aa2a9c3Schristos return (1);
2449aa2a9c3Schristos }
2459aa2a9c3Schristos
2469aa2a9c3Schristos /* Cycle through each option. */
2479aa2a9c3Schristos TAILQ_FOREACH(entry, &entrylist, next) {
2489aa2a9c3Schristos nacl = entry->acl;
2499aa2a9c3Schristos switch (entry->op) {
2509aa2a9c3Schristos case OP_ADD_ACL:
2519aa2a9c3Schristos if (R_flag && file->fts_info != FTS_D &&
2529aa2a9c3Schristos acl_type == ACL_TYPE_NFS4)
2539aa2a9c3Schristos nacl = clear_inheritance_flags(nacl);
2549aa2a9c3Schristos local_error += add_acl(nacl, entry->entry_number, &acl,
2559aa2a9c3Schristos file->fts_path);
2569aa2a9c3Schristos break;
2579aa2a9c3Schristos case OP_MERGE_ACL:
2589aa2a9c3Schristos if (R_flag && file->fts_info != FTS_D &&
2599aa2a9c3Schristos acl_type == ACL_TYPE_NFS4)
2609aa2a9c3Schristos nacl = clear_inheritance_flags(nacl);
2619aa2a9c3Schristos local_error += merge_acl(nacl, &acl, file->fts_path);
2629aa2a9c3Schristos need_mask = true;
2639aa2a9c3Schristos break;
2649aa2a9c3Schristos case OP_REMOVE_EXT:
2659aa2a9c3Schristos /*
2669aa2a9c3Schristos * Don't try to call remove_ext() for empty
2679aa2a9c3Schristos * default ACL.
2689aa2a9c3Schristos */
2699aa2a9c3Schristos if (acl_type == ACL_TYPE_DEFAULT &&
2709aa2a9c3Schristos acl_get_entry(acl, ACL_FIRST_ENTRY,
2719aa2a9c3Schristos &unused_entry) == 0) {
2729aa2a9c3Schristos local_error += remove_default(&acl,
2739aa2a9c3Schristos file->fts_path);
2749aa2a9c3Schristos break;
2759aa2a9c3Schristos }
2769aa2a9c3Schristos remove_ext(&acl, file->fts_path);
2779aa2a9c3Schristos need_mask = false;
2789aa2a9c3Schristos break;
2799aa2a9c3Schristos case OP_REMOVE_DEF:
2809aa2a9c3Schristos if (acl_type == ACL_TYPE_NFS4) {
2819aa2a9c3Schristos warnx("%s: there are no default entries in "
2829aa2a9c3Schristos "NFSv4 ACLs; cannot remove",
2839aa2a9c3Schristos file->fts_path);
2849aa2a9c3Schristos local_error++;
2859aa2a9c3Schristos break;
2869aa2a9c3Schristos }
2879aa2a9c3Schristos if (acl_delete_def_file(file->fts_accpath) == -1) {
2889aa2a9c3Schristos warn("%s: acl_delete_def_file() failed",
2899aa2a9c3Schristos file->fts_path);
2909aa2a9c3Schristos local_error++;
2919aa2a9c3Schristos }
2929aa2a9c3Schristos if (acl_type == ACL_TYPE_DEFAULT)
2939aa2a9c3Schristos local_error += remove_default(&acl,
2949aa2a9c3Schristos file->fts_path);
2959aa2a9c3Schristos need_mask = false;
2969aa2a9c3Schristos break;
2979aa2a9c3Schristos case OP_REMOVE_ACL:
2989aa2a9c3Schristos local_error += remove_acl(nacl, &acl, file->fts_path);
2999aa2a9c3Schristos need_mask = true;
3009aa2a9c3Schristos break;
3019aa2a9c3Schristos case OP_REMOVE_BY_NUMBER:
3029aa2a9c3Schristos local_error += remove_by_number(entry->entry_number,
3039aa2a9c3Schristos &acl, file->fts_path);
3049aa2a9c3Schristos need_mask = true;
3059aa2a9c3Schristos break;
3069aa2a9c3Schristos }
3079aa2a9c3Schristos
3089aa2a9c3Schristos if (nacl != entry->acl) {
3099aa2a9c3Schristos acl_free(nacl);
3109aa2a9c3Schristos nacl = NULL;
3119aa2a9c3Schristos }
3129aa2a9c3Schristos if (local_error)
3139aa2a9c3Schristos break;
3149aa2a9c3Schristos }
3159aa2a9c3Schristos
3169aa2a9c3Schristos ret = 0;
3179aa2a9c3Schristos
3189aa2a9c3Schristos /*
3199aa2a9c3Schristos * Don't try to set an empty default ACL; it will always fail.
3209aa2a9c3Schristos * Use acl_delete_def_file(3) instead.
3219aa2a9c3Schristos */
3229aa2a9c3Schristos if (acl_type == ACL_TYPE_DEFAULT &&
3239aa2a9c3Schristos acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) {
3249aa2a9c3Schristos if (acl_delete_def_file(file->fts_accpath) == -1) {
3259aa2a9c3Schristos warn("%s: acl_delete_def_file() failed",
3269aa2a9c3Schristos file->fts_path);
3279aa2a9c3Schristos ret = 1;
3289aa2a9c3Schristos }
3299aa2a9c3Schristos goto out;
3309aa2a9c3Schristos }
3319aa2a9c3Schristos
3329aa2a9c3Schristos /* Don't bother setting the ACL if something is broken. */
3339aa2a9c3Schristos if (local_error) {
3349aa2a9c3Schristos ret = 1;
3359aa2a9c3Schristos } else if (acl_type != ACL_TYPE_NFS4 && need_mask &&
3369aa2a9c3Schristos set_acl_mask(&acl, file->fts_path) == -1) {
3379aa2a9c3Schristos warnx("%s: failed to set ACL mask", file->fts_path);
3389aa2a9c3Schristos ret = 1;
3399aa2a9c3Schristos } else if (follow_symlink) {
3409aa2a9c3Schristos if (acl_set_file(file->fts_accpath, acl_type, acl) == -1) {
3419aa2a9c3Schristos warn("%s: acl_set_file() failed", file->fts_path);
3429aa2a9c3Schristos ret = 1;
3439aa2a9c3Schristos }
3449aa2a9c3Schristos } else {
3459aa2a9c3Schristos if (acl_set_link_np(file->fts_accpath, acl_type, acl) == -1) {
3469aa2a9c3Schristos warn("%s: acl_set_link_np() failed", file->fts_path);
3479aa2a9c3Schristos ret = 1;
3489aa2a9c3Schristos }
3499aa2a9c3Schristos }
3509aa2a9c3Schristos
3519aa2a9c3Schristos out:
3529aa2a9c3Schristos acl_free(acl);
3539aa2a9c3Schristos return (ret);
3549aa2a9c3Schristos }
3559aa2a9c3Schristos
3569aa2a9c3Schristos int
main(int argc,char * argv[])3579aa2a9c3Schristos main(int argc, char *argv[])
3589aa2a9c3Schristos {
3599aa2a9c3Schristos int carried_error, ch, entry_number, fts_options;
3609aa2a9c3Schristos FTS *ftsp;
3619aa2a9c3Schristos FTSENT *file;
3629aa2a9c3Schristos char **files_list;
3639aa2a9c3Schristos struct sf_entry *entry;
3649aa2a9c3Schristos char *end;
3659aa2a9c3Schristos
3669aa2a9c3Schristos acl_type = ACL_TYPE_ACCESS;
3679aa2a9c3Schristos carried_error = fts_options = 0;
3689aa2a9c3Schristos have_mask = have_stdin = n_flag = false;
3699aa2a9c3Schristos
3709aa2a9c3Schristos TAILQ_INIT(&entrylist);
3719aa2a9c3Schristos
3729aa2a9c3Schristos while ((ch = getopt(argc, argv, "HLM:PRX:a:bdhkm:nx:")) != -1)
3739aa2a9c3Schristos switch(ch) {
3749aa2a9c3Schristos case 'H':
3759aa2a9c3Schristos H_flag = true;
3769aa2a9c3Schristos L_flag = false;
3779aa2a9c3Schristos break;
3789aa2a9c3Schristos case 'L':
3799aa2a9c3Schristos L_flag = true;
3809aa2a9c3Schristos H_flag = false;
3819aa2a9c3Schristos break;
3829aa2a9c3Schristos case 'M':
3839aa2a9c3Schristos entry = zmalloc(sizeof(struct sf_entry));
3849aa2a9c3Schristos entry->acl = get_acl_from_file(optarg);
3859aa2a9c3Schristos if (entry->acl == NULL)
3869aa2a9c3Schristos err(1, "%s: get_acl_from_file() failed",
3879aa2a9c3Schristos optarg);
3889aa2a9c3Schristos entry->op = OP_MERGE_ACL;
3899aa2a9c3Schristos TAILQ_INSERT_TAIL(&entrylist, entry, next);
3909aa2a9c3Schristos break;
3919aa2a9c3Schristos case 'P':
3929aa2a9c3Schristos H_flag = L_flag = false;
3939aa2a9c3Schristos break;
3949aa2a9c3Schristos case 'R':
3959aa2a9c3Schristos R_flag = true;
3969aa2a9c3Schristos break;
3979aa2a9c3Schristos case 'X':
3989aa2a9c3Schristos entry = zmalloc(sizeof(struct sf_entry));
3999aa2a9c3Schristos entry->acl = get_acl_from_file(optarg);
4009aa2a9c3Schristos entry->op = OP_REMOVE_ACL;
4019aa2a9c3Schristos TAILQ_INSERT_TAIL(&entrylist, entry, next);
4029aa2a9c3Schristos break;
4039aa2a9c3Schristos case 'a':
4049aa2a9c3Schristos entry = zmalloc(sizeof(struct sf_entry));
4059aa2a9c3Schristos
4069aa2a9c3Schristos entry_number = strtol(optarg, &end, 10);
4079aa2a9c3Schristos if (end - optarg != (int)strlen(optarg))
4089aa2a9c3Schristos errx(1, "%s: invalid entry number", optarg);
4099aa2a9c3Schristos if (entry_number < 0)
4109aa2a9c3Schristos errx(1,
4119aa2a9c3Schristos "%s: entry number cannot be less than zero",
4129aa2a9c3Schristos optarg);
4139aa2a9c3Schristos entry->entry_number = entry_number;
4149aa2a9c3Schristos
4159aa2a9c3Schristos if (argv[optind] == NULL)
4169aa2a9c3Schristos errx(1, "missing ACL");
4179aa2a9c3Schristos entry->acl = acl_from_text(argv[optind]);
4189aa2a9c3Schristos if (entry->acl == NULL)
4199aa2a9c3Schristos err(1, "%s", argv[optind]);
4209aa2a9c3Schristos optind++;
4219aa2a9c3Schristos entry->op = OP_ADD_ACL;
4229aa2a9c3Schristos TAILQ_INSERT_TAIL(&entrylist, entry, next);
4239aa2a9c3Schristos break;
4249aa2a9c3Schristos case 'b':
4259aa2a9c3Schristos entry = zmalloc(sizeof(struct sf_entry));
4269aa2a9c3Schristos entry->op = OP_REMOVE_EXT;
4279aa2a9c3Schristos TAILQ_INSERT_TAIL(&entrylist, entry, next);
4289aa2a9c3Schristos break;
4299aa2a9c3Schristos case 'd':
4309aa2a9c3Schristos acl_type = ACL_TYPE_DEFAULT;
4319aa2a9c3Schristos break;
4329aa2a9c3Schristos case 'h':
4339aa2a9c3Schristos h_flag = 1;
4349aa2a9c3Schristos break;
4359aa2a9c3Schristos case 'k':
4369aa2a9c3Schristos entry = zmalloc(sizeof(struct sf_entry));
4379aa2a9c3Schristos entry->op = OP_REMOVE_DEF;
4389aa2a9c3Schristos TAILQ_INSERT_TAIL(&entrylist, entry, next);
4399aa2a9c3Schristos break;
4409aa2a9c3Schristos case 'm':
4419aa2a9c3Schristos entry = zmalloc(sizeof(struct sf_entry));
4429aa2a9c3Schristos entry->acl = acl_from_text(optarg);
4439aa2a9c3Schristos if (entry->acl == NULL)
4449aa2a9c3Schristos err(1, "%s", optarg);
4459aa2a9c3Schristos entry->op = OP_MERGE_ACL;
4469aa2a9c3Schristos TAILQ_INSERT_TAIL(&entrylist, entry, next);
4479aa2a9c3Schristos break;
4489aa2a9c3Schristos case 'n':
4499aa2a9c3Schristos n_flag = true;
4509aa2a9c3Schristos break;
4519aa2a9c3Schristos case 'x':
4529aa2a9c3Schristos entry = zmalloc(sizeof(struct sf_entry));
4539aa2a9c3Schristos entry_number = strtol(optarg, &end, 10);
4549aa2a9c3Schristos if (end - optarg == (int)strlen(optarg)) {
4559aa2a9c3Schristos if (entry_number < 0)
4569aa2a9c3Schristos errx(1,
4579aa2a9c3Schristos "%s: entry number cannot be less than zero",
4589aa2a9c3Schristos optarg);
4599aa2a9c3Schristos entry->entry_number = entry_number;
4609aa2a9c3Schristos entry->op = OP_REMOVE_BY_NUMBER;
4619aa2a9c3Schristos } else {
4629aa2a9c3Schristos entry->acl = acl_from_text(optarg);
4639aa2a9c3Schristos if (entry->acl == NULL)
4649aa2a9c3Schristos err(1, "%s", optarg);
4659aa2a9c3Schristos entry->op = OP_REMOVE_ACL;
4669aa2a9c3Schristos }
4679aa2a9c3Schristos TAILQ_INSERT_TAIL(&entrylist, entry, next);
4689aa2a9c3Schristos break;
4699aa2a9c3Schristos default:
4709aa2a9c3Schristos usage();
4719aa2a9c3Schristos break;
4729aa2a9c3Schristos }
4739aa2a9c3Schristos argc -= optind;
4749aa2a9c3Schristos argv += optind;
4759aa2a9c3Schristos
4769aa2a9c3Schristos if (!n_flag && TAILQ_EMPTY(&entrylist))
4779aa2a9c3Schristos usage();
4789aa2a9c3Schristos
4799aa2a9c3Schristos /* Take list of files from stdin. */
4809aa2a9c3Schristos if (argc == 0 || strcmp(argv[0], "-") == 0) {
4819aa2a9c3Schristos files_list = stdin_files();
4829aa2a9c3Schristos } else
4839aa2a9c3Schristos files_list = argv;
4849aa2a9c3Schristos
4859aa2a9c3Schristos if (R_flag) {
4869aa2a9c3Schristos if (h_flag)
4879aa2a9c3Schristos errx(1, "the -R and -h options may not be "
4889aa2a9c3Schristos "specified together.");
4899aa2a9c3Schristos if (L_flag) {
4909aa2a9c3Schristos fts_options = FTS_LOGICAL;
4919aa2a9c3Schristos } else {
4929aa2a9c3Schristos fts_options = FTS_PHYSICAL;
4939aa2a9c3Schristos
4949aa2a9c3Schristos if (H_flag) {
4959aa2a9c3Schristos fts_options |= FTS_COMFOLLOW;
4969aa2a9c3Schristos }
4979aa2a9c3Schristos }
4989aa2a9c3Schristos } else if (h_flag) {
4999aa2a9c3Schristos fts_options = FTS_PHYSICAL;
5009aa2a9c3Schristos } else {
5019aa2a9c3Schristos fts_options = FTS_LOGICAL;
5029aa2a9c3Schristos }
5039aa2a9c3Schristos
5049aa2a9c3Schristos /* Open all files. */
5059aa2a9c3Schristos if ((ftsp = fts_open(files_list, fts_options | FTS_NOSTAT, 0)) == NULL)
5069aa2a9c3Schristos err(1, "fts_open");
5079aa2a9c3Schristos while ((file = fts_read(ftsp)) != NULL)
5089aa2a9c3Schristos carried_error += handle_file(ftsp, file);
5099aa2a9c3Schristos
5109aa2a9c3Schristos return (carried_error);
5119aa2a9c3Schristos }
512