Add NFSv4 support to setfacl(1).

Reviewed by:	rwatson
This commit is contained in:
Edward Tomasz Napierala 2009-09-07 16:19:32 +00:00
parent 2ef29b4c33
commit 3f22187878
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=196936
6 changed files with 540 additions and 222 deletions

View File

@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$");
/* set the appropriate mask the given ACL's */
int
set_acl_mask(acl_t *prev_acl)
set_acl_mask(acl_t *prev_acl, const char *filename)
{
acl_entry_t entry;
acl_t acl;
@ -59,7 +59,7 @@ set_acl_mask(acl_t *prev_acl)
acl = acl_dup(*prev_acl);
if (acl == NULL)
err(1, "acl_dup() failed");
err(1, "%s: acl_dup() failed", filename);
if (n_flag == 0) {
/*
@ -70,7 +70,7 @@ set_acl_mask(acl_t *prev_acl)
* class in the resulting ACL
*/
if (acl_calc_mask(&acl)) {
warn("acl_calc_mask() failed");
warn("%s: acl_calc_mask() failed", filename);
acl_free(acl);
return (-1);
}
@ -86,7 +86,8 @@ set_acl_mask(acl_t *prev_acl)
while (acl_get_entry(acl, entry_id, &entry) == 1) {
entry_id = ACL_NEXT_ENTRY;
if (acl_get_tag_type(entry, &tag) == -1)
err(1, "acl_get_tag_type() failed");
err(1, "%s: acl_get_tag_type() failed",
filename);
if (tag == ACL_MASK) {
acl_free(acl);
@ -100,7 +101,7 @@ set_acl_mask(acl_t *prev_acl)
* file, then write an error message to standard error and
* continue with the next file.
*/
warnx("warning: no mask entry");
warnx("%s: warning: no mask entry", filename);
acl_free(acl);
return (0);
}

View File

@ -36,12 +36,15 @@ __FBSDID("$FreeBSD$");
#include "setfacl.h"
static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new);
static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new,
int acl_brand);
static int
merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new)
merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand)
{
acl_permset_t permset;
acl_entry_type_t entry_type;
acl_flagset_t flagset;
int have_entry;
uid_t *id, *id_new;
@ -59,6 +62,18 @@ merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new)
err(1, "acl_get_permset() failed");
if (acl_set_permset(*entry_new, permset) == -1)
err(1, "acl_set_permset() failed");
if (acl_brand == ACL_BRAND_NFS4) {
if (acl_get_entry_type_np(*entry, &entry_type))
err(1, "acl_get_entry_type_np() failed");
if (acl_set_entry_type_np(*entry_new, entry_type))
err(1, "acl_set_entry_type_np() failed");
if (acl_get_flagset_np(*entry, &flagset))
err(1, "acl_get_flagset_np() failed");
if (acl_set_flagset_np(*entry_new, flagset))
err(1, "acl_set_flagset_np() failed");
}
have_entry = 1;
}
acl_free(id);
@ -71,20 +86,31 @@ merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new)
* merge an ACL into existing file's ACL
*/
int
merge_acl(acl_t acl, acl_t *prev_acl)
merge_acl(acl_t acl, acl_t *prev_acl, const char *filename)
{
acl_entry_t entry, entry_new;
acl_permset_t permset;
acl_t acl_new;
acl_tag_t tag, tag_new;
int entry_id, entry_id_new, have_entry;
acl_entry_type_t entry_type, entry_type_new;
acl_flagset_t flagset;
int entry_id, entry_id_new, have_entry, entry_number = 0;
int acl_brand, prev_acl_brand;
if (acl_type == ACL_TYPE_ACCESS)
acl_new = acl_dup(prev_acl[ACCESS_ACL]);
else
acl_new = acl_dup(prev_acl[DEFAULT_ACL]);
acl_get_brand_np(acl, &acl_brand);
acl_get_brand_np(*prev_acl, &prev_acl_brand);
if (acl_brand != prev_acl_brand) {
warnx("%s: branding mismatch; existing ACL is %s, "
"entry to be merged is %s", filename,
prev_acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e",
acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e");
return (-1);
}
acl_new = acl_dup(*prev_acl);
if (acl_new == NULL)
err(1, "acl_dup() failed");
err(1, "%s: acl_dup() failed", filename);
entry_id = ACL_FIRST_ENTRY;
@ -94,28 +120,45 @@ merge_acl(acl_t acl, acl_t *prev_acl)
/* keep track of existing ACL_MASK entries */
if (acl_get_tag_type(entry, &tag) == -1)
err(1, "acl_get_tag_type() failed - invalid ACL entry");
err(1, "%s: acl_get_tag_type() failed - "
"invalid ACL entry", filename);
if (tag == ACL_MASK)
have_mask = 1;
/* check against the existing ACL entries */
entry_id_new = ACL_FIRST_ENTRY;
while (have_entry == 0 &&
acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) {
while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) {
entry_id_new = ACL_NEXT_ENTRY;
if (acl_get_tag_type(entry, &tag) == -1)
err(1, "acl_get_tag_type() failed");
err(1, "%s: acl_get_tag_type() failed",
filename);
if (acl_get_tag_type(entry_new, &tag_new) == -1)
err(1, "acl_get_tag_type() failed");
err(1, "%s: acl_get_tag_type() failed",
filename);
if (tag != tag_new)
continue;
/*
* For NFSv4, in addition to "tag" and "id" we also
* compare "entry_type".
*/
if (acl_brand == ACL_BRAND_NFS4) {
if (acl_get_entry_type_np(entry, &entry_type))
err(1, "%s: acl_get_entry_type_np() "
"failed", filename);
if (acl_get_entry_type_np(entry_new, &entry_type_new))
err(1, "%s: acl_get_entry_type_np() "
"failed", filename);
if (entry_type != entry_type_new)
continue;
}
switch(tag) {
case ACL_USER:
case ACL_GROUP:
have_entry = merge_user_group(&entry,
&entry_new);
&entry_new, acl_brand);
if (have_entry == 0)
break;
/* FALLTHROUGH */
@ -123,37 +166,127 @@ merge_acl(acl_t acl, acl_t *prev_acl)
case ACL_GROUP_OBJ:
case ACL_OTHER:
case ACL_MASK:
case ACL_EVERYONE:
if (acl_get_permset(entry, &permset) == -1)
err(1, "acl_get_permset() failed");
err(1, "%s: acl_get_permset() failed",
filename);
if (acl_set_permset(entry_new, permset) == -1)
err(1, "acl_set_permset() failed");
err(1, "%s: acl_set_permset() failed",
filename);
if (acl_brand == ACL_BRAND_NFS4) {
if (acl_get_entry_type_np(entry, &entry_type))
err(1, "%s: acl_get_entry_type_np() failed",
filename);
if (acl_set_entry_type_np(entry_new, entry_type))
err(1, "%s: acl_set_entry_type_np() failed",
filename);
if (acl_get_flagset_np(entry, &flagset))
err(1, "%s: acl_get_flagset_np() failed",
filename);
if (acl_set_flagset_np(entry_new, flagset))
err(1, "%s: acl_set_flagset_np() failed",
filename);
}
have_entry = 1;
break;
default:
/* should never be here */
errx(1, "Invalid tag type: %i", tag);
errx(1, "%s: invalid tag type: %i", filename, tag);
break;
}
}
/* if this entry has not been found, it must be new */
if (have_entry == 0) {
if (acl_create_entry(&acl_new, &entry_new) == -1) {
acl_free(acl_new);
return (-1);
/*
* NFSv4 ACL entries must be prepended to the ACL.
* Appending them at the end makes no sense, since
* in most cases they wouldn't even get evaluated.
*/
if (acl_brand == ACL_BRAND_NFS4) {
if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) {
warn("%s: acl_create_entry_np() failed", filename);
acl_free(acl_new);
return (-1);
}
/*
* Without this increment, adding several
* entries at once, for example
* "setfacl -m user:1:r:allow,user:2:r:allow",
* would make them appear in reverse order.
*/
entry_number++;
} else {
if (acl_create_entry(&acl_new, &entry_new) == -1) {
warn("%s: acl_create_entry() failed", filename);
acl_free(acl_new);
return (-1);
}
}
if (acl_copy_entry(entry_new, entry) == -1)
err(1, "acl_copy_entry() failed");
err(1, "%s: acl_copy_entry() failed", filename);
}
}
if (acl_type == ACL_TYPE_ACCESS) {
acl_free(prev_acl[ACCESS_ACL]);
prev_acl[ACCESS_ACL] = acl_new;
} else {
acl_free(prev_acl[DEFAULT_ACL]);
prev_acl[DEFAULT_ACL] = acl_new;
}
acl_free(*prev_acl);
*prev_acl = acl_new;
return (0);
}
int
add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename)
{
acl_entry_t entry, entry_new;
acl_t acl_new;
int entry_id, acl_brand, prev_acl_brand;
acl_get_brand_np(acl, &acl_brand);
acl_get_brand_np(*prev_acl, &prev_acl_brand);
if (prev_acl_brand != ACL_BRAND_NFS4) {
warnx("%s: the '-a' option is only applicable to NFSv4 ACLs",
filename);
return (-1);
}
if (acl_brand != ACL_BRAND_NFS4) {
warnx("%s: branding mismatch; existing ACL is NFSv4, "
"entry to be added is POSIX.1e", filename);
return (-1);
}
acl_new = acl_dup(*prev_acl);
if (acl_new == NULL)
err(1, "%s: acl_dup() failed", filename);
entry_id = ACL_FIRST_ENTRY;
while (acl_get_entry(acl, entry_id, &entry) == 1) {
entry_id = ACL_NEXT_ENTRY;
if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) {
warn("%s: acl_create_entry_np() failed", filename);
acl_free(acl_new);
return (-1);
}
/*
* Without this increment, adding several
* entries at once, for example
* "setfacl -m user:1:r:allow,user:2:r:allow",
* would make them appear in reverse order.
*/
entry_number++;
if (acl_copy_entry(entry_new, entry) == -1)
err(1, "%s: acl_copy_entry() failed", filename);
}
acl_free(*prev_acl);
*prev_acl = acl_new;
return (0);
}

