setfacl.c revision 216922
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 17204819Sjoel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18204819Sjoel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19204819Sjoel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20204819Sjoel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21204819Sjoel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22204819Sjoel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23204819Sjoel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24204819Sjoel * SUCH DAMAGE. 2574465Srwatson */ 2674465Srwatson 2799110Sobrien#include <sys/cdefs.h> 2899110Sobrien__FBSDID("$FreeBSD: head/bin/setfacl/setfacl.c 216922 2011-01-03 17:17:31Z jh $"); 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); 204216922Sjh carried_error++; 20574465Srwatson continue; 206196936Strasz } 207196936Strasz 208196936Strasz if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) { 209196936Strasz warnx("%s: default ACL may only be set on a directory", 210196936Strasz file->filename); 211216922Sjh carried_error++; 21274465Srwatson continue; 21374465Srwatson } 21474465Srwatson 215196936Strasz if (h_flag) 216196936Strasz ret = lpathconf(file->filename, _PC_ACL_NFS4); 217196936Strasz else 218196936Strasz ret = pathconf(file->filename, _PC_ACL_NFS4); 219196936Strasz if (ret > 0) { 220196936Strasz if (acl_type == ACL_TYPE_DEFAULT) { 221196936Strasz warnx("%s: there are no default entries " 222196936Strasz "in NFSv4 ACLs", file->filename); 223216922Sjh carried_error++; 224196936Strasz continue; 225196936Strasz } 226196936Strasz acl_type = ACL_TYPE_NFS4; 227196936Strasz } else if (ret == 0) { 228196936Strasz if (acl_type == ACL_TYPE_NFS4) 229196936Strasz acl_type = ACL_TYPE_ACCESS; 230196936Strasz } else if (ret < 0 && errno != EINVAL) { 231196936Strasz warn("%s: pathconf(..., _PC_ACL_NFS4) failed", 232196936Strasz file->filename); 233196936Strasz } 23474465Srwatson 235196936Strasz if (h_flag) 236196936Strasz acl = acl_get_link_np(file->filename, acl_type); 237196936Strasz else 238196936Strasz acl = acl_get_file(file->filename, acl_type); 239196936Strasz if (acl == NULL) { 240196936Strasz if (h_flag) 241196936Strasz warn("%s: acl_get_link_np() failed", 242196936Strasz file->filename); 243196936Strasz else 244196936Strasz warn("%s: acl_get_file() failed", 245196936Strasz file->filename); 246216922Sjh carried_error++; 247196936Strasz continue; 248196936Strasz } 249196936Strasz 25074465Srwatson /* cycle through each option */ 25175928Sjedgar TAILQ_FOREACH(entry, &entrylist, next) { 25274465Srwatson if (local_error) 25374465Srwatson continue; 25474465Srwatson 25574465Srwatson switch(entry->op) { 256196936Strasz case OP_ADD_ACL: 257196936Strasz local_error += add_acl(entry->acl, 258196936Strasz entry->entry_number, &acl, file->filename); 259196936Strasz break; 26074465Srwatson case OP_MERGE_ACL: 261196936Strasz local_error += merge_acl(entry->acl, &acl, 262196936Strasz file->filename); 26374465Srwatson need_mask = 1; 26474465Srwatson break; 26574465Srwatson case OP_REMOVE_EXT: 266196936Strasz remove_ext(&acl, file->filename); 26774465Srwatson need_mask = 0; 26874465Srwatson break; 26974465Srwatson case OP_REMOVE_DEF: 270196936Strasz if (acl_type == ACL_TYPE_NFS4) { 271196936Strasz warnx("%s: there are no default entries in NFSv4 ACLs; " 272196936Strasz "cannot remove", file->filename); 273196936Strasz local_error++; 274196936Strasz break; 275196936Strasz } 27674465Srwatson if (acl_delete_def_file(file->filename) == -1) { 277196936Strasz warn("%s: acl_delete_def_file() failed", 278196936Strasz file->filename); 27974465Srwatson local_error++; 28074465Srwatson } 281196936Strasz if (acl_type == ACL_TYPE_DEFAULT) 282196936Strasz local_error += remove_default(&acl, 283196936Strasz file->filename); 28474465Srwatson need_mask = 0; 28574465Srwatson break; 28674465Srwatson case OP_REMOVE_ACL: 287196936Strasz local_error += remove_acl(entry->acl, &acl, 288196936Strasz file->filename); 28974465Srwatson need_mask = 1; 29074465Srwatson break; 291196936Strasz case OP_REMOVE_BY_NUMBER: 292196936Strasz local_error += remove_by_number(entry->entry_number, 293196936Strasz &acl, file->filename); 294196936Strasz need_mask = 1; 295196936Strasz break; 29674465Srwatson } 29774465Srwatson } 29874465Srwatson 29974465Srwatson /* don't bother setting the ACL if something is broken */ 30074465Srwatson if (local_error) { 30174465Srwatson carried_error++; 30274465Srwatson continue; 30374465Srwatson } 30474465Srwatson 305196936Strasz if (acl_type != ACL_TYPE_NFS4 && need_mask && 306196936Strasz set_acl_mask(&acl, file->filename) == -1) { 307196936Strasz warnx("%s: failed to set ACL mask", file->filename); 30874465Srwatson carried_error++; 309167000Smckusick } else if (h_flag) { 310167000Smckusick if (acl_set_link_np(file->filename, acl_type, 311196936Strasz acl) == -1) { 312167000Smckusick carried_error++; 313196936Strasz warn("%s: acl_set_link_np() failed", 314167000Smckusick file->filename); 315167000Smckusick } 316167000Smckusick } else { 317167000Smckusick if (acl_set_file(file->filename, acl_type, 318196936Strasz acl) == -1) { 319167000Smckusick carried_error++; 320196936Strasz warn("%s: acl_set_file() failed", 321167000Smckusick file->filename); 322167000Smckusick } 32374465Srwatson } 32474465Srwatson 325196936Strasz acl_free(acl); 32674465Srwatson } 32774465Srwatson 32887254Sjedgar return (carried_error); 32974465Srwatson} 330