1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2020 Peter Tribble.
24 */
25
26 /*
27 * setfacl [-r] -f aclfile file ...
28 * setfacl [-r] -d acl_entries file ...
29 * setfacl [-r] -m acl_entries file ...
30 * setfacl [-r] -s acl_entries file ...
31 * This command deletes/adds/modifies/sets discretionary information for a file
32 * or files.
33 */
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <pwd.h>
38 #include <grp.h>
39 #include <string.h>
40 #include <locale.h>
41 #include <sys/acl.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <ctype.h>
46
47 #define ADD 1
48 #define MODIFY 2
49 #define DELETE 3
50 #define SET 4
51
52 static int get_acl_info(char *filep, aclent_t **aclpp);
53 static int mod_entries(aclent_t *, int, char *, char *, char *, int);
54 static int set_file_entries(char *, char *, int);
55 static int set_online_entries(char *, char *, int);
56 static void usage();
57 static int parse_entry_list(aclent_t **, int *, char *, int);
58 static int convert_to_aclent_t(char *, int *, aclent_t **, int);
59 static int parse_entry(char *, aclent_t *, int);
60 static void err_handle(int, aclent_t *);
61 static int conv_id(char *);
62
63 int
main(int argc,char * argv[])64 main(int argc, char *argv[])
65 {
66 int c;
67 int dflag = 0;
68 int mflag = 0;
69 int rflag = 0;
70 int sflag = 0;
71 int fflag = 0;
72 int errflag = 0;
73 int aclcnt; /* used by -m -d */
74 aclent_t *aclp; /* used by -m -d */
75 char *aclfilep = NULL; /* acl file argument */
76 char *d_entryp = NULL; /* ptr to del entry list */
77 char *m_entryp = NULL; /* ptr to mod entry list */
78 char *s_entryp = NULL; /* ptr to set entry list */
79 char *work_dp = NULL; /* working ptrs for the above */
80 char *work_mp = NULL;
81 char *work_sp = NULL;
82
83 (void) setlocale(LC_ALL, "");
84 (void) textdomain(TEXT_DOMAIN);
85
86 if (argc < 3)
87 usage();
88
89 while ((c = getopt(argc, argv, "rm:d:s:f:")) != EOF) {
90 switch (c) {
91 case 'r':
92 rflag++;
93 break;
94 case 'd':
95 if (dflag || fflag || sflag)
96 usage();
97 dflag++;
98 d_entryp = optarg;
99 break;
100 case 'm':
101 if (mflag || fflag || sflag)
102 usage();
103 mflag++;
104 m_entryp = optarg;
105 break;
106 case 's':
107 if (fflag || sflag || mflag || dflag)
108 usage();
109 sflag++;
110 s_entryp = optarg;
111 break;
112 case 'f':
113 if (fflag || sflag || mflag || dflag)
114 usage();
115 fflag++;
116 aclfilep = optarg;
117 break;
118 case '?':
119 errflag++;
120 break;
121 }
122 }
123 if (errflag)
124 usage();
125
126 /* one of these flags should be set */
127 if (!fflag && !sflag && !mflag && !dflag)
128 usage();
129
130 /* no file arguments */
131 if (optind >= argc)
132 usage();
133
134 for (; optind < argc; optind++) {
135 register char *filep;
136
137 filep = argv[optind];
138
139 /* modify and delete: we need to get the ACL first */
140 if (mflag || dflag) {
141 if (m_entryp != NULL) {
142 free(work_mp);
143 work_mp = strdup(m_entryp);
144 if (work_mp == NULL) {
145 fprintf(stderr,
146 gettext("out of memory %s\n"),
147 m_entryp);
148 exit(1);
149 }
150 }
151
152 if (d_entryp != NULL) {
153 free(work_dp);
154 work_dp = strdup(d_entryp);
155 if (work_dp == NULL) {
156 fprintf(stderr,
157 gettext("out of memory %s\n"),
158 d_entryp);
159 exit(1);
160 }
161 }
162
163 aclcnt = get_acl_info(filep, &aclp);
164 if (aclcnt == -1)
165 exit(2);
166 if (mod_entries(aclp, aclcnt, work_mp,
167 work_dp, filep, rflag) == -1)
168 exit(2);
169 } else if (fflag) {
170 if (set_file_entries(aclfilep, filep, rflag) == -1)
171 exit(2);
172 } else if (sflag) {
173 if (s_entryp != NULL) {
174 free(work_sp);
175 work_sp = strdup(s_entryp);
176 if (work_sp == NULL) {
177 fprintf(stderr,
178 gettext("out of memory %s\n"),
179 s_entryp);
180 exit(1);
181 }
182 }
183 if (set_online_entries(work_sp, filep, rflag) == -1)
184 exit(2);
185 }
186 }
187 return (0);
188 }
189
190 /*
191 * For add, modify, and delete, we need to get the ACL of the file first.
192 */
193 static int
get_acl_info(char * filep,aclent_t ** aclpp)194 get_acl_info(char *filep, aclent_t **aclpp)
195 {
196 int aclcnt;
197
198 if ((aclcnt = acl(filep, GETACLCNT, 0, NULL)) < 0) {
199 if (errno == ENOSYS) {
200 (void) fprintf(stderr,
201 gettext("File system doesn't support aclent_t "
202 "style ACL's.\n"
203 "See acl(7) for more information on "
204 "POSIX-draft ACL support.\n"));
205 return (-1);
206 }
207 (void) fprintf(stderr,
208 gettext("%s: failed to get acl count\n"), filep);
209 perror("get acl count error");
210 return (-1);
211 }
212 if (aclcnt < MIN_ACL_ENTRIES) {
213 (void) fprintf(stderr,
214 gettext("%d: acl count is too small from %s\n"),
215 aclcnt, filep);
216 return (-1);
217 }
218
219 if ((*aclpp = (aclent_t *)malloc(sizeof (aclent_t) * aclcnt)) == NULL) {
220 (void) fprintf(stderr, gettext("out of memory\n"));
221 return (-1);
222 }
223 if (acl(filep, GETACL, aclcnt, *aclpp) < 0) {
224 (void) fprintf(stderr,
225 gettext("%s: failed to get acl entries\n"), filep);
226 perror("getacl error");
227 return (-1);
228 }
229 return (aclcnt);
230 }
231
232 /*
233 * mod_entries() handles add, delete, and modify ACL entries of a file.
234 * The real action is in convert_to_aclent_t() called by parse_entry_list().
235 * aclp: points ACL of a file and may be changed by lower level routine.
236 * modp: modify entry list in ascii format
237 * delp: delete entry list in ascii format
238 * fnamep: file of interest
239 */
240 static int
mod_entries(aclent_t * aclp,int cnt,char * modp,char * delp,char * fnamep,int rfg)241 mod_entries(aclent_t *aclp, int cnt, char *modp, char *delp,
242 char *fnamep, int rfg)
243 {
244 /* modify and add: from -m option */
245 if (parse_entry_list(&aclp, &cnt, modp, MODIFY) == -1)
246 return (-1);
247
248 /* deletion: from -d option */
249 if (parse_entry_list(&aclp, &cnt, delp, DELETE) == -1)
250 return (-1);
251
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);
256 return (-1);
257 }
258
259 if (acl(fnamep, SETACL, cnt, aclp) < 0) {
260 fprintf(stderr,
261 gettext("%s: failed to set acl entries\n"), fnamep);
262 perror("setacl error");
263 return (-1);
264 }
265 return (0);
266 }
267
268 /*
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).
273 */
274 static int
set_file_entries(char * acl_fnamep,char * fnamep,int rflag)275 set_file_entries(char *acl_fnamep, char *fnamep, int rflag)
276 {
277 int aclcnt = 0;
278 FILE *acl_fp;
279 aclent_t *aclp;
280 char buf[BUFSIZ];
281 char *tp;
282
283 if (strcmp(acl_fnamep, "-") == 0)
284 acl_fp = stdin;
285 else {
286 if ((acl_fp = fopen(acl_fnamep, "r")) == NULL) {
287 fprintf(stderr, gettext("Can't open acl file %s\n"),
288 acl_fnamep);
289 return (-1);
290 }
291 }
292 while (fgets(buf, BUFSIZ, acl_fp) != NULL) {
293 if (buf[0] == '#' || buf[0] == '\n')
294 continue;
295
296 /* check effective permission: add a null after real perm */
297 if ((tp = (char *)strchr(buf, '#')) != NULL) {
298 tp--;
299 while (*tp == ' ' || *tp == '\t') {
300 if (tp != buf)
301 tp--;
302 else {
303 fprintf(stderr,
304 gettext("entry format error %s\n"),
305 buf);
306 exit(1);
307 }
308 }
309 *(tp+1) = '\0';
310 }
311
312 /* remove <nl> at the end if there is one */
313 if ((tp = (char *)strchr(buf, '\n')) != NULL)
314 *tp = '\0';
315 aclcnt++;
316 if (convert_to_aclent_t(buf, &aclcnt, &aclp, SET) == -1)
317 return (-1);
318 }
319
320 if (aclsort(aclcnt, rflag, aclp) == -1) {
321 (void) err_handle(aclcnt, aclp);
322 (void) fprintf(stderr, gettext("aclcnt %d, aclfile %s\n"),
323 aclcnt, acl_fnamep);
324 return (-1);
325 }
326
327 if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
328 fprintf(stderr,
329 gettext("%s: failed to set acl entries\n"), fnamep);
330 perror("setacl error");
331 return (-1);
332 }
333 return (0);
334 }
335
336 /*
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).
341 */
342 static int
set_online_entries(char * setp,char * fnamep,int rflag)343 set_online_entries(char *setp, char *fnamep, int rflag)
344 {
345 aclent_t *aclp;
346 int aclcnt = 0;
347
348 if (parse_entry_list(&aclp, &aclcnt, setp, SET) == -1)
349 return (-1);
350
351 if (aclsort(aclcnt, rflag, aclp) == -1) {
352 (void) err_handle(aclcnt, aclp);
353 (void) fprintf(stderr,
354 gettext("aclcnt %d, file %s\n"), aclcnt, fnamep);
355 return (-1);
356 }
357
358 if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
359 fprintf(stderr,
360 gettext("%s: failed to set acl entries\n"), fnamep);
361 perror("setacl error");
362 return (-1);
363 }
364 return (0);
365 }
366
367 /*
368 * parse_entry_list() parses entry list (listp) separated by commas.
369 * Once it gets an ACL entry, it calls convert_to_aclent_t() to convert
370 * to internal format.
371 */
372 static int
parse_entry_list(aclent_t ** aclpp,int * aclcntp,char * listp,int mode)373 parse_entry_list(aclent_t **aclpp, int *aclcntp, char *listp, int mode)
374 {
375 char *commap;
376
377 if (listp == NULL)
378 return (0);
379 while ((commap = (char *)strchr(listp, ',')) != NULL) {
380 *commap = '\0';
381 *aclcntp += 1;
382 /* aclcnt may be updated after the call: add or modify */
383 if (convert_to_aclent_t(listp, aclcntp, aclpp, mode) == -1)
384 return (-1);
385 listp = ++commap;
386 }
387 /* this is for only one entry or last entry */
388 if (*listp != '\0') {
389 *aclcntp += 1;
390 if (convert_to_aclent_t(listp, aclcntp, aclpp, mode) == -1)
391 return (-1);
392 }
393 return (0);
394 }
395
396 /*
397 * convert_to_aclent_t() converts an acl entry in ascii format (fields separated
398 * by colon) into aclent_t and appends it to the current ACL. It also handles
399 * memory allocation/deallocation for acl entries in aclent_t format.
400 * aclpp that contains acl entries in acl format will be returned.
401 * We don't check duplicates.
402 */
403 static int
convert_to_aclent_t(char * entryp,int * cntp,aclent_t ** aclpp,int mode)404 convert_to_aclent_t(char *entryp, int *cntp, aclent_t **aclpp, int mode)
405 {
406 aclent_t *new_aclp;
407 aclent_t tmpacl;
408 aclent_t *taclp, *centry = NULL, *gentry = NULL;
409 int cur_cnt;
410 int found = 0;
411 int is_obj;
412
413 if (entryp == NULL)
414 return (0);
415
416 tmpacl.a_id = 0; /* id field needs to be initialized */
417 if (entryp[0] == 'u')
418 tmpacl.a_id = getuid(); /* id field for user */
419 if (entryp[0] == 'g')
420 tmpacl.a_id = getgid(); /* id field for group */
421
422 tmpacl.a_type = 0;
423 if (parse_entry(entryp, &tmpacl, mode) == -1)
424 return (-1);
425
426 is_obj = ((tmpacl.a_type == USER_OBJ) ||
427 (tmpacl.a_type == GROUP_OBJ) ||
428 (tmpacl.a_type == CLASS_OBJ) ||
429 (tmpacl.a_type == DEF_USER_OBJ) ||
430 (tmpacl.a_type == DEF_GROUP_OBJ) ||
431 (tmpacl.a_type == DEF_OTHER_OBJ));
432
433 if (*cntp > 1)
434 new_aclp = (aclent_t *)realloc(*aclpp,
435 sizeof (aclent_t) * (*cntp));
436 else
437 new_aclp = (aclent_t *) malloc(sizeof (aclent_t) * (*cntp));
438 if (new_aclp == NULL) {
439 fprintf(stderr,
440 gettext("Insufficient memory for acl %d\n"), *cntp);
441 return (-1);
442 }
443
444 cur_cnt = *cntp - 1;
445 switch (mode) {
446 case MODIFY: /* and add */
447 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
448 if (taclp->a_type == tmpacl.a_type &&
449 ((taclp->a_id == tmpacl.a_id) || is_obj)) {
450 found++;
451 /* cnt is added before it's called */
452 *cntp -= 1;
453 taclp->a_perm = tmpacl.a_perm;
454 break;
455 }
456 }
457 if (!found) /* Add it to the end: no need to change cntp */
458 memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
459 break;
460
461 case DELETE:
462 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
463 if (taclp->a_type == tmpacl.a_type &&
464 ((taclp->a_id == tmpacl.a_id) || is_obj)) {
465 found++;
466 /* move up the rest */
467 while (cur_cnt-- > 0) {
468 memcpy(taclp, taclp+1,
469 sizeof (aclent_t));
470 taclp++;
471 }
472 *cntp = *cntp - 2;
473 break;
474 }
475 }
476 if (!found)
477 *cntp -= 1;
478 break;
479
480 case SET:
481 /* we may check duplicate before copying over?? */
482 memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
483 break;
484
485 default:
486 fprintf(stderr,
487 gettext("Unrecognized mode: internal error\n"));
488 break;
489 }
490
491 /*
492 * If converting from non-trivial acl entry to trivial one,
493 * reset CLASS_OBJ's permission with that of GROUP_OBJ.
494 */
495
496 if (mode == DELETE) {
497 boolean_t trivial = B_TRUE; /* assumption */
498 cur_cnt = *cntp;
499 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
500 switch (taclp->a_type) {
501 case USER_OBJ:
502 case OTHER_OBJ:
503 break;
504 case CLASS_OBJ:
505 centry = taclp;
506 break;
507 case GROUP_OBJ:
508 gentry = taclp;
509 break;
510 default:
511 /*
512 * Confirmed that the new acl set is
513 * still a non-trivial acl.
514 * Skip reset.
515 */
516 trivial = B_FALSE;
517 }
518 }
519 if (centry != NULL && gentry != NULL && trivial == B_TRUE)
520 centry->a_perm = gentry->a_perm;
521 }
522 *aclpp = new_aclp; /* return new acl entries */
523 return (0);
524 }
525
526 static void
usage()527 usage()
528 {
529 (void) fprintf(stderr, gettext("usage:\n"));
530 (void) fprintf(stderr,
531 gettext("\tsetfacl [-r] -f aclfile file ...\n"));
532 (void) fprintf(stderr,
533 gettext("\tsetfacl [-r] -d acl_entries file ...\n"));
534 (void) fprintf(stderr,
535 gettext("\tsetfacl [-r] -m acl_entries file ...\n"));
536 (void) fprintf(stderr,
537 gettext("\tsetfacl [-r] -s acl_entries file ...\n"));
538 exit(1);
539 }
540
541 static void
err_handle(int cnt,aclent_t * aclentp)542 err_handle(int cnt, aclent_t *aclentp)
543 {
544 int rc;
545 int which;
546
547 rc = aclcheck(aclentp, cnt, &which);
548 switch (rc) {
549 case USER_ERROR:
550 fprintf(stderr,
551 gettext("There is more than one user owner entry"));
552 fprintf(stderr,
553 gettext(" -- error found at entry index %d\n"), which);
554 break;
555 case GRP_ERROR:
556 fprintf(stderr,
557 gettext("There is more than one group owner entry"));
558 fprintf(stderr,
559 gettext(" -- error found at entry index %d\n"), which);
560 break;
561 case CLASS_ERROR:
562 fprintf(stderr,
563 gettext("There is more than one mask entry"));
564 fprintf(stderr,
565 gettext(" -- error found at entry index %d\n"), which);
566 break;
567 case OTHER_ERROR:
568 fprintf(stderr,
569 gettext("There is more than one other entry"));
570 fprintf(stderr,
571 gettext(" -- error found at entry index %d\n"), which);
572 break;
573 case DUPLICATE_ERROR:
574 fprintf(stderr,
575 gettext("Duplicate user or group entries"));
576 fprintf(stderr,
577 gettext(" -- error found at entry index %d\n"), which);
578 break;
579 case MISS_ERROR:
580 fprintf(stderr,
581 gettext("Missing user/group owner, other, mask entry\n"));
582 break;
583 case MEM_ERROR:
584 fprintf(stderr,
585 gettext("Insufficient memory\n"));
586 break;
587 case ENTRY_ERROR:
588 fprintf(stderr,
589 gettext("Unrecognized entry type"));
590 fprintf(stderr,
591 gettext(" -- error found at entry index %d\n"), which);
592 break;
593 default:
594 /* error is not from aclcheck */
595 fprintf(stderr,
596 gettext("aclsort error\n"));
597 break;
598 }
599 }
600
601 static int
parse_entry(char * fieldp,aclent_t * aclentp,int mode)602 parse_entry(char *fieldp, aclent_t *aclentp, int mode)
603 {
604 char *colonp;
605 int def_flag = 0, mo_flag = 0;
606 int id;
607 struct passwd *pwp;
608 struct group *grp;
609
610 colonp = (char *)strchr(fieldp, ':');
611 if (colonp == NULL) {
612 fprintf(stderr,
613 gettext("Can't find colon delimiter %s\n"), fieldp);
614 return (-1);
615 }
616 *colonp = '\0';
617 if ((strcmp(fieldp, "default") == 0) || (strcmp(fieldp, "d") == 0)) {
618 def_flag++;
619 fieldp = ++colonp;
620 colonp = (char *)strchr(fieldp, ':');
621 if (colonp == NULL) {
622 fprintf(stderr,
623 gettext("Can't find colon delimiter %s\n"), fieldp);
624 return (-1);
625 }
626 *colonp = '\0';
627 }
628
629 /* process entry type */
630 if ((strcmp(fieldp, "user") == 0) || (strcmp(fieldp, "u") == 0)) {
631 if (def_flag)
632 aclentp->a_type = DEF_USER;
633 else
634 aclentp->a_type = USER;
635 }
636 if ((strcmp(fieldp, "group") == 0) || (strcmp(fieldp, "g") == 0)) {
637 if (def_flag)
638 aclentp->a_type = DEF_GROUP;
639 else
640 aclentp->a_type = GROUP;
641 }
642 if ((strcmp(fieldp, "mask") == 0) || (strcmp(fieldp, "m") == 0)) {
643 if (def_flag)
644 aclentp->a_type = DEF_CLASS_OBJ;
645 else
646 aclentp->a_type = CLASS_OBJ;
647 }
648 if ((strcmp(fieldp, "other") == 0) || (strcmp(fieldp, "o") == 0)) {
649 if (def_flag)
650 aclentp->a_type = DEF_OTHER_OBJ;
651 else
652 aclentp->a_type = OTHER_OBJ;
653 }
654
655 /* still can't determine entry type */
656 if (aclentp->a_type == 0) {
657 fprintf(stderr,
658 gettext("Unrecognized entry type %s \n"), fieldp);
659 return (-1);
660 }
661
662 /* mask and other entries dont have id field */
663 if (aclentp->a_type != CLASS_OBJ && aclentp->a_type != OTHER_OBJ &&
664 aclentp->a_type != DEF_CLASS_OBJ &&
665 aclentp->a_type != DEF_OTHER_OBJ) {
666 /* process id: */
667 fieldp = ++colonp;
668 colonp = (char *)strchr(fieldp, ':');
669 if (colonp == NULL) {
670 if (mode != DELETE) {
671 fprintf(stderr,
672 gettext("Can't find colon delimiter %s\n"),
673 fieldp);
674 return (-1);
675 }
676 } else
677 *colonp = '\0';
678
679 if (*fieldp == '\0') {
680 /* empty uid */
681 if (aclentp->a_type == USER)
682 aclentp->a_type = USER_OBJ;
683 if (aclentp->a_type == DEF_USER)
684 aclentp->a_type = DEF_USER_OBJ;
685 if (aclentp->a_type == GROUP)
686 aclentp->a_type = GROUP_OBJ;
687 if (aclentp->a_type == DEF_GROUP)
688 aclentp->a_type = DEF_GROUP_OBJ;
689 } else {
690 /* see if it's a user/group name */
691 if (aclentp->a_type == USER ||
692 aclentp->a_type == USER_OBJ ||
693 aclentp->a_type == DEF_USER ||
694 aclentp->a_type == DEF_USER_OBJ) {
695 if ((pwp = getpwnam(fieldp)) != NULL)
696 aclentp->a_id = pwp->pw_uid;
697 else {
698 /* treat it as numeric id */
699 id = conv_id(fieldp);
700 if (id == -1)
701 return (-1);
702 aclentp->a_id = id;
703 }
704 } else {
705 /* group name */
706 if ((grp = getgrnam(fieldp)) != NULL)
707 aclentp->a_id = grp->gr_gid;
708 else {
709 id = conv_id(fieldp);
710 if (id == -1)
711 return (-1);
712 aclentp->a_id = id;
713 }
714 }
715 }
716 } else {
717 /* it is mask/other entry */
718 mo_flag = 1;
719 }
720
721 /* process permission: rwx and [0]n format */
722 if (mode == DELETE)
723 /* delete format: no permission field */
724 return (0);
725 fieldp = ++colonp;
726 colonp = (char *)strchr(fieldp, ':');
727 if (colonp != NULL) {
728 if (mo_flag == 1) {
729 /* Use only single : on mask/other entry */
730 (void) fprintf(stderr, gettext("use only 1 colon for "
731 "mask and other entries.\n"));
732 return (-1);
733 } else {
734 /* it's ok to have extra colon */
735 *colonp = '\0';
736 }
737 }
738
739 if ((int)strlen(fieldp) > 3) {
740 fprintf(stderr,
741 gettext("only rwx or [0]n format is allowed\n"));
742 return (-1);
743 }
744 if (strlen(fieldp) == 3) {
745 aclentp->a_perm = 0;
746 /* treat it as rwx */
747 if (*fieldp == 'r')
748 aclentp->a_perm += 4;
749 else
750 if (*fieldp != '-') {
751 fprintf(stderr,
752 gettext("Unrecognized character "));
753 fprintf(stderr,
754 gettext("found in mode field\n"));
755 return (-1);
756 }
757 fieldp++;
758 if (*fieldp == 'w')
759 aclentp->a_perm += 2;
760 else
761 if (*fieldp != '-') {
762 fprintf(stderr,
763 gettext("Unrecognized character "));
764 fprintf(stderr,
765 gettext("found in mode field\n"));
766 return (-1);
767 }
768 fieldp++;
769 if (*fieldp == 'x')
770 aclentp->a_perm += 1;
771 else
772 if (*fieldp != '-') {
773 fprintf(stderr,
774 gettext("Unrecognized character "));
775 fprintf(stderr,
776 gettext("found in mode field\n"));
777 return (-1);
778 }
779 return (0);
780 }
781
782 if (*fieldp == '\0')
783 return (0);
784
785 if (*fieldp >= '0' && *fieldp <= '7')
786 aclentp->a_perm = *fieldp - '0';
787 else {
788 fprintf(stderr, gettext("Unrecognized character "));
789 fprintf(stderr, gettext("found in mode field\n"));
790 return (-1);
791 }
792 if (aclentp->a_perm == 0 && *++fieldp != '\0') {
793 /* look at next char */
794 if (*fieldp >= '0' && *fieldp <= '7')
795 aclentp->a_perm = *fieldp - '0';
796 else {
797 fprintf(stderr, gettext("Unrecognized character "));
798 fprintf(stderr, gettext("found in mode field\n"));
799 fprintf(stderr,
800 gettext("Check also the number of fields "));
801 fprintf(stderr,
802 gettext("(default) mask and other entries\n"));
803 return (-1);
804 }
805 }
806 /* check for junk at the end ??? */
807 return (0);
808 }
809
810 /*
811 * This function is different from atoi() in that it checks for
812 * valid digit in the id field whereas atoi() won't report any
813 * error.
814 */
815 static int
conv_id(char * fieldp)816 conv_id(char *fieldp)
817 {
818 int a_id = 0;
819
820 for (; *fieldp != '\0'; fieldp++) {
821 if (!isdigit(*fieldp)) {
822 fprintf(stderr, gettext("non-digit in id field\n"));
823 return (-1);
824 }
825 a_id = a_id * 10 + (*fieldp - '0');
826 }
827 return (a_id);
828 }
829