summaryrefslogtreecommitdiffstats
path: root/tests/encryption/frag.c
blob: 86da037c6074e8f818068d64eaead51df8dde46d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/*
  Copyright (c) 2008-2013 Red Hat, Inc. <http://www.redhat.com>
  This file is part of GlusterFS.

  This file is licensed to you under your choice of the GNU Lesser
  General Public License, version 3 or any later version (LGPLv3 or
  later), or the GNU General Public License, version 2 (GPLv2), in all
  cases as published by the Free Software Foundation.
*/

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>

#define MAX_NUM_OPS (1 << 20)
#define MAX_FILE_SIZE (1 << 30)

typedef enum {
	READ_OP,
	WRITE_OP,
	TRUNC_OP,
	LAST_OP
} frag_op;

struct frag_ctx {
	int test_fd;
	int good_fd;
	char *test_buf;
	char *good_buf;
	char *content;
	int max_file_size;
};

typedef int (*frag_op_t)(struct frag_ctx *ctx, off_t offset, size_t count);

static int doread(int fd, off_t offset, size_t count,
		  char *buf, int max_file_size)
{
	int ret = 0;
	int was_read = 0;

	if (lseek(fd, offset, SEEK_SET) == -1) {
		perror("lseek failed");
                return -1;
	}
	while (count) {
		ret = read(fd, buf + offset + was_read, count);
		if (ret < 0)
			return -1;
		if (ret == 0)
			break;
		if (ret > count) {
			fprintf(stderr, "READ: read more than asked\n");
			return -1;
		}
		count -= ret;
		was_read += ret;
	}
	return ret;
}

static int dowrite(int fd, off_t offset, size_t count, char *buf)
{
	int ret;

	ret = lseek(fd, offset, SEEK_SET);
	if (ret == -1)
                return ret;
	return write(fd, buf, count);
}

static int dotrunc(int fd, off_t offset)
{
	int ret;

	ret = ftruncate(fd, offset);
	if (ret == -1)
		perror("truncate failed");
	return ret;
}

static int prepare_file(char *filename, int *fd, char **buf, int max_file_size)
{
	int ret;

	*buf = malloc(max_file_size);
	if (*buf == NULL) {
		perror("malloc failed");
	        return -1;
	}
	*fd = open(filename, O_CREAT | O_RDWR, S_IRWXU);
	if (*fd == -1) {
		perror("open failed");
		free(*buf);
		*buf = NULL;
		return -1;
	}
	return 0;
}

/*
 * @offset, @count: random values from [0, max_file_size - 1]
 */
static int frag_write(struct frag_ctx *ctx, off_t offset, size_t count)
{
	int ret;
	struct stat test_stbuf;
	struct stat good_stbuf;

	if (offset + count > ctx->max_file_size)
		offset = offset / 2;
	if (offset + count > ctx->max_file_size)
		count = count / 2;

	if (fstat(ctx->test_fd, &test_stbuf)) {
		fprintf(stderr, "WRITE: fstat of test file failed\n");
		return -1;
	}
	if (offset > test_stbuf.st_size)
		printf("writing hole\n");

	ret = dowrite(ctx->test_fd, offset, count, ctx->content);
	if (ret < 0 || ret != count){
		fprintf(stderr, "WRITE: failed to write test file\n");
		return -1;
	}
	ret = dowrite(ctx->good_fd, offset, count, ctx->content);
	if (ret < 0 || ret != count) {
		fprintf(stderr, "WRITE: failed to write test file\n");
		return -1;
	}	
	if (fstat(ctx->test_fd, &test_stbuf)) {
                fprintf(stderr, "WRITE: fstat of test file failed\n");
		return -1;
	}
	if (fstat(ctx->good_fd, &good_stbuf)) {
                fprintf(stderr, "WRITE: fstat of good file failed\n");
		return -1;
	}
	if (test_stbuf.st_size != good_stbuf.st_size) {
		fprintf(stderr,
			"READ: Bad file size %d (expected %d)\n",
			(int)test_stbuf.st_size,
			(int)good_stbuf.st_size);
		return -1;
	}
	return 0;
}

/*
 * @offset, @count: random values from [0, max_file_size - 1]
 */
static int frag_read(struct frag_ctx *ctx, off_t offset, size_t count)
{
	ssize_t test_ret;
	ssize_t good_ret;

	test_ret = doread(ctx->test_fd,
			  offset, count, ctx->test_buf, ctx->max_file_size);
	if (test_ret < 0) {
		fprintf(stderr, "READ: failed to read test file\n");
		return -1;
	}
	good_ret = doread(ctx->good_fd,
			  offset, count, ctx->good_buf, ctx->max_file_size);
	if (good_ret < 0) {
		fprintf(stderr, "READ: failed to read good file\n");
		return -1;
	}
	if (test_ret != good_ret) {
		fprintf(stderr,
			"READ: Bad return value %d (expected %d\n)",
			test_ret, good_ret);
		return -1;
	}
	if (memcmp(ctx->test_buf + offset, ctx->good_buf + offset, good_ret)) {
		fprintf(stderr, "READ: bad data\n");
		return -1;
	}
	return 0;
}

/*
 * @offset: random value from [0, max_file_size - 1]
 */
static int frag_truncate(struct frag_ctx *ctx,
			 off_t offset, __attribute__((unused))size_t count)
{
	int ret;
	struct stat test_stbuf;
	struct stat good_stbuf;

	if (fstat(ctx->test_fd, &test_stbuf)) {
		fprintf(stderr, "TRUNCATE: fstat of test file failed\n");
		return -1;
	}
	if (offset > test_stbuf.st_size)
		printf("expanding truncate to %d\n", offset);
	else if (offset < test_stbuf.st_size)
		printf("shrinking truncate to %d\n", offset);
	else
		printf("trivial truncate\n");
	
	ret = dotrunc(ctx->test_fd, offset);
	if (ret == -1) {
		fprintf(stderr, "TRUNCATE: failed for test file\n");
		return -1;
	}
	ret = dotrunc(ctx->good_fd, offset);
	if (ret == -1) {
		fprintf(stderr, "TRUNCATE: failed for good file\n");
		return -1;
	}
	if (fstat(ctx->test_fd, &test_stbuf)) {
		fprintf(stderr, "TRUNCATE: fstat of test file failed\n");
		return -1;
	}
	if (fstat(ctx->good_fd, &good_stbuf)) {
		fprintf(stderr, "TRUNCATE: fstat of good file failed\n");
		return -1;
	}
	if (test_stbuf.st_size != good_stbuf.st_size) {
		fprintf(stderr,
			"TRUNCATE: bad test file size %d (expected %d)\n",
			test_stbuf.st_size,
			good_stbuf.st_size);
		return -1;
	}
	return 0;
}

