summaryrefslogtreecommitdiffstats
path: root/bin/setfacl
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2009-09-07 16:19:32 +0000
committertrasz <trasz@FreeBSD.org>2009-09-07 16:19:32 +0000
commit365d19e2c3d1e6500b74bd65ba5a1a89bd987999 (patch)
tree4446fd2ed5aacce2cb2f6bf981a090397df53767 /bin/setfacl
parent7b97612b07db3b51448e286a27728cdfeab64b1c (diff)
downloadFreeBSD-src-365d19e2c3d1e6500b74bd65ba5a1a89bd987999.zip
FreeBSD-src-365d19e2c3d1e6500b74bd65ba5a1a89bd987999.tar.gz
Add NFSv4 support to setfacl(1).
Reviewed by: rwatson
Diffstat (limited to 'bin/setfacl')
-rw-r--r--bin/setfacl/mask.c11
-rw-r--r--bin/setfacl/merge.c189
-rw-r--r--bin/setfacl/remove.c172
-rw-r--r--bin/setfacl/setfacl.1176
-rw-r--r--bin/setfacl/setfacl.c191
-rw-r--r--bin/setfacl/setfacl.h21
6 files changed, 539 insertions, 221 deletions
diff --git a/bin/setfacl/mask.c b/bin/setfacl/mask.c
index b275893..5478302 100644
--- a/bin/setfacl/mask.c
+++ b/bin/setfacl/mask.c
@@ -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);
}
diff --git a/bin/setfacl/merge.c b/bin/setfacl/merge.c
index 9f1b5dd..495e66c 100644
--- a/bin/setfacl/merge.c
+++ b/bin/setfacl/merge.c
@@ -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);
}
diff --git a/bin/setfacl/remove.c b/bin/setfacl/remove.c
index 2324055..6cd82b3 100644
--- a/bin/setfacl/remove.c
+++ b/bin/setfacl/remove.c
@@ -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;
+
+ 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;
- if (acl_type == ACL_TYPE_ACCESS)
- acl_new = acl_dup(prev_acl[ACCESS_ACL]);
- else
- acl_new = acl_dup(prev_acl[DEFAULT_ACL]);
+ 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;
-
- 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);
- if (acl_new == NULL)
- err(1, "acl_init() failed");
- tag = ACL_UNDEFINED_TAG;
-
- /* 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;
+ acl_t acl_new;
- 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;
- }
+ acl_new = acl_strip_np(*prev_acl, !n_flag);
+ if (acl_new == NULL)
+ err(1, "%s: acl_strip_np() 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;
}
diff --git a/bin/setfacl/setfacl.1 b/bin/setfacl/setfacl.1
index e49d18a..f527bbb 100644
--- a/bin/setfacl/setfacl.1
+++ b/bin/setfacl/setfacl.1
@@ -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
diff --git a/bin/setfacl/setfacl.c b/bin/setfacl/setfacl.c
index 2286efb..2835ba8 100644
--- a/bin/setfacl/setfacl.c
+++ b/bin/setfacl/setfacl.c
@@ -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)
+ local_error = 0;
+
+ if (stat(file->filename, &sb) == -1) {
+ warn("%s: stat() failed", file->filename);
continue;
- if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) {
- warnx("Default ACL not valid for %s", file->filename);
+ }
+
+ 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;
}
- local_error = 0;
+ 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);
diff --git a/bin/setfacl/setfacl.h b/bin/setfacl/setfacl.h
index bdf052b..7c11b3a 100644
--- a/bin/setfacl/setfacl.h
+++ b/bin/setfacl/setfacl.h
@@ -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 */
OpenPOWER on IntegriCloud