setfacl.c revision 196936
1139969Simp/*- 274465Srwatson * Copyright (c) 2001 Chris D. Faulhaber 374465Srwatson * All rights reserved. 474465Srwatson * 574465Srwatson * Redistribution and use in source and binary forms, with or without 674465Srwatson * modification, are permitted provided that the following conditions 774465Srwatson * are met: 874465Srwatson * 1. Redistributions of source code must retain the above copyright 974465Srwatson * notice, this list of conditions and the following disclaimer. 1074465Srwatson * 2. Redistributions in binary form must reproduce the above copyright 1174465Srwatson * notice, this list of conditions and the following disclaimer in the 1274465Srwatson * documentation and/or other materials provided with the distribution. 1374465Srwatson * 1474465Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1574465Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1674465Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1774465Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE 1874465Srwatson * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1974465Srwatson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2074465Srwatson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2174465Srwatson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2274465Srwatson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2374465Srwatson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2474465Srwatson * POSSIBILITY OF SUCH DAMAGE. 2574465Srwatson */ 2674465Srwatson 2799110Sobrien#include <sys/cdefs.h> 2899110Sobrien__FBSDID("$FreeBSD: head/bin/setfacl/setfacl.c 196936 2009-09-07 16:19:32Z trasz $"); 2999110Sobrien 3074465Srwatson#include <sys/types.h> 3174465Srwatson#include <sys/param.h> 3274465Srwatson#include <sys/stat.h> 3374465Srwatson#include <sys/acl.h> 3474465Srwatson#include <sys/queue.h> 3574465Srwatson 3674465Srwatson#include <err.h> 37196936Strasz#include <errno.h> 3874465Srwatson#include <stdio.h> 3974465Srwatson#include <stdlib.h> 4074465Srwatson#include <string.h> 4174465Srwatson#include <unistd.h> 4274465Srwatson 4374465Srwatson#include "setfacl.h" 4474465Srwatson 45196936Straszstatic void add_filename(const char *filename); 46196936Straszstatic void usage(void); 4774465Srwatson 4874465Srwatsonstatic void 4974465Srwatsonadd_filename(const char *filename) 5074465Srwatson{ 5174465Srwatson struct sf_file *file; 5274465Srwatson 5374465Srwatson if (strlen(filename) > PATH_MAX - 1) { 5474465Srwatson warn("illegal filename"); 5574465Srwatson return; 5674465Srwatson } 5774465Srwatson file = zmalloc(sizeof(struct sf_file)); 5874465Srwatson file->filename = filename; 5975928Sjedgar TAILQ_INSERT_TAIL(&filelist, file, next); 6074465Srwatson} 6174465Srwatson 6274465Srwatsonstatic void 6374465Srwatsonusage(void) 6474465Srwatson{ 6574465Srwatson 66196936Strasz fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] " 67196936Strasz "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n"); 6887254Sjedgar exit(1); 6974465Srwatson} 7074465Srwatson 7174465Srwatsonint 7274465Srwatsonmain(int argc, char *argv[]) 7374465Srwatson{ 74196936Strasz acl_t acl; 75196936Strasz acl_type_t acl_type; 7674465Srwatson char filename[PATH_MAX]; 77196936Strasz int local_error, carried_error, ch, i, entry_number, ret; 78196936Strasz int h_flag; 7974465Srwatson struct sf_file *file; 8074465Srwatson struct sf_entry *entry; 81159463Skib const char *fn_dup; 82196936Strasz char *end; 83196936Strasz struct stat sb; 8474465Srwatson 8574465Srwatson acl_type = ACL_TYPE_ACCESS; 8674465Srwatson carried_error = local_error = 0; 87108450Srwatson h_flag = have_mask = have_stdin = n_flag = need_mask = 0; 8874465Srwatson 8975928Sjedgar TAILQ_INIT(&entrylist); 9075928Sjedgar TAILQ_INIT(&filelist); 9174465Srwatson 92196936Strasz while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1) 9374465Srwatson switch(ch) { 9474465Srwatson case 'M': 9574465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 9674465Srwatson entry->acl = get_acl_from_file(optarg); 9787254Sjedgar if (entry->acl == NULL) 98196936Strasz err(1, "%s: get_acl_from_file() failed", optarg); 9974465Srwatson entry->op = OP_MERGE_ACL; 10075928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 10174465Srwatson break; 10274465Srwatson case 'X': 10374465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 10474465Srwatson entry->acl = get_acl_from_file(optarg); 10574465Srwatson entry->op = OP_REMOVE_ACL; 10675928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 10774465Srwatson break; 108196936Strasz case 'a': 109196936Strasz entry = zmalloc(sizeof(struct sf_entry)); 110196936Strasz 111196936Strasz entry_number = strtol(optarg, &end, 10); 112196936Strasz if (end - optarg != (int)strlen(optarg)) 113196936Strasz errx(1, "%s: invalid entry number", optarg); 114196936Strasz if (entry_number < 0) 115196936Strasz errx(1, "%s: entry number cannot be less than zero", optarg); 116196936Strasz entry->entry_number = entry_number; 117196936Strasz 118196936Strasz if (argv[optind] == NULL) 119196936Strasz errx(1, "missing ACL"); 120196936Strasz entry->acl = acl_from_text(argv[optind]); 121196936Strasz if (entry->acl == NULL) 122196936Strasz err(1, "%s", argv[optind]); 123196936Strasz optind++; 124196936Strasz entry->op = OP_ADD_ACL; 125196936Strasz TAILQ_INSERT_TAIL(&entrylist, entry, next); 126196936Strasz break; 12774465Srwatson case 'b': 12874465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 12974465Srwatson entry->op = OP_REMOVE_EXT; 13075928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 13174465Srwatson break; 13274465Srwatson case 'd': 13374465Srwatson acl_type = ACL_TYPE_DEFAULT; 13474465Srwatson break; 135108450Srwatson case 'h': 136108450Srwatson h_flag = 1; 137108450Srwatson break; 13874465Srwatson case 'k': 13974465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 14074465Srwatson entry->op = OP_REMOVE_DEF; 14175928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 14274465Srwatson break; 14374465Srwatson case 'm': 14474465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 14574465Srwatson entry->acl = acl_from_text(optarg); 14687254Sjedgar if (entry->acl == NULL) 147117734Srwatson err(1, "%s", optarg); 14874465Srwatson entry->op = OP_MERGE_ACL; 14975928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 15074465Srwatson break; 15174465Srwatson case 'n': 15274465Srwatson n_flag++; 15374465Srwatson break; 15474465Srwatson case 'x': 15574465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 156196936Strasz entry_number = strtol(optarg, &end, 10); 157196936Strasz if (end - optarg == (int)strlen(optarg)) { 158196936Strasz if (entry_number < 0) 159196936Strasz errx(1, "%s: entry number cannot be less than zero", optarg); 160196936Strasz entry->entry_number = entry_number; 161196936Strasz entry->op = OP_REMOVE_BY_NUMBER; 162196936Strasz } else { 163196936Strasz entry->acl = acl_from_text(optarg); 164196936Strasz if (entry->acl == NULL) 165196936Strasz err(1, "%s", optarg); 166196936Strasz entry->op = OP_REMOVE_ACL; 167196936Strasz } 16875928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 16974465Srwatson break; 17074465Srwatson default: 17174465Srwatson usage(); 17274465Srwatson break; 17374465Srwatson } 17474465Srwatson argc -= optind; 17574465Srwatson argv += optind; 17674465Srwatson 17787254Sjedgar if (n_flag == 0 && TAILQ_EMPTY(&entrylist)) 17874465Srwatson usage(); 17974465Srwatson 18074465Srwatson /* take list of files from stdin */ 18187254Sjedgar if (argc == 0 || strcmp(argv[0], "-") == 0) { 18274465Srwatson if (have_stdin) 18387254Sjedgar err(1, "cannot have more than one stdin"); 18474465Srwatson have_stdin = 1; 18574465Srwatson bzero(&filename, sizeof(filename)); 18676881Skris while (fgets(filename, (int)sizeof(filename), stdin)) { 18774465Srwatson /* remove the \n */ 18874465Srwatson filename[strlen(filename) - 1] = '\0'; 189159463Skib fn_dup = strdup(filename); 190159463Skib if (fn_dup == NULL) 191159463Skib err(1, "strdup() failed"); 192159463Skib add_filename(fn_dup); 19374465Srwatson } 19474465Srwatson } else 19574465Srwatson for (i = 0; i < argc; i++) 19674465Srwatson add_filename(argv[i]); 19774465Srwatson 19874465Srwatson /* cycle through each file */ 19975928Sjedgar TAILQ_FOREACH(file, &filelist, next) { 200196936Strasz local_error = 0; 201196936Strasz 202196936Strasz if (stat(file->filename, &sb) == -1) { 203196936Strasz warn("%s: stat() failed", file->filename); 20474465Srwatson continue; 205196936Strasz } 206196936Strasz 207196936Strasz if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) { 208196936Strasz warnx("%s: default ACL may only be set on a directory", 209196936Strasz file->filename); 21074465Srwatson continue; 21174465Srwatson } 21274465Srwatson 213196936Strasz if (h_flag) 214196936Strasz ret = lpathconf(file->filename, _PC_ACL_NFS4); 215196936Strasz else 216196936Strasz ret = pathconf(file->filename, _PC_ACL_NFS4); 217196936Strasz if (ret > 0) { 218196936Strasz if (acl_type == ACL_TYPE_DEFAULT) { 219196936Strasz warnx("%s: there are no default entries " 220196936Strasz "in NFSv4 ACLs", file->filename); 221196936Strasz continue; 222196936Strasz } 223196936Strasz acl_type = ACL_TYPE_NFS4; 224196936Strasz } else if (ret == 0) { 225196936Strasz if (acl_type == ACL_TYPE_NFS4) 226196936Strasz acl_type = ACL_TYPE_ACCESS; 227196936Strasz } else if (ret < 0 && errno != EINVAL) { 228196936Strasz warn("%s: pathconf(..., _PC_ACL_NFS4) failed", 229196936Strasz file->filename); 230196936Strasz } 23174465Srwatson 232196936Strasz if (h_flag) 233196936Strasz acl = acl_get_link_np(file->filename, acl_type); 234196936Strasz else 235196936Strasz acl = acl_get_file(file->filename, acl_type); 236196936Strasz if (acl == NULL) { 237196936Strasz if (h_flag) 238196936Strasz warn("%s: acl_get_link_np() failed", 239196936Strasz file->filename); 240196936Strasz else 241196936Strasz warn("%s: acl_get_file() failed", 242196936Strasz file->filename); 243196936Strasz continue; 244196936Strasz } 245196936Strasz 24674465Srwatson /* cycle through each option */ 24775928Sjedgar TAILQ_FOREACH(entry, &entrylist, next) { 24874465Srwatson if (local_error) 24974465Srwatson continue; 25074465Srwatson 25174465Srwatson switch(entry->op) { 252196936Strasz case OP_ADD_ACL: 253196936Strasz local_error += add_acl(entry->acl, 254196936Strasz entry->entry_number, &acl, file->filename); 255196936Strasz break; 25674465Srwatson case OP_MERGE_ACL: 257196936Strasz local_error += merge_acl(entry->acl, &acl, 258196936Strasz file->filename); 25974465Srwatson need_mask = 1; 26074465Srwatson break; 26174465Srwatson case OP_REMOVE_EXT: 262196936Strasz remove_ext(&acl, file->filename); 26374465Srwatson need_mask = 0; 26474465Srwatson break; 26574465Srwatson case OP_REMOVE_DEF: 266196936Strasz if (acl_type == ACL_TYPE_NFS4) { 267196936Strasz warnx("%s: there are no default entries in NFSv4 ACLs; " 268196936Strasz "cannot remove", file->filename); 269196936Strasz local_error++; 270196936Strasz break; 271196936Strasz } 27274465Srwatson if (acl_delete_def_file(file->filename) == -1) { 273196936Strasz warn("%s: acl_delete_def_file() failed", 274196936Strasz file->filename); 27574465Srwatson local_error++; 27674465Srwatson } 277196936Strasz if (acl_type == ACL_TYPE_DEFAULT) 278196936Strasz local_error += remove_default(&acl, 279196936Strasz file->filename); 28074465Srwatson need_mask = 0; 28174465Srwatson break; 28274465Srwatson case OP_REMOVE_ACL: 283196936Strasz local_error += remove_acl(entry->acl, &acl, 284196936Strasz file->filename); 28574465Srwatson need_mask = 1; 28674465Srwatson break; 287196936Strasz case OP_REMOVE_BY_NUMBER: 288196936Strasz local_error += remove_by_number(entry->entry_number, 289196936Strasz &acl, file->filename); 290196936Strasz need_mask = 1; 291196936Strasz break; 29274465Srwatson } 29374465Srwatson } 29474465Srwatson 29574465Srwatson /* don't bother setting the ACL if something is broken */ 29674465Srwatson if (local_error) { 29774465Srwatson carried_error++; 29874465Srwatson continue; 29974465Srwatson } 30074465Srwatson 301196936Strasz if (acl_type != ACL_TYPE_NFS4 && need_mask && 302196936Strasz set_acl_mask(&acl, file->filename) == -1) { 303196936Strasz warnx("%s: failed to set ACL mask", file->filename); 30474465Srwatson carried_error++; 305167000Smckusick } else if (h_flag) { 306167000Smckusick if (acl_set_link_np(file->filename, acl_type, 307196936Strasz acl) == -1) { 308167000Smckusick carried_error++; 309196936Strasz warn("%s: acl_set_link_np() failed", 310167000Smckusick file->filename); 311167000Smckusick } 312167000Smckusick } else { 313167000Smckusick if (acl_set_file(file->filename, acl_type, 314196936Strasz acl) == -1) { 315167000Smckusick carried_error++; 316196936Strasz warn("%s: acl_set_file() failed", 317167000Smckusick file->filename); 318167000Smckusick } 31974465Srwatson } 32074465Srwatson 321196936Strasz acl_free(acl); 32274465Srwatson } 32374465Srwatson 32487254Sjedgar return (carried_error); 32574465Srwatson} 326