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