10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51591Scasper * Common Development and Distribution License (the "License"). 61591Scasper * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223520Sas145665 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 270Sstevel@tonic-gate /* All Rights Reserved */ 280Sstevel@tonic-gate /* */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate /* 310Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 320Sstevel@tonic-gate * The Regents of the University of California 330Sstevel@tonic-gate * All Rights Reserved 340Sstevel@tonic-gate * 350Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 360Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 370Sstevel@tonic-gate * contributors. 380Sstevel@tonic-gate */ 390Sstevel@tonic-gate 400Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 410Sstevel@tonic-gate 420Sstevel@tonic-gate /* 430Sstevel@tonic-gate * chmod option mode files 440Sstevel@tonic-gate * where 450Sstevel@tonic-gate * mode is [ugoa][+-=][rwxXlstugo] or an octal number 46789Sahrens * mode is [<+|->A[# <number] ]<aclspec> 47*5331Samw * mode is S<attrspec> 48*5331Samw * option is -R, -f, and -@ 490Sstevel@tonic-gate */ 500Sstevel@tonic-gate 510Sstevel@tonic-gate /* 520Sstevel@tonic-gate * Note that many convolutions are necessary 530Sstevel@tonic-gate * due to the re-use of bits between locking 540Sstevel@tonic-gate * and setgid 550Sstevel@tonic-gate */ 560Sstevel@tonic-gate 570Sstevel@tonic-gate #include <unistd.h> 580Sstevel@tonic-gate #include <stdlib.h> 590Sstevel@tonic-gate #include <stdio.h> 600Sstevel@tonic-gate #include <sys/types.h> 610Sstevel@tonic-gate #include <sys/stat.h> 62*5331Samw #include <fcntl.h> 630Sstevel@tonic-gate #include <dirent.h> 640Sstevel@tonic-gate #include <locale.h> 650Sstevel@tonic-gate #include <string.h> /* strerror() */ 660Sstevel@tonic-gate #include <stdarg.h> 670Sstevel@tonic-gate #include <limits.h> 68789Sahrens #include <ctype.h> 690Sstevel@tonic-gate #include <errno.h> 700Sstevel@tonic-gate #include <sys/acl.h> 71789Sahrens #include <aclutils.h> 72*5331Samw #include <libnvpair.h> 73*5331Samw #include <libcmdutils.h> 74*5331Samw #include <libgen.h> 75*5331Samw #include <attr.h> 760Sstevel@tonic-gate 770Sstevel@tonic-gate static int rflag; 780Sstevel@tonic-gate static int fflag; 790Sstevel@tonic-gate 800Sstevel@tonic-gate extern int optind; 810Sstevel@tonic-gate extern int errno; 820Sstevel@tonic-gate 830Sstevel@tonic-gate static int mac; /* Alternate to argc (for parseargs) */ 840Sstevel@tonic-gate static char **mav; /* Alternate to argv (for parseargs) */ 850Sstevel@tonic-gate 860Sstevel@tonic-gate static char *ms; /* Points to the mode argument */ 870Sstevel@tonic-gate 88*5331Samw #define ACL_ADD 1 89*5331Samw #define ACL_DELETE 2 90*5331Samw #define ACL_SLOT_DELETE 3 91*5331Samw #define ACL_REPLACE 4 92*5331Samw #define ACL_STRIP 5 93*5331Samw 94*5331Samw #define LEFTBRACE '{' 95*5331Samw #define RIGHTBRACE '}' 96*5331Samw #define A_SEP ',' 97*5331Samw #define A_SEP_TOK "," 98*5331Samw 99*5331Samw #define A_COMPACT_TYPE 'c' 100*5331Samw #define A_VERBOSE_TYPE 'v' 101*5331Samw #define A_ALLATTRS_TYPE 'a' 102*5331Samw 103*5331Samw #define A_SET_OP '+' 104*5331Samw #define A_INVERSE_OP '-' 105*5331Samw #define A_REPLACE_OP '=' 106*5331Samw #define A_UNDEF_OP '\0' 107*5331Samw 108*5331Samw #define A_SET_TEXT "set" 109*5331Samw #define A_INVERSE_TEXT "clear" 110*5331Samw 111*5331Samw #define A_SET_VAL B_TRUE 112*5331Samw #define A_CLEAR_VAL B_FALSE 113*5331Samw 114*5331Samw #define ATTR_OPTS 0 115*5331Samw #define ATTR_NAMES 1 116*5331Samw 117*5331Samw #define sec_acls secptr.acls 118*5331Samw #define sec_attrs secptr.attrs 119789Sahrens 120789Sahrens typedef struct acl_args { 121789Sahrens acl_t *acl_aclp; 122789Sahrens int acl_slot; 123789Sahrens int acl_action; 124789Sahrens } acl_args_t; 125789Sahrens 126*5331Samw typedef enum { 127*5331Samw SEC_ACL, 128*5331Samw SEC_ATTR 129*5331Samw } chmod_sec_t; 130*5331Samw 131*5331Samw typedef struct { 132*5331Samw chmod_sec_t sec_type; 133*5331Samw union { 134*5331Samw acl_args_t *acls; 135*5331Samw nvlist_t *attrs; 136*5331Samw } secptr; 137*5331Samw } sec_args_t; 1380Sstevel@tonic-gate 139*5331Samw typedef struct attr_name { 140*5331Samw char *name; 141*5331Samw struct attr_name *next; 142*5331Samw } attr_name_t; 143*5331Samw 144*5331Samw 145*5331Samw extern mode_t newmode_common(char *ms, mode_t new_mode, mode_t umsk, 146*5331Samw char *file, char *path, o_mode_t *group_clear_bits, 147*5331Samw o_mode_t *group_set_bits); 148*5331Samw 149*5331Samw static int chmodr(char *dir, char *path, mode_t mode, mode_t umsk, 150*5331Samw sec_args_t *secp, attr_name_t *attrname); 151789Sahrens static int doacl(char *file, struct stat *st, acl_args_t *aclp); 152*5331Samw static int dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, 153*5331Samw attr_name_t *attrnames); 1540Sstevel@tonic-gate static void handle_acl(char *name, o_mode_t group_clear_bits, 155789Sahrens o_mode_t group_set_bits); 156*5331Samw void errmsg(int severity, int code, char *format, ...); 157*5331Samw static void free_attr_names(attr_name_t *attrnames); 158*5331Samw static void parseargs(int ac, char *av[]); 159*5331Samw static int parse_acl_args(char *arg, sec_args_t **sec_args); 160*5331Samw static int parse_attr_args(char *arg, sec_args_t **sec_args); 161*5331Samw static void print_attrs(int flag); 162*5331Samw static int set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist); 163789Sahrens static void usage(void); 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate int 1660Sstevel@tonic-gate main(int argc, char *argv[]) 1670Sstevel@tonic-gate { 168*5331Samw int i, c; 169*5331Samw int status = 0; 170*5331Samw mode_t umsk; 171*5331Samw sec_args_t *sec_args = NULL; 172*5331Samw attr_name_t *attrnames = NULL; 173*5331Samw attr_name_t *attrend = NULL; 174*5331Samw attr_name_t *tattr; 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1770Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1780Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 1790Sstevel@tonic-gate #endif 1800Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate parseargs(argc, argv); 1830Sstevel@tonic-gate 184*5331Samw while ((c = getopt(mac, mav, "Rf@:")) != EOF) { 1850Sstevel@tonic-gate switch (c) { 1860Sstevel@tonic-gate case 'R': 1870Sstevel@tonic-gate rflag++; 1880Sstevel@tonic-gate break; 1890Sstevel@tonic-gate case 'f': 1900Sstevel@tonic-gate fflag++; 1910Sstevel@tonic-gate break; 192*5331Samw case '@': 193*5331Samw if (((tattr = malloc(sizeof (attr_name_t))) == NULL) || 194*5331Samw ((tattr->name = strdup(optarg)) == NULL)) { 195*5331Samw perror("chmod"); 196*5331Samw exit(2); 197*5331Samw } 198*5331Samw if (attrnames == NULL) { 199*5331Samw attrnames = tattr; 200*5331Samw attrnames->next = NULL; 201*5331Samw } else { 202*5331Samw attrend->next = tattr; 203*5331Samw } 204*5331Samw attrend = tattr; 205*5331Samw break; 2060Sstevel@tonic-gate case '?': 2070Sstevel@tonic-gate usage(); 2080Sstevel@tonic-gate exit(2); 2090Sstevel@tonic-gate } 2100Sstevel@tonic-gate } 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate /* 2130Sstevel@tonic-gate * Check for sufficient arguments 2140Sstevel@tonic-gate * or a usage error. 2150Sstevel@tonic-gate */ 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate mac -= optind; 2180Sstevel@tonic-gate mav += optind; 219*5331Samw if ((mac >= 2) && (mav[0][0] == 'A')) { 220*5331Samw if (attrnames != NULL) { 221*5331Samw free_attr_names(attrnames); 222*5331Samw attrnames = NULL; 223*5331Samw } 224*5331Samw if (parse_acl_args(*mav, &sec_args)) { 225789Sahrens usage(); 226789Sahrens exit(2); 227789Sahrens } 228*5331Samw } else if ((mac >= 2) && (mav[0][0] == 'S')) { 229*5331Samw if (parse_attr_args(*mav, &sec_args)) { 230*5331Samw usage(); 231*5331Samw exit(2); 232*5331Samw 233*5331Samw /* A no-op attribute operation was specified. */ 234*5331Samw } else if (sec_args->sec_attrs == NULL) { 235*5331Samw exit(0); 236*5331Samw } 237789Sahrens } else { 238789Sahrens if (mac < 2) { 239789Sahrens usage(); 240789Sahrens exit(2); 241789Sahrens } 242*5331Samw if (attrnames != NULL) { 243*5331Samw free_attr_names(attrnames); 244*5331Samw attrnames = NULL; 245*5331Samw } 2460Sstevel@tonic-gate } 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate ms = mav[0]; 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate umsk = umask(0); 2510Sstevel@tonic-gate (void) umask(umsk); 2520Sstevel@tonic-gate 253789Sahrens for (i = 1; i < mac; i++) { 254*5331Samw status += dochmod(mav[i], mav[i], umsk, sec_args, attrnames); 255789Sahrens } 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate return (fflag ? 0 : status); 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate 260*5331Samw static void 261*5331Samw free_attr_names(attr_name_t *attrnames) 262*5331Samw { 263*5331Samw attr_name_t *attrnamesptr = attrnames; 264*5331Samw attr_name_t *tptr; 265*5331Samw 266*5331Samw while (attrnamesptr != NULL) { 267*5331Samw tptr = attrnamesptr->next; 268*5331Samw if (attrnamesptr->name != NULL) { 269*5331Samw free(attrnamesptr->name); 270*5331Samw } 271*5331Samw attrnamesptr = tptr; 272*5331Samw } 273*5331Samw } 274*5331Samw 2750Sstevel@tonic-gate static int 276*5331Samw dochmod(char *name, char *path, mode_t umsk, sec_args_t *secp, 277*5331Samw attr_name_t *attrnames) 2780Sstevel@tonic-gate { 2790Sstevel@tonic-gate static struct stat st; 2800Sstevel@tonic-gate int linkflg = 0; 2810Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate if (lstat(name, &st) < 0) { 2840Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 2850Sstevel@tonic-gate return (1); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFLNK) { 2890Sstevel@tonic-gate linkflg = 1; 2900Sstevel@tonic-gate if (stat(name, &st) < 0) { 2910Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 2920Sstevel@tonic-gate return (1); 2930Sstevel@tonic-gate } 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate /* Do not recurse if directory is object of symbolic link */ 297*5331Samw if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) { 298*5331Samw return (chmodr(name, path, st.st_mode, umsk, secp, attrnames)); 299*5331Samw } 3000Sstevel@tonic-gate 301*5331Samw if (secp != NULL) { 302*5331Samw if (secp->sec_type == SEC_ACL) { 303*5331Samw return (doacl(name, &st, secp->sec_acls)); 304*5331Samw } else if (secp->sec_type == SEC_ATTR) { 305*5331Samw return (set_attrs(name, attrnames, secp->sec_attrs)); 306*5331Samw } else { 307*5331Samw return (1); 308*5331Samw } 309*5331Samw } else { 310*5331Samw if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, 311*5331Samw &group_clear_bits, &group_set_bits)) == -1) { 312*5331Samw errmsg(2, 0, gettext("can't change %s\n"), path); 313*5331Samw return (1); 314*5331Samw } 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * If the group permissions of the file are being modified, 3190Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 3200Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 3210Sstevel@tonic-gate * permissions changes to both the acl mask and the 3220Sstevel@tonic-gate * general group permissions. 3230Sstevel@tonic-gate */ 3240Sstevel@tonic-gate if (group_clear_bits || group_set_bits) 3250Sstevel@tonic-gate handle_acl(name, group_clear_bits, group_set_bits); 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate return (0); 3280Sstevel@tonic-gate } 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate static int 331*5331Samw chmodr(char *dir, char *path, mode_t mode, mode_t umsk, sec_args_t *secp, 332*5331Samw attr_name_t *attrnames) 3330Sstevel@tonic-gate { 3340Sstevel@tonic-gate 3350Sstevel@tonic-gate DIR *dirp; 3360Sstevel@tonic-gate struct dirent *dp; 3370Sstevel@tonic-gate char savedir[PATH_MAX]; /* dir name to restore */ 3380Sstevel@tonic-gate char currdir[PATH_MAX+1]; /* current dir name + '/' */ 3390Sstevel@tonic-gate char parentdir[PATH_MAX+1]; /* parent dir name + '/' */ 3400Sstevel@tonic-gate int ecode; 341789Sahrens struct stat st; 3420Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate if (getcwd(savedir, PATH_MAX) == 0) 3450Sstevel@tonic-gate errmsg(2, 255, gettext("chmod: could not getcwd %s\n"), 3460Sstevel@tonic-gate savedir); 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate /* 3490Sstevel@tonic-gate * Change what we are given before doing it's contents 3500Sstevel@tonic-gate */ 351*5331Samw if (secp != NULL) { 352789Sahrens if (lstat(dir, &st) < 0) { 353789Sahrens errmsg(2, 0, gettext("can't access %s\n"), path); 354789Sahrens return (1); 355789Sahrens } 356*5331Samw if (secp->sec_type == SEC_ACL) { 357*5331Samw if (doacl(dir, &st, secp->sec_acls) != 0) 358*5331Samw return (1); 359*5331Samw } else if (secp->sec_type == SEC_ATTR) { 360*5331Samw if (set_attrs(dir, attrnames, secp->sec_attrs) != 0) { 361*5331Samw return (1); 362*5331Samw } 363*5331Samw } else { 364789Sahrens return (1); 365*5331Samw } 366789Sahrens } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path, 3670Sstevel@tonic-gate &group_clear_bits, &group_set_bits)) < 0) { 3680Sstevel@tonic-gate errmsg(2, 0, gettext("can't change %s\n"), path); 3690Sstevel@tonic-gate return (1); 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate /* 3730Sstevel@tonic-gate * If the group permissions of the file are being modified, 3740Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 3750Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 3760Sstevel@tonic-gate * permissions changes to both the acl mask and the 3770Sstevel@tonic-gate * general group permissions. 3780Sstevel@tonic-gate */ 379789Sahrens 380*5331Samw if (secp != NULL) { 381*5331Samw /* only necessary when not setting ACL or system attributes */ 382789Sahrens if (group_clear_bits || group_set_bits) 383789Sahrens handle_acl(dir, group_clear_bits, group_set_bits); 384789Sahrens } 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate if (chdir(dir) < 0) { 3870Sstevel@tonic-gate errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno)); 3880Sstevel@tonic-gate return (1); 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate if ((dirp = opendir(".")) == NULL) { 3910Sstevel@tonic-gate errmsg(2, 0, "%s\n", strerror(errno)); 3920Sstevel@tonic-gate return (1); 3930Sstevel@tonic-gate } 3940Sstevel@tonic-gate ecode = 0; 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate /* 3970Sstevel@tonic-gate * Save parent directory path before recursive chmod. 3980Sstevel@tonic-gate * We'll need this for error printing purposes. Add 3990Sstevel@tonic-gate * a trailing '/' to the path except in the case where 4000Sstevel@tonic-gate * the path is just '/' 4010Sstevel@tonic-gate */ 4020Sstevel@tonic-gate 4033520Sas145665 if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) { 4043520Sas145665 errmsg(2, 0, gettext("directory path name too long: %s\n"), 4053520Sas145665 path); 4063520Sas145665 return (1); 4073520Sas145665 } 4080Sstevel@tonic-gate if (strcmp(path, "/") != 0) 4093520Sas145665 if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) { 4103520Sas145665 errmsg(2, 0, 4113520Sas145665 gettext("directory path name too long: %s/\n"), 4123520Sas145665 parentdir); 4133520Sas145665 return (1); 4143520Sas145665 } 4153520Sas145665 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 4183520Sas145665 4191591Scasper if (strcmp(dp->d_name, ".") == 0 || /* skip . and .. */ 4201591Scasper strcmp(dp->d_name, "..") == 0) { 4211591Scasper continue; 4221591Scasper } 4233520Sas145665 if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) { 4243520Sas145665 errmsg(2, 0, 4253520Sas145665 gettext("directory path name too long: %s\n"), 4263520Sas145665 parentdir); 4273520Sas145665 return (1); 4283520Sas145665 } 4293520Sas145665 if (strlcat(currdir, dp->d_name, PATH_MAX + 1) 4303520Sas145665 >= PATH_MAX + 1) { 4313520Sas145665 errmsg(2, 0, 4323520Sas145665 gettext("directory path name too long: %s%s\n"), 4333520Sas145665 currdir, dp->d_name); 4343520Sas145665 return (1); 4353520Sas145665 } 436*5331Samw ecode += dochmod(dp->d_name, currdir, umsk, secp, attrnames); 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate (void) closedir(dirp); 4390Sstevel@tonic-gate if (chdir(savedir) < 0) { 4400Sstevel@tonic-gate errmsg(2, 255, gettext("can't change back to %s\n"), savedir); 4410Sstevel@tonic-gate } 4420Sstevel@tonic-gate return (ecode ? 1 : 0); 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate /* PRINTFLIKE3 */ 4460Sstevel@tonic-gate void 4470Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...) 4480Sstevel@tonic-gate { 4490Sstevel@tonic-gate va_list ap; 4500Sstevel@tonic-gate static char *msg[] = { 4510Sstevel@tonic-gate "", 4520Sstevel@tonic-gate "ERROR", 4530Sstevel@tonic-gate "WARNING", 4540Sstevel@tonic-gate "" 4550Sstevel@tonic-gate }; 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate va_start(ap, format); 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate /* 4600Sstevel@tonic-gate * Always print error message if this is a fatal error (code == 0); 4610Sstevel@tonic-gate * otherwise, print message if fflag == 0 (no -f option specified) 4620Sstevel@tonic-gate */ 4630Sstevel@tonic-gate if (!fflag || (code != 0)) { 4640Sstevel@tonic-gate (void) fprintf(stderr, 465*5331Samw "chmod: %s: ", gettext(msg[severity])); 4660Sstevel@tonic-gate (void) vfprintf(stderr, format, ap); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate va_end(ap); 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate if (code != 0) 4720Sstevel@tonic-gate exit(fflag ? 0 : code); 4730Sstevel@tonic-gate } 4740Sstevel@tonic-gate 4750Sstevel@tonic-gate static void 4760Sstevel@tonic-gate usage(void) 4770Sstevel@tonic-gate { 4780Sstevel@tonic-gate (void) fprintf(stderr, gettext( 4790Sstevel@tonic-gate "usage:\tchmod [-fR] <absolute-mode> file ...\n")); 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate (void) fprintf(stderr, gettext( 482*5331Samw "\tchmod [-fR] [-@ attribute] ... " 483*5331Samw "S<attribute-operation> file ...\n")); 484*5331Samw 485*5331Samw (void) fprintf(stderr, gettext( 486789Sahrens "\tchmod [-fR] <ACL-operation> file ...\n")); 487789Sahrens 488789Sahrens (void) fprintf(stderr, gettext( 489*5331Samw "\tchmod [-fR] <symbolic-mode-list> file ...\n\n")); 490789Sahrens 4910Sstevel@tonic-gate (void) fprintf(stderr, gettext( 4920Sstevel@tonic-gate "where \t<symbolic-mode-list> is a comma-separated list of\n")); 493*5331Samw (void) fprintf(stderr, gettext( 494*5331Samw "\t[ugoa]{+|-|=}[rwxXlstugo]\n\n")); 4950Sstevel@tonic-gate 4960Sstevel@tonic-gate (void) fprintf(stderr, gettext( 497*5331Samw "where \t<attribute-operation> is a comma-separated list of\n" 498*5331Samw "\tone or more of the following\n")); 499*5331Samw (void) fprintf(stderr, gettext( 500*5331Samw "\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n" 501*5331Samw "\t[+|-|=]v[<verbose-attribute-setting>|" 502*5331Samw "\'{\'<verbose-attribute-setting-list>\'}\']\n" 503*5331Samw "\t[+|-|=]a\n")); 504*5331Samw (void) fprintf(stderr, gettext( 505*5331Samw "where \t<compact-attribute-list> is a list of zero or more of\n")); 506*5331Samw print_attrs(ATTR_OPTS); 507*5331Samw (void) fprintf(stderr, gettext( 508*5331Samw "where \t<verbose-attribute-setting> is one of\n")); 509*5331Samw print_attrs(ATTR_NAMES); 510*5331Samw (void) fprintf(stderr, gettext( 511*5331Samw "\tand can be, optionally, immediately preceded by \"no\"\n\n")); 512789Sahrens 513789Sahrens (void) fprintf(stderr, gettext( 514789Sahrens "where \t<ACL-operation> is one of the following\n")); 515789Sahrens (void) fprintf(stderr, gettext("\tA-<acl_specification>\n")); 516789Sahrens (void) fprintf(stderr, gettext("\tA[number]-\n")); 517789Sahrens (void) fprintf(stderr, gettext( 518789Sahrens "\tA[number]{+|=}<acl_specification>\n")); 519789Sahrens (void) fprintf(stderr, gettext( 520789Sahrens "where \t<acl-specification> is a comma-separated list of ACEs\n")); 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate 5230Sstevel@tonic-gate /* 5240Sstevel@tonic-gate * parseargs - generate getopt-friendly argument list for backwards 5250Sstevel@tonic-gate * compatibility with earlier Solaris usage (eg, chmod -w 5260Sstevel@tonic-gate * foo). 5270Sstevel@tonic-gate * 5280Sstevel@tonic-gate * assumes the existence of a static set of alternates to argc and argv, 5290Sstevel@tonic-gate * (namely, mac, and mav[]). 5300Sstevel@tonic-gate * 5310Sstevel@tonic-gate */ 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate static void 5340Sstevel@tonic-gate parseargs(int ac, char *av[]) 5350Sstevel@tonic-gate { 5360Sstevel@tonic-gate int i; /* current argument */ 5370Sstevel@tonic-gate int fflag; /* arg list contains "--" */ 5380Sstevel@tonic-gate size_t mav_num; /* number of entries in mav[] */ 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate /* 5410Sstevel@tonic-gate * We add an extra argument slot, in case we need to jam a "--" 5420Sstevel@tonic-gate * argument into the list. 5430Sstevel@tonic-gate */ 5440Sstevel@tonic-gate 5450Sstevel@tonic-gate mav_num = (size_t)ac+2; 5460Sstevel@tonic-gate 5470Sstevel@tonic-gate if ((mav = calloc(mav_num, sizeof (char *))) == NULL) { 5480Sstevel@tonic-gate perror("chmod"); 5490Sstevel@tonic-gate exit(2); 5500Sstevel@tonic-gate } 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate /* scan for the use of "--" in the argument list */ 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate for (fflag = i = 0; i < ac; i ++) { 5550Sstevel@tonic-gate if (strcmp(av[i], "--") == 0) 556*5331Samw fflag = 1; 5570Sstevel@tonic-gate } 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate /* process the arguments */ 5600Sstevel@tonic-gate 5610Sstevel@tonic-gate for (i = mac = 0; 5620Sstevel@tonic-gate (av[i] != (char *)NULL) && (av[i][0] != (char)NULL); 5630Sstevel@tonic-gate i++) { 5640Sstevel@tonic-gate if (!fflag && av[i][0] == '-') { 5650Sstevel@tonic-gate /* 5660Sstevel@tonic-gate * If there is not already a "--" argument specified, 5670Sstevel@tonic-gate * and the argument starts with '-' but does not 5680Sstevel@tonic-gate * contain any of the official option letters, then it 5690Sstevel@tonic-gate * is probably a mode argument beginning with '-'. 5700Sstevel@tonic-gate * Force a "--" into the argument stream in front of 5710Sstevel@tonic-gate * it. 5720Sstevel@tonic-gate */ 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate if ((strchr(av[i], 'R') == NULL && 575*5331Samw strchr(av[i], 'f') == NULL) && 576*5331Samw strchr(av[i], '@') == NULL) { 577*5331Samw if ((mav[mac++] = strdup("--")) == NULL) { 578*5331Samw perror("chmod"); 579*5331Samw exit(2); 580*5331Samw } 5810Sstevel@tonic-gate } 5820Sstevel@tonic-gate } 5830Sstevel@tonic-gate 584*5331Samw if ((mav[mac++] = strdup(av[i])) == NULL) { 585*5331Samw perror("chmod"); 586*5331Samw exit(2); 587*5331Samw } 5880Sstevel@tonic-gate } 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate mav[mac] = (char *)NULL; 5910Sstevel@tonic-gate } 5920Sstevel@tonic-gate 593*5331Samw static int 594*5331Samw parse_acl_args(char *arg, sec_args_t **sec_args) 595789Sahrens { 596789Sahrens acl_t *new_acl = NULL; 597789Sahrens int slot; 598789Sahrens int len; 599789Sahrens int action; 600789Sahrens acl_args_t *new_acl_args; 601789Sahrens char *acl_spec = NULL; 602789Sahrens char *end; 603789Sahrens 604789Sahrens if (arg[0] != 'A') 605789Sahrens return (1); 606789Sahrens 607789Sahrens slot = strtol(&arg[1], &end, 10); 608789Sahrens 609789Sahrens len = strlen(arg); 610789Sahrens switch (*end) { 611789Sahrens case '+': 612789Sahrens action = ACL_ADD; 613789Sahrens acl_spec = ++end; 614789Sahrens break; 615789Sahrens case '-': 616789Sahrens if (len == 2 && arg[0] == 'A' && arg[1] == '-') 617789Sahrens action = ACL_STRIP; 618789Sahrens else 619789Sahrens action = ACL_DELETE; 620789Sahrens if (action != ACL_STRIP) { 621789Sahrens acl_spec = ++end; 622789Sahrens if (acl_spec[0] == '\0') { 623789Sahrens action = ACL_SLOT_DELETE; 624789Sahrens acl_spec = NULL; 625789Sahrens } else if (arg[1] != '-') 626789Sahrens return (1); 627789Sahrens } 628789Sahrens break; 629789Sahrens case '=': 630865Smarks /* 631865Smarks * Was slot specified? 632865Smarks */ 633865Smarks if (arg[1] == '=') 634865Smarks slot = -1; 635789Sahrens action = ACL_REPLACE; 636789Sahrens acl_spec = ++end; 637789Sahrens break; 638789Sahrens default: 639789Sahrens return (1); 640789Sahrens } 641789Sahrens 642789Sahrens if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0') 643789Sahrens return (1); 644789Sahrens 645789Sahrens if (acl_spec) { 6461420Smarks if (acl_parse(acl_spec, &new_acl)) { 6471420Smarks exit(1); 648789Sahrens } 649789Sahrens } 650789Sahrens 651789Sahrens new_acl_args = malloc(sizeof (acl_args_t)); 652789Sahrens if (new_acl_args == NULL) 653789Sahrens return (1); 654789Sahrens 655789Sahrens new_acl_args->acl_aclp = new_acl; 656789Sahrens new_acl_args->acl_slot = slot; 657789Sahrens new_acl_args->acl_action = action; 658789Sahrens 659*5331Samw if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { 660*5331Samw perror("chmod"); 661*5331Samw exit(2); 662*5331Samw } 663*5331Samw (*sec_args)->sec_type = SEC_ACL; 664*5331Samw (*sec_args)->sec_acls = new_acl_args; 665789Sahrens 666789Sahrens return (0); 667789Sahrens } 668789Sahrens 6690Sstevel@tonic-gate /* 6700Sstevel@tonic-gate * This function is called whenever the group permissions of a file 6710Sstevel@tonic-gate * is being modified. According to the chmod(1) manpage, any 6720Sstevel@tonic-gate * change made to the group permissions must be applied to both 6730Sstevel@tonic-gate * the acl mask and the acl's GROUP_OBJ. The chmod(2) already 6740Sstevel@tonic-gate * set the mask, so this routine needs to make the same change 6750Sstevel@tonic-gate * to the GROUP_OBJ. 6760Sstevel@tonic-gate */ 6770Sstevel@tonic-gate static void 6780Sstevel@tonic-gate handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits) 6790Sstevel@tonic-gate { 6800Sstevel@tonic-gate int aclcnt, n; 6810Sstevel@tonic-gate aclent_t *aclp, *tp; 6820Sstevel@tonic-gate o_mode_t newperm; 683789Sahrens /* 684789Sahrens * if this file system support ace_t acl's 685789Sahrens * then simply return since we don't have an 686789Sahrens * acl mask to deal with 687789Sahrens */ 688789Sahrens if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED) 689789Sahrens return; 6900Sstevel@tonic-gate if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES) 6910Sstevel@tonic-gate return; /* it's just a trivial acl; no need to change it */ 6920Sstevel@tonic-gate if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt)) 6930Sstevel@tonic-gate == NULL) { 6940Sstevel@tonic-gate perror("chmod"); 6950Sstevel@tonic-gate exit(2); 6960Sstevel@tonic-gate } 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate if (acl(name, GETACL, aclcnt, aclp) < 0) { 6990Sstevel@tonic-gate free(aclp); 7000Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 7010Sstevel@tonic-gate perror(name); 7020Sstevel@tonic-gate return; 7030Sstevel@tonic-gate } 7040Sstevel@tonic-gate for (tp = aclp, n = aclcnt; n--; tp++) { 7050Sstevel@tonic-gate if (tp->a_type == GROUP_OBJ) { 7060Sstevel@tonic-gate newperm = tp->a_perm; 7070Sstevel@tonic-gate if (group_clear_bits != 0) 7080Sstevel@tonic-gate newperm &= ~group_clear_bits; 7090Sstevel@tonic-gate if (group_set_bits != 0) 7100Sstevel@tonic-gate newperm |= group_set_bits; 7110Sstevel@tonic-gate if (newperm != tp->a_perm) { 7120Sstevel@tonic-gate tp->a_perm = newperm; 7130Sstevel@tonic-gate if (acl(name, SETACL, aclcnt, aclp) 7140Sstevel@tonic-gate < 0) { 7150Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 7160Sstevel@tonic-gate perror(name); 7170Sstevel@tonic-gate } 7180Sstevel@tonic-gate } 7190Sstevel@tonic-gate break; 7200Sstevel@tonic-gate } 7210Sstevel@tonic-gate } 7220Sstevel@tonic-gate free(aclp); 7230Sstevel@tonic-gate } 724789Sahrens 725789Sahrens static int 726789Sahrens doacl(char *file, struct stat *st, acl_args_t *acl_args) 727789Sahrens { 728789Sahrens acl_t *aclp; 729789Sahrens acl_t *set_aclp; 730789Sahrens int error = 0; 731789Sahrens void *to, *from; 732789Sahrens int len; 733789Sahrens int isdir; 734789Sahrens isdir = S_ISDIR(st->st_mode); 735789Sahrens 736789Sahrens error = acl_get(file, 0, &aclp); 737789Sahrens 738789Sahrens if (error != 0) { 739789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 740789Sahrens return (1); 741789Sahrens } 742789Sahrens switch (acl_args->acl_action) { 743789Sahrens case ACL_ADD: 744789Sahrens if ((error = acl_addentries(aclp, 745*5331Samw acl_args->acl_aclp, acl_args->acl_slot)) != 0) { 746*5331Samw errmsg(1, 1, "%s\n", acl_strerror(error)); 747*5331Samw acl_free(aclp); 748*5331Samw return (1); 749789Sahrens } 750789Sahrens set_aclp = aclp; 751789Sahrens break; 752789Sahrens case ACL_SLOT_DELETE: 753789Sahrens if (acl_args->acl_slot + 1 > aclp->acl_cnt) { 754789Sahrens errmsg(1, 1, 755789Sahrens gettext("Invalid slot specified for removal\n")); 756789Sahrens acl_free(aclp); 757789Sahrens return (1); 758789Sahrens } 759789Sahrens 760789Sahrens if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) { 761789Sahrens errmsg(1, 1, 762789Sahrens gettext("Can't remove all ACL " 763789Sahrens "entries from a file\n")); 764789Sahrens acl_free(aclp); 765789Sahrens return (1); 766789Sahrens } 767789Sahrens 768789Sahrens /* 769789Sahrens * remove a single entry 770789Sahrens * 771789Sahrens * if last entry just adjust acl_cnt 772789Sahrens */ 773789Sahrens 774789Sahrens if ((acl_args->acl_slot + 1) == aclp->acl_cnt) 775789Sahrens aclp->acl_cnt--; 776789Sahrens else { 777789Sahrens to = (char *)aclp->acl_aclp + 778789Sahrens (acl_args->acl_slot * aclp->acl_entry_size); 779789Sahrens from = (char *)to + aclp->acl_entry_size; 780789Sahrens len = (aclp->acl_cnt - acl_args->acl_slot - 1) * 781789Sahrens aclp->acl_entry_size; 782789Sahrens (void) memmove(to, from, len); 783789Sahrens aclp->acl_cnt--; 784789Sahrens } 785789Sahrens set_aclp = aclp; 786789Sahrens break; 787789Sahrens 788789Sahrens case ACL_DELETE: 789789Sahrens if ((error = acl_removeentries(aclp, acl_args->acl_aclp, 790789Sahrens acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) { 791789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 792789Sahrens acl_free(aclp); 793789Sahrens return (1); 794789Sahrens } 795789Sahrens 796789Sahrens if (aclp->acl_cnt == 0) { 797789Sahrens errmsg(1, 1, 798789Sahrens gettext("Can't remove all ACL " 799789Sahrens "entries from a file\n")); 800789Sahrens acl_free(aclp); 801789Sahrens return (1); 802789Sahrens } 803789Sahrens 804789Sahrens set_aclp = aclp; 805789Sahrens break; 806789Sahrens case ACL_REPLACE: 807789Sahrens if (acl_args->acl_slot >= 0) { 808789Sahrens error = acl_modifyentries(aclp, acl_args->acl_aclp, 809789Sahrens acl_args->acl_slot); 810789Sahrens if (error) { 811789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 812789Sahrens acl_free(aclp); 813789Sahrens return (1); 814789Sahrens } 815789Sahrens set_aclp = aclp; 816789Sahrens } else { 817789Sahrens set_aclp = acl_args->acl_aclp; 818789Sahrens } 819789Sahrens break; 820789Sahrens case ACL_STRIP: 821789Sahrens error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode); 822789Sahrens if (error) { 823789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 824789Sahrens return (1); 825789Sahrens } 826789Sahrens acl_free(aclp); 827789Sahrens return (0); 828789Sahrens /*NOTREACHED*/ 829789Sahrens default: 830789Sahrens errmsg(1, 0, gettext("Unknown ACL action requested\n")); 831789Sahrens return (1); 832789Sahrens break; 833789Sahrens } 834789Sahrens error = acl_check(set_aclp, isdir); 835789Sahrens 836789Sahrens if (error) { 837789Sahrens errmsg(1, 0, "%s\n%s", acl_strerror(error), 838789Sahrens gettext("See chmod(1) for more information on " 839789Sahrens "valid ACL syntax\n")); 840789Sahrens return (1); 841789Sahrens } 842789Sahrens if ((error = acl_set(file, set_aclp)) != 0) { 843789Sahrens errmsg(1, 0, gettext("Failed to set ACL: %s\n"), 844789Sahrens acl_strerror(error)); 845789Sahrens acl_free(aclp); 846789Sahrens return (1); 847789Sahrens } 848789Sahrens acl_free(aclp); 849789Sahrens return (0); 850789Sahrens } 851*5331Samw 852*5331Samw /* 853*5331Samw * Prints out the attributes in their verbose form: 854*5331Samw * '{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}' 855*5331Samw * similar to output of ls -/v. 856*5331Samw */ 857*5331Samw static void 858*5331Samw print_nvlist(nvlist_t *attr_nvlist) 859*5331Samw { 860*5331Samw int firsttime = 1; 861*5331Samw boolean_t value; 862*5331Samw nvlist_t *lptr = attr_nvlist; 863*5331Samw nvpair_t *pair = NULL; 864*5331Samw 865*5331Samw (void) fprintf(stderr, "\t%c", LEFTBRACE); 866*5331Samw while (pair = nvlist_next_nvpair(lptr, pair)) { 867*5331Samw if (nvpair_value_boolean_value(pair, &value) == 0) { 868*5331Samw (void) fprintf(stderr, "%s%s%s", 869*5331Samw firsttime ? "" : A_SEP_TOK, 870*5331Samw (value == A_SET_VAL) ? "" : "no", 871*5331Samw nvpair_name(pair)); 872*5331Samw firsttime = 0; 873*5331Samw } else { 874*5331Samw (void) fprintf(stderr, gettext( 875*5331Samw "<error retrieving attributes: %s>"), 876*5331Samw strerror(errno)); 877*5331Samw break; 878*5331Samw } 879*5331Samw } 880*5331Samw (void) fprintf(stderr, "%c\n", RIGHTBRACE); 881*5331Samw } 882*5331Samw 883*5331Samw /* 884*5331Samw * Add an attribute name and boolean value to an nvlist if an action is to be 885*5331Samw * performed for that attribute. The nvlist will be used later to set all the 886*5331Samw * attributes in the nvlist in one operation through a call to setattrat(). 887*5331Samw * 888*5331Samw * If a set operation ('+') was specified, then a boolean representation of the 889*5331Samw * attribute's value will be added to the nvlist for that attribute name. If an 890*5331Samw * inverse operation ('-') was specified, then a boolean representation of the 891*5331Samw * inverse of the attribute's value will be added to the nvlist for that 892*5331Samw * attribute name. 893*5331Samw * 894*5331Samw * Returns an nvlist of attribute name and boolean value pairs if there are 895*5331Samw * attribute actions to be performed, otherwise returns NULL. 896*5331Samw */ 897*5331Samw static nvlist_t * 898*5331Samw set_attrs_nvlist(char *attractptr, int numofattrs) 899*5331Samw { 900*5331Samw int attribute_set = 0; 901*5331Samw f_attr_t i; 902*5331Samw nvlist_t *attr_nvlist; 903*5331Samw 904*5331Samw if (nvlist_alloc(&attr_nvlist, NV_UNIQUE_NAME, 0) != 0) { 905*5331Samw perror("chmod"); 906*5331Samw exit(2); 907*5331Samw } 908*5331Samw 909*5331Samw for (i = 0; i < numofattrs; i++) { 910*5331Samw if (attractptr[i] != '\0') { 911*5331Samw if ((nvlist_add_boolean_value(attr_nvlist, 912*5331Samw attr_to_name(i), 913*5331Samw (attractptr[i] == A_SET_OP))) != 0) { 914*5331Samw errmsg(1, 2, gettext( 915*5331Samw "unable to propagate attribute names and" 916*5331Samw "values: %s\n"), strerror(errno)); 917*5331Samw } else { 918*5331Samw attribute_set = 1; 919*5331Samw } 920*5331Samw } 921*5331Samw } 922*5331Samw return (attribute_set ? attr_nvlist : NULL); 923*5331Samw } 924*5331Samw 925*5331Samw /* 926*5331Samw * Set the attributes of file, or if specified, of the named attribute file, 927*5331Samw * attrname. Build an nvlist of attribute names and values and call setattrat() 928*5331Samw * to set the attributes in one operation. 929*5331Samw * 930*5331Samw * Returns 0 if successful, otherwise returns 1. 931*5331Samw */ 932*5331Samw static int 933*5331Samw set_file_attrs(char *file, char *attrname, nvlist_t *attr_nvlist) 934*5331Samw { 935*5331Samw int rc; 936*5331Samw char *filename; 937*5331Samw 938*5331Samw if (attrname != NULL) { 939*5331Samw filename = attrname; 940*5331Samw } else { 941*5331Samw filename = basename(file); 942*5331Samw } 943*5331Samw 944*5331Samw if ((rc = setattrat(AT_FDCWD, XATTR_VIEW_READWRITE, filename, 945*5331Samw attr_nvlist)) != 0) { 946*5331Samw char *emsg; 947*5331Samw switch (errno) { 948*5331Samw case EINVAL: 949*5331Samw emsg = gettext("not supported"); 950*5331Samw break; 951*5331Samw case EPERM: 952*5331Samw emsg = gettext("not privileged"); 953*5331Samw break; 954*5331Samw default: 955*5331Samw emsg = strerror(rc); 956*5331Samw } 957*5331Samw errmsg(1, 0, gettext( 958*5331Samw "cannot set the following attributes on " 959*5331Samw "%s%s%s%s: %s\n"), 960*5331Samw (attrname == NULL) ? "" : gettext("attribute "), 961*5331Samw (attrname == NULL) ? "" : attrname, 962*5331Samw (attrname == NULL) ? "" : gettext(" of "), 963*5331Samw file, emsg); 964*5331Samw print_nvlist(attr_nvlist); 965*5331Samw } 966*5331Samw 967*5331Samw return (rc); 968*5331Samw } 969*5331Samw 970*5331Samw static int 971*5331Samw save_cwd(void) 972*5331Samw { 973*5331Samw return (open(".", O_RDONLY)); 974*5331Samw } 975*5331Samw 976*5331Samw static void 977*5331Samw rest_cwd(int cwd) 978*5331Samw { 979*5331Samw if (cwd != -1) { 980*5331Samw if (fchdir(cwd) != 0) { 981*5331Samw errmsg(1, 1, gettext( 982*5331Samw "can't change to current working directory\n")); 983*5331Samw } 984*5331Samw (void) close(cwd); 985*5331Samw } 986*5331Samw } 987*5331Samw 988*5331Samw /* 989*5331Samw * Returns 1 if filename is a system attribute file, otherwise 990*5331Samw * returns 0. 991*5331Samw */ 992*5331Samw static int 993*5331Samw is_sattr(char *filename) 994*5331Samw { 995*5331Samw return (sysattr_type(filename) != _NOT_SATTR); 996*5331Samw } 997*5331Samw 998*5331Samw /* 999*5331Samw * Perform the action on the specified named attribute file for the file 1000*5331Samw * associated with the input file descriptor. If the named attribute file 1001*5331Samw * is "*", then the action is to be performed on all the named attribute files 1002*5331Samw * of the file associated with the input file descriptor. 1003*5331Samw */ 1004*5331Samw static int 1005*5331Samw set_named_attrs(char *file, int parentfd, char *attrname, nvlist_t *attr_nvlist) 1006*5331Samw { 1007*5331Samw int dirfd; 1008*5331Samw int error = 0; 1009*5331Samw DIR *dirp = NULL; 1010*5331Samw struct dirent *dp; 1011*5331Samw struct stat st; 1012*5331Samw 1013*5331Samw if ((attrname == NULL) || (strcmp(attrname, "*") != 0)) { 1014*5331Samw /* 1015*5331Samw * Make sure the named attribute exists and extended system 1016*5331Samw * attributes are supported on the underlying file system. 1017*5331Samw */ 1018*5331Samw if (attrname != NULL) { 1019*5331Samw if (fstatat(parentfd, attrname, &st, 1020*5331Samw AT_SYMLINK_NOFOLLOW) < 0) { 1021*5331Samw errmsg(2, 0, gettext( 1022*5331Samw "can't access attribute %s of %s\n"), 1023*5331Samw attrname, file); 1024*5331Samw return (1); 1025*5331Samw } 1026*5331Samw if (sysattr_support(attrname, _PC_SATTR_ENABLED) != 1) { 1027*5331Samw errmsg(1, 0, gettext( 1028*5331Samw "extended system attributes not supported " 1029*5331Samw "for attribute %s of %s\n"), 1030*5331Samw attrname, file); 1031*5331Samw return (1); 1032*5331Samw } 1033*5331Samw } 1034*5331Samw 1035*5331Samw error = set_file_attrs(file, attrname, attr_nvlist); 1036*5331Samw 1037*5331Samw } else { 1038*5331Samw if (((dirfd = dup(parentfd)) == -1) || 1039*5331Samw ((dirp = fdopendir(dirfd)) == NULL)) { 1040*5331Samw errmsg(1, 0, gettext( 1041*5331Samw "cannot open dir pointer of file %s\n"), file); 1042*5331Samw if (dirfd > 0) { 1043*5331Samw (void) close(dirfd); 1044*5331Samw } 1045*5331Samw return (1); 1046*5331Samw } 1047*5331Samw 1048*5331Samw while (dp = readdir(dirp)) { 1049*5331Samw /* 1050*5331Samw * Process all extended attribute files except 1051*5331Samw * ".", "..", and extended system attribute files. 1052*5331Samw */ 1053*5331Samw if ((strcmp(dp->d_name, ".") == 0) || 1054*5331Samw (strcmp(dp->d_name, "..") == 0) || 1055*5331Samw is_sattr(dp->d_name)) { 1056*5331Samw continue; 1057*5331Samw } 1058*5331Samw 1059*5331Samw if (set_named_attrs(file, parentfd, dp->d_name, 1060*5331Samw attr_nvlist) != 0) { 1061*5331Samw error++; 1062*5331Samw } 1063*5331Samw } 1064*5331Samw if (dirp != NULL) { 1065*5331Samw (void) closedir(dirp); 1066*5331Samw } 1067*5331Samw } 1068*5331Samw 1069*5331Samw return ((error == 0) ? 0 : 1); 1070*5331Samw } 1071*5331Samw 1072*5331Samw /* 1073*5331Samw * Set the attributes of the specified file, or if specified with -@ on the 1074*5331Samw * command line, the specified named attributes of the specified file. 1075*5331Samw * 1076*5331Samw * Returns 0 if successful, otherwise returns 1. 1077*5331Samw */ 1078*5331Samw static int 1079*5331Samw set_attrs(char *file, attr_name_t *attrnames, nvlist_t *attr_nvlist) 1080*5331Samw { 1081*5331Samw char *parentd; 1082*5331Samw char *tpath = NULL; 1083*5331Samw int cwd; 1084*5331Samw int error = 0; 1085*5331Samw int parentfd; 1086*5331Samw attr_name_t *tattr = attrnames; 1087*5331Samw 1088*5331Samw if (attr_nvlist == NULL) { 1089*5331Samw return (0); 1090*5331Samw } 1091*5331Samw 1092*5331Samw if (sysattr_support(file, _PC_SATTR_ENABLED) != 1) { 1093*5331Samw errmsg(1, 0, gettext( 1094*5331Samw "extended system attributes not supported for %s\n"), file); 1095*5331Samw return (1); 1096*5331Samw } 1097*5331Samw 1098*5331Samw /* 1099*5331Samw * Open the parent directory and change into it before attempting 1100*5331Samw * to set the attributes of the file. 1101*5331Samw */ 1102*5331Samw if (attrnames == NULL) { 1103*5331Samw tpath = strdup(file); 1104*5331Samw parentd = dirname(tpath); 1105*5331Samw parentfd = open(parentd, O_RDONLY); 1106*5331Samw } else { 1107*5331Samw parentfd = attropen(file, ".", O_RDONLY); 1108*5331Samw } 1109*5331Samw if (parentfd == -1) { 1110*5331Samw errmsg(1, 0, gettext( 1111*5331Samw "cannot open attribute directory of %s\n"), file); 1112*5331Samw if (tpath != NULL) { 1113*5331Samw free(tpath); 1114*5331Samw } 1115*5331Samw return (1); 1116*5331Samw } 1117*5331Samw 1118*5331Samw if ((cwd = save_cwd()) < 0) { 1119*5331Samw errmsg(1, 1, gettext( 1120*5331Samw "can't get current working directory\n")); 1121*5331Samw } 1122*5331Samw if (fchdir(parentfd) != 0) { 1123*5331Samw errmsg(1, 0, gettext( 1124*5331Samw "can't change to parent %sdirectory of %s\n"), 1125*5331Samw (attrnames == NULL) ? "" : gettext("attribute "), file); 1126*5331Samw (void) close(cwd); 1127*5331Samw (void) close(parentfd); 1128*5331Samw if (tpath != NULL) { 1129*5331Samw free(tpath); 1130*5331Samw } 1131*5331Samw return (1); 1132*5331Samw } 1133*5331Samw 1134*5331Samw /* 1135*5331Samw * If no named attribute file names were provided on the command line 1136*5331Samw * then set the attributes of the base file, otherwise, set the 1137*5331Samw * attributes for each of the named attribute files specified. 1138*5331Samw */ 1139*5331Samw if (attrnames == NULL) { 1140*5331Samw error = set_named_attrs(file, parentfd, NULL, attr_nvlist); 1141*5331Samw free(tpath); 1142*5331Samw } else { 1143*5331Samw while (tattr != NULL) { 1144*5331Samw if (set_named_attrs(file, parentfd, tattr->name, 1145*5331Samw attr_nvlist) != 0) { 1146*5331Samw error++; 1147*5331Samw } 1148*5331Samw tattr = tattr->next; 1149*5331Samw } 1150*5331Samw } 1151*5331Samw (void) close(parentfd); 1152*5331Samw rest_cwd(cwd); 1153*5331Samw 1154*5331Samw return ((error == 0) ? 0 : 1); 1155*5331Samw } 1156*5331Samw 1157*5331Samw /* 1158*5331Samw * Prints the attributes in either the compact or verbose form indicated 1159*5331Samw * by flag. 1160*5331Samw */ 1161*5331Samw static void 1162*5331Samw print_attrs(int flag) 1163*5331Samw { 1164*5331Samw f_attr_t i; 1165*5331Samw static int numofattrs; 1166*5331Samw int firsttime = 1; 1167*5331Samw 1168*5331Samw numofattrs = attr_count(); 1169*5331Samw 1170*5331Samw (void) fprintf(stderr, gettext("\t[")); 1171*5331Samw for (i = 0; i < numofattrs; i++) { 1172*5331Samw if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) || 1173*5331Samw (attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) { 1174*5331Samw continue; 1175*5331Samw } 1176*5331Samw (void) fprintf(stderr, "%s%s", 1177*5331Samw (firsttime == 1) ? "" : gettext("|"), 1178*5331Samw (flag == ATTR_OPTS) ? attr_to_option(i) : attr_to_name(i)); 1179*5331Samw firsttime = 0; 1180*5331Samw } 1181*5331Samw (void) fprintf(stderr, gettext("]\n")); 1182*5331Samw } 1183*5331Samw 1184*5331Samw /* 1185*5331Samw * Record what action should be taken on the specified attribute. Only boolean 1186*5331Samw * read-write attributes can be manipulated. 1187*5331Samw * 1188*5331Samw * Returns 0 if successful, otherwise returns 1. 1189*5331Samw */ 1190*5331Samw static int 1191*5331Samw set_attr_args(f_attr_t attr, char action, char *attractptr) 1192*5331Samw { 1193*5331Samw if ((attr_to_xattr_view(attr) == XATTR_VIEW_READWRITE) && 1194*5331Samw (attr_to_data_type(attr) == DATA_TYPE_BOOLEAN_VALUE)) { 1195*5331Samw attractptr[attr] = action; 1196*5331Samw return (0); 1197*5331Samw } 1198*5331Samw return (1); 1199*5331Samw } 1200*5331Samw 1201*5331Samw /* 1202*5331Samw * Parses the entry and assigns the appropriate action (either '+' or '-' in 1203*5331Samw * attribute's position in the character array pointed to by attractptr, where 1204*5331Samw * upon exit, attractptr is positional and the value of each character specifies 1205*5331Samw * whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the 1206*5331Samw * attribute value. 1207*5331Samw * 1208*5331Samw * If the entry is an attribute name, then the A_SET_OP action is to be 1209*5331Samw * performed for this attribute. If the entry is an attribute name proceeded 1210*5331Samw * with "no", then the A_INVERSE_OP action is to be performed for this 1211*5331Samw * attribute. If the entry is one or more attribute option letters, then step 1212*5331Samw * through each of the option letters marking the action to be performed for 1213*5331Samw * each of the attributes associated with the letter as A_SET_OP. 1214*5331Samw * 1215*5331Samw * Returns 0 if the entry was a valid attribute(s) and the action to be 1216*5331Samw * performed on that attribute(s) has been recorded, otherwise returns 1. 1217*5331Samw */ 1218*5331Samw static int 1219*5331Samw parse_entry(char *entry, char action, char atype, int len, char *attractptr) 1220*5331Samw { 1221*5331Samw char aopt[2] = {'\0', '\0'}; 1222*5331Samw char *aptr; 1223*5331Samw f_attr_t attr; 1224*5331Samw 1225*5331Samw if (atype == A_VERBOSE_TYPE) { 1226*5331Samw if ((attr = name_to_attr(entry)) != F_ATTR_INVAL) { 1227*5331Samw return (set_attr_args(attr, 1228*5331Samw (action == A_REPLACE_OP) ? A_SET_OP : action, 1229*5331Samw attractptr)); 1230*5331Samw } else if ((len > 2) && (strncmp(entry, "no", 2) == 0) && 1231*5331Samw ((attr = name_to_attr(entry + 2)) != F_ATTR_INVAL)) { 1232*5331Samw return (set_attr_args(attr, ((action == A_REPLACE_OP) || 1233*5331Samw (action == A_SET_OP)) ? A_INVERSE_OP : A_SET_OP, 1234*5331Samw attractptr)); 1235*5331Samw } else { 1236*5331Samw return (1); 1237*5331Samw } 1238*5331Samw } else if (atype == A_COMPACT_TYPE) { 1239*5331Samw for (aptr = entry; *aptr != '\0'; aptr++) { 1240*5331Samw *aopt = *aptr; 1241*5331Samw /* 1242*5331Samw * The output of 'ls' can be used as the attribute mode 1243*5331Samw * specification for chmod. This output can contain a 1244*5331Samw * hypen ('-') for each attribute that is not set. If 1245*5331Samw * so, ignore them. If a replace action is being 1246*5331Samw * performed, then all attributes that don't have an 1247*5331Samw * action set here, will be cleared down the line. 1248*5331Samw */ 1249*5331Samw if (*aptr == '-') { 1250*5331Samw continue; 1251*5331Samw } 1252*5331Samw if (set_attr_args(option_to_attr(aopt), 1253*5331Samw (action == A_REPLACE_OP) ? A_SET_OP : action, 1254*5331Samw attractptr) != 0) { 1255*5331Samw return (1); 1256*5331Samw } 1257*5331Samw } 1258*5331Samw return (0); 1259*5331Samw } 1260*5331Samw return (1); 1261*5331Samw } 1262*5331Samw 1263*5331Samw /* 1264*5331Samw * Parse the attribute specification, aoptsstr. Upon completion, attr_nvlist 1265*5331Samw * will point to an nvlist which contains pairs of attribute names and values 1266*5331Samw * to be set; attr_nvlist will be NULL if it is a no-op. 1267*5331Samw * 1268*5331Samw * The attribute specification format is 1269*5331Samw * S[oper]attr_type[attribute_list] 1270*5331Samw * where oper is 1271*5331Samw * + set operation of specified attributes in attribute list. 1272*5331Samw * This is the default operation. 1273*5331Samw * - inverse operation of specified attributes in attribute list 1274*5331Samw * = replace operation of all attributes. All attribute operations 1275*5331Samw * depend on those specified in the attribute list. Attributes 1276*5331Samw * not specified in the attribute list will be cleared. 1277*5331Samw * where attr_type is 1278*5331Samw * c compact type. Each entry in the attribute list is a character 1279*5331Samw * option representing an associated attribute name. 1280*5331Samw * v verbose type. Each entry in the attribute list is an 1281*5331Samw * an attribute name which can optionally be preceeded with "no" 1282*5331Samw * (to imply the attribute should be cleared). 1283*5331Samw * a all attributes type. The oper should be applied to all 1284*5331Samw * read-write boolean system attributes. No attribute list should 1285*5331Samw * be specified after an 'a' attribute type. 1286*5331Samw * 1287*5331Samw * Returns 0 if aoptsstr contained a valid attribute specification, 1288*5331Samw * otherwise, returns 1. 1289*5331Samw */ 1290*5331Samw static int 1291*5331Samw parse_attr_args(char *aoptsstr, sec_args_t **sec_args) 1292*5331Samw { 1293*5331Samw char action; 1294*5331Samw char *attractptr; 1295*5331Samw char atype; 1296*5331Samw char *entry; 1297*5331Samw char *eptr; 1298*5331Samw char *nextattr; 1299*5331Samw char *nextentry; 1300*5331Samw char *subentry; 1301*5331Samw char *teptr; 1302*5331Samw char tok[] = {'\0', '\0'}; 1303*5331Samw int len; 1304*5331Samw f_attr_t i; 1305*5331Samw int numofattrs; 1306*5331Samw 1307*5331Samw if ((*aoptsstr != 'S') || (*(aoptsstr + 1) == '\0')) { 1308*5331Samw return (1); 1309*5331Samw } 1310*5331Samw 1311*5331Samw if ((eptr = strdup(aoptsstr + 1)) == NULL) { 1312*5331Samw perror("chmod"); 1313*5331Samw exit(2); 1314*5331Samw } 1315*5331Samw entry = eptr; 1316*5331Samw 1317*5331Samw /* 1318*5331Samw * Create a positional character array to determine a single attribute 1319*5331Samw * operation to be performed, where each index represents the system 1320*5331Samw * attribute affected, and it's value in the array represents the action 1321*5331Samw * to be performed, i.e., a value of '+' means to set the attribute, a 1322*5331Samw * value of '-' means to clear the attribute, and a value of '\0' means 1323*5331Samw * to leave the attribute untouched. Initially, this positional 1324*5331Samw * character array is all '\0's, representing a no-op. 1325*5331Samw */ 1326*5331Samw if ((numofattrs = attr_count()) < 1) { 1327*5331Samw errmsg(1, 1, gettext("system attributes not supported\n")); 1328*5331Samw } 1329*5331Samw 1330*5331Samw if ((attractptr = calloc(numofattrs, sizeof (char))) == NULL) { 1331*5331Samw perror("chmod"); 1332*5331Samw exit(2); 1333*5331Samw } 1334*5331Samw 1335*5331Samw if ((*sec_args = malloc(sizeof (sec_args_t))) == NULL) { 1336*5331Samw perror("chmod"); 1337*5331Samw exit(2); 1338*5331Samw } 1339*5331Samw (*sec_args)->sec_type = SEC_ATTR; 1340*5331Samw (*sec_args)->sec_attrs = NULL; 1341*5331Samw 1342*5331Samw /* Parse each attribute operation within the attribute specification. */ 1343*5331Samw while ((entry != NULL) && (*entry != '\0')) { 1344*5331Samw action = A_SET_OP; 1345*5331Samw atype = '\0'; 1346*5331Samw 1347*5331Samw /* Get the operator. */ 1348*5331Samw switch (*entry) { 1349*5331Samw case A_SET_OP: 1350*5331Samw case A_INVERSE_OP: 1351*5331Samw case A_REPLACE_OP: 1352*5331Samw action = *entry++; 1353*5331Samw break; 1354*5331Samw case A_COMPACT_TYPE: 1355*5331Samw case A_VERBOSE_TYPE: 1356*5331Samw case A_ALLATTRS_TYPE: 1357*5331Samw atype = *entry++; 1358*5331Samw action = A_SET_OP; 1359*5331Samw break; 1360*5331Samw default: 1361*5331Samw break; 1362*5331Samw } 1363*5331Samw 1364*5331Samw /* An attribute type must be specified. */ 1365*5331Samw if (atype == '\0') { 1366*5331Samw if ((*entry == A_COMPACT_TYPE) || 1367*5331Samw (*entry == A_VERBOSE_TYPE) || 1368*5331Samw (*entry == A_ALLATTRS_TYPE)) { 1369*5331Samw atype = *entry++; 1370*5331Samw } else { 1371*5331Samw return (1); 1372*5331Samw } 1373*5331Samw } 1374*5331Samw 1375*5331Samw /* Get the attribute specification separator. */ 1376*5331Samw if (*entry == LEFTBRACE) { 1377*5331Samw *tok = RIGHTBRACE; 1378*5331Samw entry++; 1379*5331Samw } else { 1380*5331Samw *tok = A_SEP; 1381*5331Samw } 1382*5331Samw 1383*5331Samw /* Get the attribute operation */ 1384*5331Samw if ((nextentry = strpbrk(entry, tok)) != NULL) { 1385*5331Samw *nextentry = '\0'; 1386*5331Samw nextentry++; 1387*5331Samw } 1388*5331Samw 1389*5331Samw /* Check for a no-op */ 1390*5331Samw if ((*entry == '\0') && (atype != A_ALLATTRS_TYPE) && 1391*5331Samw (action != A_REPLACE_OP)) { 1392*5331Samw entry = nextentry; 1393*5331Samw continue; 1394*5331Samw } 1395*5331Samw 1396*5331Samw /* 1397*5331Samw * Step through the attribute operation, setting the 1398*5331Samw * appropriate values for the specified attributes in the 1399*5331Samw * character array, attractptr. A value of '+' will mean the 1400*5331Samw * attribute is to be set, and a value of '-' will mean the 1401*5331Samw * attribute is to be cleared. If the value of an attribute 1402*5331Samw * remains '\0', then no action is to be taken on that 1403*5331Samw * attribute. As multiple operations specified are 1404*5331Samw * accumulated, a single attribute setting operation is 1405*5331Samw * represented in attractptr. 1406*5331Samw */ 1407*5331Samw len = strlen(entry); 1408*5331Samw if ((*tok == RIGHTBRACE) || (action == A_REPLACE_OP) || 1409*5331Samw (atype == A_ALLATTRS_TYPE)) { 1410*5331Samw 1411*5331Samw if ((action == A_REPLACE_OP) || 1412*5331Samw (atype == A_ALLATTRS_TYPE)) { 1413*5331Samw (void) memset(attractptr, '\0', numofattrs); 1414*5331Samw } 1415*5331Samw 1416*5331Samw if (len > 0) { 1417*5331Samw if ((teptr = strdup(entry)) == NULL) { 1418*5331Samw perror("chmod"); 1419*5331Samw exit(2); 1420*5331Samw } 1421*5331Samw subentry = teptr; 1422*5331Samw while (subentry != NULL) { 1423*5331Samw if ((nextattr = strpbrk(subentry, 1424*5331Samw A_SEP_TOK)) != NULL) { 1425*5331Samw *nextattr = '\0'; 1426*5331Samw nextattr++; 1427*5331Samw } 1428*5331Samw if (parse_entry(subentry, action, 1429*5331Samw atype, len, attractptr) != 0) { 1430*5331Samw return (1); 1431*5331Samw } 1432*5331Samw subentry = nextattr; 1433*5331Samw } 1434*5331Samw free(teptr); 1435*5331Samw } 1436*5331Samw 1437*5331Samw /* 1438*5331Samw * If performing the replace action, record the 1439*5331Samw * attributes and values for the rest of the 1440*5331Samw * attributes that have not already been recorded, 1441*5331Samw * otherwise record the specified action for all 1442*5331Samw * attributes. Note: set_attr_args() will only record 1443*5331Samw * the attribute and action if it is a boolean 1444*5331Samw * read-write attribute so we don't need to worry 1445*5331Samw * about checking it here. 1446*5331Samw */ 1447*5331Samw if ((action == A_REPLACE_OP) || 1448*5331Samw (atype == A_ALLATTRS_TYPE)) { 1449*5331Samw for (i = 0; i < numofattrs; i++) { 1450*5331Samw if (attractptr[i] == A_UNDEF_OP) { 1451*5331Samw (void) set_attr_args(i, 1452*5331Samw (action == A_SET_OP) ? 1453*5331Samw A_SET_OP : A_INVERSE_OP, 1454*5331Samw attractptr); 1455*5331Samw } 1456*5331Samw } 1457*5331Samw } 1458*5331Samw 1459*5331Samw } else { 1460*5331Samw if (parse_entry(entry, action, atype, len, 1461*5331Samw attractptr) != 0) { 1462*5331Samw return (1); 1463*5331Samw } 1464*5331Samw } 1465*5331Samw entry = nextentry; 1466*5331Samw } 1467*5331Samw 1468*5331Samw /* 1469*5331Samw * Populate an nvlist with attribute name and boolean value pairs 1470*5331Samw * using the single attribute operation. 1471*5331Samw */ 1472*5331Samw (*sec_args)->sec_attrs = set_attrs_nvlist(attractptr, numofattrs); 1473*5331Samw free(attractptr); 1474*5331Samw free(eptr); 1475*5331Samw 1476*5331Samw return (0); 1477*5331Samw } 1478