1 1.3 wiz /* $NetBSD: setfacl.c,v 1.3 2020/06/18 19:44:01 wiz Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2001 Chris D. Faulhaber 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * Redistribution and use in source and binary forms, with or without 8 1.1 christos * modification, are permitted provided that the following conditions 9 1.1 christos * are met: 10 1.1 christos * 1. Redistributions of source code must retain the above copyright 11 1.1 christos * notice, this list of conditions and the following disclaimer. 12 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 christos * notice, this list of conditions and the following disclaimer in the 14 1.1 christos * documentation and/or other materials provided with the distribution. 15 1.1 christos * 16 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 christos * SUCH DAMAGE. 27 1.1 christos */ 28 1.1 christos 29 1.1 christos #include <sys/cdefs.h> 30 1.1 christos #if 0 31 1.1 christos __FBSDID("$FreeBSD: head/bin/setfacl/setfacl.c 339793 2018-10-26 21:17:06Z markj $"); 32 1.1 christos #else 33 1.3 wiz __RCSID("$NetBSD: setfacl.c,v 1.3 2020/06/18 19:44:01 wiz Exp $"); 34 1.1 christos #endif 35 1.1 christos 36 1.1 christos #include <sys/param.h> 37 1.1 christos #include <sys/acl.h> 38 1.1 christos #include <sys/queue.h> 39 1.1 christos 40 1.1 christos #include <err.h> 41 1.1 christos #include <errno.h> 42 1.1 christos #include <fts.h> 43 1.1 christos #include <stdbool.h> 44 1.1 christos #include <stdint.h> 45 1.1 christos #include <stdio.h> 46 1.1 christos #include <stdlib.h> 47 1.1 christos #include <string.h> 48 1.1 christos #include <unistd.h> 49 1.1 christos 50 1.1 christos #include "setfacl.h" 51 1.1 christos 52 1.1 christos /* file operations */ 53 1.1 christos #define OP_MERGE_ACL 0x00 /* merge acl's (-mM) */ 54 1.1 christos #define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */ 55 1.1 christos #define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */ 56 1.1 christos #define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */ 57 1.1 christos #define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */ 58 1.1 christos #define OP_ADD_ACL 0x05 /* add acls entries at a given position */ 59 1.1 christos 60 1.1 christos /* TAILQ entry for acl operations */ 61 1.1 christos struct sf_entry { 62 1.1 christos uint op; 63 1.1 christos acl_t acl; 64 1.1 christos uint entry_number; 65 1.1 christos TAILQ_ENTRY(sf_entry) next; 66 1.1 christos }; 67 1.1 christos static TAILQ_HEAD(, sf_entry) entrylist; 68 1.1 christos 69 1.1 christos bool have_mask; 70 1.1 christos bool have_stdin; 71 1.1 christos bool n_flag; 72 1.1 christos static bool h_flag; 73 1.1 christos static bool H_flag; 74 1.1 christos static bool L_flag; 75 1.1 christos static bool R_flag; 76 1.1 christos static bool need_mask; 77 1.1 christos static acl_type_t acl_type = ACL_TYPE_ACCESS; 78 1.1 christos 79 1.1 christos static int handle_file(FTS *ftsp, FTSENT *file); 80 1.1 christos static acl_t clear_inheritance_flags(acl_t acl); 81 1.1 christos static char **stdin_files(void); 82 1.2 joerg static __dead void usage(void); 83 1.1 christos 84 1.1 christos static void 85 1.1 christos usage(void) 86 1.1 christos { 87 1.1 christos 88 1.3 wiz fprintf(stderr, "usage: setfacl [-bdhkn] " 89 1.3 wiz "[-a position entries] [-M file] [-m entries] " 90 1.3 wiz "[-R [-H | -L | -P]] [-X file] [-x entries | position] [file ...]\n"); 91 1.1 christos exit(1); 92 1.1 christos } 93 1.1 christos 94 1.1 christos static char ** 95 1.1 christos stdin_files(void) 96 1.1 christos { 97 1.1 christos char **files_list; 98 1.1 christos char filename[PATH_MAX]; 99 1.1 christos size_t fl_count, i; 100 1.1 christos 101 1.1 christos if (have_stdin) 102 1.1 christos err(1, "cannot have more than one stdin"); 103 1.1 christos 104 1.1 christos i = 0; 105 1.1 christos have_stdin = true; 106 1.1 christos bzero(&filename, sizeof(filename)); 107 1.1 christos /* Start with an array size sufficient for basic cases. */ 108 1.1 christos fl_count = 1024; 109 1.1 christos files_list = zmalloc(fl_count * sizeof(char *)); 110 1.1 christos while (fgets(filename, (int)sizeof(filename), stdin)) { 111 1.1 christos /* remove the \n */ 112 1.1 christos filename[strlen(filename) - 1] = '\0'; 113 1.1 christos files_list[i] = strdup(filename); 114 1.1 christos if (files_list[i] == NULL) 115 1.1 christos err(1, "strdup() failed"); 116 1.1 christos /* Grow array if necessary. */ 117 1.1 christos if (++i == fl_count) { 118 1.1 christos fl_count <<= 1; 119 1.1 christos if (fl_count > SIZE_MAX / sizeof(char *)) 120 1.1 christos errx(1, "Too many input files"); 121 1.1 christos files_list = zrealloc(files_list, 122 1.1 christos fl_count * sizeof(char *)); 123 1.1 christos } 124 1.1 christos } 125 1.1 christos 126 1.1 christos /* fts_open() requires the last array element to be NULL. */ 127 1.1 christos files_list[i] = NULL; 128 1.1 christos 129 1.1 christos return (files_list); 130 1.1 christos } 131 1.1 christos 132 1.1 christos /* 133 1.1 christos * Remove any inheritance flags from NFSv4 ACLs when running in recursive 134 1.1 christos * mode. This is to avoid files being assigned identical ACLs to their 135 1.1 christos * parent directory while also being set to inherit them. 136 1.1 christos * 137 1.1 christos * The acl argument is assumed to be valid. 138 1.1 christos */ 139 1.1 christos static acl_t 140 1.1 christos clear_inheritance_flags(acl_t acl) 141 1.1 christos { 142 1.1 christos acl_t nacl; 143 1.1 christos acl_entry_t acl_entry; 144 1.1 christos acl_flagset_t acl_flagset; 145 1.1 christos int acl_brand, entry_id; 146 1.1 christos 147 1.1 christos (void)acl_get_brand_np(acl, &acl_brand); 148 1.1 christos if (acl_brand != ACL_BRAND_NFS4) 149 1.1 christos return (acl); 150 1.1 christos 151 1.1 christos nacl = acl_dup(acl); 152 1.1 christos if (nacl == NULL) { 153 1.1 christos warn("acl_dup() failed"); 154 1.1 christos return (acl); 155 1.1 christos } 156 1.1 christos 157 1.1 christos entry_id = ACL_FIRST_ENTRY; 158 1.1 christos while (acl_get_entry(nacl, entry_id, &acl_entry) == 1) { 159 1.1 christos entry_id = ACL_NEXT_ENTRY; 160 1.1 christos if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { 161 1.1 christos warn("acl_get_flagset_np() failed"); 162 1.1 christos continue; 163 1.1 christos } 164 1.1 christos if (acl_get_flag_np(acl_flagset, ACL_ENTRY_INHERIT_ONLY) == 1) { 165 1.1 christos if (acl_delete_entry(nacl, acl_entry) != 0) 166 1.1 christos warn("acl_delete_entry() failed"); 167 1.1 christos continue; 168 1.1 christos } 169 1.1 christos if (acl_delete_flag_np(acl_flagset, 170 1.1 christos ACL_ENTRY_FILE_INHERIT | 171 1.1 christos ACL_ENTRY_DIRECTORY_INHERIT | 172 1.1 christos ACL_ENTRY_NO_PROPAGATE_INHERIT) != 0) 173 1.1 christos warn("acl_delete_flag_np() failed"); 174 1.1 christos } 175 1.1 christos 176 1.1 christos return (nacl); 177 1.1 christos } 178 1.1 christos 179 1.1 christos static int 180 1.1 christos handle_file(FTS *ftsp, FTSENT *file) 181 1.1 christos { 182 1.1 christos acl_t acl, nacl; 183 1.1 christos acl_entry_t unused_entry; 184 1.1 christos int local_error, ret; 185 1.1 christos struct sf_entry *entry; 186 1.1 christos bool follow_symlink; 187 1.1 christos 188 1.1 christos local_error = 0; 189 1.1 christos switch (file->fts_info) { 190 1.1 christos case FTS_D: 191 1.1 christos /* Do not recurse if -R not specified. */ 192 1.1 christos if (!R_flag) 193 1.1 christos fts_set(ftsp, file, FTS_SKIP); 194 1.1 christos break; 195 1.1 christos case FTS_DP: 196 1.1 christos /* Skip the second visit to a directory. */ 197 1.1 christos return (0); 198 1.1 christos case FTS_DNR: 199 1.1 christos case FTS_ERR: 200 1.1 christos warnx("%s: %s", file->fts_path, strerror(file->fts_errno)); 201 1.1 christos return (0); 202 1.1 christos default: 203 1.1 christos break; 204 1.1 christos } 205 1.1 christos 206 1.1 christos if (acl_type == ACL_TYPE_DEFAULT && file->fts_info != FTS_D) { 207 1.1 christos warnx("%s: default ACL may only be set on a directory", 208 1.1 christos file->fts_path); 209 1.1 christos return (1); 210 1.1 christos } 211 1.1 christos 212 1.1 christos follow_symlink = (!R_flag && !h_flag) || (R_flag && L_flag) || 213 1.1 christos (R_flag && H_flag && file->fts_level == FTS_ROOTLEVEL); 214 1.1 christos 215 1.1 christos if (follow_symlink) 216 1.1 christos ret = pathconf(file->fts_accpath, _PC_ACL_NFS4); 217 1.1 christos else 218 1.1 christos ret = lpathconf(file->fts_accpath, _PC_ACL_NFS4); 219 1.1 christos if (ret > 0) { 220 1.1 christos if (acl_type == ACL_TYPE_DEFAULT) { 221 1.1 christos warnx("%s: there are no default entries in NFSv4 ACLs", 222 1.1 christos file->fts_path); 223 1.1 christos return (1); 224 1.1 christos } 225 1.1 christos acl_type = ACL_TYPE_NFS4; 226 1.1 christos } else if (ret == 0) { 227 1.1 christos if (acl_type == ACL_TYPE_NFS4) 228 1.1 christos acl_type = ACL_TYPE_ACCESS; 229 1.1 christos } else if (ret < 0 && errno != EINVAL && errno != ENOENT) { 230 1.1 christos warn("%s: pathconf(_PC_ACL_NFS4) failed", 231 1.1 christos file->fts_path); 232 1.1 christos } 233 1.1 christos 234 1.1 christos if (follow_symlink) 235 1.1 christos acl = acl_get_file(file->fts_accpath, acl_type); 236 1.1 christos else 237 1.1 christos acl = acl_get_link_np(file->fts_accpath, acl_type); 238 1.1 christos if (acl == NULL) { 239 1.1 christos if (follow_symlink) 240 1.1 christos warn("%s: acl_get_file() failed", file->fts_path); 241 1.1 christos else 242 1.1 christos warn("%s: acl_get_link_np() failed", file->fts_path); 243 1.1 christos return (1); 244 1.1 christos } 245 1.1 christos 246 1.1 christos /* Cycle through each option. */ 247 1.1 christos TAILQ_FOREACH(entry, &entrylist, next) { 248 1.1 christos nacl = entry->acl; 249 1.1 christos switch (entry->op) { 250 1.1 christos case OP_ADD_ACL: 251 1.1 christos if (R_flag && file->fts_info != FTS_D && 252 1.1 christos acl_type == ACL_TYPE_NFS4) 253 1.1 christos nacl = clear_inheritance_flags(nacl); 254 1.1 christos local_error += add_acl(nacl, entry->entry_number, &acl, 255 1.1 christos file->fts_path); 256 1.1 christos break; 257 1.1 christos case OP_MERGE_ACL: 258 1.1 christos if (R_flag && file->fts_info != FTS_D && 259 1.1 christos acl_type == ACL_TYPE_NFS4) 260 1.1 christos nacl = clear_inheritance_flags(nacl); 261 1.1 christos local_error += merge_acl(nacl, &acl, file->fts_path); 262 1.1 christos need_mask = true; 263 1.1 christos break; 264 1.1 christos case OP_REMOVE_EXT: 265 1.1 christos /* 266 1.1 christos * Don't try to call remove_ext() for empty 267 1.1 christos * default ACL. 268 1.1 christos */ 269 1.1 christos if (acl_type == ACL_TYPE_DEFAULT && 270 1.1 christos acl_get_entry(acl, ACL_FIRST_ENTRY, 271 1.1 christos &unused_entry) == 0) { 272 1.1 christos local_error += remove_default(&acl, 273 1.1 christos file->fts_path); 274 1.1 christos break; 275 1.1 christos } 276 1.1 christos remove_ext(&acl, file->fts_path); 277 1.1 christos need_mask = false; 278 1.1 christos break; 279 1.1 christos case OP_REMOVE_DEF: 280 1.1 christos if (acl_type == ACL_TYPE_NFS4) { 281 1.1 christos warnx("%s: there are no default entries in " 282 1.1 christos "NFSv4 ACLs; cannot remove", 283 1.1 christos file->fts_path); 284 1.1 christos local_error++; 285 1.1 christos break; 286 1.1 christos } 287 1.1 christos if (acl_delete_def_file(file->fts_accpath) == -1) { 288 1.1 christos warn("%s: acl_delete_def_file() failed", 289 1.1 christos file->fts_path); 290 1.1 christos local_error++; 291 1.1 christos } 292 1.1 christos if (acl_type == ACL_TYPE_DEFAULT) 293 1.1 christos local_error += remove_default(&acl, 294 1.1 christos file->fts_path); 295 1.1 christos need_mask = false; 296 1.1 christos break; 297 1.1 christos case OP_REMOVE_ACL: 298 1.1 christos local_error += remove_acl(nacl, &acl, file->fts_path); 299 1.1 christos need_mask = true; 300 1.1 christos break; 301 1.1 christos case OP_REMOVE_BY_NUMBER: 302 1.1 christos local_error += remove_by_number(entry->entry_number, 303 1.1 christos &acl, file->fts_path); 304 1.1 christos need_mask = true; 305 1.1 christos break; 306 1.1 christos } 307 1.1 christos 308 1.1 christos if (nacl != entry->acl) { 309 1.1 christos acl_free(nacl); 310 1.1 christos nacl = NULL; 311 1.1 christos } 312 1.1 christos if (local_error) 313 1.1 christos break; 314 1.1 christos } 315 1.1 christos 316 1.1 christos ret = 0; 317 1.1 christos 318 1.1 christos /* 319 1.1 christos * Don't try to set an empty default ACL; it will always fail. 320 1.1 christos * Use acl_delete_def_file(3) instead. 321 1.1 christos */ 322 1.1 christos if (acl_type == ACL_TYPE_DEFAULT && 323 1.1 christos acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { 324 1.1 christos if (acl_delete_def_file(file->fts_accpath) == -1) { 325 1.1 christos warn("%s: acl_delete_def_file() failed", 326 1.1 christos file->fts_path); 327 1.1 christos ret = 1; 328 1.1 christos } 329 1.1 christos goto out; 330 1.1 christos } 331 1.1 christos 332 1.1 christos /* Don't bother setting the ACL if something is broken. */ 333 1.1 christos if (local_error) { 334 1.1 christos ret = 1; 335 1.1 christos } else if (acl_type != ACL_TYPE_NFS4 && need_mask && 336 1.1 christos set_acl_mask(&acl, file->fts_path) == -1) { 337 1.1 christos warnx("%s: failed to set ACL mask", file->fts_path); 338 1.1 christos ret = 1; 339 1.1 christos } else if (follow_symlink) { 340 1.1 christos if (acl_set_file(file->fts_accpath, acl_type, acl) == -1) { 341 1.1 christos warn("%s: acl_set_file() failed", file->fts_path); 342 1.1 christos ret = 1; 343 1.1 christos } 344 1.1 christos } else { 345 1.1 christos if (acl_set_link_np(file->fts_accpath, acl_type, acl) == -1) { 346 1.1 christos warn("%s: acl_set_link_np() failed", file->fts_path); 347 1.1 christos ret = 1; 348 1.1 christos } 349 1.1 christos } 350 1.1 christos 351 1.1 christos out: 352 1.1 christos acl_free(acl); 353 1.1 christos return (ret); 354 1.1 christos } 355 1.1 christos 356 1.1 christos int 357 1.1 christos main(int argc, char *argv[]) 358 1.1 christos { 359 1.1 christos int carried_error, ch, entry_number, fts_options; 360 1.1 christos FTS *ftsp; 361 1.1 christos FTSENT *file; 362 1.1 christos char **files_list; 363 1.1 christos struct sf_entry *entry; 364 1.1 christos char *end; 365 1.1 christos 366 1.1 christos acl_type = ACL_TYPE_ACCESS; 367 1.1 christos carried_error = fts_options = 0; 368 1.1 christos have_mask = have_stdin = n_flag = false; 369 1.1 christos 370 1.1 christos TAILQ_INIT(&entrylist); 371 1.1 christos 372 1.1 christos while ((ch = getopt(argc, argv, "HLM:PRX:a:bdhkm:nx:")) != -1) 373 1.1 christos switch(ch) { 374 1.1 christos case 'H': 375 1.1 christos H_flag = true; 376 1.1 christos L_flag = false; 377 1.1 christos break; 378 1.1 christos case 'L': 379 1.1 christos L_flag = true; 380 1.1 christos H_flag = false; 381 1.1 christos break; 382 1.1 christos case 'M': 383 1.1 christos entry = zmalloc(sizeof(struct sf_entry)); 384 1.1 christos entry->acl = get_acl_from_file(optarg); 385 1.1 christos if (entry->acl == NULL) 386 1.1 christos err(1, "%s: get_acl_from_file() failed", 387 1.1 christos optarg); 388 1.1 christos entry->op = OP_MERGE_ACL; 389 1.1 christos TAILQ_INSERT_TAIL(&entrylist, entry, next); 390 1.1 christos break; 391 1.1 christos case 'P': 392 1.1 christos H_flag = L_flag = false; 393 1.1 christos break; 394 1.1 christos case 'R': 395 1.1 christos R_flag = true; 396 1.1 christos break; 397 1.1 christos case 'X': 398 1.1 christos entry = zmalloc(sizeof(struct sf_entry)); 399 1.1 christos entry->acl = get_acl_from_file(optarg); 400 1.1 christos entry->op = OP_REMOVE_ACL; 401 1.1 christos TAILQ_INSERT_TAIL(&entrylist, entry, next); 402 1.1 christos break; 403 1.1 christos case 'a': 404 1.1 christos entry = zmalloc(sizeof(struct sf_entry)); 405 1.1 christos 406 1.1 christos entry_number = strtol(optarg, &end, 10); 407 1.1 christos if (end - optarg != (int)strlen(optarg)) 408 1.1 christos errx(1, "%s: invalid entry number", optarg); 409 1.1 christos if (entry_number < 0) 410 1.1 christos errx(1, 411 1.1 christos "%s: entry number cannot be less than zero", 412 1.1 christos optarg); 413 1.1 christos entry->entry_number = entry_number; 414 1.1 christos 415 1.1 christos if (argv[optind] == NULL) 416 1.1 christos errx(1, "missing ACL"); 417 1.1 christos entry->acl = acl_from_text(argv[optind]); 418 1.1 christos if (entry->acl == NULL) 419 1.1 christos err(1, "%s", argv[optind]); 420 1.1 christos optind++; 421 1.1 christos entry->op = OP_ADD_ACL; 422 1.1 christos TAILQ_INSERT_TAIL(&entrylist, entry, next); 423 1.1 christos break; 424 1.1 christos case 'b': 425 1.1 christos entry = zmalloc(sizeof(struct sf_entry)); 426 1.1 christos entry->op = OP_REMOVE_EXT; 427 1.1 christos TAILQ_INSERT_TAIL(&entrylist, entry, next); 428 1.1 christos break; 429 1.1 christos case 'd': 430 1.1 christos acl_type = ACL_TYPE_DEFAULT; 431 1.1 christos break; 432 1.1 christos case 'h': 433 1.1 christos h_flag = 1; 434 1.1 christos break; 435 1.1 christos case 'k': 436 1.1 christos entry = zmalloc(sizeof(struct sf_entry)); 437 1.1 christos entry->op = OP_REMOVE_DEF; 438 1.1 christos TAILQ_INSERT_TAIL(&entrylist, entry, next); 439 1.1 christos break; 440 1.1 christos case 'm': 441 1.1 christos entry = zmalloc(sizeof(struct sf_entry)); 442 1.1 christos entry->acl = acl_from_text(optarg); 443 1.1 christos if (entry->acl == NULL) 444 1.1 christos err(1, "%s", optarg); 445 1.1 christos entry->op = OP_MERGE_ACL; 446 1.1 christos TAILQ_INSERT_TAIL(&entrylist, entry, next); 447 1.1 christos break; 448 1.1 christos case 'n': 449 1.1 christos n_flag = true; 450 1.1 christos break; 451 1.1 christos case 'x': 452 1.1 christos entry = zmalloc(sizeof(struct sf_entry)); 453 1.1 christos entry_number = strtol(optarg, &end, 10); 454 1.1 christos if (end - optarg == (int)strlen(optarg)) { 455 1.1 christos if (entry_number < 0) 456 1.1 christos errx(1, 457 1.1 christos "%s: entry number cannot be less than zero", 458 1.1 christos optarg); 459 1.1 christos entry->entry_number = entry_number; 460 1.1 christos entry->op = OP_REMOVE_BY_NUMBER; 461 1.1 christos } else { 462 1.1 christos entry->acl = acl_from_text(optarg); 463 1.1 christos if (entry->acl == NULL) 464 1.1 christos err(1, "%s", optarg); 465 1.1 christos entry->op = OP_REMOVE_ACL; 466 1.1 christos } 467 1.1 christos TAILQ_INSERT_TAIL(&entrylist, entry, next); 468 1.1 christos break; 469 1.1 christos default: 470 1.1 christos usage(); 471 1.1 christos break; 472 1.1 christos } 473 1.1 christos argc -= optind; 474 1.1 christos argv += optind; 475 1.1 christos 476 1.1 christos if (!n_flag && TAILQ_EMPTY(&entrylist)) 477 1.1 christos usage(); 478 1.1 christos 479 1.1 christos /* Take list of files from stdin. */ 480 1.1 christos if (argc == 0 || strcmp(argv[0], "-") == 0) { 481 1.1 christos files_list = stdin_files(); 482 1.1 christos } else 483 1.1 christos files_list = argv; 484 1.1 christos 485 1.1 christos if (R_flag) { 486 1.1 christos if (h_flag) 487 1.1 christos errx(1, "the -R and -h options may not be " 488 1.1 christos "specified together."); 489 1.1 christos if (L_flag) { 490 1.1 christos fts_options = FTS_LOGICAL; 491 1.1 christos } else { 492 1.1 christos fts_options = FTS_PHYSICAL; 493 1.1 christos 494 1.1 christos if (H_flag) { 495 1.1 christos fts_options |= FTS_COMFOLLOW; 496 1.1 christos } 497 1.1 christos } 498 1.1 christos } else if (h_flag) { 499 1.1 christos fts_options = FTS_PHYSICAL; 500 1.1 christos } else { 501 1.1 christos fts_options = FTS_LOGICAL; 502 1.1 christos } 503 1.1 christos 504 1.1 christos /* Open all files. */ 505 1.1 christos if ((ftsp = fts_open(files_list, fts_options | FTS_NOSTAT, 0)) == NULL) 506 1.1 christos err(1, "fts_open"); 507 1.1 christos while ((file = fts_read(ftsp)) != NULL) 508 1.1 christos carried_error += handle_file(ftsp, file); 509 1.1 christos 510 1.1 christos return (carried_error); 511 1.1 christos } 512