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 /* 22*401f278dSas145665 * 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> 477c478bd9Sstevel@tonic-gate * option is -R and -f 487c478bd9Sstevel@tonic-gate */ 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate /* 517c478bd9Sstevel@tonic-gate * Note that many convolutions are necessary 527c478bd9Sstevel@tonic-gate * due to the re-use of bits between locking 537c478bd9Sstevel@tonic-gate * and setgid 547c478bd9Sstevel@tonic-gate */ 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate #include <unistd.h> 577c478bd9Sstevel@tonic-gate #include <stdlib.h> 587c478bd9Sstevel@tonic-gate #include <stdio.h> 597c478bd9Sstevel@tonic-gate #include <sys/types.h> 607c478bd9Sstevel@tonic-gate #include <sys/stat.h> 617c478bd9Sstevel@tonic-gate #include <dirent.h> 627c478bd9Sstevel@tonic-gate #include <locale.h> 637c478bd9Sstevel@tonic-gate #include <string.h> /* strerror() */ 647c478bd9Sstevel@tonic-gate #include <stdarg.h> 657c478bd9Sstevel@tonic-gate #include <limits.h> 66fa9e4066Sahrens #include <ctype.h> 677c478bd9Sstevel@tonic-gate #include <errno.h> 687c478bd9Sstevel@tonic-gate #include <sys/acl.h> 69fa9e4066Sahrens #include <aclutils.h> 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate static int rflag; 727c478bd9Sstevel@tonic-gate static int fflag; 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate extern int optind; 757c478bd9Sstevel@tonic-gate extern int errno; 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate static int mac; /* Alternate to argc (for parseargs) */ 787c478bd9Sstevel@tonic-gate static char **mav; /* Alternate to argv (for parseargs) */ 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate static char *ms; /* Points to the mode argument */ 817c478bd9Sstevel@tonic-gate 82fa9e4066Sahrens #define ACL_ADD 1 83fa9e4066Sahrens #define ACL_DELETE 2 84fa9e4066Sahrens #define ACL_SLOT_DELETE 3 85fa9e4066Sahrens #define ACL_REPLACE 4 86fa9e4066Sahrens #define ACL_STRIP 5 87fa9e4066Sahrens 88fa9e4066Sahrens typedef struct acl_args { 89fa9e4066Sahrens acl_t *acl_aclp; 90fa9e4066Sahrens int acl_slot; 91fa9e4066Sahrens int acl_action; 92fa9e4066Sahrens } acl_args_t; 93fa9e4066Sahrens 947c478bd9Sstevel@tonic-gate extern mode_t 957c478bd9Sstevel@tonic-gate newmode_common(char *ms, mode_t new_mode, mode_t umsk, char *file, char *path, 967c478bd9Sstevel@tonic-gate o_mode_t *group_clear_bits, o_mode_t *group_set_bits); 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate static int 99fa9e4066Sahrens dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp), 100fa9e4066Sahrens chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp); 101fa9e4066Sahrens static int doacl(char *file, struct stat *st, acl_args_t *aclp); 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate static void handle_acl(char *name, o_mode_t group_clear_bits, 1047c478bd9Sstevel@tonic-gate o_mode_t group_set_bits); 1057c478bd9Sstevel@tonic-gate 106fa9e4066Sahrens static void usage(void); 1077c478bd9Sstevel@tonic-gate 108fa9e4066Sahrens void errmsg(int severity, int code, char *format, ...); 1097c478bd9Sstevel@tonic-gate 110fa9e4066Sahrens static void parseargs(int ac, char *av[]); 111fa9e4066Sahrens 112fa9e4066Sahrens int 113fa9e4066Sahrens parse_acl_args(char *arg, acl_args_t **acl_args); 1147c478bd9Sstevel@tonic-gate 1157c478bd9Sstevel@tonic-gate int 1167c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 1177c478bd9Sstevel@tonic-gate { 1187c478bd9Sstevel@tonic-gate int i, c; 1197c478bd9Sstevel@tonic-gate int status = 0; 1207c478bd9Sstevel@tonic-gate mode_t umsk; 121fa9e4066Sahrens acl_args_t *acl_args = NULL; 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 1247c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 1257c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 1267c478bd9Sstevel@tonic-gate #endif 1277c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate parseargs(argc, argv); 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate while ((c = getopt(mac, mav, "Rf")) != EOF) { 1327c478bd9Sstevel@tonic-gate switch (c) { 1337c478bd9Sstevel@tonic-gate case 'R': 1347c478bd9Sstevel@tonic-gate rflag++; 1357c478bd9Sstevel@tonic-gate break; 1367c478bd9Sstevel@tonic-gate case 'f': 1377c478bd9Sstevel@tonic-gate fflag++; 1387c478bd9Sstevel@tonic-gate break; 1397c478bd9Sstevel@tonic-gate case '?': 1407c478bd9Sstevel@tonic-gate usage(); 1417c478bd9Sstevel@tonic-gate exit(2); 1427c478bd9Sstevel@tonic-gate } 1437c478bd9Sstevel@tonic-gate } 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate /* 1467c478bd9Sstevel@tonic-gate * Check for sufficient arguments 1477c478bd9Sstevel@tonic-gate * or a usage error. 1487c478bd9Sstevel@tonic-gate */ 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate mac -= optind; 1517c478bd9Sstevel@tonic-gate mav += optind; 152fa9e4066Sahrens if (mac >= 2 && (mav[0][0] == 'A')) { 153fa9e4066Sahrens if (parse_acl_args(*mav, &acl_args)) { 154fa9e4066Sahrens usage(); 155fa9e4066Sahrens exit(2); 156fa9e4066Sahrens } 157fa9e4066Sahrens } else { 1587c478bd9Sstevel@tonic-gate if (mac < 2) { 1597c478bd9Sstevel@tonic-gate usage(); 1607c478bd9Sstevel@tonic-gate exit(2); 1617c478bd9Sstevel@tonic-gate } 162fa9e4066Sahrens } 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate ms = mav[0]; 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate umsk = umask(0); 1677c478bd9Sstevel@tonic-gate (void) umask(umsk); 1687c478bd9Sstevel@tonic-gate 169fa9e4066Sahrens for (i = 1; i < mac; i++) { 170fa9e4066Sahrens status += dochmod(mav[i], mav[i], umsk, acl_args); 171fa9e4066Sahrens } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate return (fflag ? 0 : status); 1747c478bd9Sstevel@tonic-gate } 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate static int 177fa9e4066Sahrens dochmod(char *name, char *path, mode_t umsk, acl_args_t *aclp) 1787c478bd9Sstevel@tonic-gate { 1797c478bd9Sstevel@tonic-gate static struct stat st; 1807c478bd9Sstevel@tonic-gate int linkflg = 0; 1817c478bd9Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate if (lstat(name, &st) < 0) { 1847c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 1857c478bd9Sstevel@tonic-gate return (1); 1867c478bd9Sstevel@tonic-gate } 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFLNK) { 1897c478bd9Sstevel@tonic-gate linkflg = 1; 1907c478bd9Sstevel@tonic-gate if (stat(name, &st) < 0) { 1917c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't access %s\n"), path); 1927c478bd9Sstevel@tonic-gate return (1); 1937c478bd9Sstevel@tonic-gate } 1947c478bd9Sstevel@tonic-gate } 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate /* Do not recurse if directory is object of symbolic link */ 1977c478bd9Sstevel@tonic-gate if (rflag && ((st.st_mode & S_IFMT) == S_IFDIR) && !linkflg) 198fa9e4066Sahrens return (chmodr(name, path, st.st_mode, umsk, aclp)); 1997c478bd9Sstevel@tonic-gate 200fa9e4066Sahrens if (aclp) { 201fa9e4066Sahrens return (doacl(name, &st, aclp)); 202fa9e4066Sahrens } else if (chmod(name, newmode_common(ms, st.st_mode, umsk, name, path, 2037c478bd9Sstevel@tonic-gate &group_clear_bits, &group_set_bits)) == -1) { 2047c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't change %s\n"), path); 2057c478bd9Sstevel@tonic-gate return (1); 2067c478bd9Sstevel@tonic-gate } 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate /* 2097c478bd9Sstevel@tonic-gate * If the group permissions of the file are being modified, 2107c478bd9Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 2117c478bd9Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 2127c478bd9Sstevel@tonic-gate * permissions changes to both the acl mask and the 2137c478bd9Sstevel@tonic-gate * general group permissions. 2147c478bd9Sstevel@tonic-gate */ 2157c478bd9Sstevel@tonic-gate if (group_clear_bits || group_set_bits) 2167c478bd9Sstevel@tonic-gate handle_acl(name, group_clear_bits, group_set_bits); 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate return (0); 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate static int 223fa9e4066Sahrens chmodr(char *dir, char *path, mode_t mode, mode_t umsk, acl_args_t *aclp) 2247c478bd9Sstevel@tonic-gate { 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate DIR *dirp; 2277c478bd9Sstevel@tonic-gate struct dirent *dp; 2287c478bd9Sstevel@tonic-gate char savedir[PATH_MAX]; /* dir name to restore */ 2297c478bd9Sstevel@tonic-gate char currdir[PATH_MAX+1]; /* current dir name + '/' */ 2307c478bd9Sstevel@tonic-gate char parentdir[PATH_MAX+1]; /* parent dir name + '/' */ 2317c478bd9Sstevel@tonic-gate int ecode; 232fa9e4066Sahrens struct stat st; 2337c478bd9Sstevel@tonic-gate o_mode_t group_clear_bits, group_set_bits; 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate if (getcwd(savedir, PATH_MAX) == 0) 2367c478bd9Sstevel@tonic-gate errmsg(2, 255, gettext("chmod: could not getcwd %s\n"), 2377c478bd9Sstevel@tonic-gate savedir); 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate /* 2407c478bd9Sstevel@tonic-gate * Change what we are given before doing it's contents 2417c478bd9Sstevel@tonic-gate */ 242fa9e4066Sahrens if (aclp) { 243fa9e4066Sahrens if (lstat(dir, &st) < 0) { 244fa9e4066Sahrens errmsg(2, 0, gettext("can't access %s\n"), path); 245fa9e4066Sahrens return (1); 246fa9e4066Sahrens } 247fa9e4066Sahrens if (doacl(dir, &st, aclp) != 0) 248fa9e4066Sahrens return (1); 249fa9e4066Sahrens } else if (chmod(dir, newmode_common(ms, mode, umsk, dir, path, 2507c478bd9Sstevel@tonic-gate &group_clear_bits, &group_set_bits)) < 0) { 2517c478bd9Sstevel@tonic-gate errmsg(2, 0, gettext("can't change %s\n"), path); 2527c478bd9Sstevel@tonic-gate return (1); 2537c478bd9Sstevel@tonic-gate } 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate /* 2567c478bd9Sstevel@tonic-gate * If the group permissions of the file are being modified, 2577c478bd9Sstevel@tonic-gate * make sure that the file's ACL (if it has one) is 2587c478bd9Sstevel@tonic-gate * modified also, since chmod is supposed to apply group 2597c478bd9Sstevel@tonic-gate * permissions changes to both the acl mask and the 2607c478bd9Sstevel@tonic-gate * general group permissions. 2617c478bd9Sstevel@tonic-gate */ 262fa9e4066Sahrens 263fa9e4066Sahrens if (aclp == NULL) { /* only necessary when not setting ACL */ 2647c478bd9Sstevel@tonic-gate if (group_clear_bits || group_set_bits) 2657c478bd9Sstevel@tonic-gate handle_acl(dir, group_clear_bits, group_set_bits); 266fa9e4066Sahrens } 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate if (chdir(dir) < 0) { 2697c478bd9Sstevel@tonic-gate errmsg(2, 0, "%s/%s: %s\n", savedir, dir, strerror(errno)); 2707c478bd9Sstevel@tonic-gate return (1); 2717c478bd9Sstevel@tonic-gate } 2727c478bd9Sstevel@tonic-gate if ((dirp = opendir(".")) == NULL) { 2737c478bd9Sstevel@tonic-gate errmsg(2, 0, "%s\n", strerror(errno)); 2747c478bd9Sstevel@tonic-gate return (1); 2757c478bd9Sstevel@tonic-gate } 2767c478bd9Sstevel@tonic-gate ecode = 0; 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate /* 2797c478bd9Sstevel@tonic-gate * Save parent directory path before recursive chmod. 2807c478bd9Sstevel@tonic-gate * We'll need this for error printing purposes. Add 2817c478bd9Sstevel@tonic-gate * a trailing '/' to the path except in the case where 2827c478bd9Sstevel@tonic-gate * the path is just '/' 2837c478bd9Sstevel@tonic-gate */ 2847c478bd9Sstevel@tonic-gate 285*401f278dSas145665 if (strlcpy(parentdir, path, PATH_MAX + 1) >= PATH_MAX + 1) { 286*401f278dSas145665 errmsg(2, 0, gettext("directory path name too long: %s\n"), 287*401f278dSas145665 path); 288*401f278dSas145665 return (1); 289*401f278dSas145665 } 2907c478bd9Sstevel@tonic-gate if (strcmp(path, "/") != 0) 291*401f278dSas145665 if (strlcat(parentdir, "/", PATH_MAX + 1) >= PATH_MAX + 1) { 292*401f278dSas145665 errmsg(2, 0, 293*401f278dSas145665 gettext("directory path name too long: %s/\n"), 294*401f278dSas145665 parentdir); 295*401f278dSas145665 return (1); 296*401f278dSas145665 } 297*401f278dSas145665 2987c478bd9Sstevel@tonic-gate 2997c478bd9Sstevel@tonic-gate for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 300*401f278dSas145665 301c4518760Scasper if (strcmp(dp->d_name, ".") == 0 || /* skip . and .. */ 302c4518760Scasper strcmp(dp->d_name, "..") == 0) { 303c4518760Scasper continue; 304c4518760Scasper } 305*401f278dSas145665 if (strlcpy(currdir, parentdir, PATH_MAX + 1) >= PATH_MAX + 1) { 306*401f278dSas145665 errmsg(2, 0, 307*401f278dSas145665 gettext("directory path name too long: %s\n"), 308*401f278dSas145665 parentdir); 309*401f278dSas145665 return (1); 310*401f278dSas145665 } 311*401f278dSas145665 if (strlcat(currdir, dp->d_name, PATH_MAX + 1) 312*401f278dSas145665 >= PATH_MAX + 1) { 313*401f278dSas145665 errmsg(2, 0, 314*401f278dSas145665 gettext("directory path name too long: %s%s\n"), 315*401f278dSas145665 currdir, dp->d_name); 316*401f278dSas145665 return (1); 317*401f278dSas145665 } 318fa9e4066Sahrens ecode += dochmod(dp->d_name, currdir, umsk, aclp); 3197c478bd9Sstevel@tonic-gate } 3207c478bd9Sstevel@tonic-gate (void) closedir(dirp); 3217c478bd9Sstevel@tonic-gate if (chdir(savedir) < 0) { 3227c478bd9Sstevel@tonic-gate errmsg(2, 255, gettext("can't change back to %s\n"), savedir); 3237c478bd9Sstevel@tonic-gate } 3247c478bd9Sstevel@tonic-gate return (ecode ? 1 : 0); 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate /* PRINTFLIKE3 */ 3287c478bd9Sstevel@tonic-gate void 3297c478bd9Sstevel@tonic-gate errmsg(int severity, int code, char *format, ...) 3307c478bd9Sstevel@tonic-gate { 3317c478bd9Sstevel@tonic-gate va_list ap; 3327c478bd9Sstevel@tonic-gate static char *msg[] = { 3337c478bd9Sstevel@tonic-gate "", 3347c478bd9Sstevel@tonic-gate "ERROR", 3357c478bd9Sstevel@tonic-gate "WARNING", 3367c478bd9Sstevel@tonic-gate "" 3377c478bd9Sstevel@tonic-gate }; 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate va_start(ap, format); 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate /* 3427c478bd9Sstevel@tonic-gate * Always print error message if this is a fatal error (code == 0); 3437c478bd9Sstevel@tonic-gate * otherwise, print message if fflag == 0 (no -f option specified) 3447c478bd9Sstevel@tonic-gate */ 3457c478bd9Sstevel@tonic-gate if (!fflag || (code != 0)) { 3467c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 3477c478bd9Sstevel@tonic-gate "chmod: %s: ", gettext(msg[severity])); 3487c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, ap); 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate 3517c478bd9Sstevel@tonic-gate va_end(ap); 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate if (code != 0) 3547c478bd9Sstevel@tonic-gate exit(fflag ? 0 : code); 3557c478bd9Sstevel@tonic-gate } 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate static void 3587c478bd9Sstevel@tonic-gate usage(void) 3597c478bd9Sstevel@tonic-gate { 3607c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 3617c478bd9Sstevel@tonic-gate "usage:\tchmod [-fR] <absolute-mode> file ...\n")); 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 364fa9e4066Sahrens "\tchmod [-fR] <ACL-operation> file ...\n")); 365fa9e4066Sahrens 366fa9e4066Sahrens (void) fprintf(stderr, gettext( 3677c478bd9Sstevel@tonic-gate "\tchmod [-fR] <symbolic-mode-list> file ...\n")); 3687c478bd9Sstevel@tonic-gate 369fa9e4066Sahrens 3707c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 3717c478bd9Sstevel@tonic-gate "where \t<symbolic-mode-list> is a comma-separated list of\n")); 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext( 3747c478bd9Sstevel@tonic-gate "\t[ugoa]{+|-|=}[rwxXlstugo]\n")); 375fa9e4066Sahrens 376fa9e4066Sahrens (void) fprintf(stderr, gettext( 377fa9e4066Sahrens "where \t<ACL-operation> is one of the following\n")); 378fa9e4066Sahrens (void) fprintf(stderr, gettext("\tA-<acl_specification>\n")); 379fa9e4066Sahrens (void) fprintf(stderr, gettext("\tA[number]-\n")); 380fa9e4066Sahrens (void) fprintf(stderr, gettext( 381fa9e4066Sahrens "\tA[number]{+|=}<acl_specification>\n")); 382fa9e4066Sahrens (void) fprintf(stderr, gettext( 383fa9e4066Sahrens "where \t<acl-specification> is a comma-separated list of ACEs\n")); 3847c478bd9Sstevel@tonic-gate } 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate /* 3877c478bd9Sstevel@tonic-gate * parseargs - generate getopt-friendly argument list for backwards 3887c478bd9Sstevel@tonic-gate * compatibility with earlier Solaris usage (eg, chmod -w 3897c478bd9Sstevel@tonic-gate * foo). 3907c478bd9Sstevel@tonic-gate * 3917c478bd9Sstevel@tonic-gate * assumes the existence of a static set of alternates to argc and argv, 3927c478bd9Sstevel@tonic-gate * (namely, mac, and mav[]). 3937c478bd9Sstevel@tonic-gate * 3947c478bd9Sstevel@tonic-gate */ 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate static void 3977c478bd9Sstevel@tonic-gate parseargs(int ac, char *av[]) 3987c478bd9Sstevel@tonic-gate { 3997c478bd9Sstevel@tonic-gate int i; /* current argument */ 4007c478bd9Sstevel@tonic-gate int fflag; /* arg list contains "--" */ 4017c478bd9Sstevel@tonic-gate size_t mav_num; /* number of entries in mav[] */ 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate /* 4047c478bd9Sstevel@tonic-gate * We add an extra argument slot, in case we need to jam a "--" 4057c478bd9Sstevel@tonic-gate * argument into the list. 4067c478bd9Sstevel@tonic-gate */ 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate mav_num = (size_t)ac+2; 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate if ((mav = calloc(mav_num, sizeof (char *))) == NULL) { 4117c478bd9Sstevel@tonic-gate perror("chmod"); 4127c478bd9Sstevel@tonic-gate exit(2); 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate /* scan for the use of "--" in the argument list */ 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate for (fflag = i = 0; i < ac; i ++) { 4187c478bd9Sstevel@tonic-gate if (strcmp(av[i], "--") == 0) 4197c478bd9Sstevel@tonic-gate fflag = 1; 4207c478bd9Sstevel@tonic-gate } 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate /* process the arguments */ 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate for (i = mac = 0; 4257c478bd9Sstevel@tonic-gate (av[i] != (char *)NULL) && (av[i][0] != (char)NULL); 4267c478bd9Sstevel@tonic-gate i++) { 4277c478bd9Sstevel@tonic-gate if (!fflag && av[i][0] == '-') { 4287c478bd9Sstevel@tonic-gate /* 4297c478bd9Sstevel@tonic-gate * If there is not already a "--" argument specified, 4307c478bd9Sstevel@tonic-gate * and the argument starts with '-' but does not 4317c478bd9Sstevel@tonic-gate * contain any of the official option letters, then it 4327c478bd9Sstevel@tonic-gate * is probably a mode argument beginning with '-'. 4337c478bd9Sstevel@tonic-gate * Force a "--" into the argument stream in front of 4347c478bd9Sstevel@tonic-gate * it. 4357c478bd9Sstevel@tonic-gate */ 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate if ((strchr(av[i], 'R') == NULL && 4387c478bd9Sstevel@tonic-gate strchr(av[i], 'f') == NULL)) { 4397c478bd9Sstevel@tonic-gate mav[mac++] = strdup("--"); 4407c478bd9Sstevel@tonic-gate } 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate mav[mac++] = strdup(av[i]); 4447c478bd9Sstevel@tonic-gate } 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate mav[mac] = (char *)NULL; 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate 449fa9e4066Sahrens int 450fa9e4066Sahrens parse_acl_args(char *arg, acl_args_t **acl_args) 451fa9e4066Sahrens { 452fa9e4066Sahrens acl_t *new_acl = NULL; 453fa9e4066Sahrens int slot; 454fa9e4066Sahrens int len; 455fa9e4066Sahrens int action; 456fa9e4066Sahrens acl_args_t *new_acl_args; 457fa9e4066Sahrens char *acl_spec = NULL; 458fa9e4066Sahrens char *end; 459fa9e4066Sahrens 460fa9e4066Sahrens if (arg[0] != 'A') 461fa9e4066Sahrens return (1); 462fa9e4066Sahrens 463fa9e4066Sahrens slot = strtol(&arg[1], &end, 10); 464fa9e4066Sahrens 465fa9e4066Sahrens len = strlen(arg); 466fa9e4066Sahrens switch (*end) { 467fa9e4066Sahrens case '+': 468fa9e4066Sahrens action = ACL_ADD; 469fa9e4066Sahrens acl_spec = ++end; 470fa9e4066Sahrens break; 471fa9e4066Sahrens case '-': 472fa9e4066Sahrens if (len == 2 && arg[0] == 'A' && arg[1] == '-') 473fa9e4066Sahrens action = ACL_STRIP; 474fa9e4066Sahrens else 475fa9e4066Sahrens action = ACL_DELETE; 476fa9e4066Sahrens if (action != ACL_STRIP) { 477fa9e4066Sahrens acl_spec = ++end; 478fa9e4066Sahrens if (acl_spec[0] == '\0') { 479fa9e4066Sahrens action = ACL_SLOT_DELETE; 480fa9e4066Sahrens acl_spec = NULL; 481fa9e4066Sahrens } else if (arg[1] != '-') 482fa9e4066Sahrens return (1); 483fa9e4066Sahrens } 484fa9e4066Sahrens break; 485fa9e4066Sahrens case '=': 48655601ddbSmarks /* 48755601ddbSmarks * Was slot specified? 48855601ddbSmarks */ 48955601ddbSmarks if (arg[1] == '=') 49055601ddbSmarks slot = -1; 491fa9e4066Sahrens action = ACL_REPLACE; 492fa9e4066Sahrens acl_spec = ++end; 493fa9e4066Sahrens break; 494fa9e4066Sahrens default: 495fa9e4066Sahrens return (1); 496fa9e4066Sahrens } 497fa9e4066Sahrens 498fa9e4066Sahrens if ((action == ACL_REPLACE || action == ACL_ADD) && acl_spec[0] == '\0') 499fa9e4066Sahrens return (1); 500fa9e4066Sahrens 501fa9e4066Sahrens if (acl_spec) { 5025a5eeccaSmarks if (acl_parse(acl_spec, &new_acl)) { 5035a5eeccaSmarks exit(1); 504fa9e4066Sahrens } 505fa9e4066Sahrens } 506fa9e4066Sahrens 507fa9e4066Sahrens new_acl_args = malloc(sizeof (acl_args_t)); 508fa9e4066Sahrens if (new_acl_args == NULL) 509fa9e4066Sahrens return (1); 510fa9e4066Sahrens 511fa9e4066Sahrens new_acl_args->acl_aclp = new_acl; 512fa9e4066Sahrens new_acl_args->acl_slot = slot; 513fa9e4066Sahrens new_acl_args->acl_action = action; 514fa9e4066Sahrens 515fa9e4066Sahrens *acl_args = new_acl_args; 516fa9e4066Sahrens 517fa9e4066Sahrens return (0); 518fa9e4066Sahrens } 519fa9e4066Sahrens 5207c478bd9Sstevel@tonic-gate /* 5217c478bd9Sstevel@tonic-gate * This function is called whenever the group permissions of a file 5227c478bd9Sstevel@tonic-gate * is being modified. According to the chmod(1) manpage, any 5237c478bd9Sstevel@tonic-gate * change made to the group permissions must be applied to both 5247c478bd9Sstevel@tonic-gate * the acl mask and the acl's GROUP_OBJ. The chmod(2) already 5257c478bd9Sstevel@tonic-gate * set the mask, so this routine needs to make the same change 5267c478bd9Sstevel@tonic-gate * to the GROUP_OBJ. 5277c478bd9Sstevel@tonic-gate */ 5287c478bd9Sstevel@tonic-gate static void 5297c478bd9Sstevel@tonic-gate handle_acl(char *name, o_mode_t group_clear_bits, o_mode_t group_set_bits) 5307c478bd9Sstevel@tonic-gate { 5317c478bd9Sstevel@tonic-gate int aclcnt, n; 5327c478bd9Sstevel@tonic-gate aclent_t *aclp, *tp; 5337c478bd9Sstevel@tonic-gate o_mode_t newperm; 534fa9e4066Sahrens /* 535fa9e4066Sahrens * if this file system support ace_t acl's 536fa9e4066Sahrens * then simply return since we don't have an 537fa9e4066Sahrens * acl mask to deal with 538fa9e4066Sahrens */ 539fa9e4066Sahrens if (pathconf(name, _PC_ACL_ENABLED) == _ACL_ACE_ENABLED) 540fa9e4066Sahrens return; 5417c478bd9Sstevel@tonic-gate if ((aclcnt = acl(name, GETACLCNT, 0, NULL)) <= MIN_ACL_ENTRIES) 5427c478bd9Sstevel@tonic-gate return; /* it's just a trivial acl; no need to change it */ 5437c478bd9Sstevel@tonic-gate if ((aclp = (aclent_t *)malloc((sizeof (aclent_t)) * aclcnt)) 5447c478bd9Sstevel@tonic-gate == NULL) { 5457c478bd9Sstevel@tonic-gate perror("chmod"); 5467c478bd9Sstevel@tonic-gate exit(2); 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate if (acl(name, GETACL, aclcnt, aclp) < 0) { 5507c478bd9Sstevel@tonic-gate free(aclp); 5517c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 5527c478bd9Sstevel@tonic-gate perror(name); 5537c478bd9Sstevel@tonic-gate return; 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate for (tp = aclp, n = aclcnt; n--; tp++) { 5567c478bd9Sstevel@tonic-gate if (tp->a_type == GROUP_OBJ) { 5577c478bd9Sstevel@tonic-gate newperm = tp->a_perm; 5587c478bd9Sstevel@tonic-gate if (group_clear_bits != 0) 5597c478bd9Sstevel@tonic-gate newperm &= ~group_clear_bits; 5607c478bd9Sstevel@tonic-gate if (group_set_bits != 0) 5617c478bd9Sstevel@tonic-gate newperm |= group_set_bits; 5627c478bd9Sstevel@tonic-gate if (newperm != tp->a_perm) { 5637c478bd9Sstevel@tonic-gate tp->a_perm = newperm; 5647c478bd9Sstevel@tonic-gate if (acl(name, SETACL, aclcnt, aclp) 5657c478bd9Sstevel@tonic-gate < 0) { 5667c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "chmod: "); 5677c478bd9Sstevel@tonic-gate perror(name); 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate } 5707c478bd9Sstevel@tonic-gate break; 5717c478bd9Sstevel@tonic-gate } 5727c478bd9Sstevel@tonic-gate } 5737c478bd9Sstevel@tonic-gate free(aclp); 5747c478bd9Sstevel@tonic-gate } 575fa9e4066Sahrens 576fa9e4066Sahrens static int 577fa9e4066Sahrens doacl(char *file, struct stat *st, acl_args_t *acl_args) 578fa9e4066Sahrens { 579fa9e4066Sahrens acl_t *aclp; 580fa9e4066Sahrens acl_t *set_aclp; 581fa9e4066Sahrens int error = 0; 582fa9e4066Sahrens void *to, *from; 583fa9e4066Sahrens int len; 584fa9e4066Sahrens int isdir; 585fa9e4066Sahrens isdir = S_ISDIR(st->st_mode); 586fa9e4066Sahrens 587fa9e4066Sahrens error = acl_get(file, 0, &aclp); 588fa9e4066Sahrens 589fa9e4066Sahrens if (error != 0) { 590fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 591fa9e4066Sahrens return (1); 592fa9e4066Sahrens } 593fa9e4066Sahrens switch (acl_args->acl_action) { 594fa9e4066Sahrens case ACL_ADD: 595fa9e4066Sahrens if ((error = acl_addentries(aclp, 596fa9e4066Sahrens acl_args->acl_aclp, acl_args->acl_slot)) != 0) { 597fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 598fa9e4066Sahrens acl_free(aclp); 599fa9e4066Sahrens return (1); 600fa9e4066Sahrens } 601fa9e4066Sahrens set_aclp = aclp; 602fa9e4066Sahrens break; 603fa9e4066Sahrens case ACL_SLOT_DELETE: 604fa9e4066Sahrens if (acl_args->acl_slot + 1 > aclp->acl_cnt) { 605fa9e4066Sahrens errmsg(1, 1, 606fa9e4066Sahrens gettext("Invalid slot specified for removal\n")); 607fa9e4066Sahrens acl_free(aclp); 608fa9e4066Sahrens return (1); 609fa9e4066Sahrens } 610fa9e4066Sahrens 611fa9e4066Sahrens if (acl_args->acl_slot == 0 && aclp->acl_cnt == 1) { 612fa9e4066Sahrens errmsg(1, 1, 613fa9e4066Sahrens gettext("Can't remove all ACL " 614fa9e4066Sahrens "entries from a file\n")); 615fa9e4066Sahrens acl_free(aclp); 616fa9e4066Sahrens return (1); 617fa9e4066Sahrens } 618fa9e4066Sahrens 619fa9e4066Sahrens /* 620fa9e4066Sahrens * remove a single entry 621fa9e4066Sahrens * 622fa9e4066Sahrens * if last entry just adjust acl_cnt 623fa9e4066Sahrens */ 624fa9e4066Sahrens 625fa9e4066Sahrens if ((acl_args->acl_slot + 1) == aclp->acl_cnt) 626fa9e4066Sahrens aclp->acl_cnt--; 627fa9e4066Sahrens else { 628fa9e4066Sahrens to = (char *)aclp->acl_aclp + 629fa9e4066Sahrens (acl_args->acl_slot * aclp->acl_entry_size); 630fa9e4066Sahrens from = (char *)to + aclp->acl_entry_size; 631fa9e4066Sahrens len = (aclp->acl_cnt - acl_args->acl_slot - 1) * 632fa9e4066Sahrens aclp->acl_entry_size; 633fa9e4066Sahrens (void) memmove(to, from, len); 634fa9e4066Sahrens aclp->acl_cnt--; 635fa9e4066Sahrens } 636fa9e4066Sahrens set_aclp = aclp; 637fa9e4066Sahrens break; 638fa9e4066Sahrens 639fa9e4066Sahrens case ACL_DELETE: 640fa9e4066Sahrens if ((error = acl_removeentries(aclp, acl_args->acl_aclp, 641fa9e4066Sahrens acl_args->acl_slot, ACL_REMOVE_ALL)) != 0) { 642fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 643fa9e4066Sahrens acl_free(aclp); 644fa9e4066Sahrens return (1); 645fa9e4066Sahrens } 646fa9e4066Sahrens 647fa9e4066Sahrens if (aclp->acl_cnt == 0) { 648fa9e4066Sahrens errmsg(1, 1, 649fa9e4066Sahrens gettext("Can't remove all ACL " 650fa9e4066Sahrens "entries from a file\n")); 651fa9e4066Sahrens acl_free(aclp); 652fa9e4066Sahrens return (1); 653fa9e4066Sahrens } 654fa9e4066Sahrens 655fa9e4066Sahrens set_aclp = aclp; 656fa9e4066Sahrens break; 657fa9e4066Sahrens case ACL_REPLACE: 658fa9e4066Sahrens if (acl_args->acl_slot >= 0) { 659fa9e4066Sahrens error = acl_modifyentries(aclp, acl_args->acl_aclp, 660fa9e4066Sahrens acl_args->acl_slot); 661fa9e4066Sahrens if (error) { 662fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 663fa9e4066Sahrens acl_free(aclp); 664fa9e4066Sahrens return (1); 665fa9e4066Sahrens } 666fa9e4066Sahrens set_aclp = aclp; 667fa9e4066Sahrens } else { 668fa9e4066Sahrens set_aclp = acl_args->acl_aclp; 669fa9e4066Sahrens } 670fa9e4066Sahrens break; 671fa9e4066Sahrens case ACL_STRIP: 672fa9e4066Sahrens error = acl_strip(file, st->st_uid, st->st_gid, st->st_mode); 673fa9e4066Sahrens if (error) { 674fa9e4066Sahrens errmsg(1, 1, "%s\n", acl_strerror(error)); 675fa9e4066Sahrens return (1); 676fa9e4066Sahrens } 677fa9e4066Sahrens acl_free(aclp); 678fa9e4066Sahrens return (0); 679fa9e4066Sahrens /*NOTREACHED*/ 680fa9e4066Sahrens default: 681fa9e4066Sahrens errmsg(1, 0, gettext("Unknown ACL action requested\n")); 682fa9e4066Sahrens return (1); 683fa9e4066Sahrens break; 684fa9e4066Sahrens } 685fa9e4066Sahrens error = acl_check(set_aclp, isdir); 686fa9e4066Sahrens 687fa9e4066Sahrens if (error) { 688fa9e4066Sahrens errmsg(1, 0, "%s\n%s", acl_strerror(error), 689fa9e4066Sahrens gettext("See chmod(1) for more information on " 690fa9e4066Sahrens "valid ACL syntax\n")); 691fa9e4066Sahrens return (1); 692fa9e4066Sahrens } 693fa9e4066Sahrens if ((error = acl_set(file, set_aclp)) != 0) { 694fa9e4066Sahrens errmsg(1, 0, gettext("Failed to set ACL: %s\n"), 695fa9e4066Sahrens acl_strerror(error)); 696fa9e4066Sahrens acl_free(aclp); 697fa9e4066Sahrens return (1); 698fa9e4066Sahrens } 699fa9e4066Sahrens acl_free(aclp); 700fa9e4066Sahrens return (0); 701fa9e4066Sahrens } 702