frag_op_t frag_ops[LAST_OP] = {
	[READ_OP]  = frag_read,
	[WRITE_OP] = frag_write,
	[TRUNC_OP] = frag_truncate
};

static void put_ctx(struct frag_ctx *ctx)
{
	if (ctx->test_buf)
		free(ctx->test_buf);
	if (ctx->good_buf)
		free(ctx->good_buf);
	if (ctx->content)
		free(ctx->content);
}

main (int argc, char *argv[])
{
	int i;
	int ret = 0;
	struct frag_ctx ctx;
	char *test_filename = NULL;
	char *good_filename = NULL;
	int num_ops;
	int max_file_size;

	memset(&ctx, 0, sizeof(ctx));
	if (argc != 5) {
                fprintf(stderr,
			"usage: %s <test-file-name> <good-file-name> <max-file-size> <number-of-operations>\n",
			argv[0]);
		ret = -1;
                goto exit;
        }
	test_filename = argv[1];
	good_filename = argv[2];
	max_file_size = atoi(argv[3]);
	if (max_file_size > MAX_FILE_SIZE)
		max_file_size = MAX_FILE_SIZE;
	num_ops = atoi(argv[4]);
	if (num_ops > MAX_NUM_OPS)
		num_ops = MAX_NUM_OPS;

	ret = prepare_file(test_filename,
			   &ctx.test_fd, &ctx.test_buf, max_file_size);
	if (ret)
		goto exit;
	ret = prepare_file(good_filename,
			   &ctx.good_fd, &ctx.good_buf, max_file_size);
	if (ret) {
		if (close(ctx.test_fd) == -1)
			perror("close test_buf failed");
		goto exit;
	}
	ctx.content = malloc(max_file_size);
	if (!ctx.content) {
		perror("malloc failed");
		goto close;
	}
	ctx.max_file_size = max_file_size;
	for (i = 0; i < max_file_size; i++)
                ctx.content[i] = random() % 256;

	for (i = 0; i < num_ops; i++) {
		ret = frag_ops[random() % LAST_OP](&ctx,
					random() % max_file_size, /* offset */
					random() % max_file_size  /* count */);
		if (ret)
			break;
	}
 close:
	if (close(ctx.test_fd) == -1)
		perror("close test_fd failed");
	if (close(ctx.good_fd) == -1)
		perror("close good_fd failed");	
 exit:
	put_ctx(&ctx);
	if (ret)
		exit(1);
	exit(0);
}