View File

@ -41,21 +41,31 @@ __FBSDID("$FreeBSD$");
* remove ACL entries from an ACL
*/
int
remove_acl(acl_t acl, acl_t *prev_acl)
remove_acl(acl_t acl, acl_t *prev_acl, const char *filename)
{
acl_entry_t entry;
acl_t acl_new;
acl_tag_t tag;
int carried_error, entry_id;
int carried_error, entry_id, acl_brand, prev_acl_brand;
carried_error = 0;
if (acl_type == ACL_TYPE_ACCESS)
acl_new = acl_dup(prev_acl[ACCESS_ACL]);
else
acl_new = acl_dup(prev_acl[DEFAULT_ACL]);
acl_get_brand_np(acl, &acl_brand);
acl_get_brand_np(*prev_acl, &prev_acl_brand);
if (acl_brand != prev_acl_brand) {
warnx("%s: branding mismatch; existing ACL is %s, "
"entry to be removed is %s", filename,
prev_acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e",
acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e");
return (-1);
}
carried_error = 0;
acl_new = acl_dup(*prev_acl);
if (acl_new == NULL)
err(1, "acl_dup() failed");
err(1, "%s: acl_dup() failed", filename);
tag = ACL_UNDEFINED_TAG;
@ -64,23 +74,68 @@ remove_acl(acl_t acl, acl_t *prev_acl)
while (acl_get_entry(acl, entry_id, &entry) == 1) {
entry_id = ACL_NEXT_ENTRY;
if (acl_get_tag_type(entry, &tag) == -1)
err(1, "acl_get_tag_type() failed");
err(1, "%s: acl_get_tag_type() failed", filename);
if (tag == ACL_MASK)
have_mask++;
if (acl_delete_entry(acl_new, entry) == -1) {
carried_error++;
warnx("cannot remove non-existent acl entry");
warnx("%s: cannot remove non-existent ACL entry",
filename);
}
}
if (acl_type == ACL_TYPE_ACCESS) {
acl_free(prev_acl[ACCESS_ACL]);
prev_acl[ACCESS_ACL] = acl_new;
} else {
acl_free(prev_acl[DEFAULT_ACL]);
prev_acl[DEFAULT_ACL] = acl_new;
acl_free(*prev_acl);
*prev_acl = acl_new;
if (carried_error)
return (-1);
return (0);
}
int
remove_by_number(uint entry_number, acl_t *prev_acl, const char *filename)
{
acl_entry_t entry;
acl_t acl_new;
acl_tag_t tag;
int carried_error, entry_id;
uint i;
carried_error = 0;
acl_new = acl_dup(*prev_acl);
if (acl_new == NULL)
err(1, "%s: acl_dup() failed", filename);
tag = ACL_UNDEFINED_TAG;
/*
* Find out whether we're removing the mask entry,
* to behave the same as the routine above.
*
* XXX: Is this loop actually needed?
*/
entry_id = ACL_FIRST_ENTRY;
i = 0;
while (acl_get_entry(acl_new, entry_id, &entry) == 1) {
entry_id = ACL_NEXT_ENTRY;
if (i != entry_number)
continue;
if (acl_get_tag_type(entry, &tag) == -1)
err(1, "%s: acl_get_tag_type() failed", filename);
if (tag == ACL_MASK)
have_mask++;
}
if (acl_delete_entry_np(acl_new, entry_number) == -1) {
carried_error++;
warn("%s: acl_delete_entry_np() failed", filename);
}
acl_free(*prev_acl);
*prev_acl = acl_new;
if (carried_error)
return (-1);
@ -91,18 +146,14 @@ remove_acl(acl_t acl, acl_t *prev_acl)
* remove default entries
*/
int
remove_default(acl_t *prev_acl)
remove_default(acl_t *prev_acl, const char *filename)
{
if (prev_acl[1]) {
acl_free(prev_acl[1]);
prev_acl[1] = acl_init(ACL_MAX_ENTRIES);
if (prev_acl[1] == NULL)
err(1, "acl_init() failed");
} else {
warn("cannot remove default ACL");
return (-1);
}
acl_free(*prev_acl);
*prev_acl = acl_init(ACL_MAX_ENTRIES);
if (*prev_acl == NULL)
err(1, "%s: acl_init() failed", filename);
return (0);
}
@ -110,71 +161,14 @@ remove_default(acl_t *prev_acl)
* remove extended entries
*/
void
remove_ext(acl_t *prev_acl)
remove_ext(acl_t *prev_acl, const char *filename)
{
acl_t acl_new, acl_old;
acl_entry_t entry, entry_new;
acl_permset_t perm;
acl_tag_t tag;
int entry_id, have_mask_entry;
acl_t acl_new;
if (acl_type == ACL_TYPE_ACCESS)
acl_old = acl_dup(prev_acl[ACCESS_ACL]);
else
acl_old = acl_dup(prev_acl[DEFAULT_ACL]);
if (acl_old == NULL)
err(1, "acl_dup() failed");
have_mask_entry = 0;
acl_new = acl_init(ACL_MAX_ENTRIES);
acl_new = acl_strip_np(*prev_acl, !n_flag);
if (acl_new == NULL)
err(1, "acl_init() failed");
tag = ACL_UNDEFINED_TAG;
err(1, "%s: acl_strip_np() failed", filename);
/* only save the default user/group/other entries */
entry_id = ACL_FIRST_ENTRY;
while (acl_get_entry(acl_old, entry_id, &entry) == 1) {
entry_id = ACL_NEXT_ENTRY;
if (acl_get_tag_type(entry, &tag) == -1)
err(1, "acl_get_tag_type() failed");
switch(tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_OTHER:
if (acl_get_tag_type(entry, &tag) == -1)
err(1, "acl_get_tag_type() failed");
if (acl_get_permset(entry, &perm) == -1)
err(1, "acl_get_permset() failed");
if (acl_create_entry(&acl_new, &entry_new) == -1)
err(1, "acl_create_entry() failed");
if (acl_set_tag_type(entry_new, tag) == -1)
err(1, "acl_set_tag_type() failed");
if (acl_set_permset(entry_new, perm) == -1)
err(1, "acl_get_permset() failed");
if (acl_copy_entry(entry_new, entry) == -1)
err(1, "acl_copy_entry() failed");
break;
case ACL_MASK:
have_mask_entry = 1;
break;
default:
break;
}
}
if (have_mask_entry && n_flag == 0) {
if (acl_calc_mask(&acl_new) == -1)
err(1, "acl_calc_mask() failed");
} else {
have_mask = 1;
}
if (acl_type == ACL_TYPE_ACCESS) {
acl_free(prev_acl[ACCESS_ACL]);
prev_acl[ACCESS_ACL] = acl_new;
} else {
acl_free(prev_acl[DEFAULT_ACL]);
prev_acl[DEFAULT_ACL] = acl_new;
}
acl_free(*prev_acl);
*prev_acl = acl_new;
}

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd January 7, 2001
.Dd September 5, 2009
.Dt SETFACL 1
.Os
.Sh NAME
@ -34,9 +34,10 @@
.Sh SYNOPSIS
.Nm
.Op Fl bdhkn
.Op Fl a Ar position entries
.Op Fl m Ar entries
.Op Fl M Ar file
.Op Fl x Ar entries
.Op Fl x Ar entries | position
.Op Fl X Ar file
.Op Ar
.Sh DESCRIPTION
@ -50,9 +51,19 @@ the file names are taken from the standard input.
.Pp
The following options are available:
.Bl -tag -width indent
.It Fl a Ar position entries
Modify the ACL on the specified files by inserting new
ACL entries
specified in
.Ar entries ,
starting at position
.Ar position ,
counting from zero.
This option is only applicable to NFSv4 ACLs.
.It Fl b
Remove all ACL entries except for the three required entries.
If the ACL contains a
Remove all ACL entries except for the three required entries
(POSIX.1e ACLs) or six "canonical" entries (NFSv4 ACLs).
If the POSIX.1e ACL contains a
.Dq Li mask
entry, the permissions of the
.Dq Li group
@ -66,7 +77,7 @@ entries of the current ACL.
The operations apply to the default ACL entries instead of
access ACL entries.
Currently only directories may have
default ACL's.
default ACL's. This option is not applicable to NFSv4 ACLs.
.It Fl h
If the target of the operation is a symbolic link, perform the operation
on the symbolic link itself, rather than following the link.
@ -77,7 +88,7 @@ is not considered an error if the specified files do not have
any default ACL entries.
An error will be reported if any of
the specified files cannot have a default entry (i.e.\&
non-directories).
non-directories). This option is not applicable to NFSv4 ACLs.
.It Fl m Ar entries
Modify the ACL entries on the specified files by adding new
entries and modifying existing ACL entries with the ACL entries
@ -95,11 +106,15 @@ is
the input is taken from stdin.
.It Fl n
Do not recalculate the permissions associated with the ACL
mask entry.
.It Fl x Ar entries
Remove the ACL entries specified in
mask entry. This option is not applicable to NFSv4 ACLs.
.It Fl x Ar entries | position
If
.Ar entries
is specified, remove the ACL entries specified there
from the access or default ACL of the specified files.
Otherwise, remove entry at index
.Ar position ,
counting from zero.
.It Fl X Ar file
Remove the ACL entries specified in the file
.Ar file
@ -108,8 +123,8 @@ from the access or default ACL of the specified files.
.Pp
The above options are evaluated in the order specified
on the command-line.
.Sh ACL ENTRIES
An ACL entry contains three colon-separated fields:
.Sh POSIX.1e ACL ENTRIES
A POSIX.1E ACL entry contains three colon-separated fields:
an ACL tag, an ACL qualifier, and discretionary access
permissions:
.Bl -tag -width indent
@ -223,7 +238,7 @@ previously specified; whitespace is ignored; any text after a
.Ql #
is ignored (comments).
.Pp
When ACL entries are evaluated, the access check algorithm checks
When POSIX.1e ACL entries are evaluated, the access check algorithm checks
the ACL entries in the following order: file owner,
.Dq Li user
ACL entries, file owning group,
@ -243,13 +258,135 @@ ACL entries for user, group, other and mask must be set.
For more details see the examples below.
Default ACLs can be created by using
.Fl d .
.Sh NFSv4 ACL ENTRIES
An NFSv4 ACL entry contains four or five colon-separated fields: an ACL tag,
an ACL qualifier (only for
.Dq Li user
and
.Dq Li group
tags), discretionary access permissions, ACL inheritance flags, and ACL type:
.Bl -tag -width indent
.It Ar "ACL tag"
The ACL tag specifies the ACL entry type and consists of
one of the following:
.Dq Li user
or
.Ql u
specifying the access
granted to the specified user;
.Dq Li group
or
.Ql g
specifying the access granted to the specified group;
.Dq Li owner@
specifying the access granted to the owner of the file;
.Dq Li group@
specifying the access granted to the file owning group;
.Dq Li everyone@
specifying everyone. Note that
.Dq Li everyone@
is not the same as traditional Unix
.Dq Li other
- it means,
literally, everyone, including file owner and owning group.
.It Ar "ACL qualifier"
The ACL qualifier field describes the user or group associated with
the ACL entry.
It may consist of one of the following: uid or
user name, or gid or group name. In entries whose tag type is
one of
.Dq Li owner@ ,
.Dq Li group@ ,
or
.Dq Li everyone@ ,
this field is ommited altogether, including the trailing comma.
.It Ar "access permissions"
Access permissions may be specified in either short or long form.
Short and long forms may not be mixed.
Permissions in long form are separated by the
.Ql /
character; in short form, they are concatenated together.
Valid permissions are:
.Bl -tag -width ".Dv short"
.It Short
Long
.It r
read_data
.It w
write_data
.It x
execute
.It p
append_data
.It d
delete_child
.It D
delete
.It a
read_attributes
.It A
write_attributes
.It R
read_xattr
.It W
write_xattr
.It c
read_acl
.It C
write_acl
.It o
write_owner
.It S
synchronize
.El
.It Ar "ACL inheritance flags"
Inheritance flags may be specified in either short or long form.
Short and long forms may not be mixed.
Access flags in long form are separated by the
.Ql /
character; in short form, they are concatenated together.
Valid inheritance flags are:
.Bl -tag -width ".Dv short"
.It Short
Long
.It f
file_inherit
.It d
dir_inherit
.It i
inherit_only
.It n
no_propagate
.El
.Pp
Inheritance flags may be only set on directories.
.It Ar "ACL type"
The ACL type field is either
.Dq Li allow
or
.Dq Li deny .
.El
.Pp
ACL entries applied from a file using the
.Fl M
or
.Fl X
options shall be of the following form: one ACL entry per line, as
previously specified; whitespace is ignored; any text after a
.Ql #
is ignored (comments).
.Pp
NFSv4 ACL entries are evaluated in their visible order.
.Pp
Multiple ACL entries specified on the command line are
separated by commas.
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
.Dl setfacl -d -m u::rwx,g::rx,o::rx,mask::rwx dir
.Dl setfacl -d -m g:admins:rwx dir
.Pp
The first command sets the mandatory elements of the default ACL.
The first command sets the mandatory elements of the POSIX.1e default ACL.
The second command specifies that users in group admins can have read, write, and execute
permissions for directory named "dir".
It should be noted that any files or directories created underneath "dir" will
@ -259,9 +396,13 @@ inherit these default ACLs upon creation.
.Pp
Sets read, write, and execute permissions for the
.Pa file
owner's ACL entry and read and write permissions for group mail on
owner's POSIX.1e ACL entry and read and write permissions for group mail on
.Pa file .
.Pp
.Dl setfacl -m owner@:rwxp::allow,g:mail:rwp::allow file
.Pp
Semantically equal to the example above, but for NFSv4 ACL.
.Pp
.Dl setfacl -M file1 file2
.Pp
Sets/updates the ACL entries contained in
@ -271,10 +412,15 @@ on
.Pp
.Dl setfacl -x g:mail:rw file
.Pp
Remove the group mail ACL entry containing read/write permissions
Remove the group mail POSIX.1e ACL entry containing read/write permissions
from
.Pa file .
.Pp
.Dl setfacl -x0 file
.Pp
Remove the first entry from the NFSv4 ACL from
.Pa file .
.Pp
.Dl setfacl -bn file
.Pp
Remove all

View File

@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -41,9 +42,8 @@ __FBSDID("$FreeBSD$");
#include "setfacl.h"
static void add_filename(const char *filename);
static acl_t *get_file_acls(const char *filename);
static void usage(void);
static void add_filename(const char *filename);
static void usage(void);
static void
add_filename(const char *filename)
@ -59,57 +59,28 @@ add_filename(const char *filename)
TAILQ_INSERT_TAIL(&filelist, file, next);
}
static acl_t *
get_file_acls(const char *filename)
{
acl_t *acl;
struct stat sb;
if (stat(filename, &sb) == -1) {
warn("stat() of %s failed", filename);
return (NULL);
}
acl = zmalloc(sizeof(acl_t) * 2);
if (h_flag)
acl[ACCESS_ACL] = acl_get_link_np(filename, ACL_TYPE_ACCESS);
else
acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS);
if (acl[ACCESS_ACL] == NULL)
err(1, "acl_get_file() failed");
if (S_ISDIR(sb.st_mode)) {
if (h_flag)
acl[DEFAULT_ACL] = acl_get_link_np(filename,
ACL_TYPE_DEFAULT);
else
acl[DEFAULT_ACL] = acl_get_file(filename,
ACL_TYPE_DEFAULT);
if (acl[DEFAULT_ACL] == NULL)
err(1, "acl_get_file() failed");
} else
acl[DEFAULT_ACL] = NULL;
return (acl);
}
static void
usage(void)
{
fprintf(stderr, "usage: setfacl [-bdhkn] [-m entries] [-M file] "
"[-x entries] [-X file] [file ...]\n");
fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] "
"[-m entries] [-M file] [-x entries] [-X file] [file ...]\n");
exit(1);
}
int
main(int argc, char *argv[])
{
acl_t *acl, final_acl;
acl_t acl;
acl_type_t acl_type;
char filename[PATH_MAX];
int local_error, carried_error, ch, i;
int local_error, carried_error, ch, i, entry_number, ret;
int h_flag;
struct sf_file *file;
struct sf_entry *entry;
const char *fn_dup;
char *end;
struct stat sb;
acl_type = ACL_TYPE_ACCESS;
carried_error = local_error = 0;
@ -118,13 +89,13 @@ main(int argc, char *argv[])
TAILQ_INIT(&entrylist);
TAILQ_INIT(&filelist);
while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1)
while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1)
switch(ch) {
case 'M':
entry = zmalloc(sizeof(struct sf_entry));
entry->acl = get_acl_from_file(optarg);
if (entry->acl == NULL)
err(1, "get_acl_from_file() failed");
err(1, "%s: get_acl_from_file() failed", optarg);
entry->op = OP_MERGE_ACL;
TAILQ_INSERT_TAIL(&entrylist, entry, next);
break;
@ -134,6 +105,25 @@ main(int argc, char *argv[])
entry->op = OP_REMOVE_ACL;
TAILQ_INSERT_TAIL(&entrylist, entry, next);
break;
case 'a':
entry = zmalloc(sizeof(struct sf_entry));
entry_number = strtol(optarg, &end, 10);
if (end - optarg != (int)strlen(optarg))
errx(1, "%s: invalid entry number", optarg);
if (entry_number < 0)
errx(1, "%s: entry number cannot be less than zero", optarg);
entry->entry_number = entry_number;
if (argv[optind] == NULL)
errx(1, "missing ACL");
entry->acl = acl_from_text(argv[optind]);
if (entry->acl == NULL)
err(1, "%s", argv[optind]);
optind++;
entry->op = OP_ADD_ACL;
TAILQ_INSERT_TAIL(&entrylist, entry, next);
break;
case 'b':
entry = zmalloc(sizeof(struct sf_entry));
entry->op = OP_REMOVE_EXT;
@ -163,10 +153,18 @@ main(int argc, char *argv[])
break;
case 'x':
entry = zmalloc(sizeof(struct sf_entry));
entry->acl = acl_from_text(optarg);
if (entry->acl == NULL)
err(1, "%s", optarg);
entry->op = OP_REMOVE_ACL;
entry_number = strtol(optarg, &end, 10);
if (end - optarg == (int)strlen(optarg)) {
if (entry_number < 0)
errx(1, "%s: entry number cannot be less than zero", optarg);
entry->entry_number = entry_number;
entry->op = OP_REMOVE_BY_NUMBER;
} else {
entry->acl = acl_from_text(optarg);
if (entry->acl == NULL)
err(1, "%s", optarg);
entry->op = OP_REMOVE_ACL;
}
TAILQ_INSERT_TAIL(&entrylist, entry, next);
break;
default:
@ -199,16 +197,51 @@ main(int argc, char *argv[])
/* cycle through each file */
TAILQ_FOREACH(file, &filelist, next) {
/* get our initial access and default ACL's */
acl = get_file_acls(file->filename);
if (acl == NULL)
continue;
if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) {
warnx("Default ACL not valid for %s", file->filename);
local_error = 0;
if (stat(file->filename, &sb) == -1) {
warn("%s: stat() failed", file->filename);
continue;
}
local_error = 0;
if (acl_type == ACL_TYPE_DEFAULT && S_ISDIR(sb.st_mode) == 0) {
warnx("%s: default ACL may only be set on a directory",
file->filename);
continue;
}
if (h_flag)
ret = lpathconf(file->filename, _PC_ACL_NFS4);
else
ret = pathconf(file->filename, _PC_ACL_NFS4);
if (ret > 0) {
if (acl_type == ACL_TYPE_DEFAULT) {
warnx("%s: there are no default entries "
"in NFSv4 ACLs", file->filename);
continue;
}
acl_type = ACL_TYPE_NFS4;
} else if (ret == 0) {
if (acl_type == ACL_TYPE_NFS4)
acl_type = ACL_TYPE_ACCESS;
} else if (ret < 0 && errno != EINVAL) {
warn("%s: pathconf(..., _PC_ACL_NFS4) failed",
file->filename);
}
if (h_flag)
acl = acl_get_link_np(file->filename, acl_type);
else
acl = acl_get_file(file->filename, acl_type);
if (acl == NULL) {
if (h_flag)
warn("%s: acl_get_link_np() failed",
file->filename);
else
warn("%s: acl_get_file() failed",
file->filename);
continue;
}
/* cycle through each option */
TAILQ_FOREACH(entry, &entrylist, next) {
@ -216,24 +249,44 @@ main(int argc, char *argv[])
continue;
switch(entry->op) {
case OP_ADD_ACL:
local_error += add_acl(entry->acl,
entry->entry_number, &acl, file->filename);
break;
case OP_MERGE_ACL:
local_error += merge_acl(entry->acl, acl);
local_error += merge_acl(entry->acl, &acl,
file->filename);
need_mask = 1;
break;
case OP_REMOVE_EXT:
remove_ext(acl);
remove_ext(&acl, file->filename);
need_mask = 0;
break;
case OP_REMOVE_DEF:
if (acl_type == ACL_TYPE_NFS4) {
warnx("%s: there are no default entries in NFSv4 ACLs; "
"cannot remove", file->filename);
local_error++;
break;
}
if (acl_delete_def_file(file->filename) == -1) {
warn("acl_delete_def_file() failed");
warn("%s: acl_delete_def_file() failed",
file->filename);
local_error++;
}
local_error += remove_default(acl);
if (acl_type == ACL_TYPE_DEFAULT)
local_error += remove_default(&acl,
file->filename);
need_mask = 0;
break;
case OP_REMOVE_ACL:
local_error += remove_acl(entry->acl, acl);
local_error += remove_acl(entry->acl, &acl,
file->filename);
need_mask = 1;
break;
case OP_REMOVE_BY_NUMBER:
local_error += remove_by_number(entry->entry_number,
&acl, file->filename);
need_mask = 1;
break;
}
@ -245,35 +298,27 @@ main(int argc, char *argv[])
continue;
}
if (acl_type == ACL_TYPE_ACCESS) {
final_acl = acl[ACCESS_ACL];
acl_free(acl[DEFAULT_ACL]);
} else {
final_acl = acl[DEFAULT_ACL];
acl_free(acl[ACCESS_ACL]);
}
if (need_mask && (set_acl_mask(&final_acl) == -1)) {
warnx("failed to set ACL mask on %s", file->filename);
if (acl_type != ACL_TYPE_NFS4 && need_mask &&
set_acl_mask(&acl, file->filename) == -1) {
warnx("%s: failed to set ACL mask", file->filename);
carried_error++;
} else if (h_flag) {
if (acl_set_link_np(file->filename, acl_type,
final_acl) == -1) {
acl) == -1) {
carried_error++;
warn("acl_set_link_np() failed for %s",
warn("%s: acl_set_link_np() failed",
file->filename);
}
} else {
if (acl_set_file(file->filename, acl_type,
final_acl) == -1) {
acl) == -1) {
carried_error++;
warn("acl_set_file() failed for %s",
warn("%s: acl_set_file() failed",
file->filename);
}
}
acl_free(final_acl);
free(acl);
acl_free(acl);
}
return (carried_error);

View File

@ -38,15 +38,14 @@
#define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */
#define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */
#define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */
/* ACL types for the acl array */
#define ACCESS_ACL 0
#define DEFAULT_ACL 1
#define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */
#define OP_ADD_ACL 0x05 /* add acls entries at a given position */
/* TAILQ entry for acl operations */
struct sf_entry {
uint op;
acl_t acl;
uint entry_number;
TAILQ_ENTRY(sf_entry) next;
};
TAILQ_HEAD(, sf_entry) entrylist;
@ -61,21 +60,21 @@ TAILQ_HEAD(, sf_file) filelist;
/* files.c */
acl_t get_acl_from_file(const char *filename);
/* merge.c */
int merge_acl(acl_t acl, acl_t *prev_acl);
int merge_acl(acl_t acl, acl_t *prev_acl, const char *filename);
int add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename);
/* remove.c */
int remove_acl(acl_t acl, acl_t *prev_acl);
int remove_default(acl_t *prev_acl);
void remove_ext(acl_t *prev_acl);
int remove_acl(acl_t acl, acl_t *prev_acl, const char *filename);
int remove_by_number(uint entry_number, acl_t *prev_acl, const char *filename);
int remove_default(acl_t *prev_acl, const char *filename);
void remove_ext(acl_t *prev_acl, const char *filename);
/* mask.c */
int set_acl_mask(acl_t *prev_acl);
int set_acl_mask(acl_t *prev_acl, const char *filename);
/* util.c */
void *zmalloc(size_t size);
acl_type_t acl_type;
uint have_mask;
uint need_mask;
uint have_stdin;
uint h_flag;
uint n_flag;
#endif /* _SETFACL_H */