1 /*
2 File: __acl_to_any_text.c
3
4 Copyright (C) 1999, 2000
5 Andreas Gruenbacher, <andreas.gruenbacher@gmail.com>
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "config.h"
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include "libacl.h"
29 #include "misc.h"
30
31 static ssize_t acl_entry_to_any_str(const acl_entry_t entry_d, char *text_p,
32 ssize_t size, const acl_entry_t mask_d,
33 const char *prefix, int options);
34 static ssize_t snprint_uint(char *text_p, ssize_t size, unsigned int i);
35 static const char *user_name(uid_t uid);
36 static const char *group_name(gid_t uid);
37
38 char *
39 __acl_to_any_text(acl_t acl, ssize_t *len_p, const char *prefix,
40 char separator, const char *suffix, int options)
41 {
42 acl_obj *acl_obj_p = ext2int(acl, acl);
43 ssize_t size, len = 0, entry_len = 0,
44 suffix_len = suffix ? strlen(suffix) : 0;
45 string_obj *string_obj_p, *tmp;
46 acl_entry_obj *entry_obj_p, *mask_obj_p = NULL;
47 if (!acl_obj_p)
48 return NULL;
49 size = acl->a_used * 15 + 1;
50 string_obj_p = new_var_obj_p(string, size);
51 if (!string_obj_p)
52 return NULL;
53
54 if (options & (TEXT_SOME_EFFECTIVE|TEXT_ALL_EFFECTIVE)) {
55 /* fetch the ACL_MASK entry */
56 FOREACH_ACL_ENTRY(entry_obj_p, acl_obj_p) {
57 if (entry_obj_p->etag == ACL_MASK) {
58 mask_obj_p = entry_obj_p;
59 break;
60 }
61 }
62 }
63
64 FOREACH_ACL_ENTRY(entry_obj_p, acl_obj_p) {
65 repeat:
66 entry_len = acl_entry_to_any_str(int2ext(entry_obj_p),
67 string_obj_p->sstr + len,
68 size-len,
69 int2ext(mask_obj_p),
70 prefix,
71 options);
72 if (entry_len < 0)
73 goto fail;
74 else if (len + entry_len + suffix_len + 1 > size) {
75 while (len + entry_len + suffix_len + 1 > size)
76 size <<= 1;
77 tmp = realloc_var_obj_p(string, string_obj_p, size);
78 if (tmp == NULL)
79 goto fail;
80 string_obj_p = tmp;
81 goto repeat;
82 } else
83 len += entry_len;
84 string_obj_p->sstr[len] = separator;
85 len++;
86 }
87 if (len)
88 len--;
89 if (len && suffix) {
90 strcpy(string_obj_p->sstr + len, suffix);
91 len += suffix_len;
92 } else
93 string_obj_p->sstr[len] = '\0';
94
95 if (len_p)
96 *len_p = len;
97 return (char *)int2ext(string_obj_p);
98
99 fail:
100 free_obj_p(string_obj_p);
101 return NULL;
102 }
103
104 #define ADVANCE(x) \
105 text_p += (x); \
106 size -= (x); \
107 if (size < 0) \
108 size = 0;
109
110 #define ABBREV(s, str_len) \
111 if (options & TEXT_ABBREVIATE) { \
112 if (size > 0) \
113 text_p[0] = *(s); \
114 if (size > 1) \
115 text_p[1] = ':'; \
116 ADVANCE(2); \
117 } else { \
118 strncpy(text_p, (s), size); \
119 ADVANCE(str_len); \
120 }
121
122 #define EFFECTIVE_STR "#effective:"
123
124 static ssize_t
125 acl_entry_to_any_str(const acl_entry_t entry_d, char *text_p, ssize_t size,
126 const acl_entry_t mask_d, const char *prefix, int options)
127 {
128 #define TABS 4
129 static const char *tabs = "\t\t\t\t";
130 acl_entry_obj *entry_obj_p = ext2int(acl_entry, entry_d);
131 acl_entry_obj *mask_obj_p = NULL;
132 permset_t effective;
133 acl_tag_t type;
134 ssize_t x;
135 const char *orig_text_p = text_p, *str;
136 if (!entry_obj_p)
137 return -1;
138 if (mask_d) {
139 mask_obj_p = ext2int(acl_entry, mask_d);
140 if (!mask_obj_p)
141 return -1;
142 }
143 if (text_p == NULL)
144 size = 0;
145
146 if (prefix) {
147 strncpy(text_p, prefix, size);
148 ADVANCE(strlen(prefix));
149 }
150
151 type = entry_obj_p->etag;
152 switch (type) {
153 case ACL_USER_OBJ: /* owner */
154 mask_obj_p = NULL;
155 /* fall through */
156 case ACL_USER: /* additional user */
157 ABBREV("user:", 5);
158 if (type == ACL_USER) {
159 if (options & TEXT_NUMERIC_IDS)
160 str = NULL;
161 else
162 str = __acl_quote(user_name(
163 entry_obj_p->eid.qid), ":, \t\n\r");
164 if (str != NULL) {
165 strncpy(text_p, str, size);
166 ADVANCE(strlen(str));
167 } else {
168 x = snprint_uint(text_p, size,
169 entry_obj_p->eid.qid);
170 ADVANCE(x);
171 }
172 }
173 if (size > 0)
174 *text_p = ':';
175 ADVANCE(1);
176 break;
177
178 case ACL_GROUP_OBJ: /* owning group */
179 case ACL_GROUP: /* additional group */
180 ABBREV("group:", 6);
181 if (type == ACL_GROUP) {
182 if (options & TEXT_NUMERIC_IDS)
183 str = NULL;
184 else
185 str = __acl_quote(group_name(
186 entry_obj_p->eid.qid), ":, \t\n\r");
187 if (str != NULL) {
188 strncpy(text_p, str, size);
189 ADVANCE(strlen(str));
190 } else {
191 x = snprint_uint(text_p, size,
192 entry_obj_p->eid.qid);
193 ADVANCE(x);
194 }
195 }
196 if (size > 0)
197 *text_p = ':';
198 ADVANCE(1);
199 break;
200
201 case ACL_MASK: /* acl mask */
202 mask_obj_p = NULL;
203 ABBREV("mask:", 5);
204 if (size > 0)
205 *text_p = ':';
206 ADVANCE(1);
207 break;
208
209 case ACL_OTHER: /* other users */
210 mask_obj_p = NULL;
211 /* fall through */
212 ABBREV("other:", 6);
213 if (size > 0)
214 *text_p = ':';
215 ADVANCE(1);
216 break;
217
218 default:
219 return 0;
220 }
221
222 switch ((size >= 3) ? 3 : size) {
223 case 3:
224 text_p[2] = (entry_obj_p->eperm.sperm &
225 ACL_EXECUTE) ? 'x' : '-';
226 /* fall through */
227 case 2:
228 text_p[1] = (entry_obj_p->eperm.sperm &
229 ACL_WRITE) ? 'w' : '-';
230 /* fall through */
231 case 1:
232 text_p[0] = (entry_obj_p->eperm.sperm &
233 ACL_READ) ? 'r' : '-';
234 break;
235 }
236 ADVANCE(3);
237
238 if (mask_obj_p &&
239 (options & (TEXT_SOME_EFFECTIVE|TEXT_ALL_EFFECTIVE))) {
240 mask_obj_p = ext2int(acl_entry, mask_d);
241 if (!mask_obj_p)
242 return -1;
243
244 effective = entry_obj_p->eperm.sperm &
245 mask_obj_p->eperm.sperm;
246 if (effective != entry_obj_p->eperm.sperm ||
247 options & TEXT_ALL_EFFECTIVE) {
248 x = (options & TEXT_SMART_INDENT) ?
249 ((text_p - orig_text_p)/8) : TABS-1;
250
251 /* use at least one tab for indentation */
252 if (x > (TABS-1))
253 x = (TABS-1);
254
255 strncpy(text_p, tabs+x, size);
256 ADVANCE(TABS-x);
257
258 strncpy(text_p, EFFECTIVE_STR, size);
259 ADVANCE(sizeof(EFFECTIVE_STR)-1);
260
261 switch ((size >= 3) ? 3 : size) {
262 case 3:
263 text_p[2] = (effective &
264 ACL_EXECUTE) ? 'x' : '-';
265 /* fall through */
266 case 2:
267 text_p[1] = (effective &
268 ACL_WRITE) ? 'w' : '-';
269 /* fall through */
270 case 1:
271 text_p[0] = (effective &
272 ACL_READ) ? 'r' : '-';
273 break;
274 }
275 ADVANCE(3);
276
277 }
278 }
279
280 /* zero-terminate string (but don't count '\0' character) */
281 if (size > 0)
282 *text_p = '\0';
283
284 return (text_p - orig_text_p); /* total size required, excluding
285 final NULL character. */
286 }
287
288 #undef ADVANCE
289
290
291
292 /*
293 This function is equivalent to the proposed changes to snprintf:
294 snprintf(text_p, size, "%u", i)
295 (The current snprintf returns -1 if the buffer is too small; the proposal
296 is to return the number of characters that would be required. See the
297 snprintf manual page.)
298 */
299
300 static ssize_t
301 snprint_uint(char *text_p, ssize_t size, unsigned int i)
302 {
303 unsigned int tmp = i;
304 int digits = 1;
305 unsigned int factor = 1;
306
307 while ((tmp /= 10) != 0) {
308 digits++;
309 factor *= 10;
310 }
311 if (size && (i == 0)) {
312 *text_p++ = '0';
313 } else {
314 while (size > 0 && factor > 0) {
315 *text_p++ = '0' + (i / factor);
316 size--;
317 i %= factor;
318 factor /= 10;
319 }
320 }
321 if (size)
322 *text_p = '\0';
323
324 return digits;
325 }
326
327
328 static const char *
329 user_name(uid_t uid)
330 {
331 struct passwd *passwd = getpwuid(uid);
332
333 if (passwd != NULL)
334 return passwd->pw_name;
335 else
336 return NULL;
337 }
338
339
340 static const char *
341 group_name(gid_t gid)
342 {
343 struct group *group = getgrgid(gid);
344
345 if (group != NULL)
346 return group->gr_name;
347 else
348 return NULL;
349 }
350