1 /*- 2 * Copyright (c) 2001 Chris D. Faulhaber 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <sys/stat.h> 33 #include <sys/acl.h> 34 #include <sys/queue.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include "setfacl.h" 44 45 /* file operations */ 46 #define OP_MERGE_ACL 0x00 /* merge acl's (-mM) */ 47 #define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */ 48 #define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */ 49 #define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */ 50 #define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */ 51 #define OP_ADD_ACL 0x05 /* add acls entries at a given position */ 52 53 /* TAILQ entry for acl operations */ 54 struct sf_entry { 55 uint op; 56 acl_t acl; 57 uint entry_number; 58 TAILQ_ENTRY(sf_entry) next; 59 }; 60 static TAILQ_HEAD(, sf_entry) entrylist; 61 62 /* TAILQ entry for files */ 63 struct sf_file { 64 const char *filename; 65 TAILQ_ENTRY(sf_file) next; 66 }; 67 static TAILQ_HEAD(, sf_file) filelist; 68 69 uint have_mask; 70 uint need_mask; 71 uint have_stdin; 72 uint n_flag; 73 74 static void add_filename(const char *filename); 75 static void usage(void); 76 77 static void 78 add_filename(const char *filename) 79 { 80 struct sf_file *file; 81 82 if (strlen(filename) > PATH_MAX - 1) { 83 warn("illegal filename"); 84 return; 85 } 86 file = zmalloc(sizeof(struct sf_file)); 87 file->filename = filename; 88 TAILQ_INSERT_TAIL(&filelist, file, next); 89 } 90 91 static void 92 usage(void) 93 { 94 95 fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] " 96 "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n"); 97 exit(1); 98 } 99 100 int 101 main(int argc, char *argv[]) 102 { 103 acl_t acl; 104 acl_type_t acl_type; 105 acl_entry_t unused_entry; 106 char filename[PATH_MAX]; 107 int local_error, carried_error, ch, i, entry_number, ret; 108 int h_flag; 109 struct sf_file *file; 110 struct sf_entry *entry; 111 const char *fn_dup; 112 char *end; 113 struct stat sb; 114 115 acl_type = ACL_TYPE_ACCESS; 116 carried_error = local_error = 0; 117 h_flag = have_mask = have_stdin = n_flag = need_mask = 0; 118 119 TAILQ_INIT(&entrylist); 120 TAILQ_INIT(&filelist); 121 122 while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1) 123 switch(ch) { 124 case 'M': 125 entry = zmalloc(sizeof(struct sf_entry)); 126 entry->acl = get_acl_from_file(optarg); 127 if (entry->acl == NULL) 128 err(1, "%s: get_acl_from_file() failed", optarg); 129 entry->op = OP_MERGE_ACL; 130 TAILQ_INSERT_TAIL(&entrylist, entry, next); 131 break; 132 case 'X': 133 entry = zmalloc(sizeof(struct sf_entry)); 134 entry->acl = get_acl_from_file(optarg); 135 entry->op = OP_REMOVE_ACL; 136 TAILQ_INSERT_TAIL(&entrylist, entry, next); 137 break; 138 case 'a': 139 entry = zmalloc(sizeof(struct sf_entry)); 140 141 entry_number = strtol(optarg, &end, 10); 142 if (end - optarg != (int)strlen(optarg)) 143 errx(1, "%s: invalid entry number", optarg); 144 if (entry_number < 0) 145 errx(1, "%s: entry number cannot be less than zero", optarg); 146 entry->entry_number = entry_number; 147 148 if (argv[optind] == NULL) 149 errx(1, "missing ACL"); 150 entry->acl = acl_from_text(argv[optind]); 151 if (entry->acl == NULL) 152 err(1, "%s", argv[optind]); 153 optind++; 154 entry->op = OP_ADD_ACL; 155 TAILQ_INSERT_TAIL(&entrylist, entry, next); 156 break; 157 case 'b': 158 entry = zmalloc(sizeof(struct sf_entry)); 159 entry->op = OP_REMOVE_EXT; 160 TAILQ_INSERT_TAIL(&entrylist, entry, next); 161 break; 162 case 'd': 163 acl_type = ACL_TYPE_DEFAULT; 164 break; 165 case 'h': 166 h_flag = 1; 167 break; 168 case 'k': 169 entry = zmalloc(sizeof(struct sf_entry)); 170 entry->op = OP_REMOVE_DEF; 171 TAILQ_INSERT_TAIL(&entrylist, entry, next); 172 break; 173 case 'm': 174 entry = zmalloc(sizeof(struct sf_entry)); 175 entry->acl = acl_from_text(optarg); 176 if (entry->acl == NULL) 177 err(1, "%s", optarg); 178 entry->op = OP_MERGE_ACL; 179 TAILQ_INSERT_TAIL(&entrylist, entry, next); 180 break; 181 case 'n': 182 n_flag++; 183 break; 184 case 'x': 185 entry = zmalloc(sizeof(struct sf_entry)); 186 entry_number = strtol(optarg, &end, 10); 187 if (end - optarg == (int)strlen(optarg)) { 188 if (entry_number < 0) 189 errx(1, "%s: entry number cannot be less than zero", optarg); 190 entry->entry_number = entry_number; 191 entry->op = OP_REMOVE_BY_NUMBER; 192 } else { 193 entry->acl = acl_from_text(optarg); 194 if (entry->acl == NULL) 195 err(1, "%s", optarg); 196 entry->op = OP_REMOVE_ACL; 197 } 198 TAILQ_INSERT_TAIL(&entrylist, entry, next); 199 break; 200 default: 201 usage(); 202 break; 203 } 204 argc -= optind; 205 argv += optind; 206 207 if (n_flag == 0 && TAILQ_EMPTY(&entrylist)) 208 usage(); 209 210 /* take list of files from stdin */ 211 if (argc == 0 || strcmp(argv[0], "-") == 0) { 212 if (have_stdin) 213 err(1, "cannot have more than one stdin"); 214 have_stdin = 1; 215 bzero(&filename, sizeof(filename)); 216 while (fgets(filename, (int)sizeof(filename), stdin)) { 217 /* remove the \n */ 218 filename[strlen(filename) - 1] = '\0'; 219 fn_dup = strdup(filename); 220 if (fn_dup == NULL) 221 err(1, "strdup() failed"); 222 add_filename(fn_dup); 223 } 224 } else 225 for (i = 0; i < argc; i++) 226 add_filename(argv[i]); 227 228 /* cycle through each file */ 229 TAILQ_FOREACH(file, &filelist, next) { 230 local_error = 0; 231 232 if (stat(file->filename, &sb) == -1) { 233 warn("%s: stat() failed", file->filename); 234 carried_error++; 235 continue; 236 } 237 238 if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) { 239 warnx("%s: default ACL may only be set on a directory", 240 file->filename); 241 carried_error++; 242 continue; 243 } 244 245 if (h_flag) 246 ret = lpathconf(file->filename, _PC_ACL_NFS4); 247 else 248 ret = pathconf(file->filename, _PC_ACL_NFS4); 249 if (ret > 0) { 250 if (acl_type == ACL_TYPE_DEFAULT) { 251 warnx("%s: there are no default entries " 252 "in NFSv4 ACLs", file->filename); 253 carried_error++; 254 continue; 255 } 256 acl_type = ACL_TYPE_NFS4; 257 } else if (ret == 0) { 258 if (acl_type == ACL_TYPE_NFS4) 259 acl_type = ACL_TYPE_ACCESS; 260 } else if (ret < 0 && errno != EINVAL) { 261 warn("%s: pathconf(..., _PC_ACL_NFS4) failed", 262 file->filename); 263 } 264 265 if (h_flag) 266 acl = acl_get_link_np(file->filename, acl_type); 267 else 268 acl = acl_get_file(file->filename, acl_type); 269 if (acl == NULL) { 270 if (h_flag) 271 warn("%s: acl_get_link_np() failed", 272 file->filename); 273 else 274 warn("%s: acl_get_file() failed", 275 file->filename); 276 carried_error++; 277 continue; 278 } 279 280 /* cycle through each option */ 281 TAILQ_FOREACH(entry, &entrylist, next) { 282 if (local_error) 283 continue; 284 285 switch(entry->op) { 286 case OP_ADD_ACL: 287 local_error += add_acl(entry->acl, 288 entry->entry_number, &acl, file->filename); 289 break; 290 case OP_MERGE_ACL: 291 local_error += merge_acl(entry->acl, &acl, 292 file->filename); 293 need_mask = 1; 294 break; 295 case OP_REMOVE_EXT: 296 /* 297 * Don't try to call remove_ext() for empty 298 * default ACL. 299 */ 300 if (acl_type == ACL_TYPE_DEFAULT && 301 acl_get_entry(acl, ACL_FIRST_ENTRY, 302 &unused_entry) == 0) { 303 local_error += remove_default(&acl, 304 file->filename); 305 break; 306 } 307 remove_ext(&acl, file->filename); 308 need_mask = 0; 309 break; 310 case OP_REMOVE_DEF: 311 if (acl_type == ACL_TYPE_NFS4) { 312 warnx("%s: there are no default entries in NFSv4 ACLs; " 313 "cannot remove", file->filename); 314 local_error++; 315 break; 316 } 317 if (acl_delete_def_file(file->filename) == -1) { 318 warn("%s: acl_delete_def_file() failed", 319 file->filename); 320 local_error++; 321 } 322 if (acl_type == ACL_TYPE_DEFAULT) 323 local_error += remove_default(&acl, 324 file->filename); 325 need_mask = 0; 326 break; 327 case OP_REMOVE_ACL: 328 local_error += remove_acl(entry->acl, &acl, 329 file->filename); 330 need_mask = 1; 331 break; 332 case OP_REMOVE_BY_NUMBER: 333 local_error += remove_by_number(entry->entry_number, 334 &acl, file->filename); 335 need_mask = 1; 336 break; 337 } 338 } 339 340 /* 341 * Don't try to set an empty default ACL; it will always fail. 342 * Use acl_delete_def_file(3) instead. 343 */ 344 if (acl_type == ACL_TYPE_DEFAULT && 345 acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { 346 if (acl_delete_def_file(file->filename) == -1) { 347 warn("%s: acl_delete_def_file() failed", 348 file->filename); 349 carried_error++; 350 } 351 continue; 352 } 353 354 /* don't bother setting the ACL if something is broken */ 355 if (local_error) { 356 carried_error++; 357 continue; 358 } 359 360 if (acl_type != ACL_TYPE_NFS4 && need_mask && 361 set_acl_mask(&acl, file->filename) == -1) { 362 warnx("%s: failed to set ACL mask", file->filename); 363 carried_error++; 364 } else if (h_flag) { 365 if (acl_set_link_np(file->filename, acl_type, 366 acl) == -1) { 367 carried_error++; 368 warn("%s: acl_set_link_np() failed", 369 file->filename); 370 } 371 } else { 372 if (acl_set_file(file->filename, acl_type, 373 acl) == -1) { 374 carried_error++; 375 warn("%s: acl_set_file() failed", 376 file->filename); 377 } 378 } 379 380 acl_free(acl); 381 } 382 383 return (carried_error); 384 } 385