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 acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS); 75 if (acl[ACCESS_ACL] == NULL) 76 err(1, "acl_get_file() failed"); 77 if (S_ISDIR(sb.st_mode)) { 78 acl[DEFAULT_ACL] = acl_get_file(filename, ACL_TYPE_DEFAULT); 79 if (acl[DEFAULT_ACL] == NULL) 80 err(1, "acl_get_file() failed"); 81 } else 82 acl[DEFAULT_ACL] = NULL; 83 84 return (acl); 85 } 86 87 static void 88 usage(void) 89 { 90 91 fprintf(stderr, "usage: setfacl [-bdknv] [-m entries] [-M file1] " 92 "[-x entries] [-X file2] [file ...]\n"); 93 exit(1); 94 } 95 96 int 97 main(int argc, char *argv[]) 98 { 99 acl_t *acl, final_acl; 100 char filename[PATH_MAX]; 101 int local_error, carried_error, ch, i; 102 struct sf_file *file; 103 struct sf_entry *entry; 104 105 acl_type = ACL_TYPE_ACCESS; 106 carried_error = local_error = 0; 107 have_mask = have_stdin = n_flag = need_mask = 0; 108 109 TAILQ_INIT(&entrylist); 110 TAILQ_INIT(&filelist); 111 112 while ((ch = getopt(argc, argv, "M:X:bdkm:nx:")) != -1) 113 switch(ch) { 114 case 'M': 115 entry = zmalloc(sizeof(struct sf_entry)); 116 entry->acl = get_acl_from_file(optarg); 117 if (entry->acl == NULL) 118 err(1, "get_acl_from_file() failed"); 119 entry->op = OP_MERGE_ACL; 120 TAILQ_INSERT_TAIL(&entrylist, entry, next); 121 break; 122 case 'X': 123 entry = zmalloc(sizeof(struct sf_entry)); 124 entry->acl = get_acl_from_file(optarg); 125 entry->op = OP_REMOVE_ACL; 126 TAILQ_INSERT_TAIL(&entrylist, entry, next); 127 break; 128 case 'b': 129 entry = zmalloc(sizeof(struct sf_entry)); 130 entry->op = OP_REMOVE_EXT; 131 TAILQ_INSERT_TAIL(&entrylist, entry, next); 132 break; 133 case 'd': 134 acl_type = ACL_TYPE_DEFAULT; 135 break; 136 case 'k': 137 entry = zmalloc(sizeof(struct sf_entry)); 138 entry->op = OP_REMOVE_DEF; 139 TAILQ_INSERT_TAIL(&entrylist, entry, next); 140 break; 141 case 'm': 142 entry = zmalloc(sizeof(struct sf_entry)); 143 entry->acl = acl_from_text(optarg); 144 if (entry->acl == NULL) 145 err(1, "acl_from_text() failed"); 146 entry->op = OP_MERGE_ACL; 147 TAILQ_INSERT_TAIL(&entrylist, entry, next); 148 break; 149 case 'n': 150 n_flag++; 151 break; 152 case 'x': 153 entry = zmalloc(sizeof(struct sf_entry)); 154 entry->acl = acl_from_text(optarg); 155 if (entry->acl == NULL) 156 err(1, "acl_from_text() failed"); 157 entry->op = OP_REMOVE_ACL; 158 TAILQ_INSERT_TAIL(&entrylist, entry, next); 159 break; 160 default: 161 usage(); 162 break; 163 } 164 argc -= optind; 165 argv += optind; 166 167 if (n_flag == 0 && TAILQ_EMPTY(&entrylist)) 168 usage(); 169 170 /* take list of files from stdin */ 171 if (argc == 0 || strcmp(argv[0], "-") == 0) { 172 if (have_stdin) 173 err(1, "cannot have more than one stdin"); 174 have_stdin = 1; 175 bzero(&filename, sizeof(filename)); 176 while (fgets(filename, (int)sizeof(filename), stdin)) { 177 /* remove the \n */ 178 filename[strlen(filename) - 1] = '\0'; 179 add_filename(filename); 180 } 181 } else 182 for (i = 0; i < argc; i++) 183 add_filename(argv[i]); 184 185 /* cycle through each file */ 186 TAILQ_FOREACH(file, &filelist, next) { 187 /* get our initial access and default ACL's */ 188 acl = get_file_acls(file->filename); 189 if (acl == NULL) 190 continue; 191 if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) { 192 warnx("Default ACL not valid for %s", file->filename); 193 continue; 194 } 195 196 local_error = 0; 197 198 /* cycle through each option */ 199 TAILQ_FOREACH(entry, &entrylist, next) { 200 if (local_error) 201 continue; 202 203 switch(entry->op) { 204 case OP_MERGE_ACL: 205 local_error += merge_acl(entry->acl, acl); 206 need_mask = 1; 207 break; 208 case OP_REMOVE_EXT: 209 remove_ext(acl); 210 need_mask = 0; 211 break; 212 case OP_REMOVE_DEF: 213 if (acl_delete_def_file(file->filename) == -1) { 214 warn("acl_delete_def_file() failed"); 215 local_error++; 216 } 217 local_error += remove_default(acl); 218 need_mask = 0; 219 break; 220 case OP_REMOVE_ACL: 221 local_error += remove_acl(entry->acl, acl); 222 need_mask = 1; 223 break; 224 } 225 } 226 227 /* don't bother setting the ACL if something is broken */ 228 if (local_error) { 229 carried_error++; 230 continue; 231 } 232 233 if (acl_type == ACL_TYPE_ACCESS) 234 final_acl = acl[ACCESS_ACL]; 235 else 236 final_acl = acl[DEFAULT_ACL]; 237 238 if (need_mask && (set_acl_mask(&final_acl) == -1)) { 239 warnx("failed to set ACL mask on %s", file->filename); 240 carried_error++; 241 } else if (acl_set_file(file->filename, acl_type, 242 final_acl) == -1) { 243 carried_error++; 244 warn("acl_set_file() failed for %s", file->filename); 245 } 246 247 acl_free(acl[ACCESS_ACL]); 248 acl_free(acl[DEFAULT_ACL]); 249 free(acl); 250 } 251 252 return (carried_error); 253 } 254