/*
  Local variables:
  c-indentation-style: "K&R"
  mode-name: "LC"
  c-basic-offset: 8
  tab-width: 8
  fill-column: 80
  scroll-step: 1
  End:
*/
+ ret = 0;
+out:
+ posix_acl_destroy (this, acl2);
+
+ return ret;
+}
+
diff --git a/xlators/system/posix-acl/src/posix-acl-xattr.h b/xlators/system/posix-acl/src/posix-acl-xattr.h
new file mode 100644
index 00000000000..008a55db438
--- /dev/null
+++ b/xlators/system/posix-acl/src/posix-acl-xattr.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _POSIX_ACL_XATTR_H
+#define _POSIX_ACL_XATTR_H
+
+#include <stdint.h>
+
+#include "common-utils.h"
+#include "posix-acl.h"
+
+#define POSIX_ACL_ACCESS_XATTR "system.posix_acl_access"
+#define POSIX_ACL_DEFAULT_XATTR "system.posix_acl_default"
+
+#define POSIX_ACL_VERSION 2
+
+struct posix_acl_xattr_entry {
+ uint16_t tag;
+ uint16_t perm;
+ uint32_t id;
+};
+
+struct posix_acl_xattr_header {
+ uint32_t version;
+ struct posix_acl_xattr_entry entries[0];
+};
+
+struct posix_acl *posix_acl_from_xattr (xlator_t *this, const char *buf, int size);
+
+int posix_acl_to_xattr (xlator_t *this, struct posix_acl *acl, char *buf, int size);
+
+int posix_acl_matches_xattr (xlator_t *this, struct posix_acl *acl, const char *buf, int size);
+
+
+#endif /* !_POSIX_ACL_XATTR_H */
diff --git a/xlators/system/posix-acl/src/posix-acl.c b/xlators/system/posix-acl/src/posix-acl.c
new file mode 100644
index 00000000000..f05ed3f95f8
--- /dev/null
+++ b/xlators/system/posix-acl/src/posix-acl.c
@@ -0,0 +1,1792 @@
+/*
+ Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <errno.h>
+
+#include "xlator.h"
+#include "glusterfs.h"
+
+#include "posix-acl.h"
+#include "posix-acl-xattr.h"
+
+
+#define UINT64(ptr) ((uint64_t)((long)(ptr)))
+#define PTR(num) ((void *)((long)(num)))
+
+
+int
+whitelisted_xattr (const char *key)
+{
+ if (!key)
+ return 0;
+
+ if (strcmp (POSIX_ACL_ACCESS_XATTR, key) == 0)
+ return 1;
+ if (strcmp (POSIX_ACL_DEFAULT_XATTR, key) == 0)
+ return 1;
+ return 0;
+}
+
+
+int
+frame_is_user (call_frame_t *frame, uid_t uid)
+{
+ return (frame->root->uid == uid);
+}
+
+
+int
+frame_in_group (call_frame_t *frame, gid_t gid)
+{
+ int i = 0;
+
+ if (frame->root->gid == gid)
+ return 1;
+
+ for (i = 0; i < frame->root->ngrps; i++)
+ if (frame->root->groups[i] == gid)
+ return 1;
+ return 0;
+}
+
+
+mode_t
+posix_acl_access_set_mode (struct posix_acl *acl, struct posix_acl_ctx *ctx)
+{
+ struct posix_ace *ace = NULL;
+ struct posix_ace *group_ce = NULL;
+ struct posix_ace *mask_ce = NULL;
+ int count = 0;
+ int i = 0;
+ mode_t mode = 0;
+ int mask = 0;
+
+ count = acl->count;
+
+ ace = acl->entries;
+ for (i = 0; i < count; i++) {
+ switch (ace->tag) {
+ case POSIX_ACL_USER_OBJ:
+ mask |= S_IRWXU;
+ mode |= (ace->perm << 6);
+ break;
+ case POSIX_ACL_GROUP_OBJ:
+ group_ce = ace;
+ break;
+ case POSIX_ACL_MASK:
+ mask_ce = ace;
+ break;
+ case POSIX_ACL_OTHER:
+ mask |= S_IRWXO;
+ mode |= (ace->perm);
+ break;
+ }
+ ace++;
+ }
+
+ if (mask_ce) {
+ mask |= S_IRWXG;
+ mode |= (mask_ce->perm << 3);
+ } else {
+ if (!group_ce)
+ goto out;
+ mask |= S_IRWXG;
+ mode |= (group_ce->perm << 3);
+ }
+
+out:
+ ctx->perm = (ctx->perm & ~mask) | mode;
+
+ return mode;
+}
+
+
+static int
+sticky_permits (call_frame_t *frame, inode_t *parent, inode_t *inode)
+{
+ struct posix_acl_ctx *par = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+
+ par = posix_acl_ctx_get (parent, frame->this);
+ ctx = posix_acl_ctx_get (inode, frame->this);
+
+ if (frame_is_user (frame, 0))
+ return 1;
+
+ if (!(par->perm & S_ISVTX))
+ return 1;
+
+ if (frame_is_user (frame, par->uid))
+ return 1;
+
+ if (frame_is_user (frame, ctx->uid))
+ return 1;
+
+ return 0;
+}
+
+
+static int
+acl_permits (call_frame_t *frame, inode_t *inode, int want)
+{
+ int verdict = 0;
+ int ret = 0;
+ struct posix_acl *acl = NULL;
+ struct posix_ace *ace = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+ struct posix_acl_conf *conf = NULL;
+ int i = 0;
+ int perm = 0;
+ int found = 0;
+
+ conf = frame->this->private;
+
+ ctx = posix_acl_ctx_get (inode, frame->this);
+ if (!ctx)
+ goto red;
+
+ if (frame->root->uid == 0)
+ goto green;
+
+ ret = posix_acl_get (inode, frame->this, &acl, NULL);
+
+ if (!acl) {
+ acl = posix_acl_ref (frame->this, conf->minimal_acl);
+ }
+
+ ace = acl->entries;
+
+ for (i = 0; i < acl->count; i++) {
+ switch (ace->tag) {
+ case POSIX_ACL_USER_OBJ:
+ perm = ((ctx->perm & S_IRWXU) >> 6);
+ if (frame_is_user (frame, ctx->uid))
+ goto perm_check;
+ break;
+ case POSIX_ACL_USER:
+ perm = ace->perm;
+ if (frame_is_user (frame, ace->id))
+ goto mask_check;
+ break;
+ case POSIX_ACL_GROUP_OBJ:
+ perm = ((ctx->perm & S_IRWXG) >> 3);
+ if (frame_in_group (frame, ctx->gid)) {
+ found = 1;
+ if ((perm & want) == want)
+ goto mask_check;
+ }
+ break;
+ case POSIX_ACL_GROUP:
+ perm = ace->perm;
+ if (frame_in_group (frame, ace->id)) {
+ found = 1;
+ if ((perm & want) == want)
+ goto mask_check;
+ }
+ break;
+ case POSIX_ACL_MASK:
+ break;
+ case POSIX_ACL_OTHER:
+ perm = (ctx->perm & S_IRWXO);
+ if (!found)
+ goto perm_check;
+ /* fall through */
+ default:
+ goto red;
+ }
+
+ ace++;
+ }
+
+mask_check:
+ ace = acl->entries;
+
+ for (i = 0; i < acl->count; i++, ace++) {
+ if (ace->tag != POSIX_ACL_MASK)
+ continue;
+ if ((ace->perm & perm & want) == want)
+ goto green;
+ goto red;
+ }
+
+perm_check:
+ if ((perm & want) == want)
+ goto green;
+ else
+ goto red;
+
+green:
+ verdict = 1;
+ goto out;
+red:
+ verdict = 0;
+out:
+ if (acl)
+ posix_acl_unref (frame->this, acl);
+
+ return verdict;
+}
+
+
+struct posix_acl_ctx *
+posix_acl_ctx_get (inode_t *inode, xlator_t *this)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ uint64_t int_ctx = 0;
+ int ret = 0;
+
+ ret = inode_ctx_get (inode, this, &int_ctx);
+ if ((ret == 0) && (int_ctx))
+ return PTR(int_ctx);
+
+ ctx = CALLOC (1, sizeof (*ctx));
+ if (!ctx)
+ return NULL;
+
+ ret = inode_ctx_put (inode, this, UINT64 (ctx));
+
+ return ctx;
+}
+
+
+int
+__posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access,
+ struct posix_acl *acl_default)
+{
+ int ret = 0;
+ struct posix_acl_ctx *ctx = NULL;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ goto out;
+
+ ctx->acl_access = acl_access;
+ ctx->acl_default = acl_default;
+
+out:
+ return ret;
+}
+
+
+int
+__posix_acl_get (inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p,
+ struct posix_acl **acl_default_p)
+{
+ int ret = 0;
+ struct posix_acl_ctx *ctx = NULL;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ goto out;
+
+ if (acl_access_p)
+ *acl_access_p = ctx->acl_access;
+ if (acl_default_p)
+ *acl_default_p = ctx->acl_default;
+
+out:
+ return ret;
+}
+
+
+struct posix_acl *
+posix_acl_new (xlator_t *this, int entrycnt)
+{
+ struct posix_acl *acl = NULL;
+ struct posix_ace *ace = NULL;
+
+ acl = CALLOC (1, sizeof (*acl) + (entrycnt * sizeof (*ace)));
+ if (!acl)
+ return NULL;
+
+ acl->count = entrycnt;
+
+ posix_acl_ref (this, acl);
+
+ return acl;
+}
+
+
+void
+posix_acl_destroy (xlator_t *this, struct posix_acl *acl)
+{
+ FREE (acl);
+
+ return;
+}
+
+
+struct posix_acl *
+posix_acl_ref (xlator_t *this, struct posix_acl *acl)
+{
+ struct posix_acl_conf *conf = NULL;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ acl->refcnt++;
+ }
+ UNLOCK(&conf->acl_lock);
+
+ return acl;
+}
+
+
+struct posix_acl *
+posix_acl_dup (xlator_t *this, struct posix_acl *acl)
+{
+ struct posix_acl_conf *conf = NULL;
+ struct posix_acl *dup = NULL;
+
+ conf = this->private;
+
+ dup = posix_acl_new (this, acl->count);
+ if (!dup)
+ return NULL;
+
+ memcpy (dup->entries, acl->entries,
+ sizeof (struct posix_ace) * acl->count);
+
+ return dup;
+}
+
+
+void
+posix_acl_unref (xlator_t *this, struct posix_acl *acl)
+{
+ struct posix_acl_conf *conf = NULL;
+ int refcnt = 0;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ refcnt = --acl->refcnt;
+ }
+ UNLOCK(&conf->acl_lock);
+
+ if (!refcnt)
+ posix_acl_destroy (this, acl);
+}
+
+
+int
+posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access,
+ struct posix_acl *acl_default)
+{
+ int ret = 0;
+ int oldret = 0;
+ struct posix_acl *old_access = NULL;
+ struct posix_acl *old_default = NULL;
+ struct posix_acl_conf *conf = NULL;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ oldret = __posix_acl_get (inode, this, &old_access,
+ &old_default);
+ if (acl_access)
+ acl_access->refcnt++;
+ if (acl_default)
+ acl_default->refcnt++;
+
+ ret = __posix_acl_set (inode, this, acl_access, acl_default);
+ }
+ UNLOCK(&conf->acl_lock);
+
+ if (oldret == 0) {
+ if (old_access)
+ posix_acl_unref (this, old_access);
+ if (old_default)
+ posix_acl_unref (this, old_default);
+ }
+
+ return ret;
+}
+
+
+int
+posix_acl_get (inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p,
+ struct posix_acl **acl_default_p)
+{
+ struct posix_acl_conf *conf = NULL;
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl *acl_default = NULL;
+ int ret = 0;
+
+ conf = this->private;
+
+ LOCK(&conf->acl_lock);
+ {
+ ret = __posix_acl_get (inode, this, &acl_access, &acl_default);
+
+ if (ret != 0)
+ goto unlock;
+
+ if (acl_access && acl_access_p)
+ acl_access->refcnt++;
+ if (acl_default && acl_default_p)
+ acl_default->refcnt++;
+ }
+unlock:
+ UNLOCK(&conf->acl_lock);
+
+ if (acl_access_p)
+ *acl_access_p = acl_access;
+ if (acl_default_p)
+ *acl_default_p = acl_default;
+
+ return ret;
+}
+
+
+mode_t
+posix_acl_inherit_mode (struct posix_acl *acl, mode_t modein)
+{
+ struct posix_ace *ace = NULL;
+ int count = 0;
+ int i = 0;
+ mode_t newmode = 0;
+ mode_t mode = 0;
+ struct posix_ace *mask_ce = NULL;
+ struct posix_ace *group_ce = NULL;
+
+ newmode = mode = modein;
+
+ count = acl->count;
+
+ ace = acl->entries;
+ for (i = 0; i < count; i++) {
+ switch (ace->tag) {
+ case POSIX_ACL_USER_OBJ:
+ ace->perm &= (mode >> 6) | ~S_IRWXO;
+ mode &= (ace->perm << 6) | ~S_IRWXU;
+ break;
+ case POSIX_ACL_GROUP_OBJ:
+ group_ce = ace;
+ break;
+ case POSIX_ACL_MASK:
+ mask_ce = ace;
+ break;
+ case POSIX_ACL_OTHER:
+ ace->perm &= (mode) | ~S_IRWXO;
+ mode &= (ace->perm) | ~S_IRWXO;
+ break;
+ }
+ ace++;
+ }
+
+ if (mask_ce) {
+ mask_ce->perm &= (mode >> 3) | ~S_IRWXO;
+ mode &= (mask_ce->perm << 3) | ~S_IRWXG;
+ } else {
+ group_ce->perm &= (mode >> 3) | ~S_IRWXO;
+ mode &= (group_ce->perm << 3) | ~S_IRWXG;
+ }
+
+ newmode = ((modein & S_IFMT) | (mode & (S_IRWXU|S_IRWXG|S_IRWXO)));
+
+ return newmode;
+}
+
+
+mode_t
+posix_acl_inherit (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode,
+ int is_dir)
+{
+ int ret = 0;
+ struct posix_acl *par_default = NULL;
+ struct posix_acl *acl_default = NULL;
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+ char *xattr_default = NULL;
+ char *xattr_access = NULL;
+ int size_default = 0;
+ int size_access = 0;
+ mode_t retmode = 0;
+
+ retmode = mode;
+
+ ret = posix_acl_get (loc->parent, this, NULL, &par_default);
+
+ if (!par_default)
+ goto out;
+
+ ctx = posix_acl_ctx_get (loc->inode, this);
+
+ acl_access = posix_acl_dup (this, par_default);
+ if (!acl_access)
+ goto out;
+
+ retmode = posix_acl_inherit_mode (acl_access, mode);
+ ctx->perm = retmode;
+
+ size_access = posix_acl_to_xattr (this, acl_access, NULL, 0);
+ xattr_access = CALLOC (1, size_access);
+ if (!xattr_access) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+ posix_acl_to_xattr (this, acl_access, xattr_access, size_access);
+
+ ret = dict_set_bin (params, POSIX_ACL_ACCESS_XATTR, xattr_access,
+ size_access);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+
+ if (!is_dir)
+ goto set;
+
+
+ acl_default = posix_acl_ref (this, par_default);
+
+ size_default = posix_acl_to_xattr (this, acl_default, NULL, 0);
+ xattr_default = CALLOC (1, size_default);
+ if (!xattr_default) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+ posix_acl_to_xattr (this, acl_default, xattr_default, size_default);
+
+ ret = dict_set_bin (params, POSIX_ACL_DEFAULT_XATTR, xattr_default,
+ size_default);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "out of memory");
+ ret = -1;
+ goto out;
+ }
+
+set:
+ ret = posix_acl_set (loc->inode, this, acl_access, acl_default);
+ if (ret != 0)
+ goto out;
+
+out:
+ if (par_default)
+ posix_acl_unref (this, par_default);
+ if (acl_access)
+ posix_acl_unref (this, acl_access);
+ if (acl_default)
+ posix_acl_unref (this, acl_default);
+
+ return retmode;
+}
+
+
+mode_t
+posix_acl_inherit_dir (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode)
+{
+ mode_t retmode = 0;
+
+ retmode = posix_acl_inherit (this, loc, params, mode, 1);
+
+ return retmode;
+}
+
+
+mode_t
+posix_acl_inherit_file (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode)
+{
+ mode_t retmode = 0;
+
+ retmode = posix_acl_inherit (this, loc, params, mode, 0);
+
+ return retmode;
+}
+
+
+int
+posix_acl_ctx_update (inode_t *inode, xlator_t *this, struct iatt *buf)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int ret = 0;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx) {
+ ret = -1;
+ goto out;
+ }
+
+ LOCK(&inode->lock);
+ {
+ ctx->uid = buf->ia_uid;
+ ctx->gid = buf->ia_gid;
+ ctx->perm = st_mode_from_ia (buf->ia_prot, buf->ia_type);
+ }
+ UNLOCK(&inode->lock);
+out:
+ return ret;
+}
+
+
+int
+posix_acl_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode,
+ struct iatt *buf, dict_t *xattr, struct iatt *postparent)
+{
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl *acl_default = NULL;
+ struct posix_acl *old_access = NULL;
+ struct posix_acl *old_default = NULL;
+ data_t *data = NULL;
+ int ret = 0;
+ dict_t *my_xattr = NULL;
+
+ if (op_ret != 0)
+ goto unwind;
+
+ ret = posix_acl_get (inode, this, &old_access, &old_default);
+
+ data = dict_get (xattr, POSIX_ACL_ACCESS_XATTR);
+ if (!data)
+ goto acl_default;
+
+ if (old_access &&
+ posix_acl_matches_xattr (this, old_access, data->data,
+ data->len)) {
+ acl_access = posix_acl_ref (this, old_access);
+ } else {
+ acl_access = posix_acl_from_xattr (this, data->data,
+ data->len);
+ }
+
+acl_default:
+ data = dict_get (xattr, POSIX_ACL_DEFAULT_XATTR);
+ if (!data)
+ goto acl_set;
+
+ if (old_default &&
+ posix_acl_matches_xattr (this, old_default, data->data,
+ data->len)) {
+ acl_default = posix_acl_ref (this, old_default);
+ } else {
+ acl_default = posix_acl_from_xattr (this, data->data,
+ data->len);
+ }
+
+acl_set:
+ posix_acl_ctx_update (inode, this, buf);
+
+ ret = posix_acl_set (inode, this, acl_access, acl_default);
+
+unwind:
+ my_xattr = frame->local;
+ frame->local = NULL;
+ STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, inode, buf, xattr,
+ postparent);
+
+ if (acl_access)
+ posix_acl_unref (this, acl_access);
+ if (acl_default)
+ posix_acl_unref (this, acl_default);
+ if (old_access)
+ posix_acl_unref (this, old_access);
+ if (old_default)
+ posix_acl_unref (this, old_default);
+ if (my_xattr)
+ dict_unref (my_xattr);
+
+ return 0;
+}
+
+
+int
+posix_acl_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ dict_t *xattr)
+{
+ int ret = 0;
+ dict_t *my_xattr = NULL;
+
+ if (!loc->parent)
+ /* lookup of / is always permitted */
+ goto green;
+
+ if (acl_permits (frame, loc->parent, POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+
+green:
+ if (xattr) {
+ my_xattr = dict_ref (xattr);
+ } else {
+ my_xattr = dict_new ();
+ }
+
+ ret = dict_set_int8 (my_xattr, POSIX_ACL_ACCESS_XATTR, 0);
+ ret = dict_set_int8 (my_xattr, POSIX_ACL_DEFAULT_XATTR, 0);
+
+ frame->local = my_xattr;
+ STACK_WIND (frame, posix_acl_lookup_cbk,
+ FIRST_CHILD (this), FIRST_CHILD (this)->fops->lookup,
+ loc, my_xattr);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (lookup, frame, -1, EACCES, NULL, NULL, NULL,
+ NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int mask)
+{
+ int op_ret = 0;
+ int op_errno = 0;
+ int perm = 0;
+
+
+ if (mask & R_OK)
+ perm |= POSIX_ACL_READ;
+ if (mask & W_OK)
+ perm |= POSIX_ACL_WRITE;
+ if (mask & X_OK)
+ perm |= POSIX_ACL_EXECUTE;
+ if (!perm) {
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto unwind;
+ }
+
+ if (acl_permits (frame, loc->inode, perm)) {
+ op_ret = 0;
+ op_errno = 0;
+ } else {
+ op_ret = -1;
+ op_errno = EACCES;
+ }
+
+unwind:
+ STACK_UNWIND_STRICT (access, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *prebuf,
+ struct iatt *postbuf)
+{
+ STACK_UNWIND_STRICT (truncate, frame, op_ret, op_errno, prebuf, postbuf);
+
+ return 0;
+}
+
+
+int
+posix_acl_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t off)
+{
+ if (acl_permits (frame, loc->inode, POSIX_ACL_WRITE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_truncate_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate,
+ loc, off);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (truncate, frame, -1, EACCES, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, fd_t *fd)
+{
+ STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd);
+
+ return 0;
+}
+
+
+int
+posix_acl_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
+ fd_t *fd, int wbflags)
+{
+ int perm = 0;
+
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ perm = POSIX_ACL_READ;
+ break;
+ case O_WRONLY:
+ perm = POSIX_ACL_WRITE;
+ break;
+ case O_RDWR:
+ perm = POSIX_ACL_READ|POSIX_ACL_WRITE;
+ break;
+ }
+
+ if (acl_permits (frame, loc->inode, perm))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_open_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->open,
+ loc, flags, fd, wbflags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (open, frame, -1, EACCES, NULL);
+ return 0;
+}
+
+
+
+int
+posix_acl_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, fd_t *fd)
+{
+ STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd);
+
+ return 0;
+}
+
+
+int
+posix_acl_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd)
+{
+ if (acl_permits (frame, loc->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_opendir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->opendir,
+ loc, fd);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (opendir, frame, -1, EACCES, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode, struct iatt *buf,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (mkdir, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ dict_t *params)
+{
+ mode_t newmode = 0;
+
+ newmode = mode;
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ newmode = posix_acl_inherit_dir (this, loc, params, mode);
+
+ STACK_WIND (frame, posix_acl_mkdir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir,
+ loc, newmode, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (mkdir, frame, -1, EACCES, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode, struct iatt *buf,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (mknod, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ dev_t rdev, dict_t *params)
+{
+ mode_t newmode = 0;
+
+ newmode = mode;
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ newmode = posix_acl_inherit_file (this, loc, params, mode);
+
+ STACK_WIND (frame, posix_acl_mknod_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->mknod,
+ loc, newmode, rdev, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (mknod, frame, -1, EACCES, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, fd_t *fd, inode_t *inode,
+ struct iatt *buf, struct iatt *preparent,
+ struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
+ mode_t mode, fd_t *fd, dict_t *params)
+{
+ mode_t newmode = 0;
+
+ newmode = mode;
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ newmode = posix_acl_inherit_file (this, loc, params, mode);
+
+ STACK_WIND (frame, posix_acl_create_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->create,
+ loc, flags, newmode, fd, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (create, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode,
+ struct iatt *buf, struct iatt *preparent,
+ struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, buf);
+
+unwind:
+ STACK_UNWIND_STRICT (symlink, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_symlink (call_frame_t *frame, xlator_t *this, const char *linkname,
+ loc_t *loc, dict_t *params)
+{
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_symlink_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->symlink,
+ linkname, loc, params);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (symlink, frame, -1, EACCES, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (unlink, frame, op_ret, op_errno,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc)
+{
+ if (!sticky_permits (frame, loc->parent, loc->inode))
+ goto red;
+
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_unlink_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink,
+ loc);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (unlink, frame, -1, EACCES, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (rmdir, frame, op_ret, op_errno,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags)
+{
+ if (!sticky_permits (frame, loc->parent, loc->inode))
+ goto red;
+
+ if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_rmdir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->rmdir,
+ loc, flags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (rmdir, frame, -1, EACCES, NULL, NULL);
+ return 0;
+}
+
+
+int
+posix_acl_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *buf,
+ struct iatt *preoldparent, struct iatt *postoldparent,
+ struct iatt *prenewparent, struct iatt *postnewparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (rename, frame, op_ret, op_errno, buf,
+ preoldparent, postoldparent,
+ prenewparent, postnewparent);
+ return 0;
+}
+
+
+int
+posix_acl_rename (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new)
+{
+ if (!acl_permits (frame, old->parent, POSIX_ACL_WRITE))
+ goto red;
+
+ if (!acl_permits (frame, new->parent, POSIX_ACL_WRITE))
+ goto red;
+
+ if (!sticky_permits (frame, old->parent, old->inode))
+ goto red;
+
+ if (new->inode) {
+ if (!sticky_permits (frame, new->parent, new->inode))
+ goto red;
+ }
+
+ STACK_WIND (frame, posix_acl_rename_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->rename,
+ old, new);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (rename, frame, -1, EACCES, NULL, NULL, NULL, NULL,
+ NULL);
+ return 0;
+}
+
+
+int
+posix_acl_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode, struct iatt *buf,
+ struct iatt *preparent, struct iatt *postparent)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (link, frame, op_ret, op_errno, inode, buf,
+ preparent, postparent);
+ return 0;
+}
+
+
+int
+posix_acl_link (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int op_errno = 0;
+
+ ctx = posix_acl_ctx_get (old->inode, this);
+ if (!ctx) {
+ op_errno = EIO;
+ goto red;
+ }
+
+ if (!acl_permits (frame, new->parent, POSIX_ACL_WRITE)) {
+ op_errno = EACCES;
+ goto red;
+ }
+
+ STACK_WIND (frame, posix_acl_link_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->link,
+ old, new);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (link, frame, -1, op_errno, NULL, NULL, NULL, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, gf_dirent_t *entries)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, entries);
+ return 0;
+}
+
+
+int
+posix_acl_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
+ off_t offset)
+{
+ if (acl_permits (frame, fd->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_readdir_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdir,
+ fd, size, offset);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (readdir, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, gf_dirent_t *entries)
+{
+ if (op_ret != 0)
+ goto unwind;
+unwind:
+ STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, entries);
+ return 0;
+}
+
+
+int
+posix_acl_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
+ off_t offset)
+{
+ if (acl_permits (frame, fd->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_readdirp_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdirp,
+ fd, size, offset);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (readdirp, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+setattr_scrutiny (call_frame_t *frame, inode_t *inode, struct iatt *buf,
+ int valid)
+{
+ struct posix_acl_ctx *ctx = NULL;
+
+ if (frame->root->uid == 0)
+ return 0;
+
+ ctx = posix_acl_ctx_get (inode, frame->this);
+ if (!ctx)
+ return EIO;
+
+ if (valid & GF_SET_ATTR_MODE) {
+/*
+ The effective UID of the calling process must match the owner of the
+ file, or the process must be privileged
+*/
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+/*
+ If the calling process is not privileged (Linux: does not have the
+ CAP_FSETID capability), and the group of the file does not match the
+ effective group ID of the process or one of its supplementary group
+ IDs, the S_ISGID bit will be turned off, but this will not cause an
+ error to be returned.
+
+*/
+ if (!frame_in_group (frame, ctx->gid))
+ buf->ia_prot.sgid = 0;
+ }
+
+ if (valid & (GF_SET_ATTR_ATIME | GF_SET_ATTR_MTIME)) {
+/*
+ Changing timestamps is permitted when: either the process has appropri?
+ ate privileges, or the effective user ID equals the user ID of the
+ file, or times is NULL and the process has write permission for the
+ file.
+*/
+ if (!frame_is_user (frame, ctx->uid) &&
+ !acl_permits (frame, inode, POSIX_ACL_WRITE))
+ return EACCES;
+ }
+
+ if (valid & GF_SET_ATTR_UID) {
+ if ((frame->root->uid != 0) &&
+ (buf->ia_uid != ctx->uid))
+ return EPERM;
+ }
+
+ if (valid & GF_SET_ATTR_GID) {
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+ if (!frame_in_group (frame, buf->ia_gid))
+ return EPERM;
+ }
+
+ return 0;
+}
+
+
+int
+posix_acl_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *prebuf, struct iatt *postbuf)
+{
+ inode_t *inode = NULL;
+
+ inode = frame->local;
+ frame->local = NULL;
+
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, postbuf);
+
+unwind:
+ STACK_UNWIND_STRICT (setattr, frame, op_ret, op_errno, prebuf, postbuf);
+ return 0;
+}
+
+
+int
+posix_acl_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ struct iatt *buf, int valid)
+{
+ int op_errno = 0;
+
+ op_errno = setattr_scrutiny (frame, loc->inode, buf, valid);
+
+ if (op_errno)
+ goto red;
+
+ frame->local = loc->inode;
+
+ STACK_WIND (frame, posix_acl_setattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->setattr,
+ loc, buf, valid);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (setattr, frame, -1, op_errno, NULL, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iatt *prebuf, struct iatt *postbuf)
+{
+ inode_t *inode = NULL;
+
+ inode = frame->local;
+ frame->local = NULL;
+
+ if (op_ret != 0)
+ goto unwind;
+
+ posix_acl_ctx_update (inode, this, postbuf);
+
+unwind:
+ STACK_UNWIND_STRICT (fsetattr, frame, op_ret, op_errno, prebuf, postbuf);
+ return 0;
+}
+
+
+int
+posix_acl_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iatt *buf, int valid)
+{
+ int op_errno = 0;
+
+ op_errno = setattr_scrutiny (frame, fd->inode, buf, valid);
+
+ if (op_errno)
+ goto red;
+
+ frame->local = fd->inode;
+
+ STACK_WIND (frame, posix_acl_fsetattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetattr,
+ fd, buf, valid);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (fsetattr, frame, -1, EACCES, NULL, NULL);
+
+ return 0;
+}
+
+
+int
+setxattr_scrutiny (call_frame_t *frame, inode_t *inode, dict_t *xattr)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int found = 0;
+
+ if (frame->root->uid == 0)
+ return 0;
+
+ ctx = posix_acl_ctx_get (inode, frame->this);
+ if (!ctx)
+ return EIO;
+
+ if (dict_get (xattr, POSIX_ACL_ACCESS_XATTR)) {
+ found = 1;
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+ }
+
+ if (dict_get (xattr, POSIX_ACL_DEFAULT_XATTR)) {
+ found = 1;
+ if (!frame_is_user (frame, ctx->uid))
+ return EPERM;
+ }
+
+ if (!found && !acl_permits (frame, inode, POSIX_ACL_WRITE))
+ return EACCES;
+
+ return 0;
+}
+
+
+struct posix_acl *
+posix_acl_xattr_update (xlator_t *this, inode_t *inode, dict_t *xattr,
+ char *name, struct posix_acl *old)
+{
+ struct posix_acl *acl = NULL;
+ data_t *data = NULL;
+
+ data = dict_get (xattr, name);
+ if (data) {
+ acl = posix_acl_from_xattr (this, data->data,
+ data->len);
+ }
+
+ if (!acl && old)
+ acl = posix_acl_ref (this, old);
+
+ return acl;
+}
+
+
+int
+posix_acl_setxattr_update (xlator_t *this, inode_t *inode, dict_t *xattr)
+{
+ struct posix_acl *acl_access = NULL;
+ struct posix_acl *acl_default = NULL;
+ struct posix_acl *old_access = NULL;
+ struct posix_acl *old_default = NULL;
+ struct posix_acl_ctx *ctx = NULL;
+ int ret = 0;
+ mode_t mode = 0;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ return -1;
+
+ ret = posix_acl_get (inode, this, &old_access, &old_default);
+
+ acl_access = posix_acl_xattr_update (this, inode, xattr,
+ POSIX_ACL_ACCESS_XATTR,
+ old_access);
+
+ acl_default = posix_acl_xattr_update (this, inode, xattr,
+ POSIX_ACL_DEFAULT_XATTR,
+ old_default);
+
+ ret = posix_acl_set (inode, this, acl_access, acl_default);
+
+ if (acl_access && acl_access != old_access) {
+ mode = posix_acl_access_set_mode (acl_access, ctx);
+ }
+
+ if (acl_access)
+ posix_acl_unref (this, acl_access);
+ if (acl_default)
+ posix_acl_unref (this, acl_default);
+ if (old_access)
+ posix_acl_unref (this, old_access);
+ if (old_default)
+ posix_acl_unref (this, old_default);
+
+ return 0;
+}
+
+
+int
+posix_acl_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno)
+{
+ STACK_UNWIND_STRICT (setxattr, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ dict_t *xattr, int flags)
+{
+ int op_errno = 0;
+
+ op_errno = setxattr_scrutiny (frame, loc->inode, xattr);
+
+ if (op_errno != 0)
+ goto red;
+
+ posix_acl_setxattr_update (this, loc->inode, xattr);
+
+ STACK_WIND (frame, posix_acl_setxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->setxattr,
+ loc, xattr, flags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (setxattr, frame, -1, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_fsetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno)
+{
+ STACK_UNWIND_STRICT (fsetxattr, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ dict_t *xattr, int flags)
+{
+ int op_errno = 0;
+
+ op_errno = setxattr_scrutiny (frame, fd->inode, xattr);
+
+ if (op_errno != 0)
+ goto red;
+
+ posix_acl_setxattr_update (this, fd->inode, xattr);
+
+ STACK_WIND (frame, posix_acl_fsetxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetxattr,
+ fd, xattr, flags);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (fsetxattr, frame, -1, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_getxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, dict_t *xattr)
+{
+ STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, xattr);
+
+ return 0;
+}
+
+
+int
+posix_acl_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ const char *name)
+{
+ if (whitelisted_xattr (name))
+ goto green;
+
+ if (acl_permits (frame, loc->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_getxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->getxattr,
+ loc, name);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (getxattr, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_fgetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, dict_t *xattr)
+{
+ STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, xattr);
+
+ return 0;
+}
+
+
+int
+posix_acl_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ const char *name)
+{
+ if (whitelisted_xattr (name))
+ goto green;
+
+ if (acl_permits (frame, fd->inode, POSIX_ACL_READ))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_fgetxattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fgetxattr,
+ fd, name);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (fgetxattr, frame, -1, EACCES, NULL);
+
+ return 0;
+}
+
+
+int
+posix_acl_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno)
+{
+ STACK_UNWIND_STRICT (removexattr, frame, op_ret, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ const char *name)
+{
+ struct posix_acl_ctx *ctx = NULL;
+ int op_errno = EACCES;
+
+ if (frame_is_user (frame, 0))
+ goto green;
+
+ ctx = posix_acl_ctx_get (loc->inode, this);
+ if (!ctx) {
+ op_errno = EIO;
+ goto red;
+ }
+
+ if (whitelisted_xattr (name)) {
+ if (!frame_is_user (frame, ctx->uid)) {
+ op_errno = EPERM;
+ goto red;
+ }
+ }
+
+ if (acl_permits (frame, loc->inode, POSIX_ACL_WRITE))
+ goto green;
+ else
+ goto red;
+green:
+ STACK_WIND (frame, posix_acl_removexattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->removexattr,
+ loc, name);
+ return 0;
+red:
+ STACK_UNWIND_STRICT (removexattr, frame, -1, op_errno);
+
+ return 0;
+}
+
+
+int
+posix_acl_forget (xlator_t *this, inode_t *inode)
+{
+ struct posix_acl_ctx *ctx = NULL;
+
+ ctx = posix_acl_ctx_get (inode, this);
+ if (!ctx)
+ goto out;
+
+ if (ctx->acl_access)
+ posix_acl_unref (this, ctx->acl_access);
+
+ if (ctx->acl_default)
+ posix_acl_unref (this, ctx->acl_default);
+
+ FREE (ctx);
+out:
+ return 0;
+}
+
+
+int
+init (xlator_t *this)
+{
+ struct posix_acl_conf *conf = NULL;
+ struct posix_acl *minacl = NULL;
+ struct posix_ace *minace = NULL;
+
+ conf = CALLOC (1, sizeof (*conf));
+ if (!conf) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "out of memory");
+ return -1;
+ }
+
+ LOCK_INIT (&conf->acl_lock);
+
+ this->private = conf;
+
+ minacl = posix_acl_new (this, 3);
+ if (!minacl)
+ return -1;
+
+ minace = minacl->entries;
+ minace[0].tag = POSIX_ACL_USER_OBJ;
+ minace[1].tag = POSIX_ACL_GROUP_OBJ;
+ minace[2].tag = POSIX_ACL_OTHER;
+
+ conf->minimal_acl = minacl;
+
+ return 0;
+}
+
+
+int
+fini (xlator_t *this)
+{
+ return 0;
+}
+
+
+struct xlator_fops fops = {
+ .lookup = posix_acl_lookup,
+ .open = posix_acl_open,
+ .access = posix_acl_access,
+ .truncate = posix_acl_truncate,
+ .mkdir = posix_acl_mkdir,
+ .mknod = posix_acl_mknod,
+ .create = posix_acl_create,
+ .symlink = posix_acl_symlink,
+ .unlink = posix_acl_unlink,
+ .rmdir = posix_acl_rmdir,
+ .rename = posix_acl_rename,
+ .link = posix_acl_link,
+ .opendir = posix_acl_opendir,
+ .readdir = posix_acl_readdir,
+ .readdirp = posix_acl_readdirp,
+ .setattr = posix_acl_setattr,
+ .fsetattr = posix_acl_fsetattr,
+ .setxattr = posix_acl_setxattr,
+ .fsetxattr = posix_acl_fsetxattr,
+ .getxattr = posix_acl_getxattr,
+ .fgetxattr = posix_acl_fgetxattr,
+ .removexattr = posix_acl_removexattr,
+};
+
+
+struct xlator_cbks cbks = {
+ .forget = posix_acl_forget
+};
diff --git a/xlators/system/posix-acl/src/posix-acl.h b/xlators/system/posix-acl/src/posix-acl.h
new file mode 100644
index 00000000000..46b56d6f38c
--- /dev/null
+++ b/xlators/system/posix-acl/src/posix-acl.h
@@ -0,0 +1,86 @@
+/*
+ Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef _POSIX_ACL_H
+#define _POSIX_ACL_H
+
+#include <stdint.h>
+
+#include "xlator.h"
+#include "common-utils.h"
+#include "byte-order.h"
+
+
+#define POSIX_ACL_READ (0x04)
+#define POSIX_ACL_WRITE (0x02)
+#define POSIX_ACL_EXECUTE (0x01)
+
+#define POSIX_ACL_UNDEFINED_TAG (0x00)
+#define POSIX_ACL_USER_OBJ (0x01)
+#define POSIX_ACL_USER (0x02)
+#define POSIX_ACL_GROUP_OBJ (0x04)
+#define POSIX_ACL_GROUP (0x08)
+#define POSIX_ACL_MASK (0x10)
+#define POSIX_ACL_OTHER (0x20)
+
+#define POSIX_ACL_UNDEFINED_ID ((id_t)-1)
+
+
+struct posix_ace {
+ uint16_t tag;
+ uint16_t perm;
+ uint32_t id;
+};
+
+
+struct posix_acl {
+ int refcnt;
+ int count;
+ struct posix_ace entries[0];
+};
+
+
+struct posix_acl_ctx {
+ uid_t uid;
+ gid_t gid;
+ mode_t perm;
+ struct posix_acl *acl_access;
+ struct posix_acl *acl_default;
+};
+
+
+struct posix_acl_conf {
+ gf_lock_t acl_lock;
+ struct posix_acl *minimal_acl;
+};
+
+
+struct posix_acl *posix_acl_new (xlator_t *this, int entry_count);
+struct posix_acl *posix_acl_ref (xlator_t *this, struct posix_acl *acl);
+void posix_acl_unref (xlator_t *this, struct posix_acl *acl);
+void posix_acl_destroy (xlator_t *this, struct posix_acl *acl);
+struct posix_acl_ctx *posix_acl_ctx_get (inode_t *inode, xlator_t *this);
+int posix_acl_get (inode_t *inode, xlator_t *this,
+ struct posix_acl **acl_access_p,
+ struct posix_acl **acl_default_p);
+int posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access,
+ struct posix_acl *acl_default);
+
+#endif /* !_POSIX_ACL_H */