setfacl.c revision 108450
174465Srwatson/*
274465Srwatson * Copyright (c) 2001 Chris D. Faulhaber
374465Srwatson * All rights reserved.
474465Srwatson *
574465Srwatson * Redistribution and use in source and binary forms, with or without
674465Srwatson * modification, are permitted provided that the following conditions
774465Srwatson * are met:
874465Srwatson * 1. Redistributions of source code must retain the above copyright
974465Srwatson *    notice, this list of conditions and the following disclaimer.
1074465Srwatson * 2. Redistributions in binary form must reproduce the above copyright
1174465Srwatson *    notice, this list of conditions and the following disclaimer in the
1274465Srwatson *    documentation and/or other materials provided with the distribution.
1374465Srwatson *
1474465Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1574465Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1674465Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1774465Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE
1874465Srwatson * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1974465Srwatson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2074465Srwatson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2174465Srwatson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2274465Srwatson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2374465Srwatson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2474465Srwatson * POSSIBILITY OF SUCH DAMAGE.
2574465Srwatson */
2674465Srwatson
2799110Sobrien#include <sys/cdefs.h>
2899110Sobrien__FBSDID("$FreeBSD: head/bin/setfacl/setfacl.c 108450 2002-12-30 15:36:29Z rwatson $");
2999110Sobrien
3074465Srwatson#include <sys/types.h>
3174465Srwatson#include <sys/param.h>
3274465Srwatson#include <sys/stat.h>
3374465Srwatson#include <sys/acl.h>
3474465Srwatson#include <sys/queue.h>
3574465Srwatson
3674465Srwatson#include <err.h>
3774465Srwatson#include <stdio.h>
3874465Srwatson#include <stdlib.h>
3974465Srwatson#include <string.h>
4074465Srwatson#include <unistd.h>
4174465Srwatson
4274465Srwatson#include "setfacl.h"
4374465Srwatson
4474465Srwatsonstatic void   add_filename(const char *filename);
4574465Srwatsonstatic acl_t *get_file_acls(const char *filename);
4674465Srwatsonstatic void   usage(void);
4774465Srwatson
4874465Srwatsonstatic void
4974465Srwatsonadd_filename(const char *filename)
5074465Srwatson{
5174465Srwatson	struct sf_file *file;
5274465Srwatson
5374465Srwatson	if (strlen(filename) > PATH_MAX - 1) {
5474465Srwatson		warn("illegal filename");
5574465Srwatson		return;
5674465Srwatson	}
5774465Srwatson	file = zmalloc(sizeof(struct sf_file));
5874465Srwatson	file->filename = filename;
5975928Sjedgar	TAILQ_INSERT_TAIL(&filelist, file, next);
6074465Srwatson}
6174465Srwatson
6274465Srwatsonstatic acl_t *
6374465Srwatsonget_file_acls(const char *filename)
6474465Srwatson{
6574465Srwatson	acl_t *acl;
6674465Srwatson	struct stat sb;
6774465Srwatson
6874465Srwatson	if (stat(filename, &sb) == -1) {
6974465Srwatson		warn("stat() of %s failed", filename);
7087254Sjedgar		return (NULL);
7174465Srwatson	}
7274465Srwatson
7374465Srwatson	acl = zmalloc(sizeof(acl_t) * 2);
74108450Srwatson	if (h_flag)
75108450Srwatson		acl[ACCESS_ACL] = acl_get_link_np(filename, ACL_TYPE_ACCESS);
76108450Srwatson	else
77108450Srwatson		acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS);
7887259Sjedgar	if (acl[ACCESS_ACL] == NULL)
7987254Sjedgar		err(1, "acl_get_file() failed");
8074465Srwatson	if (S_ISDIR(sb.st_mode)) {
81108450Srwatson		if (h_flag)
82108450Srwatson			acl[DEFAULT_ACL] = acl_get_link_np(filename,
83108450Srwatson			    ACL_TYPE_DEFAULT);
84108450Srwatson		else
85108450Srwatson			acl[DEFAULT_ACL] = acl_get_file(filename,
86108450Srwatson			    ACL_TYPE_DEFAULT);
8787259Sjedgar		if (acl[DEFAULT_ACL] == NULL)
8887254Sjedgar			err(1, "acl_get_file() failed");
8974465Srwatson	} else
9087259Sjedgar		acl[DEFAULT_ACL] = NULL;
9174465Srwatson
9287254Sjedgar	return (acl);
9374465Srwatson}
9474465Srwatson
9574465Srwatsonstatic void
9674465Srwatsonusage(void)
9774465Srwatson{
9874465Srwatson
99108450Srwatson	fprintf(stderr, "usage: setfacl [-bdhknv] [-m entries] [-M file1] "
10074465Srwatson	    "[-x entries] [-X file2] [file ...]\n");
10187254Sjedgar	exit(1);
10274465Srwatson}
10374465Srwatson
10474465Srwatsonint
10574465Srwatsonmain(int argc, char *argv[])
10674465Srwatson{
10774465Srwatson	acl_t *acl, final_acl;
10874465Srwatson	char filename[PATH_MAX];
10974465Srwatson	int local_error, carried_error, ch, i;
11074465Srwatson	struct sf_file *file;
11174465Srwatson	struct sf_entry *entry;
11274465Srwatson
11374465Srwatson	acl_type = ACL_TYPE_ACCESS;
11474465Srwatson	carried_error = local_error = 0;
115108450Srwatson	h_flag = have_mask = have_stdin = n_flag = need_mask = 0;
11674465Srwatson
11775928Sjedgar	TAILQ_INIT(&entrylist);
11875928Sjedgar	TAILQ_INIT(&filelist);
11974465Srwatson
120108450Srwatson	while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1)
12174465Srwatson		switch(ch) {
12274465Srwatson		case 'M':
12374465Srwatson			entry = zmalloc(sizeof(struct sf_entry));
12474465Srwatson			entry->acl = get_acl_from_file(optarg);
12587254Sjedgar			if (entry->acl == NULL)
12687254Sjedgar				err(1, "get_acl_from_file() failed");
12774465Srwatson			entry->op = OP_MERGE_ACL;
12875928Sjedgar			TAILQ_INSERT_TAIL(&entrylist, entry, next);
12974465Srwatson			break;
13074465Srwatson		case 'X':
13174465Srwatson			entry = zmalloc(sizeof(struct sf_entry));
13274465Srwatson			entry->acl = get_acl_from_file(optarg);
13374465Srwatson			entry->op = OP_REMOVE_ACL;
13475928Sjedgar			TAILQ_INSERT_TAIL(&entrylist, entry, next);
13574465Srwatson			break;
13674465Srwatson		case 'b':
13774465Srwatson			entry = zmalloc(sizeof(struct sf_entry));
13874465Srwatson			entry->op = OP_REMOVE_EXT;
13975928Sjedgar			TAILQ_INSERT_TAIL(&entrylist, entry, next);
14074465Srwatson			break;
14174465Srwatson		case 'd':
14274465Srwatson			acl_type = ACL_TYPE_DEFAULT;
14374465Srwatson			break;
144108450Srwatson		case 'h':
145108450Srwatson			h_flag = 1;
146108450Srwatson			break;
14774465Srwatson		case 'k':
14874465Srwatson			entry = zmalloc(sizeof(struct sf_entry));
14974465Srwatson			entry->op = OP_REMOVE_DEF;
15075928Sjedgar			TAILQ_INSERT_TAIL(&entrylist, entry, next);
15174465Srwatson			break;
15274465Srwatson		case 'm':
15374465Srwatson			entry = zmalloc(sizeof(struct sf_entry));
15474465Srwatson			entry->acl = acl_from_text(optarg);
15587254Sjedgar			if (entry->acl == NULL)
15687254Sjedgar				err(1, "acl_from_text() failed");
15774465Srwatson			entry->op = OP_MERGE_ACL;
15875928Sjedgar			TAILQ_INSERT_TAIL(&entrylist, entry, next);
15974465Srwatson			break;
16074465Srwatson		case 'n':
16174465Srwatson			n_flag++;
16274465Srwatson			break;
16374465Srwatson		case 'x':
16474465Srwatson			entry = zmalloc(sizeof(struct sf_entry));
16574465Srwatson			entry->acl = acl_from_text(optarg);
16687254Sjedgar			if (entry->acl == NULL)
16787254Sjedgar				err(1, "acl_from_text() failed");
16874465Srwatson			entry->op = OP_REMOVE_ACL;
16975928Sjedgar			TAILQ_INSERT_TAIL(&entrylist, entry, next);
17074465Srwatson			break;
17174465Srwatson		default:
17274465Srwatson			usage();
17374465Srwatson			break;
17474465Srwatson		}
17574465Srwatson	argc -= optind;
17674465Srwatson	argv += optind;
17774465Srwatson
17887254Sjedgar	if (n_flag == 0 && TAILQ_EMPTY(&entrylist))
17974465Srwatson		usage();
18074465Srwatson
18174465Srwatson	/* take list of files from stdin */
18287254Sjedgar	if (argc == 0 || strcmp(argv[0], "-") == 0) {
18374465Srwatson		if (have_stdin)
18487254Sjedgar			err(1, "cannot have more than one stdin");
18574465Srwatson		have_stdin = 1;
18674465Srwatson		bzero(&filename, sizeof(filename));
18776881Skris		while (fgets(filename, (int)sizeof(filename), stdin)) {
18874465Srwatson			/* remove the \n */
18974465Srwatson			filename[strlen(filename) - 1] = '\0';
19074465Srwatson			add_filename(filename);
19174465Srwatson		}
19274465Srwatson	} else
19374465Srwatson		for (i = 0; i < argc; i++)
19474465Srwatson			add_filename(argv[i]);
19574465Srwatson
19674465Srwatson	/* cycle through each file */
19775928Sjedgar	TAILQ_FOREACH(file, &filelist, next) {
19874465Srwatson		/* get our initial access and default ACL's */
19974465Srwatson		acl = get_file_acls(file->filename);
20087254Sjedgar		if (acl == NULL)
20174465Srwatson			continue;
20274465Srwatson		if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) {
20374465Srwatson			warnx("Default ACL not valid for %s", file->filename);
20474465Srwatson			continue;
20574465Srwatson		}
20674465Srwatson
20774465Srwatson		local_error = 0;
20874465Srwatson
20974465Srwatson		/* cycle through each option */
21075928Sjedgar		TAILQ_FOREACH(entry, &entrylist, next) {
21174465Srwatson			if (local_error)
21274465Srwatson				continue;
21374465Srwatson
21474465Srwatson			switch(entry->op) {
21574465Srwatson			case OP_MERGE_ACL:
21674465Srwatson				local_error += merge_acl(entry->acl, acl);
21774465Srwatson				need_mask = 1;
21874465Srwatson				break;
21974465Srwatson			case OP_REMOVE_EXT:
22074465Srwatson				remove_ext(acl);
22174465Srwatson				need_mask = 0;
22274465Srwatson				break;
22374465Srwatson			case OP_REMOVE_DEF:
22474465Srwatson				if (acl_delete_def_file(file->filename) == -1) {
22574465Srwatson					warn("acl_delete_def_file() failed");
22674465Srwatson					local_error++;
22774465Srwatson				}
22874465Srwatson				local_error += remove_default(acl);
22974465Srwatson				need_mask = 0;
23074465Srwatson				break;
23174465Srwatson			case OP_REMOVE_ACL:
23274465Srwatson				local_error += remove_acl(entry->acl, acl);
23374465Srwatson				need_mask = 1;
23474465Srwatson				break;
23574465Srwatson			}
23674465Srwatson		}
23774465Srwatson
23874465Srwatson		/* don't bother setting the ACL if something is broken */
23974465Srwatson		if (local_error) {
24074465Srwatson			carried_error++;
24174465Srwatson			continue;
24274465Srwatson		}
24374465Srwatson
24474465Srwatson		if (acl_type == ACL_TYPE_ACCESS)
24587259Sjedgar			final_acl = acl[ACCESS_ACL];
24674465Srwatson		else
24787259Sjedgar			final_acl = acl[DEFAULT_ACL];
24874465Srwatson
24975928Sjedgar		if (need_mask && (set_acl_mask(&final_acl) == -1)) {
25074465Srwatson			warnx("failed to set ACL mask on %s", file->filename);
25174465Srwatson			carried_error++;
25274465Srwatson		} else if (acl_set_file(file->filename, acl_type,
25374465Srwatson		    final_acl) == -1) {
25474465Srwatson			carried_error++;
25574465Srwatson			warn("acl_set_file() failed for %s", file->filename);
25674465Srwatson		}
25774465Srwatson
25887259Sjedgar		acl_free(acl[ACCESS_ACL]);
25987259Sjedgar		acl_free(acl[DEFAULT_ACL]);
26074465Srwatson		free(acl);
26174465Srwatson	}
26274465Srwatson
26387254Sjedgar	return (carried_error);
26474465Srwatson}
265