xref: /freebsd/lib/libc/posix1e/acl_support_nfs4.c (revision aa015c8e4ad07fbe76ec137fa491c89856b871e0)
1 /*-
2  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <err.h>
35 #include <sys/acl.h>
36 #include "acl_support.h"
37 
38 struct flagnames_struct {
39 	uint32_t	flag;
40 	const char	*name;
41 	char		letter;
42 };
43 
44 struct flagnames_struct a_flags[] =
45     {{ ACL_ENTRY_FILE_INHERIT, "file_inherit", 'f'},
46      { ACL_ENTRY_DIRECTORY_INHERIT, "dir_inherit", 'd'},
47      { ACL_ENTRY_INHERIT_ONLY, "inherit_only", 'i'},
48      { ACL_ENTRY_NO_PROPAGATE_INHERIT, "no_propagate", 'n'},
49      { ACL_ENTRY_SUCCESSFUL_ACCESS, "successfull_access", 'S'},
50      { ACL_ENTRY_FAILED_ACCESS, "failed_access", 'F'},
51      /*
52       * There is no ACE_IDENTIFIER_GROUP here - SunOS does not show it
53       * in the "flags" field.  There is no ACE_OWNER, ACE_GROUP or
54       * ACE_EVERYONE either, for obvious reasons.
55       */
56      { 0, 0, 0}};
57 
58 struct flagnames_struct a_access_masks[] =
59     {{ ACL_READ_DATA, "read_data", 'r'},
60      { ACL_WRITE_DATA, "write_data", 'w'},
61      { ACL_EXECUTE, "execute", 'x'},
62      { ACL_APPEND_DATA, "append_data", 'p'},
63      { ACL_DELETE_CHILD, "delete_child", 'D'},
64      { ACL_DELETE, "delete", 'd'},
65      { ACL_READ_ATTRIBUTES, "read_attributes", 'a'},
66      { ACL_WRITE_ATTRIBUTES, "write_attributes", 'A'},
67      { ACL_READ_NAMED_ATTRS, "read_xattr", 'R'},
68      { ACL_WRITE_NAMED_ATTRS, "write_xattr", 'W'},
69      { ACL_READ_ACL, "read_acl", 'c'},
70      { ACL_WRITE_ACL, "write_acl", 'C'},
71      { ACL_WRITE_OWNER, "write_owner", 'o'},
72      { ACL_SYNCHRONIZE, "synchronize", 's'},
73      { 0, 0, 0}};
74 
75 static const char *
76 format_flag(uint32_t *var, const struct flagnames_struct *flags)
77 {
78 
79 	for (; flags->name != 0; flags++) {
80 		if ((flags->flag & *var) == 0)
81 			continue;
82 
83 		*var &= ~flags->flag;
84 		return (flags->name);
85 	}
86 
87 	return (NULL);
88 }
89 
90 static int
91 format_flags_verbose(char *str, size_t size, uint32_t var,
92     const struct flagnames_struct *flags)
93 {
94 	size_t off = 0;
95 	const char *tmp;
96 
97 	while ((tmp = format_flag(&var, flags)) != NULL) {
98 		off += snprintf(str + off, size - off, "%s/", tmp);
99 		assert (off < size);
100 	}
101 
102 	/* If there were any flags added... */
103 	if (off > 0) {
104 		off--;
105 		/* ... then remove the last slash. */
106 		assert(str[off] == '/');
107 	}
108 
109 	str[off] = '\0';
110 
111 	return (0);
112 }
113 
114 static int
115 format_flags_compact(char *str, size_t size, uint32_t var,
116     const struct flagnames_struct *flags)
117 {
118 	size_t i;
119 
120 	for (i = 0; flags[i].name != NULL; i++) {
121 		assert(i < size);
122 		if ((flags[i].flag & var) == 0)
123 			str[i] = '-';
124 		else
125 			str[i] = flags[i].letter;
126 	}
127 
128 	str[i] = '\0';
129 
130 	return (0);
131 }
132 
133 static int
134 parse_flags_verbose(const char *strp, uint32_t *var,
135     const struct flagnames_struct *flags, const char *flags_name,
136     int *try_compact)
137 {
138 	int i, found, ever_found = 0;
139 	char *str, *flag;
140 
141 	str = strdup(strp);
142 	*try_compact = 0;
143 	*var = 0;
144 
145 	while (str != NULL) {
146 		flag = strsep(&str, "/:");
147 
148 		found = 0;
149 		for (i = 0; flags[i].name != NULL; i++) {
150 			if (strcmp(flags[i].name, flag) == 0) {
151 				*var |= flags[i].flag;
152 				found = 1;
153 				ever_found = 1;
154 			}
155 		}
156 
157 		if (!found) {
158 			if (ever_found)
159 				warnx("malformed ACL: \"%s\" field contains "
160 				    "invalid flag \"%s\"", flags_name, flag);
161 			else
162 				*try_compact = 1;
163 			free(str);
164 			return (-1);
165 		}
166 	}
167 
168 	free(str);
169 	return (0);
170 }
171 
172 static int
173 parse_flags_compact(const char *str, uint32_t *var,
174     const struct flagnames_struct *flags, const char *flags_name)
175 {
176 	int i, j, found;
177 
178 	*var = 0;
179 
180 	for (i = 0;; i++) {
181 		if (str[i] == '\0')
182 			return (0);
183 
184 		/* Ignore minus signs. */
185 		if (str[i] == '-')
186 			continue;
187 
188 		found = 0;
189 
190 		for (j = 0; flags[j].name != NULL; j++) {
191 			if (flags[j].letter == str[i]) {
192 				*var |= flags[j].flag;
193 				found = 1;
194 				break;
195 			}
196 		}
197 
198 		if (!found) {
199 			warnx("malformed ACL: \"%s\" field contains "
200 			    "invalid flag \"%c\"", flags_name, str[i]);
201 			return (-1);
202 		}
203 	}
204 }
205 
206 int
207 _nfs4_format_flags(char *str, size_t size, acl_flag_t var, int verbose)
208 {
209 
210 	if (verbose)
211 		return (format_flags_verbose(str, size, var, a_flags));
212 
213 	return (format_flags_compact(str, size, var, a_flags));
214 }
215 
216 int
217 _nfs4_format_access_mask(char *str, size_t size, acl_perm_t var, int verbose)
218 {
219 
220 	if (verbose)
221 		return (format_flags_verbose(str, size, var, a_access_masks));
222 
223 	return (format_flags_compact(str, size, var, a_access_masks));
224 }
225 
226 int
227 _nfs4_parse_flags(const char *str, acl_flag_t *flags)
228 {
229 	int error, try_compact;
230 	int tmpflags;
231 
232 	error = parse_flags_verbose(str, &tmpflags, a_flags, "flags", &try_compact);
233 	if (error && try_compact)
234 		error = parse_flags_compact(str, &tmpflags, a_flags, "flags");
235 
236 	*flags = tmpflags;
237 
238 	return (error);
239 }
240 
241 int
242 _nfs4_parse_access_mask(const char *str, acl_perm_t *perms)
243 {
244 	int error, try_compact;
245 	int tmpperms;
246 
247 	error = parse_flags_verbose(str, &tmpperms, a_access_masks,
248 	    "access permissions", &try_compact);
249 	if (error && try_compact)
250 		error = parse_flags_compact(str, &tmpperms,
251 		    a_access_masks, "access permissions");
252 
253 	*perms = tmpperms;
254 
255 	return (error);
256 }
257 /*-
258  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
259  * All rights reserved.
260  *
261  * Redistribution and use in source and binary forms, with or without
262  * modification, are permitted provided that the following conditions
263  * are met:
264  * 1. Redistributions of source code must retain the above copyright
265  *    notice, this list of conditions and the following disclaimer.
266  * 2. Redistributions in binary form must reproduce the above copyright
267  *    notice, this list of conditions and the following disclaimer in the
268  *    documentation and/or other materials provided with the distribution.
269  *
270  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
271  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
272  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
273  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
274  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
275  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
276  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
277  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
278  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
280  * SUCH DAMAGE.
281  */
282 
283 #include <sys/cdefs.h>
284 __FBSDID("$FreeBSD$");
285 
286 #include <stdio.h>
287 #include <stdlib.h>
288 #include <string.h>
289 #include <assert.h>
290 #include <err.h>
291 #include <sys/acl.h>
292 #include "acl_support.h"
293 
294 struct flagnames_struct {
295 	uint32_t	flag;
296 	const char	*name;
297 	char		letter;
298 };
299 
300 struct flagnames_struct a_flags[] =
301     {{ ACL_ENTRY_FILE_INHERIT, "file_inherit", 'f'},
302      { ACL_ENTRY_DIRECTORY_INHERIT, "dir_inherit", 'd'},
303      { ACL_ENTRY_INHERIT_ONLY, "inherit_only", 'i'},
304      { ACL_ENTRY_NO_PROPAGATE_INHERIT, "no_propagate", 'n'},
305      { ACL_ENTRY_SUCCESSFUL_ACCESS, "successfull_access", 'S'},
306      { ACL_ENTRY_FAILED_ACCESS, "failed_access", 'F'},
307      /*
308       * There is no ACE_IDENTIFIER_GROUP here - SunOS does not show it
309       * in the "flags" field.  There is no ACE_OWNER, ACE_GROUP or
310       * ACE_EVERYONE either, for obvious reasons.
311       */
312      { 0, 0, 0}};
313 
314 struct flagnames_struct a_access_masks[] =
315     {{ ACL_READ_DATA, "read_data", 'r'},
316      { ACL_WRITE_DATA, "write_data", 'w'},
317      { ACL_EXECUTE, "execute", 'x'},
318      { ACL_APPEND_DATA, "append_data", 'p'},
319      { ACL_DELETE_CHILD, "delete_child", 'D'},
320      { ACL_DELETE, "delete", 'd'},
321      { ACL_READ_ATTRIBUTES, "read_attributes", 'a'},
322      { ACL_WRITE_ATTRIBUTES, "write_attributes", 'A'},
323      { ACL_READ_NAMED_ATTRS, "read_xattr", 'R'},
324      { ACL_WRITE_NAMED_ATTRS, "write_xattr", 'W'},
325      { ACL_READ_ACL, "read_acl", 'c'},
326      { ACL_WRITE_ACL, "write_acl", 'C'},
327      { ACL_WRITE_OWNER, "write_owner", 'o'},
328      { ACL_SYNCHRONIZE, "synchronize", 's'},
329      { 0, 0, 0}};
330 
331 static const char *
332 format_flag(uint32_t *var, const struct flagnames_struct *flags)
333 {
334 
335 	for (; flags->name != 0; flags++) {
336 		if ((flags->flag & *var) == 0)
337 			continue;
338 
339 		*var &= ~flags->flag;
340 		return (flags->name);
341 	}
342 
343 	return (NULL);
344 }
345 
346 static int
347 format_flags_verbose(char *str, size_t size, uint32_t var,
348     const struct flagnames_struct *flags)
349 {
350 	size_t off = 0;
351 	const char *tmp;
352 
353 	while ((tmp = format_flag(&var, flags)) != NULL) {
354 		off += snprintf(str + off, size - off, "%s/", tmp);
355 		assert (off < size);
356 	}
357 
358 	/* If there were any flags added... */
359 	if (off > 0) {
360 		off--;
361 		/* ... then remove the last slash. */
362 		assert(str[off] == '/');
363 	}
364 
365 	str[off] = '\0';
366 
367 	return (0);
368 }
369 
370 static int
371 format_flags_compact(char *str, size_t size, uint32_t var,
372     const struct flagnames_struct *flags)
373 {
374 	size_t i;
375 
376 	for (i = 0; flags[i].name != NULL; i++) {
377 		assert(i < size);
378 		if ((flags[i].flag & var) == 0)
379 			str[i] = '-';
380 		else
381 			str[i] = flags[i].letter;
382 	}
383 
384 	str[i] = '\0';
385 
386 	return (0);
387 }
388 
389 static int
390 parse_flags_verbose(const char *strp, uint32_t *var,
391     const struct flagnames_struct *flags, const char *flags_name,
392     int *try_compact)
393 {
394 	int i, found, ever_found = 0;
395 	char *str, *flag;
396 
397 	str = strdup(strp);
398 	*try_compact = 0;
399 	*var = 0;
400 
401 	while (str != NULL) {
402 		flag = strsep(&str, "/:");
403 
404 		found = 0;
405 		for (i = 0; flags[i].name != NULL; i++) {
406 			if (strcmp(flags[i].name, flag) == 0) {
407 				*var |= flags[i].flag;
408 				found = 1;
409 				ever_found = 1;
410 			}
411 		}
412 
413 		if (!found) {
414 			if (ever_found)
415 				warnx("malformed ACL: \"%s\" field contains "
416 				    "invalid flag \"%s\"", flags_name, flag);
417 			else
418 				*try_compact = 1;
419 			free(str);
420 			return (-1);
421 		}
422 	}
423 
424 	free(str);
425 	return (0);
426 }
427 
428 static int
429 parse_flags_compact(const char *str, uint32_t *var,
430     const struct flagnames_struct *flags, const char *flags_name)
431 {
432 	int i, j, found;
433 
434 	*var = 0;
435 
436 	for (i = 0;; i++) {
437 		if (str[i] == '\0')
438 			return (0);
439 
440 		/* Ignore minus signs. */
441 		if (str[i] == '-')
442 			continue;
443 
444 		found = 0;
445 
446 		for (j = 0; flags[j].name != NULL; j++) {
447 			if (flags[j].letter == str[i]) {
448 				*var |= flags[j].flag;
449 				found = 1;
450 				break;
451 			}
452 		}
453 
454 		if (!found) {
455 			warnx("malformed ACL: \"%s\" field contains "
456 			    "invalid flag \"%c\"", flags_name, str[i]);
457 			return (-1);
458 		}
459 	}
460 }
461 
462 int
463 _nfs4_format_flags(char *str, size_t size, acl_flag_t var, int verbose)
464 {
465 
466 	if (verbose)
467 		return (format_flags_verbose(str, size, var, a_flags));
468 
469 	return (format_flags_compact(str, size, var, a_flags));
470 }
471 
472 int
473 _nfs4_format_access_mask(char *str, size_t size, acl_perm_t var, int verbose)
474 {
475 
476 	if (verbose)
477 		return (format_flags_verbose(str, size, var, a_access_masks));
478 
479 	return (format_flags_compact(str, size, var, a_access_masks));
480 }
481 
482 int
483 _nfs4_parse_flags(const char *str, acl_flag_t *flags)
484 {
485 	int error, try_compact;
486 	int tmpflags;
487 
488 	error = parse_flags_verbose(str, &tmpflags, a_flags, "flags", &try_compact);
489 	if (error && try_compact)
490 		error = parse_flags_compact(str, &tmpflags, a_flags, "flags");
491 
492 	*flags = tmpflags;
493 
494 	return (error);
495 }
496 
497 int
498 _nfs4_parse_access_mask(const char *str, acl_perm_t *perms)
499 {
500 	int error, try_compact;
501 	int tmpperms;
502 
503 	error = parse_flags_verbose(str, &tmpperms, a_access_masks,
504 	    "access permissions", &try_compact);
505 	if (error && try_compact)
506 		error = parse_flags_compact(str, &tmpperms,
507 		    a_access_masks, "access permissions");
508 
509 	*perms = tmpperms;
510 
511 	return (error);
512 }
513 /*-
514  * Copyright (c) 2008, 2009 Edward Tomasz Napierała <trasz@FreeBSD.org>
515  * All rights reserved.
516  *
517  * Redistribution and use in source and binary forms, with or without
518  * modification, are permitted provided that the following conditions
519  * are met:
520  * 1. Redistributions of source code must retain the above copyright
521  *    notice, this list of conditions and the following disclaimer.
522  * 2. Redistributions in binary form must reproduce the above copyright
523  *    notice, this list of conditions and the following disclaimer in the
524  *    documentation and/or other materials provided with the distribution.
525  *
526  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
527  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
528  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
529  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
530  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
531  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
532  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
533  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
534  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
535  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
536  * SUCH DAMAGE.
537  */
538 
539 #include <sys/cdefs.h>
540 __FBSDID("$FreeBSD$");
541 
542 #include <stdio.h>
543 #include <stdlib.h>
544 #include <string.h>
545 #include <assert.h>
546 #include <err.h>
547 #include <sys/acl.h>
548 #include "acl_support.h"
549 
550 struct flagnames_struct {
551 	uint32_t	flag;
552 	const char	*name;
553 	char		letter;
554 };
555 
556 struct flagnames_struct a_flags[] =
557     {{ ACL_ENTRY_FILE_INHERIT, "file_inherit", 'f'},
558      { ACL_ENTRY_DIRECTORY_INHERIT, "dir_inherit", 'd'},
559      { ACL_ENTRY_INHERIT_ONLY, "inherit_only", 'i'},
560      { ACL_ENTRY_NO_PROPAGATE_INHERIT, "no_propagate", 'n'},
561      { ACL_ENTRY_SUCCESSFUL_ACCESS, "successfull_access", 'S'},
562      { ACL_ENTRY_FAILED_ACCESS, "failed_access", 'F'},
563      /*
564       * There is no ACE_IDENTIFIER_GROUP here - SunOS does not show it
565       * in the "flags" field.  There is no ACE_OWNER, ACE_GROUP or
566       * ACE_EVERYONE either, for obvious reasons.
567       */
568      { 0, 0, 0}};
569 
570 struct flagnames_struct a_access_masks[] =
571     {{ ACL_READ_DATA, "read_data", 'r'},
572      { ACL_WRITE_DATA, "write_data", 'w'},
573      { ACL_EXECUTE, "execute", 'x'},
574      { ACL_APPEND_DATA, "append_data", 'p'},
575      { ACL_DELETE_CHILD, "delete_child", 'D'},
576      { ACL_DELETE, "delete", 'd'},
577      { ACL_READ_ATTRIBUTES, "read_attributes", 'a'},
578      { ACL_WRITE_ATTRIBUTES, "write_attributes", 'A'},
579      { ACL_READ_NAMED_ATTRS, "read_xattr", 'R'},
580      { ACL_WRITE_NAMED_ATTRS, "write_xattr", 'W'},
581      { ACL_READ_ACL, "read_acl", 'c'},
582      { ACL_WRITE_ACL, "write_acl", 'C'},
583      { ACL_WRITE_OWNER, "write_owner", 'o'},
584      { ACL_SYNCHRONIZE, "synchronize", 's'},
585      { 0, 0, 0}};
586 
587 static const char *
588 format_flag(uint32_t *var, const struct flagnames_struct *flags)
589 {
590 
591 	for (; flags->name != 0; flags++) {
592 		if ((flags->flag & *var) == 0)
593 			continue;
594 
595 		*var &= ~flags->flag;
596 		return (flags->name);
597 	}
598 
599 	return (NULL);
600 }
601 
602 static int
603 format_flags_verbose(char *str, size_t size, uint32_t var,
604     const struct flagnames_struct *flags)
605 {
606 	size_t off = 0;
607 	const char *tmp;
608 
609 	while ((tmp = format_flag(&var, flags)) != NULL) {
610 		off += snprintf(str + off, size - off, "%s/", tmp);
611 		assert (off < size);
612 	}
613 
614 	/* If there were any flags added... */
615 	if (off > 0) {
616 		off--;
617 		/* ... then remove the last slash. */
618 		assert(str[off] == '/');
619 	}
620 
621 	str[off] = '\0';
622 
623 	return (0);
624 }
625 
626 static int
627 format_flags_compact(char *str, size_t size, uint32_t var,
628     const struct flagnames_struct *flags)
629 {
630 	size_t i;
631 
632 	for (i = 0; flags[i].name != NULL; i++) {
633 		assert(i < size);
634 		if ((flags[i].flag & var) == 0)
635 			str[i] = '-';
636 		else
637 			str[i] = flags[i].letter;
638 	}
639 
640 	str[i] = '\0';
641 
642 	return (0);
643 }
644 
645 static int
646 parse_flags_verbose(const char *strp, uint32_t *var,
647     const struct flagnames_struct *flags, const char *flags_name,
648     int *try_compact)
649 {
650 	int i, found, ever_found = 0;
651 	char *str, *flag;
652 
653 	str = strdup(strp);
654 	*try_compact = 0;
655 	*var = 0;
656 
657 	while (str != NULL) {
658 		flag = strsep(&str, "/:");
659 
660 		found = 0;
661 		for (i = 0; flags[i].name != NULL; i++) {
662 			if (strcmp(flags[i].name, flag) == 0) {
663 				*var |= flags[i].flag;
664 				found = 1;
665 				ever_found = 1;
666 			}
667 		}
668 
669 		if (!found) {
670 			if (ever_found)
671 				warnx("malformed ACL: \"%s\" field contains "
672 				    "invalid flag \"%s\"", flags_name, flag);
673 			else
674 				*try_compact = 1;
675 			free(str);
676 			return (-1);
677 		}
678 	}
679 
680 	free(str);
681 	return (0);
682 }
683 
684 static int
685 parse_flags_compact(const char *str, uint32_t *var,
686     const struct flagnames_struct *flags, const char *flags_name)
687 {
688 	int i, j, found;
689 
690 	*var = 0;
691 
692 	for (i = 0;; i++) {
693 		if (str[i] == '\0')
694 			return (0);
695 
696 		/* Ignore minus signs. */
697 		if (str[i] == '-')
698 			continue;
699 
700 		found = 0;
701 
702 		for (j = 0; flags[j].name != NULL; j++) {
703 			if (flags[j].letter == str[i]) {
704 				*var |= flags[j].flag;
705 				found = 1;
706 				break;
707 			}
708 		}
709 
710 		if (!found) {
711 			warnx("malformed ACL: \"%s\" field contains "
712 			    "invalid flag \"%c\"", flags_name, str[i]);
713 			return (-1);
714 		}
715 	}
716 }
717 
718 int
719 _nfs4_format_flags(char *str, size_t size, acl_flag_t var, int verbose)
720 {
721 
722 	if (verbose)
723 		return (format_flags_verbose(str, size, var, a_flags));
724 
725 	return (format_flags_compact(str, size, var, a_flags));
726 }
727 
728 int
729 _nfs4_format_access_mask(char *str, size_t size, acl_perm_t var, int verbose)
730 {
731 
732 	if (verbose)
733 		return (format_flags_verbose(str, size, var, a_access_masks));
734 
735 	return (format_flags_compact(str, size, var, a_access_masks));
736 }
737 
738 int
739 _nfs4_parse_flags(const char *str, acl_flag_t *flags)
740 {
741 	int error, try_compact;
742 	int tmpflags;
743 
744 	error = parse_flags_verbose(str, &tmpflags, a_flags, "flags", &try_compact);
745 	if (error && try_compact)
746 		error = parse_flags_compact(str, &tmpflags, a_flags, "flags");
747 
748 	*flags = tmpflags;
749 
750 	return (error);
751 }
752 
753 int
754 _nfs4_parse_access_mask(const char *str, acl_perm_t *perms)
755 {
756 	int error, try_compact;
757 	int tmpperms;
758 
759 	error = parse_flags_verbose(str, &tmpperms, a_access_masks,
760 	    "access permissions", &try_compact);
761 	if (error && try_compact)
762 		error = parse_flags_compact(str, &tmpperms,
763 		    a_access_masks, "access permissions");
764 
765 	*perms = tmpperms;
766 
767 	return (error);
768 }
769