cifs.upcall: the exit code should be 0 when print version
[jlayton/cifs-utils.git] / setcifsacl.c
1 /*
2 * setcifsacl utility
3 *
4 * Copyright (C) Shirish Pargaonkar (shirishp@us.ibm.com) 2011
5 *
6 * Used to alter entries of an ACL or replace an entire ACL in a
7 * security descriptor of a file system object that belongs to a
8 * share mounted using option cifsacl.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26
27 #include <string.h>
28 #include <getopt.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <ctype.h>
37 #include <sys/xattr.h>
38
39 #include "cifsacl.h"
40 #include "idmap_plugin.h"
41
42 enum setcifsacl_actions {
43         ActUnknown = -1,
44         ActDelete,
45         ActModify,
46         ActAdd,
47         ActSet
48 };
49
50 static void *plugin_handle;
51
52 static void
53 copy_cifs_sid(struct cifs_sid *dst, const struct cifs_sid *src)
54 {
55         int i;
56
57         dst->revision = src->revision;
58         dst->num_subauth = src->num_subauth;
59         for (i = 0; i < NUM_AUTHS; i++)
60                 dst->authority[i] = src->authority[i];
61         for (i = 0; i < src->num_subauth; i++)
62                 dst->sub_auth[i] = src->sub_auth[i];
63 }
64
65 static void
66 copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
67                 int numaces, int acessize)
68 {
69         int osidsoffset, gsidsoffset, dacloffset;
70         struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
71         struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
72         struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr;
73
74         /* copy security descriptor control portion */
75         osidsoffset = le32toh(pntsd->osidoffset);
76         gsidsoffset = le32toh(pntsd->gsidoffset);
77         dacloffset = le32toh(pntsd->dacloffset);
78
79         pnntsd->revision = pntsd->revision;
80         pnntsd->type = pntsd->type;
81         pnntsd->osidoffset = pntsd->osidoffset;
82         pnntsd->gsidoffset = pntsd->gsidoffset;
83         pnntsd->dacloffset = pntsd->dacloffset;
84
85         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
86         ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset);
87
88         ndacl_ptr->revision = dacl_ptr->revision;
89         ndacl_ptr->size = htole16(acessize + sizeof(struct cifs_ctrl_acl));
90         ndacl_ptr->num_aces = htole32(numaces);
91
92         /* copy owner sid */
93         owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset);
94         nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + osidsoffset);
95         copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
96
97         /* copy group sid */
98         group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset);
99         ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + gsidsoffset);
100         copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
101
102         return;
103 }
104
105 static int
106 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
107 {
108         dace->type = sace->type;
109         dace->flags = sace->flags;
110         dace->access_req = sace->access_req;
111
112         copy_cifs_sid(&dace->sid, &sace->sid);
113
114         dace->size = sace->size;
115
116         return le16toh(dace->size);
117 }
118
119 static int
120 compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags)
121 {
122         int i;
123
124         if (compflags & COMPSID) {
125                 if (dace->sid.revision != sace->sid.revision)
126                         return 0;
127                 if (dace->sid.num_subauth != sace->sid.num_subauth)
128                         return 0;
129                 for (i = 0; i < NUM_AUTHS; i++) {
130                         if (dace->sid.authority[i] != sace->sid.authority[i])
131                                 return 0;
132                 }
133                 for (i = 0; i < sace->sid.num_subauth; i++) {
134                         if (dace->sid.sub_auth[i] != sace->sid.sub_auth[i])
135                                 return 0;
136                 }
137         }
138
139         if (compflags & COMPTYPE) {
140                 if (dace->type != sace->type)
141                         return 0;
142         }
143
144         if (compflags & COMPFLAG) {
145                 if (dace->flags != sace->flags)
146                         return 0;
147         }
148
149         if (compflags & COMPMASK) {
150                 if (dace->access_req != sace->access_req)
151                         return 0;
152         }
153
154         return 1;
155 }
156
157 static int
158 get_sec_desc_size(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
159                         int aces, ssize_t *bufsize, size_t *acesoffset)
160 {
161         unsigned int size, acessize, dacloffset;
162
163         size = sizeof(struct cifs_ntsd) +
164                 2 * sizeof(struct cifs_sid) +
165                 sizeof(struct cifs_ctrl_acl);
166
167         dacloffset = le32toh(pntsd->dacloffset);
168
169         *acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl);
170         acessize = aces * sizeof(struct cifs_ace);
171         *bufsize = size + acessize;
172
173         *npntsd = malloc(*bufsize);
174         if (!*npntsd) {
175                 printf("%s: Memory allocation failure", __func__);
176                 return errno;
177         }
178
179         return 0;
180 }
181
182 static int
183 ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
184                         struct cifs_ace **cacesptr, int numcaces)
185 {
186         int i, rc, size = 0, acessize = 0;
187         size_t acesoffset;
188         char *acesptr;
189
190         rc = get_sec_desc_size(pntsd, npntsd, numcaces, bufsize, &acesoffset);
191         if (rc)
192                 return rc;
193
194         acesptr = (char *)*npntsd + acesoffset;
195         for (i = 0; i < numcaces; ++i) {
196                 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
197                 acessize += size;
198                 acesptr += size;
199         }
200         copy_sec_desc(pntsd, *npntsd, numcaces, acessize);
201         acesptr = (char *)*npntsd + acesoffset;
202
203
204         return 0;
205 }
206
207 static int
208 ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
209                 struct cifs_ace **facesptr, int numfaces,
210                 struct cifs_ace **cacesptr, int numcaces)
211 {
212         int i, rc, numaces, size, acessize = 0;
213         size_t acesoffset;
214         char *acesptr;
215
216         numaces = numfaces + numcaces;
217         rc = get_sec_desc_size(pntsd, npntsd, numaces, bufsize, &acesoffset);
218         if (rc)
219                 return rc;
220
221         acesptr = (char *)*npntsd + acesoffset;
222         for (i = 0; i < numfaces; ++i) {
223                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
224                 acesptr += size;
225                 acessize += size;
226         }
227         for (i = 0; i < numcaces; ++i) {
228                 size = copy_ace((struct cifs_ace *)acesptr, cacesptr[i]);
229                 acesptr += size;
230                 acessize += size;
231         }
232         copy_sec_desc(pntsd, *npntsd, numaces, acessize);
233
234         return 0;
235 }
236
237 static int
238 ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
239                 struct cifs_ace **facesptr, int numfaces,
240                 struct cifs_ace **cacesptr, int numcaces)
241 {
242         int i, j, rc, size, acessize = 0;
243         size_t acesoffset;
244         char *acesptr;
245
246         if (numfaces == 0) {
247                 printf("%s: No entries to modify", __func__);
248                 return -1;
249         }
250
251         rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
252         if (rc)
253                 return rc;
254
255         for (j = 0; j < numcaces; ++j) {
256                 for (i = 0; i < numfaces; ++i) {
257                         if (compare_aces(facesptr[i], cacesptr[j],
258                                         COMPSID | COMPTYPE)) {
259                                 copy_ace(facesptr[i], cacesptr[j]);
260                                 break;
261                         }
262                 }
263         }
264
265         acesptr = (char *)*npntsd + acesoffset;
266         for (i = 0; i < numfaces; ++i) {
267                 size = copy_ace((struct cifs_ace *)acesptr, facesptr[i]);
268                 acesptr += size;
269                 acessize += size;
270         }
271
272         copy_sec_desc(pntsd, *npntsd, numfaces, acessize);
273
274         return 0;
275 }
276
277 static int
278 ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize,
279                 struct cifs_ace **facesptr, int numfaces,
280                 struct cifs_ace **cacesptr, int numcaces)
281 {
282         int i, j, numaces = 0, rc, size, acessize = 0;
283         size_t acesoffset;
284         char *acesptr;
285
286         if (numfaces == 0) {
287                 printf("%s: No entries to delete\n", __func__);
288                 return -1;
289         }
290
291         if (numfaces < numcaces) {
292                 printf("%s: Invalid entries to delete\n", __func__);
293                 return -1;
294         }
295
296         rc = get_sec_desc_size(pntsd, npntsd, numfaces, bufsize, &acesoffset);
297         if (rc)
298                 return rc;
299
300         acesptr = (char *)*npntsd + acesoffset;
301         for (i = 0; i < numfaces; ++i) {
302                 for (j = 0; j < numcaces; ++j) {
303                         if (compare_aces(facesptr[i], cacesptr[j], COMPALL))
304                                 break;
305                 }
306                 if (j == numcaces) {
307                         size = copy_ace((struct cifs_ace *)acesptr,
308                                                                 facesptr[i]);
309                         acessize += size;
310                         acesptr += size;
311                         ++numaces;
312                 }
313         }
314
315         if (numaces == numfaces) {
316                 printf("%s: Nothing to delete\n", __func__);
317                 return 1;
318         }
319         copy_sec_desc(pntsd, *npntsd, numaces, acessize);
320
321         return 0;
322 }
323
324 static int
325 get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len,
326                         struct cifs_ctrl_acl **daclptr)
327 {
328         int numfaces = 0;
329         uint32_t dacloffset;
330         struct cifs_ctrl_acl *ldaclptr;
331         char *end_of_acl = ((char *)pntsd) + acl_len;
332
333         dacloffset = le32toh(pntsd->dacloffset);
334         if (!dacloffset)
335                 return 0;
336
337         ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
338
339         /* validate that we do not go past end of acl */
340         if (end_of_acl >= (char *)ldaclptr + le16toh(ldaclptr->size)) {
341                 numfaces = le32toh(ldaclptr->num_aces);
342                 *daclptr = ldaclptr;
343         }
344
345         return numfaces;
346 }
347
348 static struct cifs_ace **
349 build_fetched_aces(char *daclptr, int numfaces)
350 {
351         int i, acl_size;
352         char *acl_base;
353         struct cifs_ace *pace, **facesptr;
354
355         facesptr = calloc(numfaces, sizeof(struct cifs_aces *));
356         if (!facesptr) {
357                 printf("%s: Error %d allocating ACE array",
358                                 __func__, errno);
359                 return facesptr;
360         }
361
362         acl_base = daclptr;
363         acl_size = sizeof(struct cifs_ctrl_acl);
364         for (i = 0; i < numfaces; ++i) {
365                 facesptr[i] = malloc(sizeof(struct cifs_ace));
366                 if (!facesptr[i])
367                         goto build_fetched_aces_err;
368                 pace = (struct cifs_ace *) (acl_base + acl_size);
369                 memcpy(facesptr[i], pace, sizeof(struct cifs_ace));
370                 acl_base = (char *)pace;
371                 acl_size = le16toh(pace->size);
372         }
373         return facesptr;
374
375 build_fetched_aces_err:
376         printf("%s: Invalid fetched ace\n", __func__);
377         for (i = 0; i < numfaces; ++i)
378                 free(facesptr[i]);
379         free(facesptr);
380         return NULL;
381 }
382
383 static int
384 verify_ace_type(char *typestr, uint8_t *typeval)
385 {
386         int i, len;
387         char *invaltype;
388
389         if (strstr(typestr, "0x")) { /* hex type value */
390                 *typeval = strtol(typestr, &invaltype, 16);
391                 if (!strlen(invaltype)) {
392                         if (*typeval != ACCESS_ALLOWED &&
393                                 *typeval != ACCESS_DENIED &&
394                                 *typeval != ACCESS_ALLOWED_OBJECT &&
395                                 *typeval != ACCESS_DENIED_OBJECT) {
396                                         printf("%s: Invalid type: %s\n",
397                                                 __func__, typestr);
398                                         return 1;
399                         }
400                         return 0;
401                 }
402         }
403
404         len = strlen(typestr);
405         for (i = 0; i < len; ++i)
406                 *(typestr + i) = toupper(*(typestr + i));
407         if (!strcmp(typestr, "ALLOWED"))
408                 *typeval = 0x0;
409         else if (!strcmp(typestr, "DENIED"))
410                 *typeval = 0x1;
411         else if (!strcmp(typestr, "ALLOWED_OBJECT"))
412                 *typeval = 0x5;
413         else if (!strcmp(typestr, "DENIED_OBJECT"))
414                 *typeval = 0x6;
415         else {
416                 printf("%s: Invalid type: %s\n", __func__, typestr);
417                 return 1;
418         }
419
420         return 0;
421 }
422
423 static uint8_t
424 ace_flag_value(char *flagstr)
425 {
426         uint8_t flagval = 0x0;
427         char *iflag;
428
429         iflag = strtok(flagstr, "|"); /* everything before | */
430         while (iflag) {
431                 if (!strcmp(iflag, "OI"))
432                         flagval += 0x1;
433                 else if (!strcmp(iflag, "CI"))
434                         flagval += 0x2;
435                 else if (!strcmp(iflag, "NP"))
436                         flagval += 0x4;
437                 else if (!strcmp(iflag, "IO"))
438                         flagval += 0x8;
439                 else if (!strcmp(iflag, "I"))
440                         flagval += 0x10;
441                 else
442                         return 0x0; /* Invalid flag */
443                 iflag = strtok(NULL, "|"); /* everything before | */
444         }
445
446         return flagval;
447 }
448
449 static int
450 verify_ace_flags(char *flagstr, uint8_t *flagval)
451 {
452         char *invalflag;
453
454         if (!strcmp(flagstr, "0") || !strcmp(flagstr, "0x0"))
455                 return 0;
456
457         if (strstr(flagstr, "0x")) { /* hex flag value */
458                 *flagval = strtol(flagstr, &invalflag, 16);
459                 if (strlen(invalflag)) {
460                         printf("%s: Invalid flags: %s\n", __func__, flagstr);
461                         return 1;
462                 }
463         } else
464                 *flagval = ace_flag_value(flagstr);
465
466         if (!*flagval || (*flagval & ~VFLAGS)) {
467                 printf("%s: Invalid flag %s and value: 0x%x\n",
468                         __func__, flagstr, *flagval);
469                 return 1;
470         }
471
472         return 0;
473 }
474
475 static uint32_t
476 ace_mask_value(char *mask)
477 {
478         uint32_t maskval = 0;
479         char cur;
480
481         if (!strcmp(mask, "FULL"))
482                 return FULL_CONTROL;
483         if (!strcmp(mask, "CHANGE"))
484                 return CHANGE;
485         if (!strcmp(mask, "READ"))
486                 return EREAD;
487
488         while((cur = *mask++)) {
489                 switch(cur) {
490                 case 'R':
491                         maskval |= EREAD;
492                         break;
493                 case 'W':
494                         maskval |= EWRITE;
495                         break;
496                 case 'X':
497                         maskval |= EXEC;
498                         break;
499                 case 'D':
500                         maskval |= DELETE;
501                         break;
502                 case 'P':
503                         maskval |= WRITE_DAC;
504                         break;
505                 case 'O':
506                         maskval |= WRITE_OWNER;
507                         break;
508                 default:
509                         return 0;
510                 }
511         }
512         return maskval;
513 }
514
515 static int
516 verify_ace_mask(char *maskstr, uint32_t *maskval)
517 {
518         unsigned long val;
519         char *ep;
520
521         errno = 0;
522         val = strtoul(maskstr, &ep, 0);
523         if (errno == 0 && *ep == '\0')
524                 *maskval = htole32((uint32_t)val);
525         else
526                 *maskval = htole32(ace_mask_value(maskstr));
527
528         if (!*maskval) {
529                 printf("%s: Invalid mask %s (value 0x%x)\n", __func__,
530                         maskstr, *maskval);
531                 return 1;
532         }
533
534         return 0;
535 }
536
537 static struct cifs_ace **
538 build_cmdline_aces(char **arrptr, int numcaces)
539 {
540         int i;
541         char *acesid, *acetype, *aceflag, *acemask;
542         struct cifs_ace **cacesptr;
543
544         cacesptr = calloc(numcaces, sizeof(struct cifs_aces *));
545         if (!cacesptr) {
546                 printf("%s: Error %d allocating ACE array", __func__, errno);
547                 return NULL;
548         }
549
550         for (i = 0; i < numcaces; ++i) {
551                 acesid = strtok(arrptr[i], ":");
552                 acetype = strtok(NULL, "/");
553                 aceflag = strtok(NULL, "/");
554                 acemask = strtok(NULL, "/");
555
556                 if (!acesid || !acetype || !aceflag || !acemask) {
557                         printf("%s: Incomplete ACE: %s\n", __func__, arrptr[i]);
558                         goto build_cmdline_aces_ret;
559                 }
560
561                 cacesptr[i] = malloc(sizeof(struct cifs_ace));
562                 if (!cacesptr[i]) {
563                         printf("%s: ACE alloc error %d\n", __func__, errno);
564                         goto build_cmdline_aces_ret;
565                 }
566
567                 if (str_to_sid(plugin_handle, acesid, &cacesptr[i]->sid)) {
568                         printf("%s: Invalid SID (%s): %s\n", __func__, arrptr[i],
569                                 plugin_errmsg);
570                         goto build_cmdline_aces_ret;
571                 }
572
573                 if (verify_ace_type(acetype, &cacesptr[i]->type)) {
574                         printf("%s: Invalid ACE type: %s\n",
575                                         __func__, arrptr[i]);
576                         goto build_cmdline_aces_ret;
577                 }
578
579                 if (verify_ace_flags(aceflag, &cacesptr[i]->flags)) {
580                         printf("%s: Invalid ACE flag: %s\n",
581                                 __func__, arrptr[i]);
582                         goto build_cmdline_aces_ret;
583                 }
584
585                 if (verify_ace_mask(acemask, &cacesptr[i]->access_req)) {
586                         printf("%s: Invalid ACE mask: %s\n",
587                                 __func__, arrptr[i]);
588                         goto build_cmdline_aces_ret;
589                 }
590
591                 cacesptr[i]->size = htole16(1 + 1 + 2 + 4 + 1 + 1 + 6 +
592                                             cacesptr[i]->sid.num_subauth * 4);
593         }
594         return cacesptr;
595
596 build_cmdline_aces_ret:
597         for (i = 0; i < numcaces; ++i)
598                 free(cacesptr[i]);
599         free(cacesptr);
600         return NULL;
601 }
602
603 static char **
604 parse_cmdline_aces(char *acelist, int numcaces)
605 {
606         int i = 0;
607         char *acestr, *vacestr, **arrptr = NULL;
608
609         arrptr = (char **)malloc(numcaces * sizeof(char *));
610         if (!arrptr) {
611                 printf("%s: Unable to allocate char array\n", __func__);
612                 return NULL;
613         }
614
615         while (i < numcaces) {
616                 acestr = strtok(acelist, ","); /* everything before , */
617                 if (!acestr)
618                         goto parse_cmdline_aces_err;
619
620                 vacestr = strstr(acestr, "ACL:"); /* ace as ACL:*" */
621                 if (!vacestr)
622                         goto parse_cmdline_aces_err;
623                 vacestr += 4; /* skip past "ACL:" */
624                 if (*vacestr) {
625                         arrptr[i] = vacestr;
626                         ++i;
627                 }
628                 acelist = NULL;
629         }
630         return arrptr;
631
632 parse_cmdline_aces_err:
633         printf("%s: Error parsing ACEs\n", __func__);
634         free(arrptr);
635         return NULL;
636 }
637
638 /* How many aces were provided on the command-line? Count the commas. */
639 static unsigned int
640 get_numcaces(const char *aces)
641 {
642         unsigned int num = 1;
643         const char *current;
644
645         current = aces;
646         while((current = strchr(current, ','))) {
647                 ++current;
648                 ++num;
649         }
650
651         return num;
652 }
653
654 static int
655 setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
656                 ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces,
657                 struct cifs_ace **cacesptr, int numcaces,
658                 enum setcifsacl_actions maction)
659 {
660         int rc = 1;
661
662         switch (maction) {
663         case ActDelete:
664                 rc = ace_delete(pntsd, npntsd, bufsize, facesptr,
665                                 numfaces, cacesptr, numcaces);
666                 break;
667         case ActModify:
668                 rc = ace_modify(pntsd, npntsd, bufsize, facesptr,
669                                 numfaces, cacesptr, numcaces);
670                 break;
671         case ActAdd:
672                 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
673                                 numfaces, cacesptr, numcaces);
674                 break;
675         case ActSet:
676                 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
677                 break;
678         default:
679                 printf("%s: Invalid action: %d\n", __func__, maction);
680                 break;
681         }
682
683         return rc;
684 }
685
686 static void
687 setcifsacl_usage(const char *prog)
688 {
689         fprintf(stderr,
690         "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
691                 prog);
692         fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
693         fprintf(stderr, "Valid options:\n");
694         fprintf(stderr, "\t-v   Version of the program\n");
695         fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
696         fprintf(stderr,
697         "\tsetcifsacl -a \"ACL:Administrator:ALLOWED/0x0/FULL\" <file_name>\n");
698         fprintf(stderr, "\n");
699         fprintf(stderr,
700         "\t-D   Delete ACE(s), separated by a comma, from an ACL\n");
701         fprintf(stderr,
702         "\tsetcifsacl -D \"ACL:Administrator:DENIED/0x0/D\" <file_name>\n");
703         fprintf(stderr, "\n");
704         fprintf(stderr,
705         "\t-M   Modify ACE(s), separated by a comma, in an ACL\n");
706         fprintf(stderr,
707         "\tsetcifsacl -M \"ACL:user1:ALLOWED/0x0/0x1e01ff\" <file_name>\n");
708         fprintf(stderr,
709         "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
710         fprintf(stderr,
711         "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
712         fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
713 }
714
715 int
716 main(const int argc, char *const argv[])
717 {
718         int i, rc, c, numcaces, numfaces;
719         enum setcifsacl_actions maction = ActUnknown;
720         ssize_t attrlen, bufsize = BUFSIZE;
721         char *ace_list, *filename, *attrval, **arrptr = NULL;
722         struct cifs_ctrl_acl *daclptr = NULL;
723         struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
724         struct cifs_ntsd *ntsdptr = NULL;
725
726         c = getopt(argc, argv, "hvD:M:a:S:");
727         switch (c) {
728         case 'D':
729                 maction = ActDelete;
730                 ace_list = optarg;
731                 break;
732         case 'M':
733                 maction = ActModify;
734                 ace_list = optarg;
735                 break;
736         case 'a':
737                 maction = ActAdd;
738                 ace_list = optarg;
739                 break;
740         case 'S':
741                 maction = ActSet;
742                 ace_list = optarg;
743                 break;
744         case 'h':
745                 setcifsacl_usage(basename(argv[0]));
746                 return 0;
747         case 'v':
748                 printf("Version: %s\n", VERSION);
749                 return 0;
750         default:
751                 setcifsacl_usage(basename(argv[0]));
752                 return -1;
753         }
754
755         /* We expect 1 argument in addition to the option */
756         if (argc != 4) {
757                 setcifsacl_usage(basename(argv[0]));
758                 return -1;
759         }
760         filename = argv[3];
761
762         if (!ace_list) {
763                 printf("%s: No valid ACEs specified\n", __func__);
764                 return -1;
765         }
766
767         if (init_plugin(&plugin_handle)) {
768                 printf("ERROR: unable to initialize idmapping plugin: %s\n",
769                         plugin_errmsg);
770                 return -1;
771         }
772
773         numcaces = get_numcaces(ace_list);
774
775         arrptr = parse_cmdline_aces(ace_list, numcaces);
776         if (!arrptr)
777                 goto setcifsacl_numcaces_ret;
778
779         cacesptr = build_cmdline_aces(arrptr, numcaces);
780         if (!cacesptr)
781                 goto setcifsacl_cmdlineparse_ret;
782
783 cifsacl:
784         if (bufsize >= XATTR_SIZE_MAX) {
785                 printf("%s: Buffer size %zd exceeds max size of %d\n",
786                                 __func__, bufsize, XATTR_SIZE_MAX);
787                 goto setcifsacl_cmdlineverify_ret;
788         }
789
790         attrval = malloc(bufsize * sizeof(char));
791         if (!attrval) {
792                 printf("error allocating memory for attribute value buffer\n");
793                 goto setcifsacl_cmdlineverify_ret;
794         }
795
796         attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
797         if (attrlen == -1) {
798                 if (errno == ERANGE) {
799                         free(attrval);
800                         bufsize += BUFSIZE;
801                         goto cifsacl;
802                 } else {
803                         printf("getxattr error: %d\n", errno);
804                         goto setcifsacl_getx_ret;
805                 }
806         }
807
808         numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
809         if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
810                 printf("%s: Empty DACL\n", __func__);
811                 goto setcifsacl_facenum_ret;
812         }
813
814         facesptr = build_fetched_aces((char *)daclptr, numfaces);
815         if (!facesptr)
816                 goto setcifsacl_facenum_ret;
817
818         bufsize = 0;
819         rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
820                 facesptr, numfaces, cacesptr, numcaces, maction);
821         if (rc)
822                 goto setcifsacl_action_ret;
823
824         attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
825         if (attrlen == -1) {
826                 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
827                 goto setcifsacl_facenum_ret;
828         }
829
830         exit_plugin(plugin_handle);
831         return 0;
832
833 setcifsacl_action_ret:
834         free(ntsdptr);
835
836 setcifsacl_facenum_ret:
837         for (i = 0; i < numfaces; ++i)
838                 free(facesptr[i]);
839         free(facesptr);
840
841 setcifsacl_getx_ret:
842         free(attrval);
843
844 setcifsacl_cmdlineverify_ret:
845         for (i = 0; i < numcaces; ++i)
846                 free(cacesptr[i]);
847         free(cacesptr);
848
849 setcifsacl_cmdlineparse_ret:
850         free(arrptr);
851
852 setcifsacl_numcaces_ret:
853         exit_plugin(plugin_handle);
854         return -1;
855 }