aboutsummaryrefslogtreecommitdiffstats
path: root/file_cmds/chmod
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /file_cmds/chmod
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'file_cmds/chmod')
-rw-r--r--file_cmds/chmod/chmod.1604
-rw-r--r--file_cmds/chmod/chmod.c458
-rw-r--r--file_cmds/chmod/chmod_acl.c859
-rw-r--r--file_cmds/chmod/chmod_acl.h90
4 files changed, 2011 insertions, 0 deletions
diff --git a/file_cmds/chmod/chmod.1 b/file_cmds/chmod/chmod.1
new file mode 100644
index 0000000..0e89f26
--- /dev/null
+++ b/file_cmds/chmod/chmod.1
@@ -0,0 +1,604 @@
+.\"-
+.\" Copyright (c) 1989, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2017
+.Dt CHMOD 1
+.Os
+.Sh NAME
+.Nm chmod
+.Nd change file modes or Access Control Lists
+.Sh SYNOPSIS
+.Nm chmod
+.Op Fl fv
+.Op Fl R Op Fl H | L | P
+.Ar mode
+.Ar
+.Nm chmod
+.Op Fl fv
+.Op Fl R Op Fl H | L | P
+.Op -a | +a | =a
+.Ar ACE
+.Ar
+.Nm chmod
+.Op Fl fhv
+.Op Fl R Op Fl H | L | P
+.Op Fl E
+.Ar
+.Nm chmod
+.Op Fl fhv
+.Op Fl R Op Fl H | L | P
+.Op Fl C
+.Ar
+.Nm chmod
+.Op Fl fhv
+.Op Fl R Op Fl H | L | P
+.Op Fl N
+.Ar
+.Sh DESCRIPTION
+The
+.Nm chmod
+utility modifies the file mode bits of the listed files
+as specified by the
+.Ar mode
+operand. It may also be used to modify the Access Control
+Lists (ACLs) associated with the listed files.
+.Pp
+The generic options are as follows:
+.Bl -tag -width indent
+.It Fl f
+Do not display a diagnostic message if
+.Nm chmod
+could not modify the mode for
+.Va file ,
+nor modify the exit status to reflect such failures.
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed
+and hence unaffected by the command.
+(Symbolic links encountered during tree traversal are not followed.)
+.It Fl h
+If the file is a symbolic link, change the mode of the link itself
+rather than the file that the link points to.
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl R
+Change the modes of the file hierarchies rooted in the files,
+instead of just the files themselves.
+Beware of unintentionally matching the
+.Dq Pa ".."
+hard link to the parent directory when using wildcards like
+.Dq Li ".*" .
+.It Fl v
+Cause
+.Nm chmod
+to be verbose, showing filenames as the mode is modified.
+If the
+.Fl v
+flag is specified more than once, the old and new modes of the file
+will also be printed, in both octal and symbolic notation.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+If
+.Nm chmod
+receives a
+.Dv SIGINFO
+signal (see the
+.Cm status
+argument for
+.Xr stty 1 ) ,
+then the current filename as well as the old and new modes are displayed.
+.Pp
+Only the owner of a file or the super-user is permitted to change
+the mode of a file.
+.Sh EXIT STATUS
+.Ex -std
+.Sh MODES
+Modes may be absolute or symbolic.
+An absolute mode is an octal number constructed from the sum of
+one or more of the following values:
+.Pp
+.Bl -tag -width 6n -compact -offset indent
+.It Li 4000
+(the setuid bit).
+Executable files with this bit set
+will run with effective uid set to the uid of the file owner.
+Directories with this bit set will force all files and
+sub-directories created in them to be owned by the directory owner
+and not by the uid of the creating process, if the underlying file
+system supports this feature: see
+.Xr chmod 2
+and the
+.Cm suiddir
+option to
+.Xr mount 8 .
+.It Li 2000
+(the setgid bit).
+Executable files with this bit set
+will run with effective gid set to the gid of the file owner.
+.It Li 1000
+(the sticky bit).
+See
+.Xr chmod 2
+and
+.Xr sticky 7 .
+.It Li 0400
+Allow read by owner.
+.It Li 0200
+Allow write by owner.
+.It Li 0100
+For files, allow execution by owner.
+For directories, allow the owner to
+search in the directory.
+.It Li 0040
+Allow read by group members.
+.It Li 0020
+Allow write by group members.
+.It Li 0010
+For files, allow execution by group members.
+For directories, allow
+group members to search in the directory.
+.It Li 0004
+Allow read by others.
+.It Li 0002
+Allow write by others.
+.It Li 0001
+For files, allow execution by others.
+For directories allow others to
+search in the directory.
+.El
+.Pp
+For example, the absolute mode that permits read, write and execute by
+the owner, read and execute by group members, read and execute by
+others, and no set-uid or set-gid behaviour is 755
+(400+200+100+040+010+004+001).
+.Pp
+The symbolic mode is described by the following grammar:
+.Bd -literal -offset indent
+mode ::= clause [, clause ...]
+clause ::= [who ...] [action ...] action
+action ::= op [perm ...]
+who ::= a | u | g | o
+op ::= + | \- | =
+perm ::= r | s | t | w | x | X | u | g | o
+.Ed
+.Pp
+The
+.Ar who
+symbols ``u'', ``g'', and ``o'' specify the user, group, and other parts
+of the mode bits, respectively.
+The
+.Ar who
+symbol ``a'' is equivalent to ``ugo''.
+.Pp
+The
+.Ar perm
+symbols represent the portions of the mode bits as follows:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It r
+The read bits.
+.It s
+The set-user-ID-on-execution and set-group-ID-on-execution bits.
+.It t
+The sticky bit.
+.It w
+The write bits.
+.It x
+The execute/search bits.
+.It X
+The execute/search bits if the file is a directory or any of the
+execute/search bits are set in the original (unmodified) mode.
+Operations with the
+.Ar perm
+symbol ``X'' are only meaningful in conjunction with the
+.Ar op
+symbol ``+'', and are ignored in all other cases.
+.It u
+The user permission bits in the original mode of the file.
+.It g
+The group permission bits in the original mode of the file.
+.It o
+The other permission bits in the original mode of the file.
+.El
+.Pp
+The
+.Ar op
+symbols represent the operation performed, as follows:
+.Bl -tag -width 4n
+.It +
+If no value is supplied for
+.Ar perm ,
+the ``+'' operation has no effect.
+If no value is supplied for
+.Ar who ,
+each permission bit specified in
+.Ar perm ,
+for which the corresponding bit in the file mode creation mask
+(see
+.Xr umask 2 )
+is clear, is set.
+Otherwise, the mode bits represented by the specified
+.Ar who
+and
+.Ar perm
+values are set.
+.It \&\-
+If no value is supplied for
+.Ar perm ,
+the ``\-'' operation has no effect.
+If no value is supplied for
+.Ar who ,
+each permission bit specified in
+.Ar perm ,
+for which the corresponding bit in the file mode creation mask
+is set, is cleared.
+Otherwise, the mode bits represented by the specified
+.Ar who
+and
+.Ar perm
+values are cleared.
+.It =
+The mode bits specified by the
+.Ar who
+value are cleared, or, if no
+.Ar who
+value is specified, the owner, group
+and other mode bits are cleared.
+Then, if no value is supplied for
+.Ar who ,
+each permission bit specified in
+.Ar perm ,
+for which the corresponding bit in the file mode creation mask
+is clear, is set.
+Otherwise, the mode bits represented by the specified
+.Ar who
+and
+.Ar perm
+values are set.
+.El
+.Pp
+Each
+.Ar clause
+specifies one or more operations to be performed on the mode
+bits, and each operation is applied to the mode bits in the
+order specified.
+.Pp
+Operations upon the other permissions only (specified by the symbol
+``o'' by itself), in combination with the
+.Ar perm
+symbols ``s'' or ``t'', are ignored.
+.Pp
+The ``w'' permission on directories will permit file creation, relocation,
+and copy into that directory.
+Files created within the directory itself will inherit its group ID.
+.Sh EXAMPLES OF VALID MODES
+.Bl -tag -width "u=rwx,go=u-w" -compact
+.It Li 644
+make a file readable by anyone and writable by the owner only.
+.Pp
+.It Li go-w
+deny write permission to group and others.
+.Pp
+.It Li =rw,+X
+set the read and write permissions to the usual defaults, but
+retain any execute permissions that are currently set.
+.Pp
+.It Li +X
+make a directory or file searchable/executable by everyone if it is
+already searchable/executable by anyone.
+.Pp
+.It Li 755
+.It Li u=rwx,go=rx
+.It Li u=rwx,go=u-w
+make a file readable/executable by everyone and writable by the owner only.
+.Pp
+.It Li go=
+clear all mode bits for group and others.
+.Pp
+.It Li g=u-w
+set the group bits equal to the user bits, but clear the group write bit.
+.El
+.Sh ACL MANIPULATION OPTIONS
+ACLs are manipulated using extensions to the symbolic mode
+grammar. Each file has one ACL, containing an ordered list of entries.
+Each entry refers to a user or group, and grants or denies a set of
+permissions.
+In cases where a user and a group exist with the same name, the
+user/group name can be prefixed with "user:" or "group:" in order to
+specify the type of name.
+.Pp
+If the user or group name contains spaces you can use ':' as the delimiter
+between name and permission.
+.Pp
+The following permissions are applicable to all filesystem objects:
+.Bl -tag -width 6n -compact -offset indent
+.It delete
+Delete the item. Deletion may be granted by either this permission
+on an object or the delete_child right on the containing directory.
+.It readattr
+Read an object's basic attributes. This is implicitly granted if
+the object can be looked up and not explicitly denied.
+.It writeattr
+Write an object's basic attributes.
+.It readextattr
+Read extended attributes.
+.It writeextattr
+Write extended attributes.
+.It readsecurity
+Read an object's extended security information (ACL).
+.It writesecurity
+Write an object's security information (ownership, mode, ACL).
+.It chown
+Change an object's ownership.
+.El
+.Pp
+The following permissions are applicable to directories:
+.Bl -tag -width 6n -compact -offset indent
+.It list
+List entries.
+.It search
+Look up files by name.
+.It add_file
+Add a file.
+.It add_subdirectory
+Add a subdirectory.
+.It delete_child
+Delete a contained object. See the file delete permission above.
+.El
+.Pp
+The following permissions are applicable to non-directory filesystem objects:
+.Bl -tag -width 6n -compact -offset indent
+.It read
+Open for reading.
+.It write
+Open for writing.
+.It append
+Open for writing, but in a fashion that only allows writes into areas of
+the file not previously written.
+.It execute
+Execute the file as a script or program.
+.El
+.Pp
+ACL inheritance is controlled with the following permissions words, which
+may only be applied to directories:
+.Bl -tag -width 6n -compact -offset indent
+.It file_inherit
+Inherit to files.
+.It directory_inherit
+Inherit to directories.
+.It limit_inherit
+This flag is only relevant to entries inherited by subdirectories; it
+causes the directory_inherit flag to be cleared in the entry that is
+inherited, preventing further nested subdirectories from also
+inheriting the entry.
+.It only_inherit
+The entry is inherited by created items but not considered when processing
+the ACL.
+.El
+.Pp
+The ACL manipulation options are as follows:
+.Bl -tag -width Ds
+.It \fB+a\fR
+The +a mode parses a new ACL entry from the next argument on
+the commandline and inserts it into the canonical location in the
+ACL. If the supplied entry refers to an identity already listed, the
+two entries are combined.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ # chmod +a "admin allow write" file1
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: admin allow write
+ # chmod +a "guest deny read" file1
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: admin allow write
+ # chmod +a "admin allow delete" file1
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: admin allow write,delete
+ # chmod +a "User 1:allow:read" file
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: User 1 allow read
+ 3: admin allow write,delete
+.Pp
+The +a mode strives to maintain correct canonical form for the ACL.
+ local deny
+ local allow
+ inherited deny
+ inherited allow
+.Pp
+By default, chmod adds entries to the top of the local deny and local
+allow lists. Inherited entries are added by using the +ai mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: admin allow write,delete
+ 3: juser inherited deny delete
+ 4: admin inherited allow delete
+ 5: backup inherited deny read
+ 6: admin inherited allow write-security
+ # chmod +ai "others allow read" file1
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: admin allow write,delete
+ 3: juser inherited deny delete
+ 4: others inherited allow read
+ 5: admin inherited allow delete
+ 6: backup inherited deny read
+ 7: admin inherited allow write-security
+.It \fB+a#\fR
+When a specific ordering is required, the exact location at which an
+entry will be inserted is specified with the +a# mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: admin allow write
+ # chmod +a# 2 "others deny read" file1
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: others deny read
+ 3: admin allow write
+.Pp
+The +ai# mode may be used to insert inherited entries at a specific
+location. Note that these modes allow non-canonical ACL ordering to
+be constructed.
+.It Fl a
+The -a mode is used to delete ACL entries. All entries exactly
+matching the supplied entry will be deleted. If the entry lists a
+subset of rights granted by an entry, only the rights listed are
+removed. Entries may also be deleted by index using the -a# mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: guest deny read
+ 2: admin allow write,delete
+ # chmod -a# 1 file1
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: admin allow write,delete
+ # chmod -a "admin allow write" file1
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: admin allow delete
+.Pp
+Inheritance is not considered when processing the -a mode; rights and
+entries will be removed regardless of their inherited state.
+.Pp
+If the user or group name contains spaces you can use ':' as the delimiter
+.Pp
+\fBExample\fR
+ # chmod +a "User 1:allow:read" file
+.It \fB=a#\fR
+Individual entries are rewritten using the =a# mode.
+.Pp
+\fBExamples\fR
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: admin allow delete
+ # chmod =a# 1 "admin allow write,chown"
+ # ls -le
+ -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1
+ owner: juser
+ 1: admin allow write,chown
+.Pp
+This mode may not be used to add new entries.
+.It Fl E
+Reads the ACL information from stdin, as a sequential list
+of ACEs, separated by newlines. If the information parses correctly,
+the existing information is replaced.
+.It Fl C
+Returns false if any of the named files have ACLs in non-canonical order.
+.It Fl i
+Removes the 'inherited' bit from all entries in the named file(s) ACLs.
+.It Fl I
+Removes all inherited entries from the named file(s) ACL(s).
+.It Fl N
+Removes the ACL from the named file(s).
+.El
+.Sh COMPATIBILITY
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr install 1 ,
+.Xr chmod 2 ,
+.Xr stat 2 ,
+.Xr umask 2 ,
+.Xr fts 3 ,
+.Xr setmode 3 ,
+.Xr sticky 7 ,
+.Xr symlink 7 ,
+.Xr chown 8 ,
+.Xr mount 8
+.Sh STANDARDS
+The
+.Nm chmod
+utility is expected to be
+.St -p1003.2
+compatible with the exception of the
+.Ar perm
+symbol
+.Dq t
+which is not included in that standard.
+.Sh HISTORY
+A
+.Nm chmod
+command appeared in
+.At v1 .
diff --git a/file_cmds/chmod/chmod.c b/file_cmds/chmod/chmod.c
new file mode 100644
index 0000000..442815d
--- /dev/null
+++ b/file_cmds/chmod/chmod.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__used static char const copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __APPLE__
+#include "chmod_acl.h"
+
+#endif /*__APPLE__*/
+
+int fflag = 0;
+
+int main(int, char *[]);
+void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ FTS *ftsp = NULL;
+ FTSENT *p = NULL;
+ mode_t *set = NULL;
+ long val = 0;
+ int oct = 0;
+ int Hflag, Lflag, Pflag, Rflag, ch, fts_options, hflag, rval;
+ int vflag;
+ char *ep, *mode;
+ mode_t newmode, omode;
+#ifdef __APPLE__
+ unsigned int acloptflags = 0;
+ long aclpos = -1;
+ int inheritance_level = 0;
+ int index = 0;
+ size_t acloptlen = 0;
+ int ace_arg_not_required = 0;
+ acl_t acl_input = NULL;
+#endif /* __APPLE__*/
+ int (*change_mode)(const char *, mode_t);
+
+ set = NULL;
+ omode = 0;
+ Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0;
+#ifndef __APPLE__
+ while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1)
+#else
+ while ((ch = getopt(argc, argv, "ACEHILNPRVXafghinorstuvwx")) != -1)
+#endif
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ Pflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ Pflag = 1;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ /*
+ * In System V (and probably POSIX.2) the -h option
+ * causes chmod to change the mode of the symbolic
+ * link. 4.4BSD's symbolic links didn't have modes,
+ * so it was an undocumented noop. In FreeBSD 3.0,
+ * lchmod(2) is introduced and this option does real
+ * work.
+ */
+ hflag = 1;
+ break;
+#ifdef __APPLE__
+ case 'a':
+ if (argv[optind - 1][0] == '-' &&
+ argv[optind - 1][1] == ch)
+ --optind;
+ goto done;
+ case 'A':
+// acloptflags |= ACL_FLAG | ACL_TO_STDOUT;
+// ace_arg_not_required = 1;
+ errx(1, "-A not implemented");
+ goto done;
+ case 'E':
+ acloptflags |= ACL_FLAG | ACL_FROM_STDIN;
+ goto done;
+ case 'C':
+ acloptflags |= ACL_FLAG | ACL_CHECK_CANONICITY;
+ ace_arg_not_required = 1;
+ goto done;
+ case 'i':
+ acloptflags |= ACL_FLAG | ACL_REMOVE_INHERIT_FLAG;
+ ace_arg_not_required = 1;
+ goto done;
+ case 'I':
+ acloptflags |= ACL_FLAG | ACL_REMOVE_INHERITED_ENTRIES;
+ ace_arg_not_required = 1;
+ goto done;
+ case 'n':
+ acloptflags |= ACL_FLAG | ACL_NO_TRANSLATE;
+ break;
+ case 'N':
+ acloptflags |= ACL_FLAG | ACL_CLEAR_FLAG;
+ ace_arg_not_required = 1;
+ goto done;
+ case 'V':
+// acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR;
+// ace_arg_not_required = 1;
+ errx(1, "-V not implemented");
+ goto done;
+#endif /* __APPLE__ */
+ /*
+ * XXX
+ * "-[rwx]" are valid mode commands. If they are the entire
+ * argument, getopt has moved past them, so decrement optind.
+ * Regardless, we're done argument processing.
+ */
+ case 'g': case 'o': case 'r': case 's':
+ case 't': case 'u': case 'w': case 'X': case 'x':
+ if (argv[optind - 1][0] == '-' &&
+ argv[optind - 1][1] == ch &&
+ argv[optind - 1][2] == '\0')
+ --optind;
+ goto done;
+ case 'v':
+ vflag++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+done: argv += optind;
+ argc -= optind;
+
+#ifdef __APPLE__
+ if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2))
+ usage();
+ if (!Rflag && (Hflag || Lflag || Pflag))
+ warnx("options -H, -L, -P only useful with -R");
+#else /* !__APPLE__ */
+ if (argc < 2)
+ usage();
+#endif /* __APPLE__ */
+
+#ifdef __APPLE__
+ if (!(acloptflags & ACL_FLAG) && ((acloptlen = strlen(argv[0])) > 1) && (argv[0][1] == 'a')) {
+ acloptflags |= ACL_FLAG;
+ switch (argv[0][0]) {
+ case '+':
+ acloptflags |= ACL_SET_FLAG;
+ break;
+ case '-':
+ acloptflags |= ACL_DELETE_FLAG;
+ break;
+ case '=':
+ acloptflags |= ACL_REWRITE_FLAG;
+ break;
+ default:
+ acloptflags &= ~ACL_FLAG;
+ goto apnoacl;
+ }
+
+ if (argc < 3)
+ usage();
+
+ if (acloptlen > 2) {
+ for (index = 2; index < acloptlen; index++) {
+ switch (argv[0][index]) {
+ case '#':
+ acloptflags |= ACL_ORDER_FLAG;
+
+ if (argc < ((acloptflags & ACL_DELETE_FLAG)
+ ? 3 : 4))
+ usage();
+ argv++;
+ argc--;
+ errno = 0;
+ aclpos = strtol(argv[0], &ep, 0);
+
+ if (aclpos > ACL_MAX_ENTRIES
+ || aclpos < 0)
+ errno = ERANGE;
+ if (errno || *ep)
+ errx(1, "Invalid ACL entry number: %ld", aclpos);
+ if (acloptflags & ACL_DELETE_FLAG)
+ ace_arg_not_required = 1;
+
+ goto apdone;
+ case 'i':
+ acloptflags |= ACL_INHERIT_FLAG;
+ /* The +aii.. syntax to specify
+ * inheritance level is rather unwieldy,
+ * find an alternative.
+ */
+ inheritance_level++;
+ if (inheritance_level > 1)
+ warnx("Inheritance across more than one generation is not currently supported");
+ if (inheritance_level >= MAX_INHERITANCE_LEVEL)
+ goto apdone;
+ break;
+ default:
+ errno = EINVAL;
+ usage();
+ }
+ }
+ }
+apdone:
+ argv++;
+ argc--;
+ }
+apnoacl:
+#endif /*__APPLE__*/
+
+ if (Rflag) {
+ fts_options = FTS_PHYSICAL;
+ if (hflag)
+ errx(1,
+ "the -R and -h options may not be specified together.");
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else
+ fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
+
+ if (hflag)
+ change_mode = lchmod;
+ else
+ change_mode = chmod;
+#ifdef __APPLE__
+ if (acloptflags & ACL_FROM_STDIN) {
+ ssize_t readval = 0;
+ size_t readtotal = 0;
+
+ mode = (char *) malloc(MAX_ACL_TEXT_SIZE);
+
+ if (mode == NULL)
+ err(1, "Unable to allocate mode string");
+ /* Read the ACEs from STDIN */
+ do {
+ readtotal += readval;
+ readval = read(STDIN_FILENO, mode + readtotal,
+ MAX_ACL_TEXT_SIZE);
+ } while ((readval > 0) && (readtotal <= MAX_ACL_TEXT_SIZE));
+
+ if (0 == readtotal)
+ errx(1, "-E specified, but read from STDIN failed");
+ else
+ mode[readtotal - 1] = '\0';
+ --argv;
+ }
+ else
+#endif /* __APPLE */
+ mode = *argv;
+
+#ifdef __APPLE__
+ if ((acloptflags & ACL_FLAG)) {
+
+ /* Are we deleting by entry number, verifying
+ * canonicity or performing some other operation that
+ * does not require an input entry? If so, there's no
+ * entry to convert.
+ */
+ if (ace_arg_not_required) {
+ --argv;
+ }
+ else {
+ /* Parse the text into an ACL*/
+ acl_input = parse_acl_entries(mode);
+ if (acl_input == NULL) {
+ errx(1, "Invalid ACL specification: %s", mode);
+ }
+ }
+ }
+ else {
+#endif /* __APPLE__*/
+ if (*mode >= '0' && *mode <= '7') {
+ errno = 0;
+ val = strtol(mode, &ep, 8);
+ if (val > USHRT_MAX || val < 0)
+ errno = ERANGE;
+ if (errno)
+ err(1, "Invalid file mode: %s", mode);
+ if (*ep)
+ errx(1, "Invalid file mode: %s", mode);
+ omode = (mode_t)val;
+ oct = 1;
+ } else {
+ if ((set = setmode(mode)) == NULL)
+ errx(1, "Invalid file mode: %s", mode);
+ oct = 0;
+ }
+#ifdef __APPLE__
+ }
+#endif /* __APPLE__*/
+ if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+ err(1, "fts_open");
+ for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (!Rflag)
+ (void)fts_set(ftsp, p, FTS_SKIP);
+ break;
+ case FTS_DNR: /* Warn, chmod, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_DP: /* Already changed at FTS_D. */
+ continue;
+ case FTS_NS:
+ if (acloptflags & ACL_FLAG) /* don't need stat for -N */
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL: /* Ignore. */
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ if (!hflag)
+ continue;
+ /* else */
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+#ifdef __APPLE__
+/* If an ACL manipulation option was specified, manipulate */
+ if (acloptflags & ACL_FLAG) {
+ if (0 != modify_file_acl(acloptflags, p->fts_accpath, acl_input, (int)aclpos, inheritance_level, !hflag))
+ rval = 1;
+ }
+ else {
+#endif /* __APPLE__ */
+ newmode = oct ? omode : getmode(set, p->fts_statp->st_mode);
+ if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS))
+ continue;
+ if ((*change_mode)(p->fts_accpath, newmode) && !fflag) {
+ warn("Unable to change file mode on %s", p->fts_path);
+ rval = 1;
+ } else {
+ if (vflag) {
+ (void)printf("%s", p->fts_accpath);
+
+ if (vflag > 1) {
+ char m1[12], m2[12];
+
+ strmode(p->fts_statp->st_mode, m1);
+ strmode((p->fts_statp->st_mode &
+ S_IFMT) | newmode, m2);
+
+ (void)printf(": 0%o [%s] -> 0%o [%s]",
+ p->fts_statp->st_mode, m1,
+ (p->fts_statp->st_mode & S_IFMT) |
+ newmode, m2);
+ }
+ (void)printf("\n");
+ }
+
+ }
+#ifdef __APPLE__
+ }
+#endif /* __APPLE__*/
+ }
+ if (errno)
+ err(1, "fts_read");
+#ifdef __APPLE__
+ if (acl_input)
+ acl_free(acl_input);
+ if (mode && (acloptflags & ACL_FROM_STDIN))
+ free(mode);
+
+#endif /* __APPLE__ */
+ if (set)
+ free(set);
+ exit(rval);
+}
+
+void
+usage(void)
+{
+#ifdef __APPLE__
+ (void)fprintf(stderr,
+ "usage:\tchmod [-fhv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...\n"
+ "\tchmod [-fhv] [-R [-H | -L | -P]] [-E | -C | -N | -i | -I] file ...\n"); /* add -A and -V when implemented */
+#else
+ (void)fprintf(stderr,
+ "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n");
+#endif /* __APPLE__ */
+ exit(1);
+}
diff --git a/file_cmds/chmod/chmod_acl.c b/file_cmds/chmod/chmod_acl.c
new file mode 100644
index 0000000..5ac4c17
--- /dev/null
+++ b/file_cmds/chmod/chmod_acl.c
@@ -0,0 +1,859 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <membership.h>
+#include "chmod_acl.h"
+
+extern void usage(void);
+
+#ifdef __APPLE__
+static struct {
+ acl_perm_t perm;
+ char *name;
+ int flags;
+#define ACL_PERM_DIR (1<<0)
+#define ACL_PERM_FILE (1<<1)
+} acl_perms[] = {
+ {ACL_READ_DATA, "read", ACL_PERM_FILE},
+ {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR},
+ {ACL_WRITE_DATA, "write", ACL_PERM_FILE},
+ {ACL_ADD_FILE, "add_file", ACL_PERM_DIR},
+ {ACL_EXECUTE, "execute", ACL_PERM_FILE},
+ {ACL_SEARCH, "search", ACL_PERM_DIR},
+ {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_APPEND_DATA, "append", ACL_PERM_FILE},
+ {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR},
+ {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR},
+ {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR},
+ {0, NULL, 0}
+};
+
+static struct {
+ acl_flag_t flag;
+ char *name;
+ int flags;
+} acl_flags[] = {
+ {ACL_ENTRY_INHERITED, "inherited", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR},
+ {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR},
+ {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR},
+ {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR},
+ {0, NULL, 0}
+};
+
+/* TBD - Many of these routines could potentially be considered for
+ * inclusion in a library. If that is done, either avoid use of "err"
+ * and implement a better fall-through strategy in case of errors,
+ * or use err_set_exit() and make various structures globals.
+ */
+
+#define NAME_USER (1)
+#define NAME_GROUP (2)
+#define NAME_EITHER (NAME_USER | NAME_GROUP)
+
+/* Perform a name to uuid mapping - calls through to memberd */
+
+uuid_t *
+name_to_uuid(char *tok, int nametype) {
+ uuid_t *entryg = NULL;
+ size_t len = strlen(tok);
+
+ if ((entryg = (uuid_t *) calloc(1, sizeof(uuid_t))) == NULL) {
+ errx(1, "Unable to allocate a uuid");
+ }
+
+ if ((nametype & NAME_USER) && mbr_identifier_to_uuid(ID_TYPE_USERNAME, tok, len, *entryg) == 0) {
+ return entryg;
+ }
+
+ if ((nametype & NAME_GROUP) && mbr_identifier_to_uuid(ID_TYPE_GROUPNAME, tok, len, *entryg) == 0) {
+ return entryg;
+ }
+
+ errx(1, "Unable to translate '%s' to a UUID", tok);
+}
+
+/* Convert an acl entry in string form to an acl_entry_t */
+int
+parse_entry(char *entrybuf, acl_entry_t newent) {
+ char *tok;
+ char *pebuf;
+ uuid_t *entryg;
+
+ acl_tag_t tag;
+ acl_permset_t perms;
+ acl_flagset_t flags;
+ unsigned permcount = 0;
+ unsigned pindex = 0;
+ char *delimiter = " ";
+ int nametype = NAME_EITHER;
+
+ acl_get_permset(newent, &perms);
+ acl_get_flagset_np(newent, &flags);
+
+ pebuf = entrybuf;
+
+ if (0 == strncmp(entrybuf, "user:", 5)) {
+ nametype = NAME_USER;
+ pebuf += 5;
+ } else if (0 == strncmp(entrybuf, "group:", 6)) {
+ nametype = NAME_GROUP;
+ pebuf += 6;
+ }
+
+ if (strchr(pebuf, ':')) /* User/Group names can have spaces */
+ delimiter = ":";
+ tok = strsep(&pebuf, delimiter);
+
+ if ((tok == NULL) || *tok == '\0') {
+ errx(1, "Invalid entry format -- expected user or group name");
+ }
+
+ /* parse the name into a qualifier */
+ entryg = name_to_uuid(tok, nametype);
+
+ tok = strsep(&pebuf, ": "); /* Stick with delimiter? */
+ if ((tok == NULL) || *tok == '\0') {
+ errx(1, "Invalid entry format -- expected allow or deny");
+ }
+
+ /* is the verb 'allow' or 'deny'? */
+ if (!strcmp(tok, "allow")) {
+ tag = ACL_EXTENDED_ALLOW;
+ } else if (!strcmp(tok, "deny")) {
+ tag = ACL_EXTENDED_DENY;
+ } else {
+ errx(1, "Unknown tag type '%s'", tok);
+ }
+
+ /* parse permissions */
+ for (; (tok = strsep(&pebuf, ",")) != NULL;) {
+ if (*tok != '\0') {
+ /* is it a permission? */
+ for (pindex = 0; acl_perms[pindex].name != NULL; pindex++) {
+ if (!strcmp(acl_perms[pindex].name, tok)) {
+ /* got one */
+ acl_add_perm(perms, acl_perms[pindex].perm);
+ permcount++;
+ goto found;
+ }
+ }
+ /* is it a flag? */
+ for (pindex = 0; acl_flags[pindex].name != NULL; pindex++) {
+ if (!strcmp(acl_flags[pindex].name, tok)) {
+ /* got one */
+ acl_add_flag_np(flags, acl_flags[pindex].flag);
+ permcount++;
+ goto found;
+ }
+ }
+ errx(1,"Invalid permission type '%s'", tok);
+ found:
+ continue;
+ }
+ }
+ if (0 == permcount) {
+ errx(1, "No permissions specified");
+ }
+ acl_set_tag_type(newent, tag);
+ acl_set_qualifier(newent, entryg);
+ acl_set_permset(newent, perms);
+ acl_set_flagset_np(newent, flags);
+ free(entryg);
+
+ return(0);
+}
+
+/* Convert one or more acl entries in string form to an acl_t */
+acl_t
+parse_acl_entries(const char *input) {
+ acl_t acl_input;
+ acl_entry_t newent;
+ char *inbuf;
+ char *oinbuf;
+
+ char **bufp, *entryv[ACL_MAX_ENTRIES];
+#if 0
+/* XXX acl_from_text(), when implemented, will presumably use the canonical
+ * text representation format, which is what chmod should be using
+ * We may need to add an entry number to the input
+ */
+ /* Translate the user supplied ACL entry */
+ /* acl_input = acl_from_text(input); */
+#else
+ inbuf = malloc(MAX_ACL_TEXT_SIZE);
+
+ if (inbuf == NULL)
+ err(1, "malloc() failed");
+ strncpy(inbuf, input, MAX_ACL_TEXT_SIZE);
+ inbuf[MAX_ACL_TEXT_SIZE - 1] = '\0';
+
+ if ((acl_input = acl_init(1)) == NULL)
+ err(1, "acl_init() failed");
+
+ oinbuf = inbuf;
+
+ for (bufp = entryv; (*bufp = strsep(&oinbuf, "\n")) != NULL;)
+ if (**bufp != '\0') {
+ if (0 != acl_create_entry(&acl_input, &newent))
+ err(1, "acl_create_entry() failed");
+ if (0 != parse_entry(*bufp, newent)) {
+ errx(1, "Failed parsing entry '%s'", *bufp);
+ }
+ if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) {
+ errx(1, "Too many entries");
+ }
+ }
+
+ free(inbuf);
+ return acl_input;
+#endif /* #if 0 */
+}
+
+/* XXX No Libc support for inherited entries and generation determination yet */
+unsigned
+get_inheritance_level(acl_entry_t entry) {
+/* XXX to be implemented */
+ return 1;
+}
+
+/* Determine a "score" for an acl entry. The entry scores higher if it's
+ * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher
+ * than inherited entries.
+ */
+
+int
+score_acl_entry(acl_entry_t entry) {
+
+ acl_tag_t tag;
+ acl_flagset_t flags;
+ acl_permset_t perms;
+
+ int score = 0;
+
+ if (entry == NULL)
+ return (MINIMUM_TIER);
+
+ if (acl_get_tag_type(entry, &tag) != 0) {
+ err(1, "Malformed ACL entry, no tag present");
+ }
+ if (acl_get_flagset_np(entry, &flags) != 0){
+ err(1, "Unable to obtain flagset");
+ }
+ if (acl_get_permset(entry, &perms) != 0)
+ err(1, "Malformed ACL entry, no permset present");
+
+ switch(tag) {
+ case ACL_EXTENDED_ALLOW:
+ break;
+ case ACL_EXTENDED_DENY:
+ score++;
+ break;
+ default:
+ errx(1, "Unknown tag type %d present in ACL entry", tag);
+ /* NOTREACHED */
+ }
+
+ if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
+ score += get_inheritance_level(entry) * INHERITANCE_TIER;
+
+ return score;
+}
+
+int
+compare_acl_qualifiers(uuid_t *qa, uuid_t *qb) {
+ return bcmp(qa, qb, sizeof(uuid_t));
+}
+
+/* Compare two ACL permsets.
+ * Returns :
+ * MATCH_SUBSET if bperms is a subset of aperms
+ * MATCH_SUPERSET if bperms is a superset of aperms
+ * MATCH_PARTIAL if the two permsets have a common subset
+ * MATCH_EXACT if the two permsets are identical
+ * MATCH_NONE if they are disjoint
+ */
+
+int
+compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms)
+{
+ int i;
+/* TBD Implement other match levels as needed */
+ for (i = 0; acl_perms[i].name != NULL; i++) {
+ if (acl_get_perm_np(aperms, acl_perms[i].perm) !=
+ acl_get_perm_np(bperms, acl_perms[i].perm))
+ return MATCH_NONE;
+ }
+ return MATCH_EXACT;
+}
+
+static int
+compare_acl_flagsets(acl_flagset_t aflags, acl_flagset_t bflags)
+{
+ int i;
+/* TBD Implement other match levels as needed */
+ for (i = 0; acl_flags[i].name != NULL; i++) {
+ if (acl_get_flag_np(aflags, acl_flags[i].flag) !=
+ acl_get_flag_np(bflags, acl_flags[i].flag))
+ return MATCH_NONE;
+ }
+ return MATCH_EXACT;
+}
+
+/* Compares two ACL entries for equality */
+int
+compare_acl_entries(acl_entry_t a, acl_entry_t b)
+{
+ acl_tag_t atag, btag;
+ acl_permset_t aperms, bperms;
+ acl_flagset_t aflags, bflags;
+ int pcmp = 0, fcmp = 0;
+ void *aqual, *bqual;
+
+ aqual = acl_get_qualifier(a);
+ bqual = acl_get_qualifier(b);
+
+ int compare = compare_acl_qualifiers(aqual, bqual);
+ acl_free(aqual);
+ acl_free(bqual);
+
+ if (compare != 0)
+ return MATCH_NONE;
+
+ if (0 != acl_get_tag_type(a, &atag))
+ err(1, "No tag type present in entry");
+ if (0!= acl_get_tag_type(b, &btag))
+ err(1, "No tag type present in entry");
+
+ if (atag != btag)
+ return MATCH_NONE;
+
+ if ((acl_get_permset(a, &aperms) != 0) ||
+ (acl_get_flagset_np(a, &aflags) != 0) ||
+ (acl_get_permset(b, &bperms) != 0) ||
+ (acl_get_flagset_np(b, &bflags) != 0))
+ err(1, "error fetching permissions");
+
+ pcmp = compare_acl_permsets(aperms, bperms);
+ fcmp = compare_acl_flagsets(aflags, bflags);
+
+ if ((pcmp == MATCH_NONE) || (fcmp == MATCH_NONE))
+ return(MATCH_PARTIAL);
+ else
+ return(MATCH_EXACT);
+}
+
+/* Verify that an ACL is in canonical order. Currently, the canonical
+ * form is:
+ * local deny
+ * local allow
+ * inherited deny (parent)
+ * inherited allow (parent)
+ * inherited deny (grandparent)
+ * inherited allow (grandparent)
+ * ...
+ */
+unsigned int
+is_canonical(acl_t acl) {
+
+ unsigned aindex;
+ acl_entry_t entry;
+ int score = 0, next_score = 0;
+
+/* XXX - is a zero entry ACL in canonical form? */
+ if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry))
+ return 1;
+
+ score = score_acl_entry(entry);
+
+ for (aindex = 0; acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) == 0;
+ aindex++) {
+ if (score < (next_score = score_acl_entry(entry)))
+ return 0;
+ score = next_score;
+ }
+ return 1;
+}
+
+
+/* Iterate through an ACL, and find the canonical position for the
+ * specified entry
+ */
+unsigned int
+find_canonical_position(acl_t acl, acl_entry_t modifier) {
+
+ acl_entry_t entry;
+ int mscore = 0;
+ unsigned mpos = 0;
+
+ /* Check if there's an entry with the same qualifier
+ * and tag type; if not, find the appropriate slot
+ * for the score.
+ */
+
+ if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry))
+ return 0;
+
+ mscore = score_acl_entry(modifier);
+
+ while (mscore < score_acl_entry(entry)) {
+
+ mpos++;
+
+ if (0 != acl_get_entry(acl, ACL_NEXT_ENTRY, &entry))
+ break;
+
+ }
+ return mpos;
+}
+
+int canonicalize_acl_entries(acl_t acl);
+
+/* For a given acl_entry_t "modifier", find the first exact or
+ * partially matching entry from the specified acl_t acl
+ */
+
+int
+find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentryp,
+ unsigned match_inherited) {
+
+ acl_entry_t entry = NULL;
+
+ unsigned aindex;
+ int cmp, fcmp = MATCH_NONE;
+
+ for (aindex = 0;
+ acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY :
+ ACL_NEXT_ENTRY, &entry) == 0;
+ aindex++) {
+ cmp = compare_acl_entries(entry, modifier);
+ if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) {
+ if (match_inherited) {
+ acl_flagset_t eflags, mflags;
+
+ if (0 != acl_get_flagset_np(modifier, &mflags))
+ err(1, "Unable to get flagset");
+
+ if (0 != acl_get_flagset_np(entry, &eflags))
+ err(1, "Unable to get flagset");
+
+ if (compare_acl_flagsets(mflags, eflags) == MATCH_EXACT) {
+ *rentryp = entry;
+ fcmp = cmp;
+ }
+ }
+ else {
+ *rentryp = entry;
+ fcmp = cmp;
+ }
+ }
+ if (fcmp == MATCH_EXACT)
+ break;
+ }
+ return fcmp;
+}
+
+/* Remove all perms specified in modifier from rentry*/
+int
+subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int* valid_perms)
+{
+ acl_permset_t rperms, mperms;
+ acl_flagset_t rflags, mflags;
+ if (valid_perms)
+ *valid_perms = 0;
+ int i;
+
+ if ((acl_get_permset(rentry, &rperms) != 0) ||
+ (acl_get_flagset_np(rentry, &rflags) != 0) ||
+ (acl_get_permset(modifier, &mperms) != 0) ||
+ (acl_get_flagset_np(modifier, &mflags) != 0))
+ err(1, "error computing ACL modification");
+
+ for (i = 0; acl_perms[i].name != NULL; i++) {
+ if (acl_get_perm_np(mperms, acl_perms[i].perm))
+ acl_delete_perm(rperms, acl_perms[i].perm);
+ else if (valid_perms && acl_get_perm_np(rperms, acl_perms[i].perm))
+ (*valid_perms)++;
+ }
+ for (i = 0; acl_flags[i].name != NULL; i++) {
+ if (acl_get_flag_np(mflags, acl_flags[i].flag))
+ acl_delete_flag_np(rflags, acl_flags[i].flag);
+ }
+ acl_set_permset(rentry, rperms);
+ acl_set_flagset_np(rentry, rflags);
+ return 0;
+}
+/* Add the perms specified in modifier to rentry */
+static int
+merge_entry_perms(acl_entry_t rentry, acl_entry_t modifier)
+{
+ acl_permset_t rperms, mperms;
+ acl_flagset_t rflags, mflags;
+ int i;
+
+ if ((acl_get_permset(rentry, &rperms) != 0) ||
+ (acl_get_flagset_np(rentry, &rflags) != 0) ||
+ (acl_get_permset(modifier, &mperms) != 0) ||
+ (acl_get_flagset_np(modifier, &mflags) != 0))
+ err(1, "error computing ACL modification");
+
+ for (i = 0; acl_perms[i].name != NULL; i++) {
+ if (acl_get_perm_np(mperms, acl_perms[i].perm))
+ acl_add_perm(rperms, acl_perms[i].perm);
+ }
+ for (i = 0; acl_flags[i].name != NULL; i++) {
+ if (acl_get_flag_np(mflags, acl_flags[i].flag))
+ acl_add_flag_np(rflags, acl_flags[i].flag);
+ }
+ acl_set_permset(rentry, rperms);
+ acl_set_flagset_np(rentry, rflags);
+ return 0;
+}
+
+int
+modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags,
+ int position, int inheritance_level,
+ unsigned flag_new_acl, const char* path) {
+
+ unsigned cpos = 0;
+ acl_entry_t newent = NULL;
+ int dmatch = 0;
+ acl_entry_t rentry = NULL;
+ unsigned retval = 0;
+ acl_t oacl = *oaclp;
+
+/* Add the inherited flag if requested by the user*/
+ if (modifier && (optflags & ACL_INHERIT_FLAG)) {
+ acl_flagset_t mflags;
+
+ acl_get_flagset_np(modifier, &mflags);
+ acl_add_flag_np(mflags, ACL_ENTRY_INHERITED);
+ acl_set_flagset_np(modifier, mflags);
+ }
+
+ if (optflags & ACL_SET_FLAG) {
+ if (position != -1) {
+ if (0 != acl_create_entry_np(&oacl, &newent, position))
+ err(1, "acl_create_entry() failed");
+ acl_copy_entry(newent, modifier);
+ } else {
+/* If an entry exists, add the new permissions to it, else add an
+ * entry in the canonical position.
+ */
+
+/* First, check for a matching entry - if one exists, merge flags */
+ dmatch = find_matching_entry(oacl, modifier, &rentry, 1);
+
+ if (dmatch != MATCH_NONE) {
+ if (dmatch == MATCH_EXACT)
+/* Nothing to be done */
+ goto ma_exit;
+
+ if (dmatch == MATCH_PARTIAL) {
+ merge_entry_perms(rentry, modifier);
+ goto ma_exit;
+ }
+ }
+/* Insert the entry in canonical order */
+ cpos = find_canonical_position(oacl, modifier);
+ if (0!= acl_create_entry_np(&oacl, &newent, cpos))
+ err(1, "acl_create_entry() failed");
+ acl_copy_entry(newent, modifier);
+ }
+ } else if (optflags & ACL_DELETE_FLAG) {
+ if (flag_new_acl) {
+ warnx("No ACL present '%s'", path);
+ retval = 1;
+ } else if (position != -1 ) {
+ if (0 != acl_get_entry(oacl, position, &rentry)) {
+ warnx("Invalid entry number '%s'", path);
+ retval = 1;
+ } else {
+ acl_delete_entry(oacl, rentry);
+ }
+ } else {
+ unsigned match_found = 0, aindex;
+ for (aindex = 0;
+ acl_get_entry(oacl, rentry == NULL ?
+ ACL_FIRST_ENTRY :
+ ACL_NEXT_ENTRY, &rentry) == 0;
+ aindex++) {
+ unsigned cmp;
+ cmp = compare_acl_entries(rentry, modifier);
+ if ((cmp == MATCH_EXACT) ||
+ (cmp == MATCH_PARTIAL)) {
+ match_found++;
+ if (cmp == MATCH_EXACT)
+ acl_delete_entry(oacl, rentry);
+ else {
+ int valid_perms;
+/* In the event of a partial match, remove the specified perms from the
+ * entry */
+ subtract_from_entry(rentry, modifier, &valid_perms);
+ /* if no perms survived then delete the entry */
+ if (valid_perms == 0)
+ acl_delete_entry(oacl, rentry);
+ }
+ }
+ }
+ if (0 == match_found) {
+ warnx("Entry not found when attempting delete '%s'",path);
+ retval = 1;
+ }
+ }
+ } else if (optflags & ACL_REWRITE_FLAG) {
+ acl_entry_t rentry;
+
+ if (-1 == position) {
+ usage();
+ }
+ if (0 == flag_new_acl) {
+ if (0 != acl_get_entry(oacl, position,
+ &rentry))
+ err(1, "Invalid entry number '%s'", path);
+
+ if (0 != acl_delete_entry(oacl, rentry))
+ err(1, "Unable to delete entry '%s'", path);
+ }
+ if (0!= acl_create_entry_np(&oacl, &newent, position))
+ err(1, "acl_create_entry() failed");
+ acl_copy_entry(newent, modifier);
+ }
+ma_exit:
+ *oaclp = oacl;
+ return retval;
+}
+
+int
+modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow) {
+
+ acl_t oacl = NULL;
+ unsigned aindex = 0, flag_new_acl = 0;
+ acl_entry_t newent = NULL;
+ acl_entry_t entry = NULL;
+ unsigned retval = 0;
+
+ extern int fflag;
+
+/* XXX acl_get_file() returns a zero entry ACL if an ACL was previously
+ * associated with the file, and has had its entries removed.
+ * However, POSIX 1003.1e states that a zero entry ACL should be
+ * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is
+ * associated with the path; it
+ * does not specifically state that a request for ACL_TYPE_EXTENDED
+ * should not return a zero entry ACL, however.
+ */
+
+/* Determine if we've been given a zero entry ACL, or create an ACL if
+ * none exists. There are some issues to consider here: Should we create
+ * a zero-entry ACL for a delete or check canonicity operation?
+ */
+
+ if (path == NULL)
+ usage();
+
+ if (optflags & ACL_CLEAR_FLAG) {
+ filesec_t fsec = filesec_init();
+ if (fsec == NULL) {
+ err(1, "filesec_init() failed");
+ }
+ if (filesec_set_property(fsec, FILESEC_ACL, _FILESEC_REMOVE_ACL) != 0) {
+ err(1, "filesec_set_property() failed");
+ }
+ if (follow) {
+ if (chmodx_np(path, fsec) != 0) {
+ if (!fflag) {
+ warn("Failed to clear ACL on file %s", path);
+ }
+ retval = 1;
+ }
+ } else {
+ int fd = open(path, O_SYMLINK);
+ if (fd != -1) {
+ if (fchmodx_np(fd, fsec) != 0) {
+ if (!fflag) {
+ warn("Failed to clear ACL on file %s", path);
+ }
+ retval = 1;
+ }
+ close(fd);
+ } else {
+ if (!fflag) {
+ warn("Failed to open file %s", path);
+ }
+ retval = 1;
+ }
+ }
+ filesec_free(fsec);
+ return (retval);
+ }
+
+ if (optflags & ACL_FROM_STDIN) {
+ oacl = acl_dup(modifier);
+ } else {
+ if (follow) {
+ oacl = acl_get_file(path, ACL_TYPE_EXTENDED);
+ } else {
+ int fd = open(path, O_SYMLINK);
+ if (fd != -1) {
+ oacl = acl_get_fd_np(fd, ACL_TYPE_EXTENDED);
+ close(fd);
+ }
+ }
+ if ((oacl == NULL) ||
+ (acl_get_entry(oacl,ACL_FIRST_ENTRY, &newent) != 0)) {
+ if ((oacl = acl_init(1)) == NULL)
+ err(1, "acl_init() failed");
+ flag_new_acl = 1;
+ position = 0;
+ }
+
+ if ((0 == flag_new_acl) && (optflags & (ACL_REMOVE_INHERIT_FLAG |
+ ACL_REMOVE_INHERITED_ENTRIES))) {
+ acl_t facl = NULL;
+ if ((facl = acl_init(1)) == NULL)
+ err(1, "acl_init() failed");
+ for (aindex = 0;
+ acl_get_entry(oacl,
+ (entry == NULL ? ACL_FIRST_ENTRY :
+ ACL_NEXT_ENTRY), &entry) == 0;
+ aindex++) {
+ acl_flagset_t eflags;
+ acl_entry_t fent = NULL;
+ if (acl_get_flagset_np(entry, &eflags) != 0) {
+ err(1, "Unable to obtain flagset");
+ }
+
+ if (acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) {
+ if (optflags & ACL_REMOVE_INHERIT_FLAG) {
+ acl_delete_flag_np(eflags, ACL_ENTRY_INHERITED);
+ acl_set_flagset_np(entry, eflags);
+ acl_create_entry(&facl, &fent);
+ acl_copy_entry(fent, entry);
+ }
+ }
+ else {
+ acl_create_entry(&facl, &fent);
+ acl_copy_entry(fent, entry);
+ }
+ }
+ if (oacl)
+ acl_free(oacl);
+ oacl = facl;
+ } else if (optflags & ACL_TO_STDOUT) {
+ ssize_t len; /* need to get printacl() from ls(1) */
+ char *text = acl_to_text(oacl, &len);
+ puts(text);
+ acl_free(text);
+ } else if (optflags & ACL_CHECK_CANONICITY) {
+ if (flag_new_acl) {
+ warnx("No ACL currently associated with file '%s'", path);
+ }
+ retval = is_canonical(oacl);
+ } else if ((optflags & ACL_SET_FLAG) && (position == -1) &&
+ (!is_canonical(oacl))) {
+ warnx("The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# ", path);
+ retval = 1;
+ } else if (((optflags & ACL_DELETE_FLAG) && (position != -1))
+ || (optflags & ACL_CHECK_CANONICITY)) {
+ retval = modify_acl(&oacl, NULL, optflags, position,
+ inheritance_level, flag_new_acl, path);
+ } else if ((optflags & (ACL_REMOVE_INHERIT_FLAG|ACL_REMOVE_INHERITED_ENTRIES)) && flag_new_acl) {
+ warnx("No ACL currently associated with file '%s'", path);
+ retval = 1;
+ } else {
+ if (!modifier) { /* avoid bus error in acl_get_entry */
+ errx(1, "Internal error: modifier should not be NULL");
+ }
+ for (aindex = 0;
+ acl_get_entry(modifier,
+ (entry == NULL ? ACL_FIRST_ENTRY :
+ ACL_NEXT_ENTRY), &entry) == 0;
+ aindex++) {
+
+ retval += modify_acl(&oacl, entry, optflags,
+ position, inheritance_level,
+ flag_new_acl, path);
+ }
+ }
+ }
+
+/* XXX Potential race here, since someone else could've modified or
+ * read the ACL on this file (with the intention of modifying it) in
+ * the interval from acl_get_file() to acl_set_file(); we can
+ * minimize one aspect of this window by comparing the original acl
+ * to a fresh one from acl_get_file() but we could consider a
+ * "changeset" mechanism, common locking strategy, or kernel
+ * supplied reservation mechanism to prevent this race.
+ */
+ if (!(optflags & (ACL_TO_STDOUT|ACL_CHECK_CANONICITY))) {
+ int status = -1;
+ if (follow) {
+ status = acl_set_file(path, ACL_TYPE_EXTENDED, oacl);
+ } else {
+ int fd = open(path, O_SYMLINK);
+ if (fd != -1) {
+ status = acl_set_fd_np(fd, oacl,
+ ACL_TYPE_EXTENDED);
+ close(fd);
+ }
+ }
+ if (status != 0) {
+ if (!fflag)
+ warn("Failed to set ACL on file '%s'", path);
+ retval = 1;
+ }
+ }
+
+ if (oacl)
+ acl_free(oacl);
+
+ return retval;
+}
+
+#endif /*__APPLE__*/
diff --git a/file_cmds/chmod/chmod_acl.h b/file_cmds/chmod/chmod_acl.h
new file mode 100644
index 0000000..b4667e9
--- /dev/null
+++ b/file_cmds/chmod/chmod_acl.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _CHMOD_ACL_H_
+#define _CHMOD_ACL_H_
+
+#ifdef __APPLE__
+#include <pwd.h>
+#include <grp.h>
+#include <ctype.h>
+#include <sys/acl.h>
+#include <sys/kauth.h>
+#include <uuid/uuid.h>
+
+#define ACL_FLAG (1<<0)
+#define ACL_SET_FLAG (1<<1)
+#define ACL_DELETE_FLAG (1<<2)
+#define ACL_REWRITE_FLAG (1<<3)
+#define ACL_ORDER_FLAG (1<<4)
+#define ACL_INHERIT_FLAG (1<<5)
+#define ACL_FOLLOW_LINK (1<<6)
+#define ACL_FROM_STDIN (1<<7)
+#define ACL_CHECK_CANONICITY (1<<8)
+#define ACL_REMOVE_INHERIT_FLAG (1<<9)
+#define ACL_REMOVE_INHERITED_ENTRIES (1<<10)
+#define ACL_NO_TRANSLATE (1<<11)
+#define ACL_INVOKE_EDITOR (1<<12)
+#define ACL_TO_STDOUT (1<<13)
+#define ACL_CLEAR_FLAG (1<<14)
+
+#define INHERITANCE_TIER (-5)
+#define MINIMUM_TIER (-1000)
+
+#define MATCH_EXACT (2)
+#define MATCH_PARTIAL (1)
+#define MATCH_NONE (-1)
+#define MATCH_SUBSET (-2)
+#define MATCH_SUPERSET (-3)
+
+#define MAX_ACL_TEXT_SIZE 4096
+#define MAX_INHERITANCE_LEVEL 1024
+
+extern int search_acl_block(char *tok);
+extern int parse_entry(char *entrybuf, acl_entry_t newent);
+extern acl_t parse_acl_entries(const char *input);
+extern int score_acl_entry(acl_entry_t entry);
+extern unsigned get_inheritance_level(acl_entry_t entry);
+extern int compare_acl_qualifiers(uuid_t *qa, uuid_t *qb);
+extern int compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms);
+extern int compare_acl_entries(acl_entry_t a, acl_entry_t b);
+extern unsigned is_canonical(acl_t acl);
+extern int find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentry, unsigned match_inherited);
+extern unsigned find_canonical_position(acl_t acl, acl_entry_t modifier);
+extern int subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int *valid_perms);
+extern int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, int position, int inheritance_level, unsigned flag_new_acl, const char* path);
+extern int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow);
+extern uuid_t *name_to_uuid(char *tok, int nametype);
+#endif /* __APPLE__*/
+
+#endif /* _CHMOD_ACL_H_ */