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 113 acl_type = ACL_TYPE_ACCESS; 114 carried_error = local_error = 0; 115 h_flag = have_mask = have_stdin = n_flag = need_mask = 0; 116 117 TAILQ_INIT(&entrylist); 118 TAILQ_INIT(&filelist); 119 120 while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1) 121 switch(ch) { 122 case 'M': 123 entry = zmalloc(sizeof(struct sf_entry)); 124 entry->acl = get_acl_from_file(optarg); 125 if (entry->acl == NULL) 126 err(1, "get_acl_from_file() failed"); 127 entry->op = OP_MERGE_ACL; 128 TAILQ_INSERT_TAIL(&entrylist, entry, next); 129 break; 130 case 'X': 131 entry = zmalloc(sizeof(struct sf_entry)); 132 entry->acl = get_acl_from_file(optarg); 133 entry->op = OP_REMOVE_ACL; 134 TAILQ_INSERT_TAIL(&entrylist, entry, next); 135 break; 136 case 'b': 137 entry = zmalloc(sizeof(struct sf_entry)); 138 entry->op = OP_REMOVE_EXT; 139 TAILQ_INSERT_TAIL(&entrylist, entry, next); 140 break; 141 case 'd': 142 acl_type = ACL_TYPE_DEFAULT; 143 break; 144 case 'h': 145 h_flag = 1; 146 break; 147 case 'k': 148 entry = zmalloc(sizeof(struct sf_entry)); 149 entry->op = OP_REMOVE_DEF; 150 TAILQ_INSERT_TAIL(&entrylist, entry, next); 151 break; 152 case 'm': 153 entry = zmalloc(sizeof(struct sf_entry)); 154 entry->acl = acl_from_text(optarg); 155 if (entry->acl == NULL) 156 err(1, "%s", optarg); 157 entry->op = OP_MERGE_ACL; 158 TAILQ_INSERT_TAIL(&entrylist, entry, next); 159 break; 160 case 'n': 161 n_flag++; 162 break; 163 case 'x': 164 entry = zmalloc(sizeof(struct sf_entry)); 165 entry->acl = acl_from_text(optarg); 166 if (entry->acl == NULL) 167 err(1, "%s", optarg); 168 entry->op = OP_REMOVE_ACL; 169 TAILQ_INSERT_TAIL(&entrylist, entry, next); 170 break; 171 default: 172 usage(); 173 break; 174 } 175 argc -= optind; 176 argv += optind; 177 178 if (n_flag == 0 && TAILQ_EMPTY(&entrylist)) 179 usage(); 180 181 /* take list of files from stdin */ 182 if (argc == 0 || strcmp(argv[0], "-") == 0) { 183 if (have_stdin) 184 err(1, "cannot have more than one stdin"); 185 have_stdin = 1; 186 bzero(&filename, sizeof(filename)); 187 while (fgets(filename, (int)sizeof(filename), stdin)) { 188 /* remove the \n */ 189 filename[strlen(filename) - 1] = '\0'; 190 add_filename(filename); 191 } 192 } else 193 for (i = 0; i < argc; i++) 194 add_filename(argv[i]); 195 196 /* cycle through each file */ 197 TAILQ_FOREACH(file, &filelist, next) { 198 /* get our initial access and default ACL's */ 199 acl = get_file_acls(file->filename); 200 if (acl == NULL) 201 continue; 202 if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) { 203 warnx("Default ACL not valid for %s", file->filename); 204 continue; 205 } 206 207 local_error = 0; 208 209 /* cycle through each option */ 210 TAILQ_FOREACH(entry, &entrylist, next) { 211 if (local_error) 212 continue; 213 214 switch(entry->op) { 215 case OP_MERGE_ACL: 216 local_error += merge_acl(entry->acl, acl); 217 need_mask = 1; 218 break; 219 case OP_REMOVE_EXT: 220 remove_ext(acl); 221 need_mask = 0; 222 break; 223 case OP_REMOVE_DEF: 224 if (acl_delete_def_file(file->filename) == -1) { 225 warn("acl_delete_def_file() failed"); 226 local_error++; 227 } 228 local_error += remove_default(acl); 229 need_mask = 0; 230 break; 231 case OP_REMOVE_ACL: 232 local_error += remove_acl(entry->acl, acl); 233 need_mask = 1; 234 break; 235 } 236 } 237 238 /* don't bother setting the ACL if something is broken */ 239 if (local_error) { 240 carried_error++; 241 continue; 242 } 243 244 if (acl_type == ACL_TYPE_ACCESS) 245 final_acl = acl[ACCESS_ACL]; 246 else 247 final_acl = acl[DEFAULT_ACL]; 248 249 if (need_mask && (set_acl_mask(&final_acl) == -1)) { 250 warnx("failed to set ACL mask on %s", file->filename); 251 carried_error++; 252 } else if (acl_set_file(file->filename, acl_type, 253 final_acl) == -1) { 254 carried_error++; 255 warn("acl_set_file() failed for %s", file->filename); 256 } 257 258 acl_free(acl[ACCESS_ACL]); 259 acl_free(acl[DEFAULT_ACL]); 260 free(acl); 261 } 262 263 return (carried_error); 264 } 265