17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5c4518760Scasper * Common Development and Distribution License (the "License"). 6c4518760Scasper * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22401f278dSas145665 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 277c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 287c478bd9Sstevel@tonic-gate /* */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate /* 317c478bd9Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 327c478bd9Sstevel@tonic-gate * The Regents of the University of California 337c478bd9Sstevel@tonic-gate * All Rights Reserved 347c478bd9Sstevel@tonic-gate * 357c478bd9Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 367c478bd9Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 377c478bd9Sstevel@tonic-gate * contributors. 387c478bd9Sstevel@tonic-gate */ 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate /* 437c478bd9Sstevel@tonic-gate * chmod option mode files 447c478bd9Sstevel@tonic-gate * where 457c478bd9Sstevel@tonic-gate * mode is [ugoa][+-=][rwxXlstugo] or an octal number 46fa9e4066Sahrens * mode is [<+|->A[# <number] ]<aclspec> 47*da6c28aaSamw * mode is S<attrspec> 48*da6c28aaSamw * option is -R, -f, and -@ 497c478bd9Sstevel@tonic-gate */ 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate /* 527c478bd9Sstevel@tonic-gate * Note that many convolutions are necessary 537c478bd9Sstevel@tonic-gate * due to the re-use of bits between locking 547c478bd9Sstevel@tonic-gate * and setgid 557c478bd9Sstevel@tonic-gate */ 567c478bd9Sstevel@tonic-gate 577c478bd9Sstevel@tonic-gate #include <unistd.h> 587c478bd9Sstevel@tonic-gate #include <stdlib.h> 597c478bd9Sstevel@tonic-gate #include <stdio.h> 607c478bd9Sstevel@tonic-gate #include <sys/types.h> 617c478bd9Sstevel@tonic-gate #include <sys/stat.h> 62*da6c28aaSamw #include <fcntl.h> 637c478bd9Sstevel@tonic-gate #include <dirent.h> 647c478bd9Sstevel@tonic-gate #include <locale.h> 657c478bd9Sstevel@tonic-gate #include <string.h> /* strerror() */ 667c478bd9Sstevel@tonic-gate #include <stdarg.h> 677c478bd9Sstevel@tonic-gate #include <limits.h> 68fa9e4066Sahrens #include <ctype.h> 697c478bd9Sstevel@tonic-gate #include <errno.h> 707c478bd9Sstevel@tonic-gate #include <sys/acl.h> 71fa9e4066Sahrens #include <aclutils.h> 72*da6c28aaSamw #include <libnvpair.h> 73*da6c28aaSamw #include <libcmdutils.h> 74*da6c28aaSamw #include <libgen.h> 75*da6c28aaSamw #include <attr.h> 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate static int rflag; 787c478bd9Sstevel@tonic-gate static int fflag; 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate extern int optind; 817c478bd9Sstevel@tonic-gate extern int errno; 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate static int mac; /* Alternate to argc (for parseargs) */ 847c478bd9Sstevel@tonic-gate static char **mav; /* Alternate to argv (for parseargs) */ 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate static char *ms; /* Points to the mode argument */ 877c478bd9Sstevel@tonic-gate 88fa9e4066Sahrens #define ACL_ADD 1 89fa9e4066Sahrens #define ACL_DELETE 2 90fa9e4066Sahrens #define ACL_SLOT_DELETE 3 91fa9e4066Sahrens #define ACL_REPLACE 4 92fa9e4066Sahrens #define ACL_STRIP 5 93fa9e4066Sahrens 94*da6c28aaSamw #define LEFTBRACE '{' 95*da6c28aaSamw #define RIGHTBRACE '}' 96*da6c28aaSamw #define A_SEP ',' 97*da6c28aaSamw #define A_SEP_TOK "," 98*da6c28aaSamw 99*da6c28aaSamw #define A_COMPACT_TYPE 'c' 100*da6c28aaSamw #define A_VERBOSE_TYPE 'v' 101*da6c28aaSamw #define A_ALLATTRS_TYPE 'a' 102*da6c28aaSamw 103*da6c28aaSamw #define A_SET_OP '+' 104*da6c28aaSamw #define A_INVERSE_OP '-' 105*da6c28aaSamw #define A_REPLACE_OP '=' 106*da6c28aaSamw #define A_UNDEF_OP '\0' 107*da6c28aaSamw 108*da6c28aaSamw #define A_SET_TEXT "set" 109*da6c28aaSamw #define A_INVERSE_TEXT "clear" 110*da6c28aaSamw 111*da6c28aaSamw #define A_SET_VAL B_TRUE 112*da6c28aaSamw #define A_CLEAR_VAL B_FALSE 113*da6c28aaSamw 114*da6c28aaSamw #define ATTR_OPTS 0 115*da6c28aaSamw #define ATTR_NAMES 1 116*da6c28aaSamw 117*da6c28aaSamw #define sec_acls secptr.acls 118*da6c28aaSamw #define sec_attrs secptr.attrs 119*da6c28aaSamw 120fa9e4066Sahrens typedef struct acl_args { 121fa9e4066Sahrens acl_t *acl_aclp; 122fa9e4066Sahrens int acl_slot; 123fa9e4066Sahrens int acl_action; 124fa9e4066Sahrens } acl_args_t; 125fa9e4066Sahrens 126*da6c28aaSamw typedef enum { 127*da6c28aaSamw SEC_ACL, 128*da6c28aaSamw SEC_ATTR 129*da6c28aaSamw } chmod_sec_t; 1307c478bd9Sstevel@tonic-gate 131*da6c28aaSamw typedef struct { 132*da6c28aaSamw chmod_sec_t sec_type; 133*da6c28aaSamw union { 134*da6c28aaSamw acl_args_t *acls; 135*da6c28aaSamw nvlist_t *attrs; 136*da6c28aaSamw } secptr; 137*da6c28aaSamw } sec_args_t; 138*da6c28aaSamw 139*da6c28aaSamw typedef struct attr_name { 140*da6c28aaSamw char *name; 141*da6c28aaSamw struct attr_name *next; 142*da6c28aaSamw } attr_name_t; 143*da6c28aaSamw 144*da6c28aaSamw 145*da6c28aaSamw extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk, 146*da6c28aaSamw char *file, char *path, o_mode_t *group_clear_bits, 147*da6c28aaSamw o_mode_t *group_set_bits); 148*da6c28aaSamw 149*da6c28aaSamw static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk, 150*da6c28aaSamw sec_args_t *secp, attr_name_t *attrname); 151fa9e4066Sahrens static int doacl(char *file, struct stat *st, acl_args_t *aclp); 152*da6c28aaSamw static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, 153*da6c28aaSamw attr_name_t *attrnames); 1547c478bd9Sstevel@tonic-gate static void handle_acl(char *name, o_mode_t group_clear_bits, 1557c478bd9Sstevel@tonic-gate o_mode_t group_set_bits); 156fa9e4066Sahrens void errmsg(int severity, int code, char *format, ...); 157*da6c28aaSamw static void free_attr_names(attr_name_t *attrnames); 158fa9e4066Sahrens static void parseargs(int ac, char *av[]); 159*da6c28aaSamw static int parse_acl_args(char *arg, sec_args_t **sec_args); 160*da6c28aaSamw static int parse_attr_args(char *arg, sec_args_t **sec_args); 161*da6c28aaSamw static void print_attrs(int flag); 162*da6c28aaSamw static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist); 163*da6c28aaSamw static void usage(void); 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate int 1667c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 1677c478bd9Sstevel@tonic-gate { 1687c478bd9Sstevel@tonic-gate int i, c; 1697c478bd9Sstevel@tonic-gate int status = 0; 1707c478bd9Sstevel@tonic-gate mode_t umsk; 171*da6c28aaSamw sec_args_t *sec_args = NULL; 172*da6c28aaSamw attr_name_t *attrnames = NULL; 173*da6c28aaSamw attr_name_t *attrend = NULL; 174*da6c28aaSamw attr_name_t *tattr; 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1777c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1787c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 1797c478bd9Sstevel@tonic-gate #endif 1807c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate parseargs(argc, argv); 1837c478bd9Sstevel@tonic-gate 184*da6c28aaSamw while ((c = getopt(mac, mav, "Rf@:")) != EOF) { 1857c478bd9Sstevel@tonic-gate switch (c) { 1867c478bd9Sstevel@tonic-gate case 'R': 1877c478bd9Sstevel@tonic-gate rflag++; 1887c478bd9Sstevel@tonic-gate break; 1897c478bd9Sstevel@tonic-gate case 'f': 1907c478bd9Sstevel@tonic-gate fflag++; 1917c478bd9Sstevel@tonic-gate break; 192*da6c28aaSamw case '@': 193*da6c28aaSamw if (((tattr = malloc(sizeof (attr_name_t))) == NULL) || 194*da6c28aaSamw ((tattr->name = strdup(optarg)) == NULL)) { 195*da6c28aaSamw perror("chmod"); 196*da6c28aaSamw exit(2); 197*da6c28aaSamw } 198*da6c28aaSamw if (attrnames == NULL) { 199*da6c28aaSamw attrnames = tattr; 200*da6c28aaSamw attrnames->next = NULL; 201*da6c28aaSamw } else { 202*da6c28aaSamw attrend->next = tattr; 203*da6c28aaSamw } 204*da6c28aaSamw attrend = tattr; 205*da6c28aaSamw break; 2067c478bd9Sstevel@tonic-gate case '?': 2077c478bd9Sstevel@tonic-gate usage(); 2087c478bd9Sstevel@tonic-gate exit(2); 2097c478bd9Sstevel@tonic-gate } 2107c478bd9Sstevel@tonic-gate } 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate /* 2137c478bd9Sstevel@tonic-gate * Check for sufficient arguments 2147c478bd9Sstevel@tonic-gate * or a usage error. 2157c478bd9Sstevel@tonic-gate */ 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate mac -= optind; 2187c478bd9Sstevel@tonic-gate mav += optind; 219*da6c28aaSamw if ((mac >= 2) && (mav[0][0] == 'A')) { 220*da6c28aaSamw if (attrnames != NULL) { 221*da6c28aaSamw free_attr_names(attrnames); 222*da6c28aaSamw attrnames = NULL; 223*da6c28aaSamw } 224*da6c28aaSamw if (parse_acl_args(*mav, &sec_args)) { 225fa9e4066Sahrens usage(); 226fa9e4066Sahrens exit(2); 227fa9e4066Sahrens } 228*da6c28aaSamw } else if ((mac >= 2) && (mav[0][0] == 'S')) { 229*da6c28aaSamw if (parse_attr_args(*mav, &sec_args)) { 230*da6c28aaSamw usage(); 231*da6c28aaSamw exit(2); 232*da6c28aaSamw 233*da6c28aaSamw /* A no-op attribute operation was specified. */ 234*da6c28aaSamw } else if (sec_args->sec_attrs == NULL) { 235*da6c28aaSamw exit(0); 236*da6c28aaSamw } 237fa9e4066Sahrens } else { 2387c478bd9Sstevel@tonic-gate if (mac < 2) { 2397c478bd9Sstevel@tonic-gate usage(); 2407c478bd9Sstevel@tonic-gate exit(2); 2417c478bd9Sstevel@tonic-gate } 242*da6c28aaSamw if (attrnames != NULL) { 243*da6c28aaSamw free_attr_names(attrnames); 244*da6c28aaSamw attrnames = NULL; 245*da6c28aaSamw } 246fa9e4066Sahrens } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate ms = mav[0]; 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate umsk = umask(0); 2517c478bd9Sstevel@tonic-gate (void) umask(umsk); 2527c478bd9Sstevel@tonic-gate 253fa9e4066Sahrens for (i = 1; i < mac; i++) { 254*da6c28aaSamw status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames); 255fa9e4066Sahrens } 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate return (fflag ? 0 : status); 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 260*da6c28aaSamw static void 261*da6c28aaSamw free_attr_names(attr_name_t *attrnames) 262*da6c28aaSamw { 263*da6c28aaSamw attr_name_t *attrnamesptr = attrnames; 264*da6c28aaSamw attr_name_t *tptr; 265*da6c28aaSamw 266*da6c28aaSamw while (attrnamesptr != NULL) { 267*da6c28aaSamw tptr = attrnamesptr->next; 268*da6c28aaSamw if (attrnamesptr->name != NULL) { 269*da6c28aaSamw free(attrnamesptr->name); 270*da6c28aaSamw } 271*da6c28aaSamw attrnamesptr = tptr; 272*da6c28aaSamw } 273*da6c28aaSamw } 274*da6c28aaSamw 2757c478bd9Sstevel@tonic-gate static int 276*da6c28aaSamw dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, 277*da6c28aaSamw attr_name_t *attrnames) 2787c478bd9Sstevel@tonic-gate { 2797c478bd9Sstevel@tonic-gate static struct stat st; 2807c478bd9Sstevel@tonic-gate int linkflg = 0; 2817c478bd9Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate if (lstat(name, &st) < 0) { 2847c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 2857c478bd9Sstevel@tonic-gate return (1); 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFLNK) { 2897c478bd9Sstevel@tonic-gate linkflg = 1; 2907c478bd9Sstevel@tonic-gate if (stat(name, &st) < 0) { 2917c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 2927c478bd9Sstevel@tonic-gate return (1); 2937c478bd9Sstevel@tonic-gate } 2947c478bd9Sstevel@tonic-gate } 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate /* Do not recurse if directory is object of symbolic link */ 297*da6c28aaSamw if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) { 298*da6c28aaSamw return (chmodr(name, path, st.st_mode, umsk, secp, attrnames)); 299*da6c28aaSamw } 3007c478bd9Sstevel@tonic-gate 301*da6c28aaSamw if (secp != NULL) { 302*da6c28aaSamw if (secp->sec_type == SEC_ACL) { 303*da6c28aaSamw return (doacl(name, &st, secp->sec_acls)); 304*da6c28aaSamw } else if (secp->sec_type == SEC_ATTR) { 305*da6c28aaSamw return (set_attrs(name, attrnames, secp->sec_attrs)); 306*da6c28aaSamw } else { 307*da6c28aaSamw return (1); 308*da6c28aaSamw } 309*da6c28aaSamw } else { 310*da6c28aaSamw if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, 3117c478bd9Sstevel@tonic-gate &group_clear_bits, &group_set_bits)) == -1) { 3127c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't change %s\n"), path); 3137c478bd9Sstevel@tonic-gate return (1); 3147c478bd9Sstevel@tonic-gate } 315*da6c28aaSamw } 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /* 3187c478bd9Sstevel@tonic-gate * If the group permissions of the file are being modified, 3197c478bd9Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 3207c478bd9Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 3217c478bd9Sstevel@tonic-gate * permissions changes to both the acl mask and the 3227c478bd9Sstevel@tonic-gate * general group permissions. 3237c478bd9Sstevel@tonic-gate */ 3247c478bd9Sstevel@tonic-gate if (group_clear_bits || group_set_bits) 3257c478bd9Sstevel@tonic-gate handle_acl(name, group_clear_bits, group_set_bits); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate return (0); 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate static int 331*da6c28aaSamw chmodr(char *dir, char *path, mode_t mode, mode_t umsk, sec_args_t *secp, 332*da6c28aaSamw attr_name_t *attrnames) 3337c478bd9Sstevel@tonic-gate { 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate DIR *dirp; 3367c478bd9Sstevel@tonic-gate struct dirent *dp; 3377c478bd9Sstevel@tonic-gate char savedir[PATH_MAX]; /* dir name to restore */ 3387c478bd9Sstevel@tonic-gate char currdir[PATH_MAX+1]; /* current dir name + '/' */ 3397c478bd9Sstevel@tonic-gate char parentdir[PATH_MAX+1]; /* parent dir name + '/' */ 3407c478bd9Sstevel@tonic-gate int ecode; 341fa9e4066Sahrens struct stat st; 3427c478bd9Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate if (getcwd(savedir, PATH_MAX) == 0) 3457c478bd9Sstevel@tonic-gate errmsg(2, 255, gettext("chmod: could not getcwd %s\n"), 3467c478bd9Sstevel@tonic-gate savedir); 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate /* 3497c478bd9Sstevel@tonic-gate * Change what we are given before doing it's contents 3507c478bd9Sstevel@tonic-gate */ 351*da6c28aaSamw if (secp != NULL) { 352fa9e4066Sahrens if (lstat(dir, &st) < 0) { 353fa9e4066Sahrens errmsg(2, 0, gettext("can't access %s\n"), path); 354fa9e4066Sahrens return (1); 355fa9e4066Sahrens } 356*da6c28aaSamw if (secp->sec_type == SEC_ACL) { 357*da6c28aaSamw if (doacl(dir, &st, secp->sec_acls) != 0) 358fa9e4066Sahrens return (1); 359*da6c28aaSamw } else if (secp->sec_type == SEC_ATTR) { 360*da6c28aaSamw if (set_attrs(dir, attrnames, secp->sec_attrs) != 0) { 361*da6c28aaSamw return (1); 362*da6c28aaSamw } 363*da6c28aaSamw } else { 364*da6c28aaSamw return (1); 365*da6c28aaSamw } 366fa9e4066Sahrens } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path, 3677c478bd9Sstevel@tonic-gate &group_clear_bits, &group_set_bits)) < 0) { 3687c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't change %s\n"), path); 3697c478bd9Sstevel@tonic-gate return (1); 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate /* 3737c478bd9Sstevel@tonic-gate * If the group permissions of the file are being modified, 3747c478bd9Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 3757c478bd9Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 3767c478bd9Sstevel@tonic-gate * permissions changes to both the acl mask and the 3777c478bd9Sstevel@tonic-gate * general group permissions. 3787c478bd9Sstevel@tonic-gate */ 379fa9e4066Sahrens 380*da6c28aaSamw if (secp != NULL) { 381*da6c28aaSamw /* only necessary when not setting ACL or system attributes */ 3827c478bd9Sstevel@tonic-gate if (group_clear_bits || group_set_bits) 3837c478bd9Sstevel@tonic-gate handle_acl(dir, group_clear_bits, group_set_bits); 384fa9e4066Sahrens } 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate if (chdir(dir) < 0) { 3877c478bd9Sstevel@tonic-gate errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno)); 3887c478bd9Sstevel@tonic-gate return (1); 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate if ((dirp = opendir(".")) == NULL) { 3917c478bd9Sstevel@tonic-gate errmsg(2, 0, "%s\n", strerror(errno)); 3927c478bd9Sstevel@tonic-gate return (1); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate ecode = 0; 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate /* 3977c478bd9Sstevel@tonic-gate * Save parent directory path before recursive chmod. 3987c478bd9Sstevel@tonic-gate * We'll need this for error printing purposes. Add 3997c478bd9Sstevel@tonic-gate * a trailing '/' to the path except in the case where 4007c478bd9Sstevel@tonic-gate * the path is just '/' 4017c478bd9Sstevel@tonic-gate */ 4027c478bd9Sstevel@tonic-gate 403401f278dSas145665 if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) { 404401f278dSas145665 errmsg(2, 0, gettext("directory path name too long: %s\n"), 405401f278dSas145665 path); 406401f278dSas145665 return (1); 407401f278dSas145665 } 4087c478bd9Sstevel@tonic-gate if (strcmp(path, "/") != 0) 409401f278dSas145665 if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) { 410401f278dSas145665 errmsg(2, 0, 411401f278dSas145665 gettext("directory path name too long: %s/\n"), 412401f278dSas145665 parentdir); 413401f278dSas145665 return (1); 414401f278dSas145665 } 415401f278dSas145665 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 418401f278dSas145665 419c4518760Scasper if (strcmp(dp->d_name, ".") == 0 || /* skip . and .. */ 420c4518760Scasper strcmp(dp->d_name, "..") == 0) { 421c4518760Scasper continue; 422c4518760Scasper } 423401f278dSas145665 if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) { 424401f278dSas145665 errmsg(2, 0, 425401f278dSas145665 gettext("directory path name too long: %s\n"), 426401f278dSas145665 parentdir); 427401f278dSas145665 return (1); 428401f278dSas145665 } 429401f278dSas145665 if (strlcat(currdir, dp->d_name, PATH_MAX + 1) 430401f278dSas145665 >= PATH_MAX + 1) { 431401f278dSas145665 errmsg(2, 0, 432401f278dSas145665 gettext("directory path name too long: %s%s\n"), 433401f278dSas145665 currdir, dp->d_name); 434401f278dSas145665 return (1); 435401f278dSas145665 } 436*da6c28aaSamw ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames); 4377c478bd9Sstevel@tonic-gate } 4387c478bd9Sstevel@tonic-gate (void) closedir(dirp); 4397c478bd9Sstevel@tonic-gate if (chdir(savedir) < 0) { 4407c478bd9Sstevel@tonic-gate errmsg(2, 255, gettext("can't change back to %s\n"), savedir); 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate return (ecode ? 1 : 0); 4437c478bd9Sstevel@tonic-gate } 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate /* PRINTFLIKE3 */ 4467c478bd9Sstevel@tonic-gate void 4477c478bd9Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...) 4487c478bd9Sstevel@tonic-gate { 4497c478bd9Sstevel@tonic-gate va_list ap; 4507c478bd9Sstevel@tonic-gate static char *msg[] = { 4517c478bd9Sstevel@tonic-gate "", 4527c478bd9Sstevel@tonic-gate "ERROR", 4537c478bd9Sstevel@tonic-gate "WARNING", 4547c478bd9Sstevel@tonic-gate "" 4557c478bd9Sstevel@tonic-gate }; 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate va_start(ap, format); 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate /* 4607c478bd9Sstevel@tonic-gate * Always print error message if this is a fatal error (code == 0); 4617c478bd9Sstevel@tonic-gate * otherwise, print message if fflag == 0 (no -f option specified) 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate if (!fflag || (code != 0)) { 4647c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 4657c478bd9Sstevel@tonic-gate "chmod: %s: ", gettext(msg[severity])); 4667c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, ap); 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate va_end(ap); 4707c478bd9Sstevel@tonic-gate 4717c478bd9Sstevel@tonic-gate if (code != 0) 4727c478bd9Sstevel@tonic-gate exit(fflag ? 0 : code); 4737c478bd9Sstevel@tonic-gate } 4747c478bd9Sstevel@tonic-gate 4757c478bd9Sstevel@tonic-gate static void 4767c478bd9Sstevel@tonic-gate usage(void) 4777c478bd9Sstevel@tonic-gate { 4787c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 4797c478bd9Sstevel@tonic-gate "usage:\tchmod [-fR] <absolute-mode> file ...\n")); 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 482*da6c28aaSamw "\tchmod [-fR] [-@ attribute] ... " 483*da6c28aaSamw "S<attribute-operation> file ...\n")); 484*da6c28aaSamw 485*da6c28aaSamw (void) fprintf(stderr, gettext( 486fa9e4066Sahrens "\tchmod [-fR] <ACL-operation> file ...\n")); 487fa9e4066Sahrens 488fa9e4066Sahrens (void) fprintf(stderr, gettext( 489*da6c28aaSamw "\tchmod [-fR] <symbolic-mode-list> file ...\n\n")); 490fa9e4066Sahrens 4917c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 4927c478bd9Sstevel@tonic-gate "where \t<symbolic-mode-list> is a comma-separated list of\n")); 493*da6c28aaSamw (void) fprintf(stderr, gettext( 494*da6c28aaSamw "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n")); 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 497*da6c28aaSamw "where \t<attribute-operation> is a comma-separated list of\n" 498*da6c28aaSamw "\tone or more of the following\n")); 499*da6c28aaSamw (void) fprintf(stderr, gettext( 500*da6c28aaSamw "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n" 501*da6c28aaSamw "\t[+|-|=]v[<verbose-attribute-setting>|" 502*da6c28aaSamw "\'{\'<verbose-attribute-setting-list>\'}\']\n" 503*da6c28aaSamw "\t[+|-|=]a\n")); 504*da6c28aaSamw (void) fprintf(stderr, gettext( 505*da6c28aaSamw "where \t<compact-attribute-list> is a list of zero or more of\n")); 506*da6c28aaSamw print_attrs(ATTR_OPTS); 507*da6c28aaSamw (void) fprintf(stderr, gettext( 508*da6c28aaSamw "where \t<verbose-attribute-setting> is one of\n")); 509*da6c28aaSamw print_attrs(ATTR_NAMES); 510*da6c28aaSamw (void) fprintf(stderr, gettext( 511*da6c28aaSamw "\tand can be, optionally, immediately preceded by \"no\"\n\n")); 512fa9e4066Sahrens 513fa9e4066Sahrens (void) fprintf(stderr, gettext( 514fa9e4066Sahrens "where \t<ACL-operation> is one of the following\n")); 515fa9e4066Sahrens (void) fprintf(stderr, gettext("\tA-<acl_specification>\n")); 516fa9e4066Sahrens (void) fprintf(stderr, gettext("\tA[number]-\n")); 517fa9e4066Sahrens (void) fprintf(stderr, gettext( 518fa9e4066Sahrens "\tA[number]{+|=}<acl_specification>\n")); 519fa9e4066Sahrens (void) fprintf(stderr, gettext( 520fa9e4066Sahrens "where \t<acl-specification> is a comma-separated list of ACEs\n")); 5217c478bd9Sstevel@tonic-gate } 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate /* 5247c478bd9Sstevel@tonic-gate * parseargs - generate getopt-friendly argument list for backwards 5257c478bd9Sstevel@tonic-gate * compatibility with earlier Solaris usage (eg, chmod -w 5267c478bd9Sstevel@tonic-gate * foo). 5277c478bd9Sstevel@tonic-gate * 5287c478bd9Sstevel@tonic-gate * assumes the existence of a static set of alternates to argc and argv, 5297c478bd9Sstevel@tonic-gate * (namely, mac, and mav[]). 5307c478bd9Sstevel@tonic-gate * 5317c478bd9Sstevel@tonic-gate */ 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate static void 5347c478bd9Sstevel@tonic-gate parseargs(int ac, char *av[]) 5357c478bd9Sstevel@tonic-gate { 5367c478bd9Sstevel@tonic-gate int i; /* current argument */ 5377c478bd9Sstevel@tonic-gate int fflag; /* arg list contains "--" */ 5387c478bd9Sstevel@tonic-gate size_t mav_num; /* number of entries in mav[] */ 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate /* 5417c478bd9Sstevel@tonic-gate * We add an extra argument slot, in case we need to jam a "--" 5427c478bd9Sstevel@tonic-gate * argument into the list. 5437c478bd9Sstevel@tonic-gate */ 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate mav_num = (size_t)ac+2; 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate if ((mav = calloc(mav_num, sizeof (char *))) == NULL) { 5487c478bd9Sstevel@tonic-gate perror("chmod"); 5497c478bd9Sstevel@tonic-gate exit(2); 5507c478bd9Sstevel@tonic-gate } 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate /* scan for the use of "--" in the argument list */ 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate for (fflag = i = 0; i < ac; i ++) { 5557c478bd9Sstevel@tonic-gate if (strcmp(av[i], "--") == 0) 5567c478bd9Sstevel@tonic-gate fflag = 1; 5577c478bd9Sstevel@tonic-gate } 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate /* process the arguments */ 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate for (i = mac = 0; 5627c478bd9Sstevel@tonic-gate (av[i] != (char *)NULL) && (av[i][0] != (char)NULL); 5637c478bd9Sstevel@tonic-gate i++) { 5647c478bd9Sstevel@tonic-gate if (!fflag && av[i][0] == '-') { 5657c478bd9Sstevel@tonic-gate /* 5667c478bd9Sstevel@tonic-gate * If there is not already a "--" argument specified, 5677c478bd9Sstevel@tonic-gate * and the argument starts with '-' but does not 5687c478bd9Sstevel@tonic-gate * contain any of the official option letters, then it 5697c478bd9Sstevel@tonic-gate * is probably a mode argument beginning with '-'. 5707c478bd9Sstevel@tonic-gate * Force a "--" into the argument stream in front of 5717c478bd9Sstevel@tonic-gate * it. 5727c478bd9Sstevel@tonic-gate */ 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate if ((strchr(av[i], 'R') == NULL && 575*da6c28aaSamw strchr(av[i], 'f') == NULL) && 576*da6c28aaSamw strchr(av[i], '@') == NULL) { 577*da6c28aaSamw if ((mav[mac++] = strdup("--")) == NULL) { 578*da6c28aaSamw perror("chmod"); 579*da6c28aaSamw exit(2); 580*da6c28aaSamw } 5817c478bd9Sstevel@tonic-gate } 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate 584*da6c28aaSamw if ((mav[mac++] = strdup(av[i])) == NULL) { 585*da6c28aaSamw perror("chmod"); 586*da6c28aaSamw exit(2); 587*da6c28aaSamw } 5887c478bd9Sstevel@tonic-gate } 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate mav[mac] = (char *)NULL; 5917c478bd9Sstevel@tonic-gate } 5927c478bd9Sstevel@tonic-gate 593*da6c28aaSamw static int 594*da6c28aaSamw parse_acl_args(char *arg, sec_args_t **sec_args) 595fa9e4066Sahrens { 596fa9e4066Sahrens acl_t *new_acl = NULL; 597fa9e4066Sahrens int slot; 598fa9e4066Sahrens int len; 599fa9e4066Sahrens int action; 600fa9e4066Sahrens acl_args_t *new_acl_args; 601fa9e4066Sahrens char *acl_spec = NULL; 602fa9e4066Sahrens char *end; 603fa9e4066Sahrens 604fa9e4066Sahrens if (arg[0] != 'A') 605fa9e4066Sahrens return (1); 606fa9e4066Sahrens 607fa9e4066Sahrens slot = strtol(&arg[1], &end, 10); 608fa9e4066Sahrens 609fa9e4066Sahrens len = strlen(arg); 610fa9e4066Sahrens switch (*end) { 611fa9e4066Sahrens case '+': 612fa9e4066Sahrens action = ACL_ADD; 613fa9e4066Sahrens acl_spec = ++end; 614fa9e4066Sahrens break; 615fa9e4066Sahrens case '-': 616fa9e4066Sahrens if (len == 2 && arg[0] == 'A' && arg[1] == '-') 617fa9e4066Sahrens action = ACL_STRIP; 618fa9e4066Sahrens else 619fa9e4066Sahrens action = ACL_DELETE; 620fa9e4066Sahrens if (action != ACL_STRIP) { 621fa9e4066Sahrens acl_spec = ++end; 622fa9e4066Sahrens if (acl_spec[0] == '\0') { 623fa9e4066Sahrens action = ACL_SLOT_DELETE; 624fa9e4066Sahrens acl_spec = NULL; 625fa9e4066Sahrens } else if (arg[1] != '-') 626fa9e4066Sahrens return (1); 627fa9e4066Sahrens } 628fa9e4066Sahrens break; 629fa9e4066Sahrens case '=': 63055601ddbSmarks /* 63155601ddbSmarks * Was slot specified? 63255601ddbSmarks */ 63355601ddbSmarks if (arg[1] == '=') 63455601ddbSmarks slot = -1; 635fa9e4066Sahrens action = ACL_REPLACE; 636fa9e4066Sahrens acl_spec = ++end; 637fa9e4066Sahrens break; 638fa9e4066Sahrens default: 639fa9e4066Sahrens return (1); 640fa9e4066Sahrens } 641fa9e4066Sahrens 642fa9e4066Sahrens if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0') 643fa9e4066Sahrens return (1); 644fa9e4066Sahrens 645fa9e4066Sahrens if (acl_spec) { 6465a5eeccaSmarks if (acl_parse(acl_spec, &new_acl)) { 6475a5eeccaSmarks exit(1); 648fa9e4066Sahrens } 649fa9e4066Sahrens } 650fa9e4066Sahrens 651fa9e4066Sahrens new_acl_args = malloc(sizeof (acl_args_t)); 652fa9e4066Sahrens if (new_acl_args == NULL) 653fa9e4066Sahrens return (1); 654fa9e4066Sahrens 655fa9e4066Sahrens new_acl_args->acl_aclp = new_acl; 656fa9e4066Sahrens new_acl_args->acl_slot = slot; 657fa9e4066Sahrens new_acl_args->acl_action = action; 658fa9e4066Sahrens 659*da6c28aaSamw if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { 660*da6c28aaSamw perror("chmod"); 661*da6c28aaSamw exit(2); 662*da6c28aaSamw } 663*da6c28aaSamw (*sec_args)->sec_type = SEC_ACL; 664*da6c28aaSamw (*sec_args)->sec_acls = new_acl_args; 665fa9e4066Sahrens 666fa9e4066Sahrens return (0); 667fa9e4066Sahrens } 668fa9e4066Sahrens 6697c478bd9Sstevel@tonic-gate /* 6707c478bd9Sstevel@tonic-gate * This function is called whenever the group permissions of a file 6717c478bd9Sstevel@tonic-gate * is being modified. According to the chmod(1) manpage, any 6727c478bd9Sstevel@tonic-gate * change made to the group permissions must be applied to both 6737c478bd9Sstevel@tonic-gate * the acl mask and the acl's GROUP_OBJ. The chmod(2) already 6747c478bd9Sstevel@tonic-gate * set the mask, so this routine needs to make the same change 6757c478bd9Sstevel@tonic-gate * to the GROUP_OBJ. 6767c478bd9Sstevel@tonic-gate */ 6777c478bd9Sstevel@tonic-gate static void 6787c478bd9Sstevel@tonic-gate handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits) 6797c478bd9Sstevel@tonic-gate { 6807c478bd9Sstevel@tonic-gate int aclcnt, n; 6817c478bd9Sstevel@tonic-gate aclent_t *aclp, *tp; 6827c478bd9Sstevel@tonic-gate o_mode_t newperm; 683fa9e4066Sahrens /* 684fa9e4066Sahrens * if this file system support ace_t acl's 685fa9e4066Sahrens * then simply return since we don't have an 686fa9e4066Sahrens * acl mask to deal with 687fa9e4066Sahrens */ 688fa9e4066Sahrens if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED) 689fa9e4066Sahrens return; 6907c478bd9Sstevel@tonic-gate if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES) 6917c478bd9Sstevel@tonic-gate return; /* it's just a trivial acl; no need to change it */ 6927c478bd9Sstevel@tonic-gate if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt)) 6937c478bd9Sstevel@tonic-gate == NULL) { 6947c478bd9Sstevel@tonic-gate perror("chmod"); 6957c478bd9Sstevel@tonic-gate exit(2); 6967c478bd9Sstevel@tonic-gate } 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate if (acl(name, GETACL, aclcnt, aclp) < 0) { 6997c478bd9Sstevel@tonic-gate free(aclp); 7007c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 7017c478bd9Sstevel@tonic-gate perror(name); 7027c478bd9Sstevel@tonic-gate return; 7037c478bd9Sstevel@tonic-gate } 7047c478bd9Sstevel@tonic-gate for (tp = aclp, n = aclcnt; n--; tp++) { 7057c478bd9Sstevel@tonic-gate if (tp->a_type == GROUP_OBJ) { 7067c478bd9Sstevel@tonic-gate newperm = tp->a_perm; 7077c478bd9Sstevel@tonic-gate if (group_clear_bits != 0) 7087c478bd9Sstevel@tonic-gate newperm &= ~group_clear_bits; 7097c478bd9Sstevel@tonic-gate if (group_set_bits != 0) 7107c478bd9Sstevel@tonic-gate newperm |= group_set_bits; 7117c478bd9Sstevel@tonic-gate if (newperm != tp->a_perm) { 7127c478bd9Sstevel@tonic-gate tp->a_perm = newperm; 7137c478bd9Sstevel@tonic-gate if (acl(name, SETACL, aclcnt, aclp) 7147c478bd9Sstevel@tonic-gate < 0) { 7157c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 7167c478bd9Sstevel@tonic-gate perror(name); 7177c478bd9Sstevel@tonic-gate } 7187c478bd9Sstevel@tonic-gate } 7197c478bd9Sstevel@tonic-gate break; 7207c478bd9Sstevel@tonic-gate } 7217c478bd9Sstevel@tonic-gate } 7227c478bd9Sstevel@tonic-gate free(aclp); 7237c478bd9Sstevel@tonic-gate } 724fa9e4066Sahrens 725fa9e4066Sahrens static int 726fa9e4066Sahrens doacl(char *file, struct stat *st, acl_args_t *acl_args) 727fa9e4066Sahrens { 728fa9e4066Sahrens acl_t *aclp; 729fa9e4066Sahrens acl_t *set_aclp; 730fa9e4066Sahrens int error = 0; 731fa9e4066Sahrens void *to, *from; 732fa9e4066Sahrens int len; 733fa9e4066Sahrens int isdir; 734fa9e4066Sahrens isdir = S_ISDIR(st->st_mode); 735fa9e4066Sahrens 736fa9e4066Sahrens error = acl_get(file, 0, &aclp); 737fa9e4066Sahrens 738fa9e4066Sahrens if (error != 0) { 739fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 740fa9e4066Sahrens return (1); 741fa9e4066Sahrens } 742fa9e4066Sahrens switch (acl_args->acl_action) { 743fa9e4066Sahrens case ACL_ADD: 744fa9e4066Sahrens if ((error = acl_addentries(aclp, 745fa9e4066Sahrens acl_args->acl_aclp, acl_args->acl_slot)) != 0) { 746fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 747fa9e4066Sahrens acl_free(aclp); 748fa9e4066Sahrens return (1); 749fa9e4066Sahrens } 750fa9e4066Sahrens set_aclp = aclp; 751fa9e4066Sahrens break; 752fa9e4066Sahrens case ACL_SLOT_DELETE: 753fa9e4066Sahrens if (acl_args->acl_slot + 1 > aclp->acl_cnt) { 754fa9e4066Sahrens errmsg(1, 1, 755fa9e4066Sahrens gettext("Invalid slot specified for removal\n")); 756fa9e4066Sahrens acl_free(aclp); 757fa9e4066Sahrens return (1); 758fa9e4066Sahrens } 759fa9e4066Sahrens 760fa9e4066Sahrens if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) { 761fa9e4066Sahrens errmsg(1, 1, 762fa9e4066Sahrens gettext("Can't remove all ACL " 763fa9e4066Sahrens "entries from a file\n")); 764fa9e4066Sahrens acl_free(aclp); 765fa9e4066Sahrens return (1); 766fa9e4066Sahrens } 767fa9e4066Sahrens 768fa9e4066Sahrens /* 769fa9e4066Sahrens * remove a single entry 770fa9e4066Sahrens * 771fa9e4066Sahrens * if last entry just adjust acl_cnt 772fa9e4066Sahrens */ 773fa9e4066Sahrens 774fa9e4066Sahrens if ((acl_args->acl_slot + 1) == aclp->acl_cnt) 775fa9e4066Sahrens aclp->acl_cnt--; 776fa9e4066Sahrens else { 777fa9e4066Sahrens to = (char *)aclp->acl_aclp + 778fa9e4066Sahrens (acl_args->acl_slot * aclp->acl_entry_size); 779fa9e4066Sahrens from = (char *)to + aclp->acl_entry_size; 780fa9e4066Sahrens len = (aclp->acl_cnt - acl_args->acl_slot - 1) * 781fa9e4066Sahrens aclp->acl_entry_size; 782fa9e4066Sahrens (void) memmove(to, from, len); 783fa9e4066Sahrens aclp->acl_cnt--; 784fa9e4066Sahrens } 785fa9e4066Sahrens set_aclp = aclp; 786fa9e4066Sahrens break; 787fa9e4066Sahrens 788fa9e4066Sahrens case ACL_DELETE: 789fa9e4066Sahrens if ((error = acl_removeentries(aclp, acl_args->acl_aclp, 790fa9e4066Sahrens acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) { 791fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 792fa9e4066Sahrens acl_free(aclp); 793fa9e4066Sahrens return (1); 794fa9e4066Sahrens } 795fa9e4066Sahrens 796fa9e4066Sahrens if (aclp->acl_cnt == 0) { 797fa9e4066Sahrens errmsg(1, 1, 798fa9e4066Sahrens gettext("Can't remove all ACL " 799fa9e4066Sahrens "entries from a file\n")); 800fa9e4066Sahrens acl_free(aclp); 801fa9e4066Sahrens return (1); 802fa9e4066Sahrens } 803fa9e4066Sahrens 804fa9e4066Sahrens set_aclp = aclp; 805fa9e4066Sahrens break; 806fa9e4066Sahrens case ACL_REPLACE: 807fa9e4066Sahrens if (acl_args->acl_slot >= 0) { 808fa9e4066Sahrens error = acl_modifyentries(aclp, acl_args->acl_aclp, 809fa9e4066Sahrens acl_args->acl_slot); 810fa9e4066Sahrens if (error) { 811fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 812fa9e4066Sahrens acl_free(aclp); 813fa9e4066Sahrens return (1); 814fa9e4066Sahrens } 815fa9e4066Sahrens set_aclp = aclp; 816fa9e4066Sahrens } else { 817fa9e4066Sahrens set_aclp = acl_args->acl_aclp; 818fa9e4066Sahrens } 819fa9e4066Sahrens break; 820fa9e4066Sahrens case ACL_STRIP: 821fa9e4066Sahrens error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode); 822fa9e4066Sahrens if (error) { 823fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 824fa9e4066Sahrens return (1); 825fa9e4066Sahrens } 826fa9e4066Sahrens acl_free(aclp); 827fa9e4066Sahrens return (0); 828fa9e4066Sahrens /*NOTREACHED*/ 829fa9e4066Sahrens default: 830fa9e4066Sahrens errmsg(1, 0, gettext("Unknown ACL action requested\n")); 831fa9e4066Sahrens return (1); 832fa9e4066Sahrens break; 833fa9e4066Sahrens } 834fa9e4066Sahrens error = acl_check(set_aclp, isdir); 835fa9e4066Sahrens 836fa9e4066Sahrens if (error) { 837fa9e4066Sahrens errmsg(1, 0, "%s\n%s", acl_strerror(error), 838fa9e4066Sahrens gettext("See chmod(1) for more information on " 839fa9e4066Sahrens "valid ACL syntax\n")); 840fa9e4066Sahrens return (1); 841fa9e4066Sahrens } 842fa9e4066Sahrens if ((error = acl_set(file, set_aclp)) != 0) { 843fa9e4066Sahrens errmsg(1, 0, gettext("Failed to set ACL: %s\n"), 844fa9e4066Sahrens acl_strerror(error)); 845fa9e4066Sahrens acl_free(aclp); 846fa9e4066Sahrens return (1); 847fa9e4066Sahrens } 848fa9e4066Sahrens acl_free(aclp); 849fa9e4066Sahrens return (0); 850fa9e4066Sahrens } 851*da6c28aaSamw 852*da6c28aaSamw /* 853*da6c28aaSamw * Prints out the attributes in their verbose form: 854*da6c28aaSamw * '{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}' 855*da6c28aaSamw * similar to output of ls -/v. 856*da6c28aaSamw */ 857*da6c28aaSamw static void 858*da6c28aaSamw print_nvlist(nvlist_t *attr_nvlist) 859*da6c28aaSamw { 860*da6c28aaSamw int firsttime = 1; 861*da6c28aaSamw boolean_t value; 862*da6c28aaSamw nvlist_t *lptr = attr_nvlist; 863*da6c28aaSamw nvpair_t *pair = NULL; 864*da6c28aaSamw 865*da6c28aaSamw (void) fprintf(stderr, "\t%c", LEFTBRACE); 866*da6c28aaSamw while (pair = nvlist_next_nvpair(lptr, pair)) { 867*da6c28aaSamw if (nvpair_value_boolean_value(pair, &value) == 0) { 868*da6c28aaSamw (void) fprintf(stderr, "%s%s%s", 869*da6c28aaSamw firsttime ? "" : A_SEP_TOK, 870*da6c28aaSamw (value == A_SET_VAL) ? "" : "no", 871*da6c28aaSamw nvpair_name(pair)); 872*da6c28aaSamw firsttime = 0; 873*da6c28aaSamw } else { 874*da6c28aaSamw (void) fprintf(stderr, gettext( 875*da6c28aaSamw "<error retrieving attributes: %s>"), 876*da6c28aaSamw strerror(errno)); 877*da6c28aaSamw break; 878*da6c28aaSamw } 879*da6c28aaSamw } 880*da6c28aaSamw (void) fprintf(stderr, "%c\n", RIGHTBRACE); 881*da6c28aaSamw } 882*da6c28aaSamw 883*da6c28aaSamw /* 884*da6c28aaSamw * Add an attribute name and boolean value to an nvlist if an action is to be 885*da6c28aaSamw * performed for that attribute. The nvlist will be used later to set all the 886*da6c28aaSamw * attributes in the nvlist in one operation through a call to setattrat(). 887*da6c28aaSamw * 888*da6c28aaSamw * If a set operation ('+') was specified, then a boolean representation of the 889*da6c28aaSamw * attribute's value will be added to the nvlist for that attribute name. If an 890*da6c28aaSamw * inverse operation ('-') was specified, then a boolean representation of the 891*da6c28aaSamw * inverse of the attribute's value will be added to the nvlist for that 892*da6c28aaSamw * attribute name. 893*da6c28aaSamw * 894*da6c28aaSamw * Returns an nvlist of attribute name and boolean value pairs if there are 895*da6c28aaSamw * attribute actions to be performed, otherwise returns NULL. 896*da6c28aaSamw */ 897*da6c28aaSamw static nvlist_t * 898*da6c28aaSamw set_attrs_nvlist(char *attractptr, int numofattrs) 899*da6c28aaSamw { 900*da6c28aaSamw int attribute_set = 0; 901*da6c28aaSamw f_attr_t i; 902*da6c28aaSamw nvlist_t *attr_nvlist; 903*da6c28aaSamw 904*da6c28aaSamw if (nvlist_alloc(&attr_nvlist, NV_UNIQUE_NAME, 0) != 0) { 905*da6c28aaSamw perror("chmod"); 906*da6c28aaSamw exit(2); 907*da6c28aaSamw } 908*da6c28aaSamw 909*da6c28aaSamw for (i = 0; i < numofattrs; i++) { 910*da6c28aaSamw if (attractptr[i] != '\0') { 911*da6c28aaSamw if ((nvlist_add_boolean_value(attr_nvlist, 912*da6c28aaSamw attr_to_name(i), 913*da6c28aaSamw (attractptr[i] == A_SET_OP))) != 0) { 914*da6c28aaSamw errmsg(1, 2, gettext( 915*da6c28aaSamw "unable to propagate attribute names and" 916*da6c28aaSamw "values: %s\n"), strerror(errno)); 917*da6c28aaSamw } else { 918*da6c28aaSamw attribute_set = 1; 919*da6c28aaSamw } 920*da6c28aaSamw } 921*da6c28aaSamw } 922*da6c28aaSamw return (attribute_set ? attr_nvlist : NULL); 923*da6c28aaSamw } 924*da6c28aaSamw 925*da6c28aaSamw /* 926*da6c28aaSamw * Set the attributes of file, or if specified, of the named attribute file, 927*da6c28aaSamw * attrname. Build an nvlist of attribute names and values and call setattrat() 928*da6c28aaSamw * to set the attributes in one operation. 929*da6c28aaSamw * 930*da6c28aaSamw * Returns 0 if successful, otherwise returns 1. 931*da6c28aaSamw */ 932*da6c28aaSamw static int 933*da6c28aaSamw set_file_attrs(char *file, char *attrname, nvlist_t *attr_nvlist) 934*da6c28aaSamw { 935*da6c28aaSamw int rc; 936*da6c28aaSamw char *filename; 937*da6c28aaSamw 938*da6c28aaSamw if (attrname != NULL) { 939*da6c28aaSamw filename = attrname; 940*da6c28aaSamw } else { 941*da6c28aaSamw filename = basename(file); 942*da6c28aaSamw } 943*da6c28aaSamw 944*da6c28aaSamw if ((rc = setattrat(AT_FDCWD, XATTR_VIEW_READWRITE, filename, 945*da6c28aaSamw attr_nvlist)) != 0) { 946*da6c28aaSamw char *emsg; 947*da6c28aaSamw switch (errno) { 948*da6c28aaSamw case EINVAL: 949*da6c28aaSamw emsg = gettext("not supported"); 950*da6c28aaSamw break; 951*da6c28aaSamw case EPERM: 952*da6c28aaSamw emsg = gettext("not privileged"); 953*da6c28aaSamw break; 954*da6c28aaSamw default: 955*da6c28aaSamw emsg = strerror(rc); 956*da6c28aaSamw } 957*da6c28aaSamw errmsg(1, 0, gettext( 958*da6c28aaSamw "cannot set the following attributes on " 959*da6c28aaSamw "%s%s%s%s: %s\n"), 960*da6c28aaSamw (attrname == NULL) ? "" : gettext("attribute "), 961*da6c28aaSamw (attrname == NULL) ? "" : attrname, 962*da6c28aaSamw (attrname == NULL) ? "" : gettext(" of "), 963*da6c28aaSamw file, emsg); 964*da6c28aaSamw print_nvlist(attr_nvlist); 965*da6c28aaSamw } 966*da6c28aaSamw 967*da6c28aaSamw return (rc); 968*da6c28aaSamw } 969*da6c28aaSamw 970*da6c28aaSamw static int 971*da6c28aaSamw save_cwd(void) 972*da6c28aaSamw { 973*da6c28aaSamw return (open(".", O_RDONLY)); 974*da6c28aaSamw } 975*da6c28aaSamw 976*da6c28aaSamw static void 977*da6c28aaSamw rest_cwd(int cwd) 978*da6c28aaSamw { 979*da6c28aaSamw if (cwd != -1) { 980*da6c28aaSamw if (fchdir(cwd) != 0) { 981*da6c28aaSamw errmsg(1, 1, gettext( 982*da6c28aaSamw "can't change to current working directory\n")); 983*da6c28aaSamw } 984*da6c28aaSamw (void) close(cwd); 985*da6c28aaSamw } 986*da6c28aaSamw } 987*da6c28aaSamw 988*da6c28aaSamw /* 989*da6c28aaSamw * Returns 1 if filename is a system attribute file, otherwise 990*da6c28aaSamw * returns 0. 991*da6c28aaSamw */ 992*da6c28aaSamw static int 993*da6c28aaSamw is_sattr(char *filename) 994*da6c28aaSamw { 995*da6c28aaSamw return (sysattr_type(filename) != _NOT_SATTR); 996*da6c28aaSamw } 997*da6c28aaSamw 998*da6c28aaSamw /* 999*da6c28aaSamw * Perform the action on the specified named attribute file for the file 1000*da6c28aaSamw * associated with the input file descriptor. If the named attribute file 1001*da6c28aaSamw * is "*", then the action is to be performed on all the named attribute files 1002*da6c28aaSamw * of the file associated with the input file descriptor. 1003*da6c28aaSamw */ 1004*da6c28aaSamw static int 1005*da6c28aaSamw set_named_attrs(char *file, int parentfd, char *attrname, nvlist_t *attr_nvlist) 1006*da6c28aaSamw { 1007*da6c28aaSamw int dirfd; 1008*da6c28aaSamw int error = 0; 1009*da6c28aaSamw DIR *dirp = NULL; 1010*da6c28aaSamw struct dirent *dp; 1011*da6c28aaSamw struct stat st; 1012*da6c28aaSamw 1013*da6c28aaSamw if ((attrname == NULL) || (strcmp(attrname, "*") != 0)) { 1014*da6c28aaSamw /* 1015*da6c28aaSamw * Make sure the named attribute exists and extended system 1016*da6c28aaSamw * attributes are supported on the underlying file system. 1017*da6c28aaSamw */ 1018*da6c28aaSamw if (attrname != NULL) { 1019*da6c28aaSamw if (fstatat(parentfd, attrname, &st, 1020*da6c28aaSamw AT_SYMLINK_NOFOLLOW) < 0) { 1021*da6c28aaSamw errmsg(2, 0, gettext( 1022*da6c28aaSamw "can't access attribute %s of %s\n"), 1023*da6c28aaSamw attrname, file); 1024*da6c28aaSamw return (1); 1025*da6c28aaSamw } 1026*da6c28aaSamw if (sysattr_support(attrname, _PC_SATTR_ENABLED) != 1) { 1027*da6c28aaSamw errmsg(1, 0, gettext( 1028*da6c28aaSamw "extended system attributes not supported " 1029*da6c28aaSamw "for attribute %s of %s\n"), 1030*da6c28aaSamw attrname, file); 1031*da6c28aaSamw return (1); 1032*da6c28aaSamw } 1033*da6c28aaSamw } 1034*da6c28aaSamw 1035*da6c28aaSamw error = set_file_attrs(file, attrname, attr_nvlist); 1036*da6c28aaSamw 1037*da6c28aaSamw } else { 1038*da6c28aaSamw if (((dirfd = dup(parentfd)) == -1) || 1039*da6c28aaSamw ((dirp = fdopendir(dirfd)) == NULL)) { 1040*da6c28aaSamw errmsg(1, 0, gettext( 1041*da6c28aaSamw "cannot open dir pointer of file %s\n"), file); 1042*da6c28aaSamw if (dirfd > 0) { 1043*da6c28aaSamw (void) close(dirfd); 1044*da6c28aaSamw } 1045*da6c28aaSamw return (1); 1046*da6c28aaSamw } 1047*da6c28aaSamw 1048*da6c28aaSamw while (dp = readdir(dirp)) { 1049*da6c28aaSamw /* 1050*da6c28aaSamw * Process all extended attribute files except 1051*da6c28aaSamw * ".", "..", and extended system attribute files. 1052*da6c28aaSamw */ 1053*da6c28aaSamw if ((strcmp(dp->d_name, ".") == 0) || 1054*da6c28aaSamw (strcmp(dp->d_name, "..") == 0) || 1055*da6c28aaSamw is_sattr(dp->d_name)) { 1056*da6c28aaSamw continue; 1057*da6c28aaSamw } 1058*da6c28aaSamw 1059*da6c28aaSamw if (set_named_attrs(file, parentfd, dp->d_name, 1060*da6c28aaSamw attr_nvlist) != 0) { 1061*da6c28aaSamw error++; 1062*da6c28aaSamw } 1063*da6c28aaSamw } 1064*da6c28aaSamw if (dirp != NULL) { 1065*da6c28aaSamw (void) closedir(dirp); 1066*da6c28aaSamw } 1067*da6c28aaSamw } 1068*da6c28aaSamw 1069*da6c28aaSamw return ((error == 0) ? 0 : 1); 1070*da6c28aaSamw } 1071*da6c28aaSamw 1072*da6c28aaSamw /* 1073*da6c28aaSamw * Set the attributes of the specified file, or if specified with -@ on the 1074*da6c28aaSamw * command line, the specified named attributes of the specified file. 1075*da6c28aaSamw * 1076*da6c28aaSamw * Returns 0 if successful, otherwise returns 1. 1077*da6c28aaSamw */ 1078*da6c28aaSamw static int 1079*da6c28aaSamw set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist) 1080*da6c28aaSamw { 1081*da6c28aaSamw char *parentd; 1082*da6c28aaSamw char *tpath = NULL; 1083*da6c28aaSamw int cwd; 1084*da6c28aaSamw int error = 0; 1085*da6c28aaSamw int parentfd; 1086*da6c28aaSamw attr_name_t *tattr = attrnames; 1087*da6c28aaSamw 1088*da6c28aaSamw if (attr_nvlist == NULL) { 1089*da6c28aaSamw return (0); 1090*da6c28aaSamw } 1091*da6c28aaSamw 1092*da6c28aaSamw if (sysattr_support(file, _PC_SATTR_ENABLED) != 1) { 1093*da6c28aaSamw errmsg(1, 0, gettext( 1094*da6c28aaSamw "extended system attributes not supported for %s\n"), file); 1095*da6c28aaSamw return (1); 1096*da6c28aaSamw } 1097*da6c28aaSamw 1098*da6c28aaSamw /* 1099*da6c28aaSamw * Open the parent directory and change into it before attempting 1100*da6c28aaSamw * to set the attributes of the file. 1101*da6c28aaSamw */ 1102*da6c28aaSamw if (attrnames == NULL) { 1103*da6c28aaSamw tpath = strdup(file); 1104*da6c28aaSamw parentd = dirname(tpath); 1105*da6c28aaSamw parentfd = open(parentd, O_RDONLY); 1106*da6c28aaSamw } else { 1107*da6c28aaSamw parentfd = attropen(file, ".", O_RDONLY); 1108*da6c28aaSamw } 1109*da6c28aaSamw if (parentfd == -1) { 1110*da6c28aaSamw errmsg(1, 0, gettext( 1111*da6c28aaSamw "cannot open attribute directory of %s\n"), file); 1112*da6c28aaSamw if (tpath != NULL) { 1113*da6c28aaSamw free(tpath); 1114*da6c28aaSamw } 1115*da6c28aaSamw return (1); 1116*da6c28aaSamw } 1117*da6c28aaSamw 1118*da6c28aaSamw if ((cwd = save_cwd()) < 0) { 1119*da6c28aaSamw errmsg(1, 1, gettext( 1120*da6c28aaSamw "can't get current working directory\n")); 1121*da6c28aaSamw } 1122*da6c28aaSamw if (fchdir(parentfd) != 0) { 1123*da6c28aaSamw errmsg(1, 0, gettext( 1124*da6c28aaSamw "can't change to parent %sdirectory of %s\n"), 1125*da6c28aaSamw (attrnames == NULL) ? "" : gettext("attribute "), file); 1126*da6c28aaSamw (void) close(cwd); 1127*da6c28aaSamw (void) close(parentfd); 1128*da6c28aaSamw if (tpath != NULL) { 1129*da6c28aaSamw free(tpath); 1130*da6c28aaSamw } 1131*da6c28aaSamw return (1); 1132*da6c28aaSamw } 1133*da6c28aaSamw 1134*da6c28aaSamw /* 1135*da6c28aaSamw * If no named attribute file names were provided on the command line 1136*da6c28aaSamw * then set the attributes of the base file, otherwise, set the 1137*da6c28aaSamw * attributes for each of the named attribute files specified. 1138*da6c28aaSamw */ 1139*da6c28aaSamw if (attrnames == NULL) { 1140*da6c28aaSamw error = set_named_attrs(file, parentfd, NULL, attr_nvlist); 1141*da6c28aaSamw free(tpath); 1142*da6c28aaSamw } else { 1143*da6c28aaSamw while (tattr != NULL) { 1144*da6c28aaSamw if (set_named_attrs(file, parentfd, tattr->name, 1145*da6c28aaSamw attr_nvlist) != 0) { 1146*da6c28aaSamw error++; 1147*da6c28aaSamw } 1148*da6c28aaSamw tattr = tattr->next; 1149*da6c28aaSamw } 1150*da6c28aaSamw } 1151*da6c28aaSamw (void) close(parentfd); 1152*da6c28aaSamw rest_cwd(cwd); 1153*da6c28aaSamw 1154*da6c28aaSamw return ((error == 0) ? 0 : 1); 1155*da6c28aaSamw } 1156*da6c28aaSamw 1157*da6c28aaSamw /* 1158*da6c28aaSamw * Prints the attributes in either the compact or verbose form indicated 1159*da6c28aaSamw * by flag. 1160*da6c28aaSamw */ 1161*da6c28aaSamw static void 1162*da6c28aaSamw print_attrs(int flag) 1163*da6c28aaSamw { 1164*da6c28aaSamw f_attr_t i; 1165*da6c28aaSamw static int numofattrs; 1166*da6c28aaSamw int firsttime = 1; 1167*da6c28aaSamw 1168*da6c28aaSamw numofattrs = attr_count(); 1169*da6c28aaSamw 1170*da6c28aaSamw (void) fprintf(stderr, gettext("\t[")); 1171*da6c28aaSamw for (i = 0; i < numofattrs; i++) { 1172*da6c28aaSamw if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) || 1173*da6c28aaSamw (attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) { 1174*da6c28aaSamw continue; 1175*da6c28aaSamw } 1176*da6c28aaSamw (void) fprintf(stderr, "%s%s", 1177*da6c28aaSamw (firsttime == 1) ? "" : gettext("|"), 1178*da6c28aaSamw (flag == ATTR_OPTS) ? attr_to_option(i) : attr_to_name(i)); 1179*da6c28aaSamw firsttime = 0; 1180*da6c28aaSamw } 1181*da6c28aaSamw (void) fprintf(stderr, gettext("]\n")); 1182*da6c28aaSamw } 1183*da6c28aaSamw 1184*da6c28aaSamw /* 1185*da6c28aaSamw * Record what action should be taken on the specified attribute. Only boolean 1186*da6c28aaSamw * read-write attributes can be manipulated. 1187*da6c28aaSamw * 1188*da6c28aaSamw * Returns 0 if successful, otherwise returns 1. 1189*da6c28aaSamw */ 1190*da6c28aaSamw static int 1191*da6c28aaSamw set_attr_args(f_attr_t attr, char action, char *attractptr) 1192*da6c28aaSamw { 1193*da6c28aaSamw if ((attr_to_xattr_view(attr) == XATTR_VIEW_READWRITE) && 1194*da6c28aaSamw (attr_to_data_type(attr) == DATA_TYPE_BOOLEAN_VALUE)) { 1195*da6c28aaSamw attractptr[attr] = action; 1196*da6c28aaSamw return (0); 1197*da6c28aaSamw } 1198*da6c28aaSamw return (1); 1199*da6c28aaSamw } 1200*da6c28aaSamw 1201*da6c28aaSamw /* 1202*da6c28aaSamw * Parses the entry and assigns the appropriate action (either '+' or '-' in 1203*da6c28aaSamw * attribute's position in the character array pointed to by attractptr, where 1204*da6c28aaSamw * upon exit, attractptr is positional and the value of each character specifies 1205*da6c28aaSamw * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the 1206*da6c28aaSamw * attribute value. 1207*da6c28aaSamw * 1208*da6c28aaSamw * If the entry is an attribute name, then the A_SET_OP action is to be 1209*da6c28aaSamw * performed for this attribute. If the entry is an attribute name proceeded 1210*da6c28aaSamw * with "no", then the A_INVERSE_OP action is to be performed for this 1211*da6c28aaSamw * attribute. If the entry is one or more attribute option letters, then step 1212*da6c28aaSamw * through each of the option letters marking the action to be performed for 1213*da6c28aaSamw * each of the attributes associated with the letter as A_SET_OP. 1214*da6c28aaSamw * 1215*da6c28aaSamw * Returns 0 if the entry was a valid attribute(s) and the action to be 1216*da6c28aaSamw * performed on that attribute(s) has been recorded, otherwise returns 1. 1217*da6c28aaSamw */ 1218*da6c28aaSamw static int 1219*da6c28aaSamw parse_entry(char *entry, char action, char atype, int len, char *attractptr) 1220*da6c28aaSamw { 1221*da6c28aaSamw char aopt[2] = {'\0', '\0'}; 1222*da6c28aaSamw char *aptr; 1223*da6c28aaSamw f_attr_t attr; 1224*da6c28aaSamw 1225*da6c28aaSamw if (atype == A_VERBOSE_TYPE) { 1226*da6c28aaSamw if ((attr = name_to_attr(entry)) != F_ATTR_INVAL) { 1227*da6c28aaSamw return (set_attr_args(attr, 1228*da6c28aaSamw (action == A_REPLACE_OP) ? A_SET_OP : action, 1229*da6c28aaSamw attractptr)); 1230*da6c28aaSamw } else if ((len > 2) && (strncmp(entry, "no", 2) == 0) && 1231*da6c28aaSamw ((attr = name_to_attr(entry + 2)) != F_ATTR_INVAL)) { 1232*da6c28aaSamw return (set_attr_args(attr, ((action == A_REPLACE_OP) || 1233*da6c28aaSamw (action == A_SET_OP)) ? A_INVERSE_OP : A_SET_OP, 1234*da6c28aaSamw attractptr)); 1235*da6c28aaSamw } else { 1236*da6c28aaSamw return (1); 1237*da6c28aaSamw } 1238*da6c28aaSamw } else if (atype == A_COMPACT_TYPE) { 1239*da6c28aaSamw for (aptr = entry; *aptr != '\0'; aptr++) { 1240*da6c28aaSamw *aopt = *aptr; 1241*da6c28aaSamw /* 1242*da6c28aaSamw * The output of 'ls' can be used as the attribute mode 1243*da6c28aaSamw * specification for chmod. This output can contain a 1244*da6c28aaSamw * hypen ('-') for each attribute that is not set. If 1245*da6c28aaSamw * so, ignore them. If a replace action is being 1246*da6c28aaSamw * performed, then all attributes that don't have an 1247*da6c28aaSamw * action set here, will be cleared down the line. 1248*da6c28aaSamw */ 1249*da6c28aaSamw if (*aptr == '-') { 1250*da6c28aaSamw continue; 1251*da6c28aaSamw } 1252*da6c28aaSamw if (set_attr_args(option_to_attr(aopt), 1253*da6c28aaSamw (action == A_REPLACE_OP) ? A_SET_OP : action, 1254*da6c28aaSamw attractptr) != 0) { 1255*da6c28aaSamw return (1); 1256*da6c28aaSamw } 1257*da6c28aaSamw } 1258*da6c28aaSamw return (0); 1259*da6c28aaSamw } 1260*da6c28aaSamw return (1); 1261*da6c28aaSamw } 1262*da6c28aaSamw 1263*da6c28aaSamw /* 1264*da6c28aaSamw * Parse the attribute specification, aoptsstr. Upon completion, attr_nvlist 1265*da6c28aaSamw * will point to an nvlist which contains pairs of attribute names and values 1266*da6c28aaSamw * to be set; attr_nvlist will be NULL if it is a no-op. 1267*da6c28aaSamw * 1268*da6c28aaSamw * The attribute specification format is 1269*da6c28aaSamw * S[oper]attr_type[attribute_list] 1270*da6c28aaSamw * where oper is 1271*da6c28aaSamw * + set operation of specified attributes in attribute list. 1272*da6c28aaSamw * This is the default operation. 1273*da6c28aaSamw * - inverse operation of specified attributes in attribute list 1274*da6c28aaSamw * = replace operation of all attributes. All attribute operations 1275*da6c28aaSamw * depend on those specified in the attribute list. Attributes 1276*da6c28aaSamw * not specified in the attribute list will be cleared. 1277*da6c28aaSamw * where attr_type is 1278*da6c28aaSamw * c compact type. Each entry in the attribute list is a character 1279*da6c28aaSamw * option representing an associated attribute name. 1280*da6c28aaSamw * v verbose type. Each entry in the attribute list is an 1281*da6c28aaSamw * an attribute name which can optionally be preceeded with "no" 1282*da6c28aaSamw * (to imply the attribute should be cleared). 1283*da6c28aaSamw * a all attributes type. The oper should be applied to all 1284*da6c28aaSamw * read-write boolean system attributes. No attribute list should 1285*da6c28aaSamw * be specified after an 'a' attribute type. 1286*da6c28aaSamw * 1287*da6c28aaSamw * Returns 0 if aoptsstr contained a valid attribute specification, 1288*da6c28aaSamw * otherwise, returns 1. 1289*da6c28aaSamw */ 1290*da6c28aaSamw static int 1291*da6c28aaSamw parse_attr_args(char *aoptsstr, sec_args_t **sec_args) 1292*da6c28aaSamw { 1293*da6c28aaSamw char action; 1294*da6c28aaSamw char *attractptr; 1295*da6c28aaSamw char atype; 1296*da6c28aaSamw char *entry; 1297*da6c28aaSamw char *eptr; 1298*da6c28aaSamw char *nextattr; 1299*da6c28aaSamw char *nextentry; 1300*da6c28aaSamw char *subentry; 1301*da6c28aaSamw char *teptr; 1302*da6c28aaSamw char tok[] = {'\0', '\0'}; 1303*da6c28aaSamw int len; 1304*da6c28aaSamw f_attr_t i; 1305*da6c28aaSamw int numofattrs; 1306*da6c28aaSamw 1307*da6c28aaSamw if ((*aoptsstr != 'S') || (*(aoptsstr + 1) == '\0')) { 1308*da6c28aaSamw return (1); 1309*da6c28aaSamw } 1310*da6c28aaSamw 1311*da6c28aaSamw if ((eptr = strdup(aoptsstr + 1)) == NULL) { 1312*da6c28aaSamw perror("chmod"); 1313*da6c28aaSamw exit(2); 1314*da6c28aaSamw } 1315*da6c28aaSamw entry = eptr; 1316*da6c28aaSamw 1317*da6c28aaSamw /* 1318*da6c28aaSamw * Create a positional character array to determine a single attribute 1319*da6c28aaSamw * operation to be performed, where each index represents the system 1320*da6c28aaSamw * attribute affected, and it's value in the array represents the action 1321*da6c28aaSamw * to be performed, i.e., a value of '+' means to set the attribute, a 1322*da6c28aaSamw * value of '-' means to clear the attribute, and a value of '\0' means 1323*da6c28aaSamw * to leave the attribute untouched. Initially, this positional 1324*da6c28aaSamw * character array is all '\0's, representing a no-op. 1325*da6c28aaSamw */ 1326*da6c28aaSamw if ((numofattrs = attr_count()) < 1) { 1327*da6c28aaSamw errmsg(1, 1, gettext("system attributes not supported\n")); 1328*da6c28aaSamw } 1329*da6c28aaSamw 1330*da6c28aaSamw if ((attractptr = calloc(numofattrs, sizeof (char))) == NULL) { 1331*da6c28aaSamw perror("chmod"); 1332*da6c28aaSamw exit(2); 1333*da6c28aaSamw } 1334*da6c28aaSamw 1335*da6c28aaSamw if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { 1336*da6c28aaSamw perror("chmod"); 1337*da6c28aaSamw exit(2); 1338*da6c28aaSamw } 1339*da6c28aaSamw (*sec_args)->sec_type = SEC_ATTR; 1340*da6c28aaSamw (*sec_args)->sec_attrs = NULL; 1341*da6c28aaSamw 1342*da6c28aaSamw /* Parse each attribute operation within the attribute specification. */ 1343*da6c28aaSamw while ((entry != NULL) && (*entry != '\0')) { 1344*da6c28aaSamw action = A_SET_OP; 1345*da6c28aaSamw atype = '\0'; 1346*da6c28aaSamw 1347*da6c28aaSamw /* Get the operator. */ 1348*da6c28aaSamw switch (*entry) { 1349*da6c28aaSamw case A_SET_OP: 1350*da6c28aaSamw case A_INVERSE_OP: 1351*da6c28aaSamw case A_REPLACE_OP: 1352*da6c28aaSamw action = *entry++; 1353*da6c28aaSamw break; 1354*da6c28aaSamw case A_COMPACT_TYPE: 1355*da6c28aaSamw case A_VERBOSE_TYPE: 1356*da6c28aaSamw case A_ALLATTRS_TYPE: 1357*da6c28aaSamw atype = *entry++; 1358*da6c28aaSamw action = A_SET_OP; 1359*da6c28aaSamw break; 1360*da6c28aaSamw default: 1361*da6c28aaSamw break; 1362*da6c28aaSamw } 1363*da6c28aaSamw 1364*da6c28aaSamw /* An attribute type must be specified. */ 1365*da6c28aaSamw if (atype == '\0') { 1366*da6c28aaSamw if ((*entry == A_COMPACT_TYPE) || 1367*da6c28aaSamw (*entry == A_VERBOSE_TYPE) || 1368*da6c28aaSamw (*entry == A_ALLATTRS_TYPE)) { 1369*da6c28aaSamw atype = *entry++; 1370*da6c28aaSamw } else { 1371*da6c28aaSamw return (1); 1372*da6c28aaSamw } 1373*da6c28aaSamw } 1374*da6c28aaSamw 1375*da6c28aaSamw /* Get the attribute specification separator. */ 1376*da6c28aaSamw if (*entry == LEFTBRACE) { 1377*da6c28aaSamw *tok = RIGHTBRACE; 1378*da6c28aaSamw entry++; 1379*da6c28aaSamw } else { 1380*da6c28aaSamw *tok = A_SEP; 1381*da6c28aaSamw } 1382*da6c28aaSamw 1383*da6c28aaSamw /* Get the attribute operation */ 1384*da6c28aaSamw if ((nextentry = strpbrk(entry, tok)) != NULL) { 1385*da6c28aaSamw *nextentry = '\0'; 1386*da6c28aaSamw nextentry++; 1387*da6c28aaSamw } 1388*da6c28aaSamw 1389*da6c28aaSamw /* Check for a no-op */ 1390*da6c28aaSamw if ((*entry == '\0') && (atype != A_ALLATTRS_TYPE) && 1391*da6c28aaSamw (action != A_REPLACE_OP)) { 1392*da6c28aaSamw entry = nextentry; 1393*da6c28aaSamw continue; 1394*da6c28aaSamw } 1395*da6c28aaSamw 1396*da6c28aaSamw /* 1397*da6c28aaSamw * Step through the attribute operation, setting the 1398*da6c28aaSamw * appropriate values for the specified attributes in the 1399*da6c28aaSamw * character array, attractptr. A value of '+' will mean the 1400*da6c28aaSamw * attribute is to be set, and a value of '-' will mean the 1401*da6c28aaSamw * attribute is to be cleared. If the value of an attribute 1402*da6c28aaSamw * remains '\0', then no action is to be taken on that 1403*da6c28aaSamw * attribute. As multiple operations specified are 1404*da6c28aaSamw * accumulated, a single attribute setting operation is 1405*da6c28aaSamw * represented in attractptr. 1406*da6c28aaSamw */ 1407*da6c28aaSamw len = strlen(entry); 1408*da6c28aaSamw if ((*tok == RIGHTBRACE) || (action == A_REPLACE_OP) || 1409*da6c28aaSamw (atype == A_ALLATTRS_TYPE)) { 1410*da6c28aaSamw 1411*da6c28aaSamw if ((action == A_REPLACE_OP) || 1412*da6c28aaSamw (atype == A_ALLATTRS_TYPE)) { 1413*da6c28aaSamw (void) memset(attractptr, '\0', numofattrs); 1414*da6c28aaSamw } 1415*da6c28aaSamw 1416*da6c28aaSamw if (len > 0) { 1417*da6c28aaSamw if ((teptr = strdup(entry)) == NULL) { 1418*da6c28aaSamw perror("chmod"); 1419*da6c28aaSamw exit(2); 1420*da6c28aaSamw } 1421*da6c28aaSamw subentry = teptr; 1422*da6c28aaSamw while (subentry != NULL) { 1423*da6c28aaSamw if ((nextattr = strpbrk(subentry, 1424*da6c28aaSamw A_SEP_TOK)) != NULL) { 1425*da6c28aaSamw *nextattr = '\0'; 1426*da6c28aaSamw nextattr++; 1427*da6c28aaSamw } 1428*da6c28aaSamw if (parse_entry(subentry, action, 1429*da6c28aaSamw atype, len, attractptr) != 0) { 1430*da6c28aaSamw return (1); 1431*da6c28aaSamw } 1432*da6c28aaSamw subentry = nextattr; 1433*da6c28aaSamw } 1434*da6c28aaSamw free(teptr); 1435*da6c28aaSamw } 1436*da6c28aaSamw 1437*da6c28aaSamw /* 1438*da6c28aaSamw * If performing the replace action, record the 1439*da6c28aaSamw * attributes and values for the rest of the 1440*da6c28aaSamw * attributes that have not already been recorded, 1441*da6c28aaSamw * otherwise record the specified action for all 1442*da6c28aaSamw * attributes. Note: set_attr_args() will only record 1443*da6c28aaSamw * the attribute and action if it is a boolean 1444*da6c28aaSamw * read-write attribute so we don't need to worry 1445*da6c28aaSamw * about checking it here. 1446*da6c28aaSamw */ 1447*da6c28aaSamw if ((action == A_REPLACE_OP) || 1448*da6c28aaSamw (atype == A_ALLATTRS_TYPE)) { 1449*da6c28aaSamw for (i = 0; i < numofattrs; i++) { 1450*da6c28aaSamw if (attractptr[i] == A_UNDEF_OP) { 1451*da6c28aaSamw (void) set_attr_args(i, 1452*da6c28aaSamw (action == A_SET_OP) ? 1453*da6c28aaSamw A_SET_OP : A_INVERSE_OP, 1454*da6c28aaSamw attractptr); 1455*da6c28aaSamw } 1456*da6c28aaSamw } 1457*da6c28aaSamw } 1458*da6c28aaSamw 1459*da6c28aaSamw } else { 1460*da6c28aaSamw if (parse_entry(entry, action, atype, len, 1461*da6c28aaSamw attractptr) != 0) { 1462*da6c28aaSamw return (1); 1463*da6c28aaSamw } 1464*da6c28aaSamw } 1465*da6c28aaSamw entry = nextentry; 1466*da6c28aaSamw } 1467*da6c28aaSamw 1468*da6c28aaSamw /* 1469*da6c28aaSamw * Populate an nvlist with attribute name and boolean value pairs 1470*da6c28aaSamw * using the single attribute operation. 1471*da6c28aaSamw */ 1472*da6c28aaSamw (*sec_args)->sec_attrs = set_attrs_nvlist(attractptr, numofattrs); 1473*da6c28aaSamw free(attractptr); 1474*da6c28aaSamw free(eptr); 1475*da6c28aaSamw 1476*da6c28aaSamw return (0); 1477*da6c28aaSamw } 1478