/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/*
* chmod option mode files
* where
* mode is [ugoa][+-=][rwxXlstugo] or an octal number
* mode is [<+|->A[# <number] ]<aclspec>
* mode is S<attrspec>
* option is -R, -f, and -@
*/
/*
* Note that many convolutions are necessary
* due to the re-use of bits between locking
* and setgid
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <dirent.h>
#include <locale.h>
#include <string.h> /* strerror() */
#include <stdarg.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#include <aclutils.h>
#include <libnvpair.h>
#include <libcmdutils.h>
#include <libgen.h>
#include <attr.h>
static int rflag;
static int fflag;
extern int optind;
extern int errno;
#define ATTR_OPTS 0
typedef struct acl_args {
int acl_slot;
int acl_action;
} acl_args_t;
typedef enum {
} chmod_sec_t;
typedef struct {
union {
} secptr;
} sec_args_t;
typedef struct attr_name {
char *name;
} attr_name_t;
static void print_attrs(int flag);
static void usage(void);
int
{
int i, c;
int status = 0;
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
switch (c) {
case 'R':
rflag++;
break;
case 'f':
fflag++;
break;
case '@':
perror("chmod");
exit(2);
}
} else {
}
break;
case '?':
usage();
exit(2);
}
}
/*
* Check for sufficient arguments
* or a usage error.
*/
}
usage();
exit(2);
}
usage();
exit(2);
/* A no-op attribute operation was specified. */
exit(0);
}
} else {
if (mac < 2) {
usage();
exit(2);
}
}
}
for (i = 1; i < mac; i++) {
}
}
static void
{
while (attrnamesptr != NULL) {
}
attrnamesptr = tptr;
}
}
static int
{
int linkflg = 0;
return (1);
}
linkflg = 1;
return (1);
}
}
/* Do not recurse if directory is object of symbolic link */
}
} else {
return (1);
}
} else {
return (1);
}
}
/*
* If the group permissions of the file are being modified,
* make sure that the file's ACL (if it has one) is
* modified also, since chmod is supposed to apply group
* permissions changes to both the acl mask and the
* general group permissions.
*/
if (group_clear_bits || group_set_bits)
return (0);
}
static int
{
int ecode;
savedir);
/*
* Change what we are given before doing it's contents
*/
return (1);
}
} else {
return (1);
}
&group_clear_bits, &group_set_bits)) < 0) {
}
/*
* If the group permissions of the file are being modified,
* make sure that the file's ACL (if it has one) is
* modified also, since chmod is supposed to apply group
* permissions changes to both the acl mask and the
* general group permissions.
*/
/* only necessary when not setting ACL or system attributes */
if (group_clear_bits || group_set_bits)
}
return (1);
}
return (1);
}
ecode = 0;
/*
* Save parent directory path before recursive chmod.
* We'll need this for error printing purposes. Add
* a trailing '/' to the path except in the case where
* the path is just '/'
*/
path);
return (1);
}
errmsg(2, 0,
gettext("directory path name too long: %s/\n"),
return (1);
}
continue;
}
errmsg(2, 0,
gettext("directory path name too long: %s\n"),
return (1);
}
>= PATH_MAX + 1) {
errmsg(2, 0,
gettext("directory path name too long: %s%s\n"),
return (1);
}
}
}
return (ecode ? 1 : 0);
}
/* PRINTFLIKE3 */
void
{
static char *msg[] = {
"",
"ERROR",
"WARNING",
""
};
/*
* Always print error message if this is a fatal error (code != 0);
* otherwise, print message if fflag == 0 (no -f option specified)
*/
}
if (code != 0)
}
static void
usage(void)
{
"usage:\tchmod [-fR] <absolute-mode> file ...\n"));
"\tchmod [-fR] [-@ attribute] ... "
"S<attribute-operation> file ...\n"));
"\tchmod [-fR] <ACL-operation> file ...\n"));
"\tchmod [-fR] <symbolic-mode-list> file ...\n\n"));
"where \t<symbolic-mode-list> is a comma-separated list of\n"));
"\t[ugoa]{+|-|=}[rwxXlstugo]\n\n"));
"where \t<attribute-operation> is a comma-separated list of\n"
"\tone or more of the following\n"));
"\t[+|-|=]c[<compact-attribute-list>|{<compact-attribute-list>}]\n"
"\t[+|-|=]v[<verbose-attribute-setting>|"
"\'{\'<verbose-attribute-setting-list>\'}\']\n"
"\t[+|-|=]a\n"));
"where \t<compact-attribute-list> is a list of zero or more of\n"));
"where \t<verbose-attribute-setting> is one of\n"));
"\tand can be, optionally, immediately preceded by \"no\"\n\n"));
"where \t<ACL-operation> is one of the following\n"));
"\tA[number]{+|=}<acl_specification>\n"));
"where \t<acl-specification> is a comma-separated list of ACEs\n"));
}
/*
* parseargs - generate getopt-friendly argument list for backwards
* compatibility with earlier Solaris usage (eg, chmod -w
* foo).
*
* assumes the existence of a static set of alternates to argc and argv,
* (namely, mac, and mav[]).
*
*/
static void
{
int i; /* current argument */
/*
* We add an extra argument slot, in case we need to jam a "--"
* argument into the list.
*/
perror("chmod");
exit(2);
}
/* scan for the use of "--" in the argument list */
fflag = 1;
}
/* process the arguments */
for (i = mac = 0;
i++) {
/*
* If there is not already a "--" argument specified,
* and the argument starts with '-' but does not
* contain any of the official option letters, then it
* is probably a mode argument beginning with '-'.
* Force a "--" into the argument stream in front of
* it.
*/
perror("chmod");
exit(2);
}
}
}
perror("chmod");
exit(2);
}
}
}
static int
{
int slot;
int len;
int action;
char *end;
if (arg[0] != 'A')
return (1);
switch (*end) {
case '+':
break;
case '-':
else
action = ACL_DELETE;
if (acl_spec[0] == '\0') {
return (1);
}
break;
case '=':
/*
* Was slot specified?
*/
slot = -1;
break;
default:
return (1);
}
return (1);
if (acl_spec) {
exit(1);
}
}
if (new_acl_args == NULL)
return (1);
perror("chmod");
exit(2);
}
return (0);
}
/*
* This function is called whenever the group permissions of a file
* is being modified. According to the chmod(1) manpage, any
* change made to the group permissions must be applied to both
* the acl mask and the acl's GROUP_OBJ. The chmod(2) already
* set the mask, so this routine needs to make the same change
* to the GROUP_OBJ.
*/
static void
{
int aclcnt, n;
/*
* if this file system support ace_t acl's
* then simply return since we don't have an
* acl mask to deal with
*/
return;
return; /* it's just a trivial acl; no need to change it */
== NULL) {
perror("chmod");
exit(2);
}
return;
}
if (group_clear_bits != 0)
newperm &= ~group_clear_bits;
if (group_set_bits != 0)
< 0) {
}
}
break;
}
}
}
static int
{
int error = 0;
int len;
int isdir;
if (error != 0) {
return (1);
}
switch (acl_args->acl_action) {
case ACL_ADD:
return (1);
}
break;
case ACL_SLOT_DELETE:
errmsg(1, 0,
gettext("Invalid slot specified for removal\n"));
return (1);
}
errmsg(1, 0,
gettext("Can't remove all ACL "
"entries from a file\n"));
return (1);
}
/*
* remove a single entry
*
* if last entry just adjust acl_cnt
*/
else {
}
break;
case ACL_DELETE:
return (1);
}
errmsg(1, 0,
gettext("Can't remove all ACL "
"entries from a file\n"));
return (1);
}
break;
case ACL_REPLACE:
if (error) {
return (1);
}
} else {
}
break;
case ACL_STRIP:
if (error) {
return (1);
}
return (0);
/*NOTREACHED*/
default:
/*NOTREACHED*/
}
if (error) {
gettext("See chmod(1) for more information on "
"valid ACL syntax\n"));
}
return (1);
}
return (0);
}
/*
* Prints out the attributes in their verbose form:
* '{'[["no"]<attribute-name>][,["no"]<attribute-name>]...'}'
* similar to output of ls -/v.
*/
static void
{
nvpair_name(pair));
firsttime = 0;
} else {
"<error retrieving attributes: %s>"),
break;
}
}
}
/*
* Add an attribute name and boolean value to an nvlist if an action is to be
* performed for that attribute. The nvlist will be used later to set all the
* attributes in the nvlist in one operation through a call to setattrat().
*
* If a set operation ('+') was specified, then a boolean representation of the
* attribute's value will be added to the nvlist for that attribute name. If an
* inverse operation ('-') was specified, then a boolean representation of the
* inverse of the attribute's value will be added to the nvlist for that
* attribute name.
*
* Returns an nvlist of attribute name and boolean value pairs if there are
* attribute actions to be performed, otherwise returns NULL.
*/
static nvlist_t *
{
int attribute_set = 0;
f_attr_t i;
perror("chmod");
exit(2);
}
for (i = 0; i < numofattrs; i++) {
if (attractptr[i] != '\0') {
attr_to_name(i),
(attractptr[i] == A_SET_OP))) != 0) {
"unable to propagate attribute names and"
} else {
attribute_set = 1;
}
}
}
}
/*
* Set the attributes of file, or if specified, of the named attribute file,
* attrname. Build an nvlist of attribute names and values and call setattrat()
* to set the attributes in one operation.
*
* Returns 0 if successful, otherwise returns 1.
*/
static int
{
int rc;
char *filename;
} else {
}
attr_nvlist)) != 0) {
char *emsg;
switch (errno) {
case EINVAL:
break;
case EPERM:
break;
default:
}
"cannot set the following attributes on "
"%s%s%s%s: %s\n"),
}
return (rc);
}
static int
save_cwd(void)
{
}
static void
{
if (cwd != -1) {
"can't change to current working directory\n"));
}
}
}
/*
* Returns 1 if filename is a system attribute file, otherwise
* returns 0.
*/
static int
{
}
/*
* Perform the action on the specified named attribute file for the file
* associated with the input file descriptor. If the named attribute file
* is "*", then the action is to be performed on all the named attribute files
* of the file associated with the input file descriptor.
*/
static int
{
int dirfd;
int error = 0;
/*
* Make sure the named attribute exists and extended system
* attributes are supported on the underlying file system.
*/
AT_SYMLINK_NOFOLLOW) < 0) {
"can't access attribute %s of %s\n"),
return (1);
}
"extended system attributes not supported "
"for attribute %s of %s\n"),
return (1);
}
}
} else {
"cannot open dir pointer of file %s\n"), file);
if (dirfd > 0) {
}
return (1);
}
/*
* Process all extended attribute files except
* ".", "..", and extended system attribute files.
*/
continue;
}
attr_nvlist) != 0) {
error++;
}
}
}
}
return ((error == 0) ? 0 : 1);
}
/*
* Set the attributes of the specified file, or if specified with -@ on the
* command line, the specified named attributes of the specified file.
*
* Returns 0 if successful, otherwise returns 1.
*/
static int
{
char *parentd;
int cwd;
int error = 0;
int parentfd;
if (attr_nvlist == NULL) {
return (0);
}
"extended system attributes not supported for %s\n"), file);
return (1);
}
/*
* Open the parent directory and change into it before attempting
* to set the attributes of the file.
*/
} else {
}
if (parentfd == -1) {
"cannot open attribute directory of %s\n"), file);
}
return (1);
}
"can't get current working directory\n"));
}
"can't change to parent %sdirectory of %s\n"),
}
return (1);
}
/*
* If no named attribute file names were provided on the command line
* then set the attributes of the base file, otherwise, set the
* attributes for each of the named attribute files specified.
*/
} else {
attr_nvlist) != 0) {
error++;
}
}
}
return ((error == 0) ? 0 : 1);
}
/*
* Prints the attributes in either the compact or verbose form indicated
* by flag.
*/
static void
{
f_attr_t i;
static int numofattrs;
numofattrs = attr_count();
for (i = 0; i < numofattrs; i++) {
if ((attr_to_xattr_view(i) != XATTR_VIEW_READWRITE) ||
(attr_to_data_type(i) != DATA_TYPE_BOOLEAN_VALUE)) {
continue;
}
firsttime = 0;
}
}
/*
* Record what action should be taken on the specified attribute. Only boolean
* read-write attributes can be manipulated.
*
* Returns 0 if successful, otherwise returns 1.
*/
static int
{
return (0);
}
return (1);
}
/*
* Parses the entry and assigns the appropriate action (either '+' or '-' in
* attribute's position in the character array pointed to by attractptr, where
* upon exit, attractptr is positional and the value of each character specifies
* whether to set (a '+'), clear (a '-'), or leave untouched (a '\0') the
* attribute value.
*
* If the entry is an attribute name, then the A_SET_OP action is to be
* performed for this attribute. If the entry is an attribute name proceeded
* with "no", then the A_INVERSE_OP action is to be performed for this
* attribute. If the entry is one or more attribute option letters, then step
* through each of the option letters marking the action to be performed for
* each of the attributes associated with the letter as A_SET_OP.
*
* Returns 0 if the entry was a valid attribute(s) and the action to be
* performed on that attribute(s) has been recorded, otherwise returns 1.
*/
static int
{
char *aptr;
if (atype == A_VERBOSE_TYPE) {
return (set_attr_args(attr,
attractptr));
attractptr));
} else {
return (1);
}
} else if (atype == A_COMPACT_TYPE) {
/*
* The output of 'ls' can be used as the attribute mode
* specification for chmod. This output can contain a
* hypen ('-') for each attribute that is not set. If
* so, ignore them. If a replace action is being
* performed, then all attributes that don't have an
* action set here, will be cleared down the line.
*/
if (*aptr == '-') {
continue;
}
attractptr) != 0) {
return (1);
}
}
return (0);
}
return (1);
}
/*
* Parse the attribute specification, aoptsstr. Upon completion, attr_nvlist
* will point to an nvlist which contains pairs of attribute names and values
* to be set; attr_nvlist will be NULL if it is a no-op.
*
* The attribute specification format is
* S[oper]attr_type[attribute_list]
* where oper is
* + set operation of specified attributes in attribute list.
* This is the default operation.
* - inverse operation of specified attributes in attribute list
* = replace operation of all attributes. All attribute operations
* depend on those specified in the attribute list. Attributes
* not specified in the attribute list will be cleared.
* where attr_type is
* c compact type. Each entry in the attribute list is a character
* option representing an associated attribute name.
* v verbose type. Each entry in the attribute list is an
* an attribute name which can optionally be preceeded with "no"
* (to imply the attribute should be cleared).
* a all attributes type. The oper should be applied to all
* read-write boolean system attributes. No attribute list should
* be specified after an 'a' attribute type.
*
* Returns 0 if aoptsstr contained a valid attribute specification,
* otherwise, returns 1.
*/
static int
{
char action;
char *attractptr;
char atype;
char *entry;
char *eptr;
char *nextattr;
char *nextentry;
char *subentry;
char *teptr;
int len;
f_attr_t i;
int numofattrs;
return (1);
}
perror("chmod");
exit(2);
}
/*
* Create a positional character array to determine a single attribute
* operation to be performed, where each index represents the system
* attribute affected, and it's value in the array represents the action
* to be performed, i.e., a value of '+' means to set the attribute, a
* value of '-' means to clear the attribute, and a value of '\0' means
* to leave the attribute untouched. Initially, this positional
* character array is all '\0's, representing a no-op.
*/
}
perror("chmod");
exit(2);
}
perror("chmod");
exit(2);
}
/* Parse each attribute operation within the attribute specification. */
atype = '\0';
/* Get the operator. */
switch (*entry) {
case A_SET_OP:
case A_INVERSE_OP:
case A_REPLACE_OP:
break;
case A_COMPACT_TYPE:
case A_VERBOSE_TYPE:
case A_ALLATTRS_TYPE:
break;
default:
break;
}
/* An attribute type must be specified. */
if (atype == '\0') {
if ((*entry == A_COMPACT_TYPE) ||
(*entry == A_VERBOSE_TYPE) ||
(*entry == A_ALLATTRS_TYPE)) {
} else {
return (1);
}
}
/* Get the attribute specification separator. */
*tok = RIGHTBRACE;
entry++;
} else {
}
/* Get the attribute operation */
*nextentry = '\0';
nextentry++;
}
/* Check for a no-op */
(action != A_REPLACE_OP)) {
continue;
}
/*
* Step through the attribute operation, setting the
* appropriate values for the specified attributes in the
* character array, attractptr. A value of '+' will mean the
* attribute is to be set, and a value of '-' will mean the
* attribute is to be cleared. If the value of an attribute
* remains '\0', then no action is to be taken on that
* attribute. As multiple operations specified are
* accumulated, a single attribute setting operation is
* represented in attractptr.
*/
(atype == A_ALLATTRS_TYPE)) {
if ((action == A_REPLACE_OP) ||
(atype == A_ALLATTRS_TYPE)) {
}
if (len > 0) {
perror("chmod");
exit(2);
}
*nextattr = '\0';
nextattr++;
}
return (1);
}
}
}
/*
* If performing the replace action, record the
* attributes and values for the rest of the
* attributes that have not already been recorded,
* otherwise record the specified action for all
* attributes. Note: set_attr_args() will only record
* the attribute and action if it is a boolean
* read-write attribute so we don't need to worry
* about checking it here.
*/
if ((action == A_REPLACE_OP) ||
(atype == A_ALLATTRS_TYPE)) {
for (i = 0; i < numofattrs; i++) {
if (attractptr[i] == A_UNDEF_OP) {
(void) set_attr_args(i,
}
}
}
} else {
attractptr) != 0) {
return (1);
}
}
}
/*
* Populate an nvlist with attribute name and boolean value pairs
* using the single attribute operation.
*/
return (0);
}