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 THE VOICES IN HIS HEAD BE 18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF 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 <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "setfacl.h" 43 44 static void add_filename(const char *filename); 45 static acl_t *get_file_acls(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 acl_t * 63 get_file_acls(const char *filename) 64 { 65 acl_t *acl; 66 struct stat sb; 67 68 if (stat(filename, &sb) == -1) { 69 warn("stat() of %s failed", filename); 70 return (NULL); 71 } 72 73 acl = zmalloc(sizeof(acl_t) * 2); 74 if (h_flag) 75 acl[ACCESS_ACL] = acl_get_link_np(filename, ACL_TYPE_ACCESS); 76 else 77 acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS); 78 if (acl[ACCESS_ACL] == NULL) 79 err(1, "acl_get_file() failed"); 80 if (S_ISDIR(sb.st_mode)) { 81 if (h_flag) 82 acl[DEFAULT_ACL] = acl_get_link_np(filename, 83 ACL_TYPE_DEFAULT); 84 else 85 acl[DEFAULT_ACL] = acl_get_file(filename, 86 ACL_TYPE_DEFAULT); 87 if (acl[DEFAULT_ACL] == NULL) 88 err(1, "acl_get_file() failed"); 89 } else 90 acl[DEFAULT_ACL] = NULL; 91 92 return (acl); 93 } 94 95 static void 96 usage(void) 97 { 98 99 fprintf(stderr, "usage: setfacl [-bdhkn] [-m entries] [-M file] " 100 "[-x entries] [-X file] [file ...]\n"); 101 exit(1); 102 } 103 104 int 105 main(int argc, char *argv[]) 106 { 107 acl_t *acl, final_acl; 108 char filename[PATH_MAX]; 109 int local_error, carried_error, ch, i; 110 struct sf_file *file; 111 struct sf_entry *entry; 112 const char *fn_dup; 113 114 acl_type = ACL_TYPE_ACCESS; 115 carried_error = local_error = 0; 116 h_flag = have_mask = have_stdin = n_flag = need_mask = 0; 117 118 TAILQ_INIT(&entrylist); 119 TAILQ_INIT(&filelist); 120 121 while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1) 122 switch(ch) { 123 case 'M': 124 entry = zmalloc(sizeof(struct sf_entry)); 125 entry->acl = get_acl_from_file(optarg); 126 if (entry->acl == NULL) 127 err(1, "get_acl_from_file() failed"); 128 entry->op = OP_MERGE_ACL; 129 TAILQ_INSERT_TAIL(&entrylist, entry, next); 130 break; 131 case 'X': 132 entry = zmalloc(sizeof(struct sf_entry)); 133 entry->acl = get_acl_from_file(optarg); 134 entry->op = OP_REMOVE_ACL; 135 TAILQ_INSERT_TAIL(&entrylist, entry, next); 136 break; 137 case 'b': 138 entry = zmalloc(sizeof(struct sf_entry)); 139 entry->op = OP_REMOVE_EXT; 140 TAILQ_INSERT_TAIL(&entrylist, entry, next); 141 break; 142 case 'd': 143 acl_type = ACL_TYPE_DEFAULT; 144 break; 145 case 'h': 146 h_flag = 1; 147 break; 148 case 'k': 149 entry = zmalloc(sizeof(struct sf_entry)); 150 entry->op = OP_REMOVE_DEF; 151 TAILQ_INSERT_TAIL(&entrylist, entry, next); 152 break; 153 case 'm': 154 entry = zmalloc(sizeof(struct sf_entry)); 155 entry->acl = acl_from_text(optarg); 156 if (entry->acl == NULL) 157 err(1, "%s", optarg); 158 entry->op = OP_MERGE_ACL; 159 TAILQ_INSERT_TAIL(&entrylist, entry, next); 160 break; 161 case 'n': 162 n_flag++; 163 break; 164 case 'x': 165 entry = zmalloc(sizeof(struct sf_entry)); 166 entry->acl = acl_from_text(optarg); 167 if (entry->acl == NULL) 168 err(1, "%s", optarg); 169 entry->op = OP_REMOVE_ACL; 170 TAILQ_INSERT_TAIL(&entrylist, entry, next); 171 break; 172 default: 173 usage(); 174 break; 175 } 176 argc -= optind; 177 argv += optind; 178 179 if (n_flag == 0 && TAILQ_EMPTY(&entrylist)) 180 usage(); 181 182 /* take list of files from stdin */ 183 if (argc == 0 || strcmp(argv[0], "-") == 0) { 184 if (have_stdin) 185 err(1, "cannot have more than one stdin"); 186 have_stdin = 1; 187 bzero(&filename, sizeof(filename)); 188 while (fgets(filename, (int)sizeof(filename), stdin)) { 189 /* remove the \n */ 190 filename[strlen(filename) - 1] = '\0'; 191 fn_dup = strdup(filename); 192 if (fn_dup == NULL) 193 err(1, "strdup() failed"); 194 add_filename(fn_dup); 195 } 196 } else 197 for (i = 0; i < argc; i++) 198 add_filename(argv[i]); 199 200 /* cycle through each file */ 201 TAILQ_FOREACH(file, &filelist, next) { 202 /* get our initial access and default ACL's */ 203 acl = get_file_acls(file->filename); 204 if (acl == NULL) 205 continue; 206 if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) { 207 warnx("Default ACL not valid for %s", file->filename); 208 continue; 209 } 210 211 local_error = 0; 212 213 /* cycle through each option */ 214 TAILQ_FOREACH(entry, &entrylist, next) { 215 if (local_error) 216 continue; 217 218 switch(entry->op) { 219 case OP_MERGE_ACL: 220 local_error += merge_acl(entry->acl, acl); 221 need_mask = 1; 222 break; 223 case OP_REMOVE_EXT: 224 remove_ext(acl); 225 need_mask = 0; 226 break; 227 case OP_REMOVE_DEF: 228 if (acl_delete_def_file(file->filename) == -1) { 229 warn("acl_delete_def_file() failed"); 230 local_error++; 231 } 232 local_error += remove_default(acl); 233 need_mask = 0; 234 break; 235 case OP_REMOVE_ACL: 236 local_error += remove_acl(entry->acl, acl); 237 need_mask = 1; 238 break; 239 } 240 } 241 242 /* don't bother setting the ACL if something is broken */ 243 if (local_error) { 244 carried_error++; 245 continue; 246 } 247 248 if (acl_type == ACL_TYPE_ACCESS) { 249 final_acl = acl[ACCESS_ACL]; 250 acl_free(acl[DEFAULT_ACL]); 251 } else { 252 final_acl = acl[DEFAULT_ACL]; 253 acl_free(acl[ACCESS_ACL]); 254 } 255 256 if (need_mask && (set_acl_mask(&final_acl) == -1)) { 257 warnx("failed to set ACL mask on %s", file->filename); 258 carried_error++; 259 } else if (h_flag) { 260 if (acl_set_link_np(file->filename, acl_type, 261 final_acl) == -1) { 262 carried_error++; 263 warn("acl_set_link_np() failed for %s", 264 file->filename); 265 } 266 } else { 267 if (acl_set_file(file->filename, acl_type, 268 final_acl) == -1) { 269 carried_error++; 270 warn("acl_set_file() failed for %s", 271 file->filename); 272 } 273 } 274 275 acl_free(final_acl); 276 free(acl); 277 } 278 279 return (carried_error); 280 } 281