setfacl.c revision 240084
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 240084 2012-09-04 11:50:13Z 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; 76240083Strasz acl_entry_t unused_entry; 7774465Srwatson char filename[PATH_MAX]; 78196936Strasz int local_error, carried_error, ch, i, entry_number, ret; 79196936Strasz int h_flag; 8074465Srwatson struct sf_file *file; 8174465Srwatson struct sf_entry *entry; 82159463Skib const char *fn_dup; 83196936Strasz char *end; 84196936Strasz struct stat sb; 8574465Srwatson 8674465Srwatson acl_type = ACL_TYPE_ACCESS; 8774465Srwatson carried_error = local_error = 0; 88108450Srwatson h_flag = have_mask = have_stdin = n_flag = need_mask = 0; 8974465Srwatson 9075928Sjedgar TAILQ_INIT(&entrylist); 9175928Sjedgar TAILQ_INIT(&filelist); 9274465Srwatson 93196936Strasz while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1) 9474465Srwatson switch(ch) { 9574465Srwatson case 'M': 9674465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 9774465Srwatson entry->acl = get_acl_from_file(optarg); 9887254Sjedgar if (entry->acl == NULL) 99196936Strasz err(1, "%s: get_acl_from_file() failed", optarg); 10074465Srwatson entry->op = OP_MERGE_ACL; 10175928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 10274465Srwatson break; 10374465Srwatson case 'X': 10474465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 10574465Srwatson entry->acl = get_acl_from_file(optarg); 10674465Srwatson entry->op = OP_REMOVE_ACL; 10775928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 10874465Srwatson break; 109196936Strasz case 'a': 110196936Strasz entry = zmalloc(sizeof(struct sf_entry)); 111196936Strasz 112196936Strasz entry_number = strtol(optarg, &end, 10); 113196936Strasz if (end - optarg != (int)strlen(optarg)) 114196936Strasz errx(1, "%s: invalid entry number", optarg); 115196936Strasz if (entry_number < 0) 116196936Strasz errx(1, "%s: entry number cannot be less than zero", optarg); 117196936Strasz entry->entry_number = entry_number; 118196936Strasz 119196936Strasz if (argv[optind] == NULL) 120196936Strasz errx(1, "missing ACL"); 121196936Strasz entry->acl = acl_from_text(argv[optind]); 122196936Strasz if (entry->acl == NULL) 123196936Strasz err(1, "%s", argv[optind]); 124196936Strasz optind++; 125196936Strasz entry->op = OP_ADD_ACL; 126196936Strasz TAILQ_INSERT_TAIL(&entrylist, entry, next); 127196936Strasz break; 12874465Srwatson case 'b': 12974465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 13074465Srwatson entry->op = OP_REMOVE_EXT; 13175928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 13274465Srwatson break; 13374465Srwatson case 'd': 13474465Srwatson acl_type = ACL_TYPE_DEFAULT; 13574465Srwatson break; 136108450Srwatson case 'h': 137108450Srwatson h_flag = 1; 138108450Srwatson break; 13974465Srwatson case 'k': 14074465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 14174465Srwatson entry->op = OP_REMOVE_DEF; 14275928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 14374465Srwatson break; 14474465Srwatson case 'm': 14574465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 14674465Srwatson entry->acl = acl_from_text(optarg); 14787254Sjedgar if (entry->acl == NULL) 148117734Srwatson err(1, "%s", optarg); 14974465Srwatson entry->op = OP_MERGE_ACL; 15075928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 15174465Srwatson break; 15274465Srwatson case 'n': 15374465Srwatson n_flag++; 15474465Srwatson break; 15574465Srwatson case 'x': 15674465Srwatson entry = zmalloc(sizeof(struct sf_entry)); 157196936Strasz entry_number = strtol(optarg, &end, 10); 158196936Strasz if (end - optarg == (int)strlen(optarg)) { 159196936Strasz if (entry_number < 0) 160196936Strasz errx(1, "%s: entry number cannot be less than zero", optarg); 161196936Strasz entry->entry_number = entry_number; 162196936Strasz entry->op = OP_REMOVE_BY_NUMBER; 163196936Strasz } else { 164196936Strasz entry->acl = acl_from_text(optarg); 165196936Strasz if (entry->acl == NULL) 166196936Strasz err(1, "%s", optarg); 167196936Strasz entry->op = OP_REMOVE_ACL; 168196936Strasz } 16975928Sjedgar TAILQ_INSERT_TAIL(&entrylist, entry, next); 17074465Srwatson break; 17174465Srwatson default: 17274465Srwatson usage(); 17374465Srwatson break; 17474465Srwatson } 17574465Srwatson argc -= optind; 17674465Srwatson argv += optind; 17774465Srwatson 17887254Sjedgar if (n_flag == 0 && TAILQ_EMPTY(&entrylist)) 17974465Srwatson usage(); 18074465Srwatson 18174465Srwatson /* take list of files from stdin */ 18287254Sjedgar if (argc == 0 || strcmp(argv[0], "-") == 0) { 18374465Srwatson if (have_stdin) 18487254Sjedgar err(1, "cannot have more than one stdin"); 18574465Srwatson have_stdin = 1; 18674465Srwatson bzero(&filename, sizeof(filename)); 18776881Skris while (fgets(filename, (int)sizeof(filename), stdin)) { 18874465Srwatson /* remove the \n */ 18974465Srwatson filename[strlen(filename) - 1] = '\0'; 190159463Skib fn_dup = strdup(filename); 191159463Skib if (fn_dup == NULL) 192159463Skib err(1, "strdup() failed"); 193159463Skib add_filename(fn_dup); 19474465Srwatson } 19574465Srwatson } else 19674465Srwatson for (i = 0; i < argc; i++) 19774465Srwatson add_filename(argv[i]); 19874465Srwatson 19974465Srwatson /* cycle through each file */ 20075928Sjedgar TAILQ_FOREACH(file, &filelist, next) { 201196936Strasz local_error = 0; 202196936Strasz 203196936Strasz if (stat(file->filename, &sb) == -1) { 204196936Strasz warn("%s: stat() failed", file->filename); 205216922Sjh carried_error++; 20674465Srwatson continue; 207196936Strasz } 208196936Strasz 209196936Strasz if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) { 210196936Strasz warnx("%s: default ACL may only be set on a directory", 211196936Strasz file->filename); 212216922Sjh carried_error++; 21374465Srwatson continue; 21474465Srwatson } 21574465Srwatson 216196936Strasz if (h_flag) 217196936Strasz ret = lpathconf(file->filename, _PC_ACL_NFS4); 218196936Strasz else 219196936Strasz ret = pathconf(file->filename, _PC_ACL_NFS4); 220196936Strasz if (ret > 0) { 221196936Strasz if (acl_type == ACL_TYPE_DEFAULT) { 222196936Strasz warnx("%s: there are no default entries " 223196936Strasz "in NFSv4 ACLs", file->filename); 224216922Sjh carried_error++; 225196936Strasz continue; 226196936Strasz } 227196936Strasz acl_type = ACL_TYPE_NFS4; 228196936Strasz } else if (ret == 0) { 229196936Strasz if (acl_type == ACL_TYPE_NFS4) 230196936Strasz acl_type = ACL_TYPE_ACCESS; 231196936Strasz } else if (ret < 0 && errno != EINVAL) { 232196936Strasz warn("%s: pathconf(..., _PC_ACL_NFS4) failed", 233196936Strasz file->filename); 234196936Strasz } 23574465Srwatson 236196936Strasz if (h_flag) 237196936Strasz acl = acl_get_link_np(file->filename, acl_type); 238196936Strasz else 239196936Strasz acl = acl_get_file(file->filename, acl_type); 240196936Strasz if (acl == NULL) { 241196936Strasz if (h_flag) 242196936Strasz warn("%s: acl_get_link_np() failed", 243196936Strasz file->filename); 244196936Strasz else 245196936Strasz warn("%s: acl_get_file() failed", 246196936Strasz file->filename); 247216922Sjh carried_error++; 248196936Strasz continue; 249196936Strasz } 250196936Strasz 25174465Srwatson /* cycle through each option */ 25275928Sjedgar TAILQ_FOREACH(entry, &entrylist, next) { 25374465Srwatson if (local_error) 25474465Srwatson continue; 25574465Srwatson 25674465Srwatson switch(entry->op) { 257196936Strasz case OP_ADD_ACL: 258196936Strasz local_error += add_acl(entry->acl, 259196936Strasz entry->entry_number, &acl, file->filename); 260196936Strasz break; 26174465Srwatson case OP_MERGE_ACL: 262196936Strasz local_error += merge_acl(entry->acl, &acl, 263196936Strasz file->filename); 26474465Srwatson need_mask = 1; 26574465Srwatson break; 26674465Srwatson case OP_REMOVE_EXT: 267240084Strasz /* 268240084Strasz * Don't try to call remove_ext() for empty 269240084Strasz * default ACL. 270240084Strasz */ 271240084Strasz if (acl_type == ACL_TYPE_DEFAULT && 272240084Strasz acl_get_entry(acl, ACL_FIRST_ENTRY, 273240084Strasz &unused_entry) == 0) { 274240084Strasz local_error += remove_default(&acl, 275240084Strasz file->filename); 276240084Strasz break; 277240084Strasz } 278196936Strasz remove_ext(&acl, file->filename); 27974465Srwatson need_mask = 0; 28074465Srwatson break; 28174465Srwatson case OP_REMOVE_DEF: 282196936Strasz if (acl_type == ACL_TYPE_NFS4) { 283196936Strasz warnx("%s: there are no default entries in NFSv4 ACLs; " 284196936Strasz "cannot remove", file->filename); 285196936Strasz local_error++; 286196936Strasz break; 287196936Strasz } 28874465Srwatson if (acl_delete_def_file(file->filename) == -1) { 289196936Strasz warn("%s: acl_delete_def_file() failed", 290196936Strasz file->filename); 29174465Srwatson local_error++; 29274465Srwatson } 293196936Strasz if (acl_type == ACL_TYPE_DEFAULT) 294196936Strasz local_error += remove_default(&acl, 295196936Strasz file->filename); 29674465Srwatson need_mask = 0; 29774465Srwatson break; 29874465Srwatson case OP_REMOVE_ACL: 299196936Strasz local_error += remove_acl(entry->acl, &acl, 300196936Strasz file->filename); 30174465Srwatson need_mask = 1; 30274465Srwatson break; 303196936Strasz case OP_REMOVE_BY_NUMBER: 304196936Strasz local_error += remove_by_number(entry->entry_number, 305196936Strasz &acl, file->filename); 306196936Strasz need_mask = 1; 307196936Strasz break; 30874465Srwatson } 30974465Srwatson } 31074465Srwatson 311240083Strasz /* 312240083Strasz * Don't try to set an empty default ACL; it will always fail. 313240083Strasz * Use acl_delete_def_file(3) instead. 314240083Strasz */ 315240083Strasz if (acl_type == ACL_TYPE_DEFAULT && 316240083Strasz acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { 317240083Strasz if (acl_delete_def_file(file->filename) == -1) { 318240083Strasz warn("%s: acl_delete_def_file() failed", 319240083Strasz file->filename); 320240083Strasz carried_error++; 321240083Strasz } 322240083Strasz continue; 323240083Strasz } 324240083Strasz 32574465Srwatson /* don't bother setting the ACL if something is broken */ 32674465Srwatson if (local_error) { 32774465Srwatson carried_error++; 32874465Srwatson continue; 32974465Srwatson } 33074465Srwatson 331196936Strasz if (acl_type != ACL_TYPE_NFS4 && need_mask && 332196936Strasz set_acl_mask(&acl, file->filename) == -1) { 333196936Strasz warnx("%s: failed to set ACL mask", file->filename); 33474465Srwatson carried_error++; 335167000Smckusick } else if (h_flag) { 336167000Smckusick if (acl_set_link_np(file->filename, acl_type, 337196936Strasz acl) == -1) { 338167000Smckusick carried_error++; 339196936Strasz warn("%s: acl_set_link_np() failed", 340167000Smckusick file->filename); 341167000Smckusick } 342167000Smckusick } else { 343167000Smckusick if (acl_set_file(file->filename, acl_type, 344196936Strasz acl) == -1) { 345167000Smckusick carried_error++; 346196936Strasz warn("%s: acl_set_file() failed", 347167000Smckusick file->filename); 348167000Smckusick } 34974465Srwatson } 35074465Srwatson 351196936Strasz acl_free(acl); 35274465Srwatson } 35374465Srwatson 35487254Sjedgar return (carried_error); 35574465Srwatson} 356