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 static void add_filename(const char *filename); 46 static void usage(void); 47 48 static void 49 add_filename(const char *filename) 50 { 51 struct sf_file *file; 52 53 if (strlen(filename) > PATH_MAX - 1) { 54 warn("illegal filename"); 55 return; 56 } 57 file = zmalloc(sizeof(struct sf_file)); 58 file->filename = filename; 59 TAILQ_INSERT_TAIL(&filelist, file, next); 60 } 61 62 static void 63 usage(void) 64 { 65 66 fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] " 67 "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n"); 68 exit(1); 69 } 70 71 int 72 main(int argc, char *argv[]) 73 { 74 acl_t acl; 75 acl_type_t acl_type; 76 char filename[PATH_MAX]; 77 int local_error, carried_error, ch, i, entry_number, ret; 78 int h_flag; 79 struct sf_file *file; 80 struct sf_entry *entry; 81 const char *fn_dup; 82 char *end; 83 struct stat sb; 84 85 acl_type = ACL_TYPE_ACCESS; 86 carried_error = local_error = 0; 87 h_flag = have_mask = have_stdin = n_flag = need_mask = 0; 88 89 TAILQ_INIT(&entrylist); 90 TAILQ_INIT(&filelist); 91 92 while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1) 93 switch(ch) { 94 case 'M': 95 entry = zmalloc(sizeof(struct sf_entry)); 96 entry->acl = get_acl_from_file(optarg); 97 if (entry->acl == NULL) 98 err(1, "%s: get_acl_from_file() failed", optarg); 99 entry->op = OP_MERGE_ACL; 100 TAILQ_INSERT_TAIL(&entrylist, entry, next); 101 break; 102 case 'X': 103 entry = zmalloc(sizeof(struct sf_entry)); 104 entry->acl = get_acl_from_file(optarg); 105 entry->op = OP_REMOVE_ACL; 106 TAILQ_INSERT_TAIL(&entrylist, entry, next); 107 break; 108 case 'a': 109 entry = zmalloc(sizeof(struct sf_entry)); 110 111 entry_number = strtol(optarg, &end, 10); 112 if (end - optarg != (int)strlen(optarg)) 113 errx(1, "%s: invalid entry number", optarg); 114 if (entry_number < 0) 115 errx(1, "%s: entry number cannot be less than zero", optarg); 116 entry->entry_number = entry_number; 117 118 if (argv[optind] == NULL) 119 errx(1, "missing ACL"); 120 entry->acl = acl_from_text(argv[optind]); 121 if (entry->acl == NULL) 122 err(1, "%s", argv[optind]); 123 optind++; 124 entry->op = OP_ADD_ACL; 125 TAILQ_INSERT_TAIL(&entrylist, entry, next); 126 break; 127 case 'b': 128 entry = zmalloc(sizeof(struct sf_entry)); 129 entry->op = OP_REMOVE_EXT; 130 TAILQ_INSERT_TAIL(&entrylist, entry, next); 131 break; 132 case 'd': 133 acl_type = ACL_TYPE_DEFAULT; 134 break; 135 case 'h': 136 h_flag = 1; 137 break; 138 case 'k': 139 entry = zmalloc(sizeof(struct sf_entry)); 140 entry->op = OP_REMOVE_DEF; 141 TAILQ_INSERT_TAIL(&entrylist, entry, next); 142 break; 143 case 'm': 144 entry = zmalloc(sizeof(struct sf_entry)); 145 entry->acl = acl_from_text(optarg); 146 if (entry->acl == NULL) 147 err(1, "%s", optarg); 148 entry->op = OP_MERGE_ACL; 149 TAILQ_INSERT_TAIL(&entrylist, entry, next); 150 break; 151 case 'n': 152 n_flag++; 153 break; 154 case 'x': 155 entry = zmalloc(sizeof(struct sf_entry)); 156 entry_number = strtol(optarg, &end, 10); 157 if (end - optarg == (int)strlen(optarg)) { 158 if (entry_number < 0) 159 errx(1, "%s: entry number cannot be less than zero", optarg); 160 entry->entry_number = entry_number; 161 entry->op = OP_REMOVE_BY_NUMBER; 162 } else { 163 entry->acl = acl_from_text(optarg); 164 if (entry->acl == NULL) 165 err(1, "%s", optarg); 166 entry->op = OP_REMOVE_ACL; 167 } 168 TAILQ_INSERT_TAIL(&entrylist, entry, next); 169 break; 170 default: 171 usage(); 172 break; 173 } 174 argc -= optind; 175 argv += optind; 176 177 if (n_flag == 0 && TAILQ_EMPTY(&entrylist)) 178 usage(); 179 180 /* take list of files from stdin */ 181 if (argc == 0 || strcmp(argv[0], "-") == 0) { 182 if (have_stdin) 183 err(1, "cannot have more than one stdin"); 184 have_stdin = 1; 185 bzero(&filename, sizeof(filename)); 186 while (fgets(filename, (int)sizeof(filename), stdin)) { 187 /* remove the \n */ 188 filename[strlen(filename) - 1] = '\0'; 189 fn_dup = strdup(filename); 190 if (fn_dup == NULL) 191 err(1, "strdup() failed"); 192 add_filename(fn_dup); 193 } 194 } else 195 for (i = 0; i < argc; i++) 196 add_filename(argv[i]); 197 198 /* cycle through each file */ 199 TAILQ_FOREACH(file, &filelist, next) { 200 local_error = 0; 201 202 if (stat(file->filename, &sb) == -1) { 203 warn("%s: stat() failed", file->filename); 204 carried_error++; 205 continue; 206 } 207 208 if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) { 209 warnx("%s: default ACL may only be set on a directory", 210 file->filename); 211 carried_error++; 212 continue; 213 } 214 215 if (h_flag) 216 ret = lpathconf(file->filename, _PC_ACL_NFS4); 217 else 218 ret = pathconf(file->filename, _PC_ACL_NFS4); 219 if (ret > 0) { 220 if (acl_type == ACL_TYPE_DEFAULT) { 221 warnx("%s: there are no default entries " 222 "in NFSv4 ACLs", file->filename); 223 carried_error++; 224 continue; 225 } 226 acl_type = ACL_TYPE_NFS4; 227 } else if (ret == 0) { 228 if (acl_type == ACL_TYPE_NFS4) 229 acl_type = ACL_TYPE_ACCESS; 230 } else if (ret < 0 && errno != EINVAL) { 231 warn("%s: pathconf(..., _PC_ACL_NFS4) failed", 232 file->filename); 233 } 234 235 if (h_flag) 236 acl = acl_get_link_np(file->filename, acl_type); 237 else 238 acl = acl_get_file(file->filename, acl_type); 239 if (acl == NULL) { 240 if (h_flag) 241 warn("%s: acl_get_link_np() failed", 242 file->filename); 243 else 244 warn("%s: acl_get_file() failed", 245 file->filename); 246 carried_error++; 247 continue; 248 } 249 250 /* cycle through each option */ 251 TAILQ_FOREACH(entry, &entrylist, next) { 252 if (local_error) 253 continue; 254 255 switch(entry->op) { 256 case OP_ADD_ACL: 257 local_error += add_acl(entry->acl, 258 entry->entry_number, &acl, file->filename); 259 break; 260 case OP_MERGE_ACL: 261 local_error += merge_acl(entry->acl, &acl, 262 file->filename); 263 need_mask = 1; 264 break; 265 case OP_REMOVE_EXT: 266 remove_ext(&acl, file->filename); 267 need_mask = 0; 268 break; 269 case OP_REMOVE_DEF: 270 if (acl_type == ACL_TYPE_NFS4) { 271 warnx("%s: there are no default entries in NFSv4 ACLs; " 272 "cannot remove", file->filename); 273 local_error++; 274 break; 275 } 276 if (acl_delete_def_file(file->filename) == -1) { 277 warn("%s: acl_delete_def_file() failed", 278 file->filename); 279 local_error++; 280 } 281 if (acl_type == ACL_TYPE_DEFAULT) 282 local_error += remove_default(&acl, 283 file->filename); 284 need_mask = 0; 285 break; 286 case OP_REMOVE_ACL: 287 local_error += remove_acl(entry->acl, &acl, 288 file->filename); 289 need_mask = 1; 290 break; 291 case OP_REMOVE_BY_NUMBER: 292 local_error += remove_by_number(entry->entry_number, 293 &acl, file->filename); 294 need_mask = 1; 295 break; 296 } 297 } 298 299 /* don't bother setting the ACL if something is broken */ 300 if (local_error) { 301 carried_error++; 302 continue; 303 } 304 305 if (acl_type != ACL_TYPE_NFS4 && need_mask && 306 set_acl_mask(&acl, file->filename) == -1) { 307 warnx("%s: failed to set ACL mask", file->filename); 308 carried_error++; 309 } else if (h_flag) { 310 if (acl_set_link_np(file->filename, acl_type, 311 acl) == -1) { 312 carried_error++; 313 warn("%s: acl_set_link_np() failed", 314 file->filename); 315 } 316 } else { 317 if (acl_set_file(file->filename, acl_type, 318 acl) == -1) { 319 carried_error++; 320 warn("%s: acl_set_file() failed", 321 file->filename); 322 } 323 } 324 325 acl_free(acl); 326 } 327 328 return (carried_error); 329 } 330