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