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