1194955Strasz/*-
2194955Strasz * Copyright (c) 2008, 2009 Edward Tomasz Napiera��a <trasz@FreeBSD.org>
3194955Strasz * All rights reserved.
4194955Strasz *
5194955Strasz * Redistribution and use in source and binary forms, with or without
6194955Strasz * modification, are permitted provided that the following conditions
7194955Strasz * are met:
8194955Strasz * 1. Redistributions of source code must retain the above copyright
9194955Strasz *    notice, this list of conditions and the following disclaimer.
10194955Strasz * 2. Redistributions in binary form must reproduce the above copyright
11194955Strasz *    notice, this list of conditions and the following disclaimer in the
12194955Strasz *    documentation and/or other materials provided with the distribution.
13194955Strasz *
14194955Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15194955Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16194955Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17194955Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18194955Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19194955Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20194955Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21194955Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22194955Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23194955Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24194955Strasz * SUCH DAMAGE.
25194955Strasz */
26194955Strasz
27194955Strasz#include <sys/cdefs.h>
28194955Strasz__FBSDID("$FreeBSD$");
29194955Strasz
30194955Strasz#include <stdio.h>
31194955Strasz#include <stdlib.h>
32194955Strasz#include <string.h>
33194955Strasz#include <assert.h>
34194955Strasz#include <err.h>
35194955Strasz#include <sys/acl.h>
36194955Strasz#include "acl_support.h"
37194955Strasz
38194955Straszstruct flagnames_struct {
39194955Strasz	uint32_t	flag;
40194955Strasz	const char	*name;
41194955Strasz	char		letter;
42194955Strasz};
43194955Strasz
44194955Straszstruct flagnames_struct a_flags[] =
45194955Strasz    {{ ACL_ENTRY_FILE_INHERIT, "file_inherit", 'f'},
46194955Strasz     { ACL_ENTRY_DIRECTORY_INHERIT, "dir_inherit", 'd'},
47194955Strasz     { ACL_ENTRY_INHERIT_ONLY, "inherit_only", 'i'},
48194955Strasz     { ACL_ENTRY_NO_PROPAGATE_INHERIT, "no_propagate", 'n'},
49194955Strasz     { ACL_ENTRY_SUCCESSFUL_ACCESS, "successfull_access", 'S'},
50194955Strasz     { ACL_ENTRY_FAILED_ACCESS, "failed_access", 'F'},
51194955Strasz     /*
52194955Strasz      * There is no ACE_IDENTIFIER_GROUP here - SunOS does not show it
53194955Strasz      * in the "flags" field.  There is no ACE_OWNER, ACE_GROUP or
54194955Strasz      * ACE_EVERYONE either, for obvious reasons.
55194955Strasz      */
56194955Strasz     { 0, 0, 0}};
57194955Strasz
58194955Straszstruct flagnames_struct a_access_masks[] =
59194955Strasz    {{ ACL_READ_DATA, "read_data", 'r'},
60194955Strasz     { ACL_WRITE_DATA, "write_data", 'w'},
61194955Strasz     { ACL_EXECUTE, "execute", 'x'},
62194955Strasz     { ACL_APPEND_DATA, "append_data", 'p'},
63194955Strasz     { ACL_DELETE_CHILD, "delete_child", 'D'},
64194955Strasz     { ACL_DELETE, "delete", 'd'},
65194955Strasz     { ACL_READ_ATTRIBUTES, "read_attributes", 'a'},
66194955Strasz     { ACL_WRITE_ATTRIBUTES, "write_attributes", 'A'},
67194955Strasz     { ACL_READ_NAMED_ATTRS, "read_xattr", 'R'},
68194955Strasz     { ACL_WRITE_NAMED_ATTRS, "write_xattr", 'W'},
69194955Strasz     { ACL_READ_ACL, "read_acl", 'c'},
70194955Strasz     { ACL_WRITE_ACL, "write_acl", 'C'},
71194955Strasz     { ACL_WRITE_OWNER, "write_owner", 'o'},
72194955Strasz     { ACL_SYNCHRONIZE, "synchronize", 's'},
73220465Strasz     { ACL_FULL_SET, "full_set", '\0'},
74220465Strasz     { ACL_MODIFY_SET, "modify_set", '\0'},
75220465Strasz     { ACL_READ_SET, "read_set", '\0'},
76220465Strasz     { ACL_WRITE_SET, "write_set", '\0'},
77194955Strasz     { 0, 0, 0}};
78194955Strasz
79194955Straszstatic const char *
80194955Straszformat_flag(uint32_t *var, const struct flagnames_struct *flags)
81194955Strasz{
82194955Strasz
83194955Strasz	for (; flags->name != 0; flags++) {
84194955Strasz		if ((flags->flag & *var) == 0)
85194955Strasz			continue;
86194955Strasz
87194955Strasz		*var &= ~flags->flag;
88194955Strasz		return (flags->name);
89194955Strasz	}
90194955Strasz
91194955Strasz	return (NULL);
92194955Strasz}
93194955Strasz
94194955Straszstatic int
95194955Straszformat_flags_verbose(char *str, size_t size, uint32_t var,
96194955Strasz    const struct flagnames_struct *flags)
97194955Strasz{
98194955Strasz	size_t off = 0;
99194955Strasz	const char *tmp;
100194955Strasz
101194955Strasz	while ((tmp = format_flag(&var, flags)) != NULL) {
102194955Strasz		off += snprintf(str + off, size - off, "%s/", tmp);
103194955Strasz		assert (off < size);
104194955Strasz	}
105194955Strasz
106194955Strasz	/* If there were any flags added... */
107194955Strasz	if (off > 0) {
108194955Strasz		off--;
109194955Strasz		/* ... then remove the last slash. */
110194955Strasz		assert(str[off] == '/');
111194955Strasz	}
112194955Strasz
113194955Strasz	str[off] = '\0';
114194955Strasz
115194955Strasz	return (0);
116194955Strasz}
117194955Strasz
118194955Straszstatic int
119194955Straszformat_flags_compact(char *str, size_t size, uint32_t var,
120194955Strasz    const struct flagnames_struct *flags)
121194955Strasz{
122194955Strasz	size_t i;
123194955Strasz
124220465Strasz	for (i = 0; flags[i].letter != '\0'; i++) {
125194955Strasz		assert(i < size);
126194955Strasz		if ((flags[i].flag & var) == 0)
127194955Strasz			str[i] = '-';
128194955Strasz		else
129194955Strasz			str[i] = flags[i].letter;
130194955Strasz	}
131194955Strasz
132194955Strasz	str[i] = '\0';
133194955Strasz
134194955Strasz	return (0);
135194955Strasz}
136194955Strasz
137194955Straszstatic int
138194955Straszparse_flags_verbose(const char *strp, uint32_t *var,
139194955Strasz    const struct flagnames_struct *flags, const char *flags_name,
140194955Strasz    int *try_compact)
141194955Strasz{
142194955Strasz	int i, found, ever_found = 0;
143194955Strasz	char *str, *flag;
144194955Strasz
145194955Strasz	str = strdup(strp);
146194955Strasz	*try_compact = 0;
147194955Strasz	*var = 0;
148194955Strasz
149194955Strasz	while (str != NULL) {
150194955Strasz		flag = strsep(&str, "/:");
151194955Strasz
152194955Strasz		found = 0;
153194955Strasz		for (i = 0; flags[i].name != NULL; i++) {
154194955Strasz			if (strcmp(flags[i].name, flag) == 0) {
155194955Strasz				*var |= flags[i].flag;
156194955Strasz				found = 1;
157194955Strasz				ever_found = 1;
158194955Strasz			}
159194955Strasz		}
160194955Strasz
161194955Strasz		if (!found) {
162194955Strasz			if (ever_found)
163194955Strasz				warnx("malformed ACL: \"%s\" field contains "
164194955Strasz				    "invalid flag \"%s\"", flags_name, flag);
165194955Strasz			else
166194955Strasz				*try_compact = 1;
167194955Strasz			free(str);
168194955Strasz			return (-1);
169194955Strasz		}
170194955Strasz	}
171194955Strasz
172194955Strasz	free(str);
173194955Strasz	return (0);
174194955Strasz}
175194955Strasz
176194955Straszstatic int
177194955Straszparse_flags_compact(const char *str, uint32_t *var,
178194955Strasz    const struct flagnames_struct *flags, const char *flags_name)
179194955Strasz{
180194955Strasz	int i, j, found;
181194955Strasz
182194955Strasz	*var = 0;
183194955Strasz
184194955Strasz	for (i = 0;; i++) {
185194955Strasz		if (str[i] == '\0')
186194955Strasz			return (0);
187194955Strasz
188194955Strasz		/* Ignore minus signs. */
189194955Strasz		if (str[i] == '-')
190194955Strasz			continue;
191194955Strasz
192194955Strasz		found = 0;
193194955Strasz
194194955Strasz		for (j = 0; flags[j].name != NULL; j++) {
195194955Strasz			if (flags[j].letter == str[i]) {
196194955Strasz				*var |= flags[j].flag;
197194955Strasz				found = 1;
198194955Strasz				break;
199194955Strasz			}
200194955Strasz		}
201194955Strasz
202194955Strasz		if (!found) {
203194955Strasz			warnx("malformed ACL: \"%s\" field contains "
204194955Strasz			    "invalid flag \"%c\"", flags_name, str[i]);
205194955Strasz			return (-1);
206194955Strasz		}
207194955Strasz	}
208194955Strasz}
209194955Strasz
210194955Straszint
211194955Strasz_nfs4_format_flags(char *str, size_t size, acl_flag_t var, int verbose)
212194955Strasz{
213194955Strasz
214194955Strasz	if (verbose)
215194955Strasz		return (format_flags_verbose(str, size, var, a_flags));
216194955Strasz
217194955Strasz	return (format_flags_compact(str, size, var, a_flags));
218194955Strasz}
219194955Strasz
220194955Straszint
221194955Strasz_nfs4_format_access_mask(char *str, size_t size, acl_perm_t var, int verbose)
222194955Strasz{
223194955Strasz
224194955Strasz	if (verbose)
225194955Strasz		return (format_flags_verbose(str, size, var, a_access_masks));
226194955Strasz
227194955Strasz	return (format_flags_compact(str, size, var, a_access_masks));
228194955Strasz}
229194955Strasz
230194955Straszint
231194955Strasz_nfs4_parse_flags(const char *str, acl_flag_t *flags)
232194955Strasz{
233194955Strasz	int error, try_compact;
234194955Strasz	int tmpflags;
235194955Strasz
236194955Strasz	error = parse_flags_verbose(str, &tmpflags, a_flags, "flags", &try_compact);
237194955Strasz	if (error && try_compact)
238194955Strasz		error = parse_flags_compact(str, &tmpflags, a_flags, "flags");
239194955Strasz
240194955Strasz	*flags = tmpflags;
241194955Strasz
242194955Strasz	return (error);
243194955Strasz}
244194955Strasz
245194955Straszint
246194955Strasz_nfs4_parse_access_mask(const char *str, acl_perm_t *perms)
247194955Strasz{
248194955Strasz	int error, try_compact;
249194955Strasz	int tmpperms;
250194955Strasz
251194955Strasz	error = parse_flags_verbose(str, &tmpperms, a_access_masks,
252194955Strasz	    "access permissions", &try_compact);
253194955Strasz	if (error && try_compact)
254194955Strasz		error = parse_flags_compact(str, &tmpperms,
255194955Strasz		    a_access_masks, "access permissions");
256194955Strasz
257194955Strasz	*perms = tmpperms;
258194955Strasz
259194955Strasz	return (error);
260194955Strasz}
261