4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
26 * setfacl [-r] -f aclfile file ...
27 * setfacl [-r] -d acl_entries file ...
28 * setfacl [-r] -m acl_entries file ...
29 * setfacl [-r] -s acl_entries file ...
30 * This command deletes/adds/modifies/sets discretionary information for a file
41 #include <sys/types.h>
50 static int get_acl_info(char *filep
, aclent_t
**aclpp
);
51 static int mod_entries(aclent_t
*, int, char *, char *, char *, int);
52 static int set_file_entries(char *, char *, int);
53 static int set_online_entries(char *, char *, int);
55 static int parse_entry_list(aclent_t
**, int *, char *, int);
56 static int convert_to_aclent_t(char *, int *, aclent_t
**, int);
57 static int parse_entry(char *, aclent_t
*, int);
58 static void err_handle(int, aclent_t
*);
59 static int conv_id(char *);
62 main(int argc
, char *argv
[])
71 int aclcnt
; /* used by -m -d */
72 aclent_t
*aclp
; /* used by -m -d */
73 char *aclfilep
; /* acl file argument */
74 char *d_entryp
= NULL
; /* ptr to del entry list */
75 char *m_entryp
= NULL
; /* ptr to mod entry list */
76 char *s_entryp
= NULL
; /* ptr to set entry list */
77 char *work_dp
= NULL
; /* working ptrs for the above */
81 (void) setlocale(LC_ALL
, "");
82 (void) textdomain(TEXT_DOMAIN
);
87 while ((c
= getopt(argc
, argv
, "rm:d:s:f:")) != EOF
) {
93 if (dflag
|| fflag
|| sflag
)
99 if (mflag
|| fflag
|| sflag
)
105 if (fflag
|| sflag
|| mflag
|| dflag
)
111 if (fflag
|| sflag
|| mflag
|| dflag
)
124 /* one of these flags should be set */
125 if (!fflag
&& !sflag
&& !mflag
&& !dflag
)
128 /* no file arguments */
132 for (; optind
< argc
; optind
++) {
133 register char *filep
;
135 filep
= argv
[optind
];
137 /* modify and delete: we need to get the ACL first */
138 if (mflag
|| dflag
) {
139 if (m_entryp
!= NULL
) {
141 work_mp
= strdup(m_entryp
);
142 if (work_mp
== NULL
) {
144 gettext("out of memory %s\n"),
150 if (d_entryp
!= NULL
) {
152 work_dp
= strdup(d_entryp
);
153 if (work_dp
== NULL
) {
155 gettext("out of memory %s\n"),
161 aclcnt
= get_acl_info(filep
, &aclp
);
164 if (mod_entries(aclp
, aclcnt
, work_mp
,
165 work_dp
, filep
, rflag
) == -1)
168 if (set_file_entries(aclfilep
, filep
, rflag
) == -1)
171 if (s_entryp
!= NULL
) {
173 work_sp
= strdup(s_entryp
);
174 if (work_sp
== NULL
) {
176 gettext("out of memory %s\n"),
181 if (set_online_entries(work_sp
, filep
, rflag
) == -1)
189 * For add, modify, and delete, we need to get the ACL of the file first.
192 get_acl_info(char *filep
, aclent_t
**aclpp
)
196 if ((aclcnt
= acl(filep
, GETACLCNT
, 0, NULL
)) < 0) {
197 if (errno
== ENOSYS
) {
198 (void) fprintf(stderr
,
199 gettext("File system doesn't support aclent_t "
201 "See acl(5) for more information on"
202 " ACL styles support by Solaris.\n"));
205 (void) fprintf(stderr
,
206 gettext("%s: failed to get acl count\n"), filep
);
207 perror("get acl count error");
210 if (aclcnt
< MIN_ACL_ENTRIES
) {
211 (void) fprintf(stderr
,
212 gettext("%d: acl count is too small from %s\n"),
217 if ((*aclpp
= (aclent_t
*)malloc(sizeof (aclent_t
) * aclcnt
)) == NULL
) {
218 (void) fprintf(stderr
, gettext("out of memory\n"));
221 if (acl(filep
, GETACL
, aclcnt
, *aclpp
) < 0) {
222 (void) fprintf(stderr
,
223 gettext("%s: failed to get acl entries\n"), filep
);
224 perror("getacl error");
231 * mod_entries() handles add, delete, and modify ACL entries of a file.
232 * The real action is in convert_to_aclent_t() called by parse_entry_list().
233 * aclp: points ACL of a file and may be changed by lower level routine.
234 * modp: modify entry list in ascii format
235 * delp: delete entry list in ascii format
236 * fnamep: file of interest
239 mod_entries(aclent_t
*aclp
, int cnt
, char *modp
, char *delp
,
240 char *fnamep
, int rfg
)
242 int rc
; /* return code */
244 /* modify and add: from -m option */
245 if (parse_entry_list(&aclp
, &cnt
, modp
, MODIFY
) == -1)
248 /* deletion: from -d option */
249 if (parse_entry_list(&aclp
, &cnt
, delp
, DELETE
) == -1)
252 if (aclsort(cnt
, rfg
, aclp
) == -1) {
253 (void) err_handle(cnt
, aclp
);
254 (void) fprintf(stderr
,
255 gettext("aclcnt %d, file %s\n"), cnt
, fnamep
);
259 if (acl(fnamep
, SETACL
, cnt
, aclp
) < 0) {
261 gettext("%s: failed to set acl entries\n"), fnamep
);
262 perror("setacl error");
269 * set_file_entries() creates ACL entries from ACL file (acl_fnamep).
270 * It opens the file and converts every line (one line per acl entry)
271 * into aclent_t format. It then recalculates the mask according to rflag.
272 * Finally it sets ACL to the file (fnamep).
275 set_file_entries(char *acl_fnamep
, char *fnamep
, int rflag
)
283 if (strcmp(acl_fnamep
, "-") == 0)
286 if ((acl_fp
= fopen(acl_fnamep
, "r")) == NULL
) {
287 fprintf(stderr
, gettext("Can't open acl file %s\n"),
292 while (fgets(buf
, BUFSIZ
, acl_fp
) != NULL
) {
293 if (buf
[0] == '#' || buf
[0] == '\n')
296 /* check effective permission: add a null after real perm */
297 if ((tp
= (char *)strchr(buf
, '#')) != NULL
) {
299 while (*tp
== ' ' || *tp
== '\t') {
304 gettext("entry format error %s\n"),
312 /* remove <nl> at the end if there is one */
313 if ((tp
= (char *)strchr(buf
, '\n')) != NULL
)
316 if (convert_to_aclent_t(buf
, &aclcnt
, &aclp
, SET
) == -1)
320 if (aclsort(aclcnt
, rflag
, aclp
) == -1) {
321 (void) err_handle(aclcnt
, aclp
);
322 (void) fprintf(stderr
, gettext("aclcnt %d, aclfile %s\n"),
327 if (acl(fnamep
, SETACL
, aclcnt
, aclp
) < 0) {
329 gettext("%s: failed to set acl entries\n"), fnamep
);
330 perror("setacl error");
337 * set_online_entries() parses the acl entries from command line (setp).
338 * It converts the comma separated acl entries into aclent_t format.
339 * It then recalculates the mask according to rflag.
340 * Finally it sets ACL to the file (fnamep).
343 set_online_entries(char *setp
, char *fnamep
, int rflag
)
349 if (parse_entry_list(&aclp
, &aclcnt
, setp
, SET
) == -1)
352 if (aclsort(aclcnt
, rflag
, aclp
) == -1) {
353 (void) err_handle(aclcnt
, aclp
);
354 (void) fprintf(stderr
,
355 gettext("aclcnt %d, file %s\n"), aclcnt
, fnamep
);
359 if (acl(fnamep
, SETACL
, aclcnt
, aclp
) < 0) {
361 gettext("%s: failed to set acl entries\n"), fnamep
);
362 perror("setacl error");
369 * parse_entry_list() parses entry list (listp) separated by commas.
370 * Once it gets an ACL entry, it calls convert_to_aclent_t() to convert
371 * to internal format.
374 parse_entry_list(aclent_t
**aclpp
, int *aclcntp
, char *listp
, int mode
)
380 while ((commap
= (char *)strchr(listp
, ',')) != NULL
) {
383 /* aclcnt may be updated after the call: add or modify */
384 if (convert_to_aclent_t(listp
, aclcntp
, aclpp
, mode
) == -1)
388 /* this is for only one entry or last entry */
389 if (*listp
!= '\0') {
391 if (convert_to_aclent_t(listp
, aclcntp
, aclpp
, mode
) == -1)
398 * convert_to_aclent_t() converts an acl entry in ascii format (fields separated
399 * by colon) into aclent_t and appends it to the current ACL. It also handles
400 * memory allocation/deallocation for acl entries in aclent_t format.
401 * aclpp that contains acl entries in acl format will be returned.
402 * We don't check duplicates.
405 convert_to_aclent_t(char *entryp
, int *cntp
, aclent_t
**aclpp
, int mode
)
409 aclent_t
*taclp
, *centry
= NULL
, *gentry
= NULL
;
418 new_aclp
= (aclent_t
*)realloc(*aclpp
,
419 sizeof (aclent_t
) * (*cntp
));
421 new_aclp
= (aclent_t
*) malloc(sizeof (aclent_t
) * (*cntp
));
422 if (new_aclp
== NULL
) {
424 gettext("Insufficient memory for acl %d\n"), *cntp
);
428 tmpacl
.a_id
= 0; /* id field needs to be initialized */
429 if (entryp
[0] == 'u')
430 tmpacl
.a_id
= getuid(); /* id field for user */
431 if (entryp
[0] == 'g')
432 tmpacl
.a_id
= getgid(); /* id field for group */
435 if (parse_entry(entryp
, &tmpacl
, mode
) == -1)
438 is_obj
= ((tmpacl
.a_type
== USER_OBJ
) ||
439 (tmpacl
.a_type
== GROUP_OBJ
) ||
440 (tmpacl
.a_type
== CLASS_OBJ
) ||
441 (tmpacl
.a_type
== DEF_USER_OBJ
) ||
442 (tmpacl
.a_type
== DEF_GROUP_OBJ
) ||
443 (tmpacl
.a_type
== DEF_OTHER_OBJ
));
447 case MODIFY
: /* and add */
448 for (taclp
= new_aclp
; cur_cnt
-- > 0; taclp
++) {
449 if (taclp
->a_type
== tmpacl
.a_type
&&
450 ((taclp
->a_id
== tmpacl
.a_id
) || is_obj
)) {
452 /* cnt is added before it's called */
454 taclp
->a_perm
= tmpacl
.a_perm
;
458 if (!found
) /* Add it to the end: no need to change cntp */
459 memcpy(new_aclp
+ *cntp
-1, &tmpacl
, sizeof (aclent_t
));
463 for (taclp
= new_aclp
; cur_cnt
-- > 0; taclp
++) {
464 if (taclp
->a_type
== tmpacl
.a_type
&&
465 ((taclp
->a_id
== tmpacl
.a_id
) || is_obj
)) {
467 /* move up the rest */
468 while (cur_cnt
-- > 0) {
469 memcpy(taclp
, taclp
+1,
482 /* we may check duplicate before copying over?? */
483 memcpy(new_aclp
+ *cntp
-1, &tmpacl
, sizeof (aclent_t
));
488 gettext("Unrecognized mode: internal error\n"));
493 * If converting from non-trivial acl entry to trivial one,
494 * reset CLASS_OBJ's permission with that of GROUP_OBJ.
497 if (mode
== DELETE
) {
498 boolean_t trivial
= B_TRUE
; /* assumption */
500 for (taclp
= new_aclp
; cur_cnt
-- > 0; taclp
++) {
501 switch (taclp
->a_type
) {
513 * Confirmed that the new acl set is
514 * still a non-trivial acl.
520 if (centry
!= NULL
&& gentry
!= NULL
&& trivial
== B_TRUE
)
521 centry
->a_perm
= gentry
->a_perm
;
523 *aclpp
= new_aclp
; /* return new acl entries */
530 (void) fprintf(stderr
, gettext("usage:\n"));
531 (void) fprintf(stderr
,
532 gettext("\tsetfacl [-r] -f aclfile file ...\n"));
533 (void) fprintf(stderr
,
534 gettext("\tsetfacl [-r] -d acl_entries file ...\n"));
535 (void) fprintf(stderr
,
536 gettext("\tsetfacl [-r] -m acl_entries file ...\n"));
537 (void) fprintf(stderr
,
538 gettext("\tsetfacl [-r] -s acl_entries file ...\n"));
543 err_handle(int cnt
, aclent_t
*aclentp
)
548 rc
= aclcheck(aclentp
, cnt
, &which
);
552 gettext("There is more than one user owner entry"));
554 gettext(" -- error found at entry index %d\n"), which
);
558 gettext("There is more than one group owner entry"));
560 gettext(" -- error found at entry index %d\n"), which
);
564 gettext("There is more than one mask entry"));
566 gettext(" -- error found at entry index %d\n"), which
);
570 gettext("There is more than one other entry"));
572 gettext(" -- error found at entry index %d\n"), which
);
574 case DUPLICATE_ERROR
:
576 gettext("Duplicate user or group entries"));
578 gettext(" -- error found at entry index %d\n"), which
);
582 gettext("Missing user/group owner, other, mask entry\n"));
586 gettext("Insufficient memory\n"));
590 gettext("Unrecognized entry type"));
592 gettext(" -- error found at entry index %d\n"), which
);
595 /* error is not from aclcheck */
597 gettext("aclsort error\n"));
603 parse_entry(char *fieldp
, aclent_t
*aclentp
, int mode
)
606 int def_flag
= 0, mo_flag
= 0;
611 colonp
= (char *)strchr(fieldp
, ':');
612 if (colonp
== NULL
) {
614 gettext("Can't find colon delimiter %s\n"), fieldp
);
618 if ((strcmp(fieldp
, "default") == 0) || (strcmp(fieldp
, "d") == 0)) {
621 colonp
= (char *)strchr(fieldp
, ':');
622 if (colonp
== NULL
) {
624 gettext("Can't find colon delimiter %s\n"), fieldp
);
630 /* process entry type */
631 if ((strcmp(fieldp
, "user") == 0) || (strcmp(fieldp
, "u") == 0)) {
633 aclentp
->a_type
= DEF_USER
;
635 aclentp
->a_type
= USER
;
637 if ((strcmp(fieldp
, "group") == 0) || (strcmp(fieldp
, "g") == 0)) {
639 aclentp
->a_type
= DEF_GROUP
;
641 aclentp
->a_type
= GROUP
;
643 if ((strcmp(fieldp
, "mask") == 0) || (strcmp(fieldp
, "m") == 0)) {
645 aclentp
->a_type
= DEF_CLASS_OBJ
;
647 aclentp
->a_type
= CLASS_OBJ
;
649 if ((strcmp(fieldp
, "other") == 0) || (strcmp(fieldp
, "o") == 0)) {
651 aclentp
->a_type
= DEF_OTHER_OBJ
;
653 aclentp
->a_type
= OTHER_OBJ
;
656 /* still can't determine entry type */
657 if (aclentp
->a_type
== 0) {
659 gettext("Unrecognized entry type %s \n"), fieldp
);
663 /* mask and other entries dont have id field */
664 if (aclentp
->a_type
!= CLASS_OBJ
&& aclentp
->a_type
!= OTHER_OBJ
&&
665 aclentp
->a_type
!= DEF_CLASS_OBJ
&&
666 aclentp
->a_type
!= DEF_OTHER_OBJ
) {
669 colonp
= (char *)strchr(fieldp
, ':');
670 if (colonp
== NULL
) {
671 if (mode
!= DELETE
) {
673 gettext("Can't find colon delimiter %s\n"),
680 if (*fieldp
== '\0') {
682 if (aclentp
->a_type
== USER
)
683 aclentp
->a_type
= USER_OBJ
;
684 if (aclentp
->a_type
== DEF_USER
)
685 aclentp
->a_type
= DEF_USER_OBJ
;
686 if (aclentp
->a_type
== GROUP
)
687 aclentp
->a_type
= GROUP_OBJ
;
688 if (aclentp
->a_type
== DEF_GROUP
)
689 aclentp
->a_type
= DEF_GROUP_OBJ
;
691 /* see if it's a user/group name */
692 if (aclentp
->a_type
== USER
||
693 aclentp
->a_type
== USER_OBJ
||
694 aclentp
->a_type
== DEF_USER
||
695 aclentp
->a_type
== DEF_USER_OBJ
) {
696 if ((pwp
= getpwnam(fieldp
)) != NULL
)
697 aclentp
->a_id
= pwp
->pw_uid
;
699 /* treat it as numeric id */
700 id
= conv_id(fieldp
);
707 if ((grp
= getgrnam(fieldp
)) != NULL
)
708 aclentp
->a_id
= grp
->gr_gid
;
710 id
= conv_id(fieldp
);
718 /* it is mask/other entry */
722 /* process permission: rwx and [0]n format */
724 /* delete format: no permission field */
727 colonp
= (char *)strchr(fieldp
, ':');
728 if (colonp
!= NULL
) {
730 /* Use only single : on mask/other entry */
731 (void) fprintf(stderr
, gettext("use only 1 colon for "
732 "mask and other entries.\n"));
735 /* it's ok to have extra colon */
740 if ((int)strlen(fieldp
) > 3) {
742 gettext("only rwx or [0]n format is allowed\n"));
745 if (strlen(fieldp
) == 3) {
747 /* treat it as rwx */
749 aclentp
->a_perm
+= 4;
751 if (*fieldp
!= '-') {
753 gettext("Unrecognized character "));
755 gettext("found in mode field\n"));
760 aclentp
->a_perm
+= 2;
762 if (*fieldp
!= '-') {
764 gettext("Unrecognized character "));
766 gettext("found in mode field\n"));
771 aclentp
->a_perm
+= 1;
773 if (*fieldp
!= '-') {
775 gettext("Unrecognized character "));
777 gettext("found in mode field\n"));
786 if (*fieldp
>= '0' && *fieldp
<= '7')
787 aclentp
->a_perm
= *fieldp
- '0';
789 fprintf(stderr
, gettext("Unrecognized character "));
790 fprintf(stderr
, gettext("found in mode field\n"));
793 if (aclentp
->a_perm
== 0 && *++fieldp
!= '\0') {
794 /* look at next char */
795 if (*fieldp
>= '0' && *fieldp
<= '7')
796 aclentp
->a_perm
= *fieldp
- '0';
798 fprintf(stderr
, gettext("Unrecognized character "));
799 fprintf(stderr
, gettext("found in mode field\n"));
801 gettext("Check also the number of fields "));
803 gettext("(default) mask and other entries\n"));
807 /* check for junk at the end ??? */
812 * This function is different from atoi() in that it checks for
813 * valid digit in the id field whereas atoi() won't report any
817 conv_id(char *fieldp
)
821 for (; *fieldp
!= '\0'; fieldp
++) {
822 if (!isdigit(*fieldp
)) {
823 fprintf(stderr
, gettext("non-digit in id field\n"));
826 a_id
= a_id
* 10 + (*fieldp
- '0');