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 /* 22*3520Sas145665 * 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> 470Sstevel@tonic-gate * option is -R and -f 480Sstevel@tonic-gate */ 490Sstevel@tonic-gate 500Sstevel@tonic-gate /* 510Sstevel@tonic-gate * Note that many convolutions are necessary 520Sstevel@tonic-gate * due to the re-use of bits between locking 530Sstevel@tonic-gate * and setgid 540Sstevel@tonic-gate */ 550Sstevel@tonic-gate 560Sstevel@tonic-gate #include <unistd.h> 570Sstevel@tonic-gate #include <stdlib.h> 580Sstevel@tonic-gate #include <stdio.h> 590Sstevel@tonic-gate #include <sys/types.h> 600Sstevel@tonic-gate #include <sys/stat.h> 610Sstevel@tonic-gate #include <dirent.h> 620Sstevel@tonic-gate #include <locale.h> 630Sstevel@tonic-gate #include <string.h> /* strerror() */ 640Sstevel@tonic-gate #include <stdarg.h> 650Sstevel@tonic-gate #include <limits.h> 66789Sahrens #include <ctype.h> 670Sstevel@tonic-gate #include <errno.h> 680Sstevel@tonic-gate #include <sys/acl.h> 69789Sahrens #include <aclutils.h> 700Sstevel@tonic-gate 710Sstevel@tonic-gate static int rflag; 720Sstevel@tonic-gate static int fflag; 730Sstevel@tonic-gate 740Sstevel@tonic-gate extern int optind; 750Sstevel@tonic-gate extern int errno; 760Sstevel@tonic-gate 770Sstevel@tonic-gate static int mac; /* Alternate to argc (for parseargs) */ 780Sstevel@tonic-gate static char **mav; /* Alternate to argv (for parseargs) */ 790Sstevel@tonic-gate 800Sstevel@tonic-gate static char *ms; /* Points to the mode argument */ 810Sstevel@tonic-gate 82789Sahrens #define ACL_ADD 1 83789Sahrens #define ACL_DELETE 2 84789Sahrens #define ACL_SLOT_DELETE 3 85789Sahrens #define ACL_REPLACE 4 86789Sahrens #define ACL_STRIP 5 87789Sahrens 88789Sahrens typedef struct acl_args { 89789Sahrens acl_t *acl_aclp; 90789Sahrens int acl_slot; 91789Sahrens int acl_action; 92789Sahrens } acl_args_t; 93789Sahrens 940Sstevel@tonic-gate extern mode_t 950Sstevel@tonic-gate newmode_common(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path, 960Sstevel@tonic-gate o_mode_t *group_clear_bits, o_mode_t *group_set_bits); 970Sstevel@tonic-gate 980Sstevel@tonic-gate static int 99789Sahrens dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp), 100789Sahrens chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp); 101789Sahrens static int doacl(char *file, struct stat *st, acl_args_t *aclp); 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate static void handle_acl(char *name, o_mode_t group_clear_bits, 104789Sahrens o_mode_t group_set_bits); 1050Sstevel@tonic-gate 106789Sahrens static void usage(void); 1070Sstevel@tonic-gate 108789Sahrens void errmsg(int severity, int code, char *format, ...); 1090Sstevel@tonic-gate 110789Sahrens static void parseargs(int ac, char *av[]); 111789Sahrens 112789Sahrens int 113789Sahrens parse_acl_args(char *arg, acl_args_t **acl_args); 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate int 1160Sstevel@tonic-gate main(int argc, char *argv[]) 1170Sstevel@tonic-gate { 1180Sstevel@tonic-gate int i, c; 1190Sstevel@tonic-gate int status = 0; 1200Sstevel@tonic-gate mode_t umsk; 121789Sahrens acl_args_t *acl_args = NULL; 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1240Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1250Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 1260Sstevel@tonic-gate #endif 1270Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate parseargs(argc, argv); 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate while ((c = getopt(mac, mav, "Rf")) != EOF) { 1320Sstevel@tonic-gate switch (c) { 1330Sstevel@tonic-gate case 'R': 1340Sstevel@tonic-gate rflag++; 1350Sstevel@tonic-gate break; 1360Sstevel@tonic-gate case 'f': 1370Sstevel@tonic-gate fflag++; 1380Sstevel@tonic-gate break; 1390Sstevel@tonic-gate case '?': 1400Sstevel@tonic-gate usage(); 1410Sstevel@tonic-gate exit(2); 1420Sstevel@tonic-gate } 1430Sstevel@tonic-gate } 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate /* 1460Sstevel@tonic-gate * Check for sufficient arguments 1470Sstevel@tonic-gate * or a usage error. 1480Sstevel@tonic-gate */ 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate mac -= optind; 1510Sstevel@tonic-gate mav += optind; 152789Sahrens if (mac >= 2 && (mav[0][0] == 'A')) { 153789Sahrens if (parse_acl_args(*mav, &acl_args)) { 154789Sahrens usage(); 155789Sahrens exit(2); 156789Sahrens } 157789Sahrens } else { 158789Sahrens if (mac < 2) { 159789Sahrens usage(); 160789Sahrens exit(2); 161789Sahrens } 1620Sstevel@tonic-gate } 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate ms = mav[0]; 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate umsk = umask(0); 1670Sstevel@tonic-gate (void) umask(umsk); 1680Sstevel@tonic-gate 169789Sahrens for (i = 1; i < mac; i++) { 170789Sahrens status += dochmod(mav[i], mav[i], umsk, acl_args); 171789Sahrens } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate return (fflag ? 0 : status); 1740Sstevel@tonic-gate } 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate static int 177789Sahrens dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) 1780Sstevel@tonic-gate { 1790Sstevel@tonic-gate static struct stat st; 1800Sstevel@tonic-gate int linkflg = 0; 1810Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate if (lstat(name, &st) < 0) { 1840Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 1850Sstevel@tonic-gate return (1); 1860Sstevel@tonic-gate } 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFLNK) { 1890Sstevel@tonic-gate linkflg = 1; 1900Sstevel@tonic-gate if (stat(name, &st) < 0) { 1910Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 1920Sstevel@tonic-gate return (1); 1930Sstevel@tonic-gate } 1940Sstevel@tonic-gate } 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate /* Do not recurse if directory is object of symbolic link */ 1970Sstevel@tonic-gate if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) 198789Sahrens return (chmodr(name, path, st.st_mode, umsk, aclp)); 1990Sstevel@tonic-gate 200789Sahrens if (aclp) { 201789Sahrens return (doacl(name, &st, aclp)); 202789Sahrens } else if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, 2030Sstevel@tonic-gate &group_clear_bits, &group_set_bits)) == -1) { 2040Sstevel@tonic-gate errmsg(2, 0, gettext("can't change %s\n"), path); 2050Sstevel@tonic-gate return (1); 2060Sstevel@tonic-gate } 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate /* 2090Sstevel@tonic-gate * If the group permissions of the file are being modified, 2100Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 2110Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 2120Sstevel@tonic-gate * permissions changes to both the acl mask and the 2130Sstevel@tonic-gate * general group permissions. 2140Sstevel@tonic-gate */ 2150Sstevel@tonic-gate if (group_clear_bits || group_set_bits) 2160Sstevel@tonic-gate handle_acl(name, group_clear_bits, group_set_bits); 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate return (0); 2190Sstevel@tonic-gate } 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate static int 223789Sahrens chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) 2240Sstevel@tonic-gate { 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate DIR *dirp; 2270Sstevel@tonic-gate struct dirent *dp; 2280Sstevel@tonic-gate char savedir[PATH_MAX]; /* dir name to restore */ 2290Sstevel@tonic-gate char currdir[PATH_MAX+1]; /* current dir name + '/' */ 2300Sstevel@tonic-gate char parentdir[PATH_MAX+1]; /* parent dir name + '/' */ 2310Sstevel@tonic-gate int ecode; 232789Sahrens struct stat st; 2330Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate if (getcwd(savedir, PATH_MAX) == 0) 2360Sstevel@tonic-gate errmsg(2, 255, gettext("chmod: could not getcwd %s\n"), 2370Sstevel@tonic-gate savedir); 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate /* 2400Sstevel@tonic-gate * Change what we are given before doing it's contents 2410Sstevel@tonic-gate */ 242789Sahrens if (aclp) { 243789Sahrens if (lstat(dir, &st) < 0) { 244789Sahrens errmsg(2, 0, gettext("can't access %s\n"), path); 245789Sahrens return (1); 246789Sahrens } 247789Sahrens if (doacl(dir, &st, aclp) != 0) 248789Sahrens return (1); 249789Sahrens } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path, 2500Sstevel@tonic-gate &group_clear_bits, &group_set_bits)) < 0) { 2510Sstevel@tonic-gate errmsg(2, 0, gettext("can't change %s\n"), path); 2520Sstevel@tonic-gate return (1); 2530Sstevel@tonic-gate } 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate /* 2560Sstevel@tonic-gate * If the group permissions of the file are being modified, 2570Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 2580Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 2590Sstevel@tonic-gate * permissions changes to both the acl mask and the 2600Sstevel@tonic-gate * general group permissions. 2610Sstevel@tonic-gate */ 262789Sahrens 263789Sahrens if (aclp == NULL) { /* only necessary when not setting ACL */ 264789Sahrens if (group_clear_bits || group_set_bits) 265789Sahrens handle_acl(dir, group_clear_bits, group_set_bits); 266789Sahrens } 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate if (chdir(dir) < 0) { 2690Sstevel@tonic-gate errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno)); 2700Sstevel@tonic-gate return (1); 2710Sstevel@tonic-gate } 2720Sstevel@tonic-gate if ((dirp = opendir(".")) == NULL) { 2730Sstevel@tonic-gate errmsg(2, 0, "%s\n", strerror(errno)); 2740Sstevel@tonic-gate return (1); 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate ecode = 0; 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* 2790Sstevel@tonic-gate * Save parent directory path before recursive chmod. 2800Sstevel@tonic-gate * We'll need this for error printing purposes. Add 2810Sstevel@tonic-gate * a trailing '/' to the path except in the case where 2820Sstevel@tonic-gate * the path is just '/' 2830Sstevel@tonic-gate */ 2840Sstevel@tonic-gate 285*3520Sas145665 if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) { 286*3520Sas145665 errmsg(2, 0, gettext("directory path name too long: %s\n"), 287*3520Sas145665 path); 288*3520Sas145665 return (1); 289*3520Sas145665 } 2900Sstevel@tonic-gate if (strcmp(path, "/") != 0) 291*3520Sas145665 if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) { 292*3520Sas145665 errmsg(2, 0, 293*3520Sas145665 gettext("directory path name too long: %s/\n"), 294*3520Sas145665 parentdir); 295*3520Sas145665 return (1); 296*3520Sas145665 } 297*3520Sas145665 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 300*3520Sas145665 3011591Scasper if (strcmp(dp->d_name, ".") == 0 || /* skip . and .. */ 3021591Scasper strcmp(dp->d_name, "..") == 0) { 3031591Scasper continue; 3041591Scasper } 305*3520Sas145665 if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) { 306*3520Sas145665 errmsg(2, 0, 307*3520Sas145665 gettext("directory path name too long: %s\n"), 308*3520Sas145665 parentdir); 309*3520Sas145665 return (1); 310*3520Sas145665 } 311*3520Sas145665 if (strlcat(currdir, dp->d_name, PATH_MAX + 1) 312*3520Sas145665 >= PATH_MAX + 1) { 313*3520Sas145665 errmsg(2, 0, 314*3520Sas145665 gettext("directory path name too long: %s%s\n"), 315*3520Sas145665 currdir, dp->d_name); 316*3520Sas145665 return (1); 317*3520Sas145665 } 318789Sahrens ecode += dochmod(dp->d_name, currdir, umsk, aclp); 3190Sstevel@tonic-gate } 3200Sstevel@tonic-gate (void) closedir(dirp); 3210Sstevel@tonic-gate if (chdir(savedir) < 0) { 3220Sstevel@tonic-gate errmsg(2, 255, gettext("can't change back to %s\n"), savedir); 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate return (ecode ? 1 : 0); 3250Sstevel@tonic-gate } 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate /* PRINTFLIKE3 */ 3280Sstevel@tonic-gate void 3290Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...) 3300Sstevel@tonic-gate { 3310Sstevel@tonic-gate va_list ap; 3320Sstevel@tonic-gate static char *msg[] = { 3330Sstevel@tonic-gate "", 3340Sstevel@tonic-gate "ERROR", 3350Sstevel@tonic-gate "WARNING", 3360Sstevel@tonic-gate "" 3370Sstevel@tonic-gate }; 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate va_start(ap, format); 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate /* 3420Sstevel@tonic-gate * Always print error message if this is a fatal error (code == 0); 3430Sstevel@tonic-gate * otherwise, print message if fflag == 0 (no -f option specified) 3440Sstevel@tonic-gate */ 3450Sstevel@tonic-gate if (!fflag || (code != 0)) { 3460Sstevel@tonic-gate (void) fprintf(stderr, 3470Sstevel@tonic-gate "chmod: %s: ", gettext(msg[severity])); 3480Sstevel@tonic-gate (void) vfprintf(stderr, format, ap); 3490Sstevel@tonic-gate } 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate va_end(ap); 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate if (code != 0) 3540Sstevel@tonic-gate exit(fflag ? 0 : code); 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate static void 3580Sstevel@tonic-gate usage(void) 3590Sstevel@tonic-gate { 3600Sstevel@tonic-gate (void) fprintf(stderr, gettext( 3610Sstevel@tonic-gate "usage:\tchmod [-fR] <absolute-mode> file ...\n")); 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate (void) fprintf(stderr, gettext( 364789Sahrens "\tchmod [-fR] <ACL-operation> file ...\n")); 365789Sahrens 366789Sahrens (void) fprintf(stderr, gettext( 3670Sstevel@tonic-gate "\tchmod [-fR] <symbolic-mode-list> file ...\n")); 3680Sstevel@tonic-gate 369789Sahrens 3700Sstevel@tonic-gate (void) fprintf(stderr, gettext( 3710Sstevel@tonic-gate "where \t<symbolic-mode-list> is a comma-separated list of\n")); 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate (void) fprintf(stderr, gettext( 3740Sstevel@tonic-gate "\t[ugoa]{+|-|=}[rwxXlstugo]\n")); 375789Sahrens 376789Sahrens (void) fprintf(stderr, gettext( 377789Sahrens "where \t<ACL-operation> is one of the following\n")); 378789Sahrens (void) fprintf(stderr, gettext("\tA-<acl_specification>\n")); 379789Sahrens (void) fprintf(stderr, gettext("\tA[number]-\n")); 380789Sahrens (void) fprintf(stderr, gettext( 381789Sahrens "\tA[number]{+|=}<acl_specification>\n")); 382789Sahrens (void) fprintf(stderr, gettext( 383789Sahrens "where \t<acl-specification> is a comma-separated list of ACEs\n")); 3840Sstevel@tonic-gate } 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate /* 3870Sstevel@tonic-gate * parseargs - generate getopt-friendly argument list for backwards 3880Sstevel@tonic-gate * compatibility with earlier Solaris usage (eg, chmod -w 3890Sstevel@tonic-gate * foo). 3900Sstevel@tonic-gate * 3910Sstevel@tonic-gate * assumes the existence of a static set of alternates to argc and argv, 3920Sstevel@tonic-gate * (namely, mac, and mav[]). 3930Sstevel@tonic-gate * 3940Sstevel@tonic-gate */ 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate static void 3970Sstevel@tonic-gate parseargs(int ac, char *av[]) 3980Sstevel@tonic-gate { 3990Sstevel@tonic-gate int i; /* current argument */ 4000Sstevel@tonic-gate int fflag; /* arg list contains "--" */ 4010Sstevel@tonic-gate size_t mav_num; /* number of entries in mav[] */ 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate /* 4040Sstevel@tonic-gate * We add an extra argument slot, in case we need to jam a "--" 4050Sstevel@tonic-gate * argument into the list. 4060Sstevel@tonic-gate */ 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate mav_num = (size_t)ac+2; 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate if ((mav = calloc(mav_num, sizeof (char *))) == NULL) { 4110Sstevel@tonic-gate perror("chmod"); 4120Sstevel@tonic-gate exit(2); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate /* scan for the use of "--" in the argument list */ 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate for (fflag = i = 0; i < ac; i ++) { 4180Sstevel@tonic-gate if (strcmp(av[i], "--") == 0) 4190Sstevel@tonic-gate fflag = 1; 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate /* process the arguments */ 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate for (i = mac = 0; 4250Sstevel@tonic-gate (av[i] != (char *)NULL) && (av[i][0] != (char)NULL); 4260Sstevel@tonic-gate i++) { 4270Sstevel@tonic-gate if (!fflag && av[i][0] == '-') { 4280Sstevel@tonic-gate /* 4290Sstevel@tonic-gate * If there is not already a "--" argument specified, 4300Sstevel@tonic-gate * and the argument starts with '-' but does not 4310Sstevel@tonic-gate * contain any of the official option letters, then it 4320Sstevel@tonic-gate * is probably a mode argument beginning with '-'. 4330Sstevel@tonic-gate * Force a "--" into the argument stream in front of 4340Sstevel@tonic-gate * it. 4350Sstevel@tonic-gate */ 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate if ((strchr(av[i], 'R') == NULL && 4380Sstevel@tonic-gate strchr(av[i], 'f') == NULL)) { 4390Sstevel@tonic-gate mav[mac++] = strdup("--"); 4400Sstevel@tonic-gate } 4410Sstevel@tonic-gate } 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate mav[mac++] = strdup(av[i]); 4440Sstevel@tonic-gate } 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate mav[mac] = (char *)NULL; 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 449789Sahrens int 450789Sahrens parse_acl_args(char *arg, acl_args_t **acl_args) 451789Sahrens { 452789Sahrens acl_t *new_acl = NULL; 453789Sahrens int slot; 454789Sahrens int len; 455789Sahrens int action; 456789Sahrens acl_args_t *new_acl_args; 457789Sahrens char *acl_spec = NULL; 458789Sahrens char *end; 459789Sahrens 460789Sahrens if (arg[0] != 'A') 461789Sahrens return (1); 462789Sahrens 463789Sahrens slot = strtol(&arg[1], &end, 10); 464789Sahrens 465789Sahrens len = strlen(arg); 466789Sahrens switch (*end) { 467789Sahrens case '+': 468789Sahrens action = ACL_ADD; 469789Sahrens acl_spec = ++end; 470789Sahrens break; 471789Sahrens case '-': 472789Sahrens if (len == 2 && arg[0] == 'A' && arg[1] == '-') 473789Sahrens action = ACL_STRIP; 474789Sahrens else 475789Sahrens action = ACL_DELETE; 476789Sahrens if (action != ACL_STRIP) { 477789Sahrens acl_spec = ++end; 478789Sahrens if (acl_spec[0] == '\0') { 479789Sahrens action = ACL_SLOT_DELETE; 480789Sahrens acl_spec = NULL; 481789Sahrens } else if (arg[1] != '-') 482789Sahrens return (1); 483789Sahrens } 484789Sahrens break; 485789Sahrens case '=': 486865Smarks /* 487865Smarks * Was slot specified? 488865Smarks */ 489865Smarks if (arg[1] == '=') 490865Smarks slot = -1; 491789Sahrens action = ACL_REPLACE; 492789Sahrens acl_spec = ++end; 493789Sahrens break; 494789Sahrens default: 495789Sahrens return (1); 496789Sahrens } 497789Sahrens 498789Sahrens if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0') 499789Sahrens return (1); 500789Sahrens 501789Sahrens if (acl_spec) { 5021420Smarks if (acl_parse(acl_spec, &new_acl)) { 5031420Smarks exit(1); 504789Sahrens } 505789Sahrens } 506789Sahrens 507789Sahrens new_acl_args = malloc(sizeof (acl_args_t)); 508789Sahrens if (new_acl_args == NULL) 509789Sahrens return (1); 510789Sahrens 511789Sahrens new_acl_args->acl_aclp = new_acl; 512789Sahrens new_acl_args->acl_slot = slot; 513789Sahrens new_acl_args->acl_action = action; 514789Sahrens 515789Sahrens *acl_args = new_acl_args; 516789Sahrens 517789Sahrens return (0); 518789Sahrens } 519789Sahrens 5200Sstevel@tonic-gate /* 5210Sstevel@tonic-gate * This function is called whenever the group permissions of a file 5220Sstevel@tonic-gate * is being modified. According to the chmod(1) manpage, any 5230Sstevel@tonic-gate * change made to the group permissions must be applied to both 5240Sstevel@tonic-gate * the acl mask and the acl's GROUP_OBJ. The chmod(2) already 5250Sstevel@tonic-gate * set the mask, so this routine needs to make the same change 5260Sstevel@tonic-gate * to the GROUP_OBJ. 5270Sstevel@tonic-gate */ 5280Sstevel@tonic-gate static void 5290Sstevel@tonic-gate handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits) 5300Sstevel@tonic-gate { 5310Sstevel@tonic-gate int aclcnt, n; 5320Sstevel@tonic-gate aclent_t *aclp, *tp; 5330Sstevel@tonic-gate o_mode_t newperm; 534789Sahrens /* 535789Sahrens * if this file system support ace_t acl's 536789Sahrens * then simply return since we don't have an 537789Sahrens * acl mask to deal with 538789Sahrens */ 539789Sahrens if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED) 540789Sahrens return; 5410Sstevel@tonic-gate if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES) 5420Sstevel@tonic-gate return; /* it's just a trivial acl; no need to change it */ 5430Sstevel@tonic-gate if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt)) 5440Sstevel@tonic-gate == NULL) { 5450Sstevel@tonic-gate perror("chmod"); 5460Sstevel@tonic-gate exit(2); 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate if (acl(name, GETACL, aclcnt, aclp) < 0) { 5500Sstevel@tonic-gate free(aclp); 5510Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 5520Sstevel@tonic-gate perror(name); 5530Sstevel@tonic-gate return; 5540Sstevel@tonic-gate } 5550Sstevel@tonic-gate for (tp = aclp, n = aclcnt; n--; tp++) { 5560Sstevel@tonic-gate if (tp->a_type == GROUP_OBJ) { 5570Sstevel@tonic-gate newperm = tp->a_perm; 5580Sstevel@tonic-gate if (group_clear_bits != 0) 5590Sstevel@tonic-gate newperm &= ~group_clear_bits; 5600Sstevel@tonic-gate if (group_set_bits != 0) 5610Sstevel@tonic-gate newperm |= group_set_bits; 5620Sstevel@tonic-gate if (newperm != tp->a_perm) { 5630Sstevel@tonic-gate tp->a_perm = newperm; 5640Sstevel@tonic-gate if (acl(name, SETACL, aclcnt, aclp) 5650Sstevel@tonic-gate < 0) { 5660Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 5670Sstevel@tonic-gate perror(name); 5680Sstevel@tonic-gate } 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate break; 5710Sstevel@tonic-gate } 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate free(aclp); 5740Sstevel@tonic-gate } 575789Sahrens 576789Sahrens static int 577789Sahrens doacl(char *file, struct stat *st, acl_args_t *acl_args) 578789Sahrens { 579789Sahrens acl_t *aclp; 580789Sahrens acl_t *set_aclp; 581789Sahrens int error = 0; 582789Sahrens void *to, *from; 583789Sahrens int len; 584789Sahrens int isdir; 585789Sahrens isdir = S_ISDIR(st->st_mode); 586789Sahrens 587789Sahrens error = acl_get(file, 0, &aclp); 588789Sahrens 589789Sahrens if (error != 0) { 590789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 591789Sahrens return (1); 592789Sahrens } 593789Sahrens switch (acl_args->acl_action) { 594789Sahrens case ACL_ADD: 595789Sahrens if ((error = acl_addentries(aclp, 596789Sahrens acl_args->acl_aclp, acl_args->acl_slot)) != 0) { 597789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 598789Sahrens acl_free(aclp); 599789Sahrens return (1); 600789Sahrens } 601789Sahrens set_aclp = aclp; 602789Sahrens break; 603789Sahrens case ACL_SLOT_DELETE: 604789Sahrens if (acl_args->acl_slot + 1 > aclp->acl_cnt) { 605789Sahrens errmsg(1, 1, 606789Sahrens gettext("Invalid slot specified for removal\n")); 607789Sahrens acl_free(aclp); 608789Sahrens return (1); 609789Sahrens } 610789Sahrens 611789Sahrens if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) { 612789Sahrens errmsg(1, 1, 613789Sahrens gettext("Can't remove all ACL " 614789Sahrens "entries from a file\n")); 615789Sahrens acl_free(aclp); 616789Sahrens return (1); 617789Sahrens } 618789Sahrens 619789Sahrens /* 620789Sahrens * remove a single entry 621789Sahrens * 622789Sahrens * if last entry just adjust acl_cnt 623789Sahrens */ 624789Sahrens 625789Sahrens if ((acl_args->acl_slot + 1) == aclp->acl_cnt) 626789Sahrens aclp->acl_cnt--; 627789Sahrens else { 628789Sahrens to = (char *)aclp->acl_aclp + 629789Sahrens (acl_args->acl_slot * aclp->acl_entry_size); 630789Sahrens from = (char *)to + aclp->acl_entry_size; 631789Sahrens len = (aclp->acl_cnt - acl_args->acl_slot - 1) * 632789Sahrens aclp->acl_entry_size; 633789Sahrens (void) memmove(to, from, len); 634789Sahrens aclp->acl_cnt--; 635789Sahrens } 636789Sahrens set_aclp = aclp; 637789Sahrens break; 638789Sahrens 639789Sahrens case ACL_DELETE: 640789Sahrens if ((error = acl_removeentries(aclp, acl_args->acl_aclp, 641789Sahrens acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) { 642789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 643789Sahrens acl_free(aclp); 644789Sahrens return (1); 645789Sahrens } 646789Sahrens 647789Sahrens if (aclp->acl_cnt == 0) { 648789Sahrens errmsg(1, 1, 649789Sahrens gettext("Can't remove all ACL " 650789Sahrens "entries from a file\n")); 651789Sahrens acl_free(aclp); 652789Sahrens return (1); 653789Sahrens } 654789Sahrens 655789Sahrens set_aclp = aclp; 656789Sahrens break; 657789Sahrens case ACL_REPLACE: 658789Sahrens if (acl_args->acl_slot >= 0) { 659789Sahrens error = acl_modifyentries(aclp, acl_args->acl_aclp, 660789Sahrens acl_args->acl_slot); 661789Sahrens if (error) { 662789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 663789Sahrens acl_free(aclp); 664789Sahrens return (1); 665789Sahrens } 666789Sahrens set_aclp = aclp; 667789Sahrens } else { 668789Sahrens set_aclp = acl_args->acl_aclp; 669789Sahrens } 670789Sahrens break; 671789Sahrens case ACL_STRIP: 672789Sahrens error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode); 673789Sahrens if (error) { 674789Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 675789Sahrens return (1); 676789Sahrens } 677789Sahrens acl_free(aclp); 678789Sahrens return (0); 679789Sahrens /*NOTREACHED*/ 680789Sahrens default: 681789Sahrens errmsg(1, 0, gettext("Unknown ACL action requested\n")); 682789Sahrens return (1); 683789Sahrens break; 684789Sahrens } 685789Sahrens error = acl_check(set_aclp, isdir); 686789Sahrens 687789Sahrens if (error) { 688789Sahrens errmsg(1, 0, "%s\n%s", acl_strerror(error), 689789Sahrens gettext("See chmod(1) for more information on " 690789Sahrens "valid ACL syntax\n")); 691789Sahrens return (1); 692789Sahrens } 693789Sahrens if ((error = acl_set(file, set_aclp)) != 0) { 694789Sahrens errmsg(1, 0, gettext("Failed to set ACL: %s\n"), 695789Sahrens acl_strerror(error)); 696789Sahrens acl_free(aclp); 697789Sahrens return (1); 698789Sahrens } 699789Sahrens acl_free(aclp); 700789Sahrens return (0); 701789Sahrens } 702