setfacl.c revision 75928
133965Sjdp/*
233965Sjdp * Copyright (c) 2001 Chris D. Faulhaber
3218822Sdim * All rights reserved.
433965Sjdp *
5218822Sdim * Redistribution and use in source and binary forms, with or without
633965Sjdp * modification, are permitted provided that the following conditions
733965Sjdp * are met:
8218822Sdim * 1. Redistributions of source code must retain the above copyright
9218822Sdim *    notice, this list of conditions and the following disclaimer.
10218822Sdim * 2. Redistributions in binary form must reproduce the above copyright
1133965Sjdp *    notice, this list of conditions and the following disclaimer in the
12218822Sdim *    documentation and/or other materials provided with the distribution.
13218822Sdim *
14218822Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15218822Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16218822Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17218822Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE
18218822Sdim * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19218822Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20218822Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21218822Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22218822Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23218822Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24218822Sdim * POSSIBILITY OF SUCH DAMAGE.
25218822Sdim *
26218822Sdim * $FreeBSD: head/bin/setfacl/setfacl.c 75928 2001-04-24 22:45:41Z jedgar $
27218822Sdim */
28218822Sdim
29218822Sdim#include <sys/types.h>
30218822Sdim#include <sys/param.h>
31218822Sdim#include <sys/stat.h>
32218822Sdim#include <sys/acl.h>
33218822Sdim#include <sys/queue.h>
34218822Sdim
35218822Sdim#include <err.h>
36218822Sdim#include <stdio.h>
37218822Sdim#include <stdlib.h>
38218822Sdim#include <string.h>
39218822Sdim#include <sysexits.h>
40218822Sdim#include <unistd.h>
41218822Sdim
42218822Sdim#include "setfacl.h"
43218822Sdim
44218822Sdimstatic void   add_filename(const char *filename);
45218822Sdimstatic acl_t *get_file_acls(const char *filename);
46218822Sdimstatic void   usage(void);
47218822Sdim
48218822Sdimstatic void
49218822Sdimadd_filename(const char *filename)
50218822Sdim{
51218822Sdim	struct sf_file *file;
52218822Sdim
53218822Sdim	if (strlen(filename) > PATH_MAX - 1) {
54218822Sdim		warn("illegal filename");
55218822Sdim		return;
56218822Sdim	}
57218822Sdim	file = zmalloc(sizeof(struct sf_file));
58218822Sdim	file->filename = filename;
59218822Sdim	TAILQ_INSERT_TAIL(&filelist, file, next);
60218822Sdim}
61218822Sdim
62218822Sdimstatic acl_t *
63218822Sdimget_file_acls(const char *filename)
64218822Sdim{
65218822Sdim	acl_t *acl;
66218822Sdim	struct stat sb;
67218822Sdim
68218822Sdim	if (stat(filename, &sb) == -1) {
69218822Sdim		warn("stat() of %s failed", filename);
70218822Sdim		return NULL;
71218822Sdim	}
72218822Sdim
73218822Sdim	acl = zmalloc(sizeof(acl_t) * 2);
74218822Sdim	acl[0] = acl_get_file(filename, ACL_TYPE_ACCESS);
75218822Sdim	if (!acl[0])
76218822Sdim		err(EX_OSERR, "acl_get_file() failed");
77218822Sdim	if (S_ISDIR(sb.st_mode)) {
78218822Sdim		acl[1] = acl_get_file(filename, ACL_TYPE_DEFAULT);
79218822Sdim		if (!acl[1])
80218822Sdim			err(EX_OSERR, "acl_get_file() failed");
81218822Sdim	} else
82218822Sdim		acl[1] = NULL;
83218822Sdim
84218822Sdim	return acl;
85218822Sdim}
86218822Sdim
87218822Sdimstatic void
88218822Sdimusage(void)
89218822Sdim{
90218822Sdim
91218822Sdim	fprintf(stderr, "usage: setfacl [-bdknv] [-m entries] [-M file1] "
92218822Sdim	    "[-x entries] [-X file2] [file ...]\n");
93218822Sdim	exit(EX_USAGE);
94218822Sdim}
95218822Sdim
96218822Sdimint
97218822Sdimmain(int argc, char *argv[])
98218822Sdim{
99218822Sdim	acl_t *acl, final_acl;
100218822Sdim	char filename[PATH_MAX];
101218822Sdim	int local_error, carried_error, ch, i;
102218822Sdim	struct sf_file *file;
103218822Sdim	struct sf_entry *entry;
104218822Sdim
105218822Sdim	acl_type = ACL_TYPE_ACCESS;
106218822Sdim	carried_error = local_error = 0;
107218822Sdim	have_mask = have_stdin = n_flag = need_mask = 0;
108218822Sdim
109218822Sdim	TAILQ_INIT(&entrylist);
110218822Sdim	TAILQ_INIT(&filelist);
111218822Sdim
112218822Sdim	while ((ch = getopt(argc, argv, "M:X:bdkm:nx:")) != -1)
113218822Sdim		switch(ch) {
114218822Sdim		case 'M':
115218822Sdim			entry = zmalloc(sizeof(struct sf_entry));
116218822Sdim			entry->acl = get_acl_from_file(optarg);
117218822Sdim			if (!entry->acl)
118218822Sdim				err(EX_OSERR, "get_acl_from_file() failed");
119218822Sdim			entry->op = OP_MERGE_ACL;
120218822Sdim			TAILQ_INSERT_TAIL(&entrylist, entry, next);
121218822Sdim			break;
122218822Sdim		case 'X':
123218822Sdim			entry = zmalloc(sizeof(struct sf_entry));
124218822Sdim			entry->acl = get_acl_from_file(optarg);
125218822Sdim			entry->op = OP_REMOVE_ACL;
126218822Sdim			TAILQ_INSERT_TAIL(&entrylist, entry, next);
127218822Sdim			break;
128218822Sdim		case 'b':
129218822Sdim			entry = zmalloc(sizeof(struct sf_entry));
130218822Sdim			entry->op = OP_REMOVE_EXT;
131218822Sdim			TAILQ_INSERT_TAIL(&entrylist, entry, next);
132218822Sdim			break;
133218822Sdim		case 'd':
134218822Sdim			acl_type = ACL_TYPE_DEFAULT;
135218822Sdim			break;
136218822Sdim		case 'k':
137218822Sdim			entry = zmalloc(sizeof(struct sf_entry));
138218822Sdim			entry->op = OP_REMOVE_DEF;
139218822Sdim			TAILQ_INSERT_TAIL(&entrylist, entry, next);
140218822Sdim			break;
141218822Sdim		case 'm':
142218822Sdim			entry = zmalloc(sizeof(struct sf_entry));
143218822Sdim			entry->acl = acl_from_text(optarg);
144218822Sdim			if (!entry->acl)
145218822Sdim				err(EX_USAGE, "acl_from_text() failed");
146218822Sdim			entry->op = OP_MERGE_ACL;
147218822Sdim			TAILQ_INSERT_TAIL(&entrylist, entry, next);
148218822Sdim			break;
149218822Sdim		case 'n':
150218822Sdim			n_flag++;
151218822Sdim			break;
152218822Sdim		case 'x':
153218822Sdim			entry = zmalloc(sizeof(struct sf_entry));
154218822Sdim			entry->acl = acl_from_text(optarg);
155218822Sdim			if (!entry->acl)
156218822Sdim				err(EX_USAGE, "acl_from_text() failed");
157218822Sdim			entry->op = OP_REMOVE_ACL;
158218822Sdim			TAILQ_INSERT_TAIL(&entrylist, entry, next);
159218822Sdim			break;
160218822Sdim		default:
161218822Sdim			usage();
162218822Sdim			break;
163218822Sdim		}
164218822Sdim	argc -= optind;
165218822Sdim	argv += optind;
166218822Sdim
167218822Sdim	if (!n_flag && TAILQ_EMPTY(&entrylist))
168218822Sdim		usage();
169218822Sdim
170218822Sdim	/* take list of files from stdin */
171218822Sdim	if (argc == 0 || !strcmp(argv[0], "-")) {
172218822Sdim		if (have_stdin)
173218822Sdim			err(EX_USAGE, "cannot have more than one stdin");
174218822Sdim		have_stdin = 1;
175218822Sdim		bzero(&filename, sizeof(filename));
176218822Sdim		while (fgets(filename, sizeof(filename), stdin)) {
177218822Sdim			/* remove the \n */
178218822Sdim			filename[strlen(filename) - 1] = '\0';
179218822Sdim			add_filename(filename);
180218822Sdim		}
181218822Sdim	} else
182218822Sdim		for (i = 0; i < argc; i++)
183218822Sdim			add_filename(argv[i]);
184218822Sdim
185218822Sdim	/* cycle through each file */
186218822Sdim	TAILQ_FOREACH(file, &filelist, next) {
187218822Sdim		/* get our initial access and default ACL's */
188218822Sdim		acl = get_file_acls(file->filename);
189218822Sdim		if (!acl)
190218822Sdim			continue;
191218822Sdim		if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) {
192218822Sdim			warnx("Default ACL not valid for %s", file->filename);
193218822Sdim			continue;
194218822Sdim		}
195218822Sdim
196218822Sdim		local_error = 0;
197218822Sdim
198218822Sdim		/* cycle through each option */
199218822Sdim		TAILQ_FOREACH(entry, &entrylist, next) {
200218822Sdim			if (local_error)
201218822Sdim				continue;
202218822Sdim
203218822Sdim			switch(entry->op) {
204218822Sdim			case OP_MERGE_ACL:
205218822Sdim				local_error += merge_acl(entry->acl, acl);
206218822Sdim				need_mask = 1;
207218822Sdim				break;
208218822Sdim			case OP_REMOVE_EXT:
209218822Sdim				remove_ext(acl);
210218822Sdim				need_mask = 0;
211218822Sdim				break;
212218822Sdim			case OP_REMOVE_DEF:
213218822Sdim				if (acl_delete_def_file(file->filename) == -1) {
214218822Sdim					warn("acl_delete_def_file() failed");
215218822Sdim					local_error++;
216218822Sdim				}
217218822Sdim				local_error += remove_default(acl);
218218822Sdim				need_mask = 0;
219218822Sdim				break;
220218822Sdim			case OP_REMOVE_ACL:
221218822Sdim				local_error += remove_acl(entry->acl, acl);
222218822Sdim				need_mask = 1;
223218822Sdim				break;
224218822Sdim			/* NOTREACHED */
225218822Sdim			}
226218822Sdim		}
227218822Sdim
228218822Sdim		/* don't bother setting the ACL if something is broken */
229218822Sdim		if (local_error) {
230218822Sdim			carried_error++;
231218822Sdim			continue;
232218822Sdim		}
233218822Sdim
234218822Sdim		if (acl_type == ACL_TYPE_ACCESS)
235218822Sdim			final_acl = acl[0];
236218822Sdim		else
237218822Sdim			final_acl = acl[1];
238218822Sdim
239218822Sdim		if (need_mask && (set_acl_mask(&final_acl) == -1)) {
240218822Sdim			warnx("failed to set ACL mask on %s", file->filename);
241218822Sdim			carried_error++;
242218822Sdim		} else if (acl_set_file(file->filename, acl_type,
243218822Sdim		    final_acl) == -1) {
244218822Sdim			carried_error++;
245218822Sdim			warn("acl_set_file() failed for %s", file->filename);
246218822Sdim		}
247218822Sdim
248218822Sdim		acl_free(acl[0]);
249218822Sdim		acl_free(acl[1]);
250218822Sdim		free(acl);
251218822Sdim	}
252218822Sdim
253218822Sdim	return carried_error;
254218822Sdim}
255218822Sdim