]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2011 New Dream Network | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | #include "include/types.h" | |
15 | #include "gtest/gtest.h" | |
16 | #include "include/cephfs/libcephfs.h" | |
17 | #include "include/ceph_fs.h" | |
18 | #include "client/posix_acl.h" | |
19 | #include <errno.h> | |
20 | #include <fcntl.h> | |
21 | #include <unistd.h> | |
22 | #include <sys/types.h> | |
23 | #include <sys/stat.h> | |
eafe8130 | 24 | #ifdef __linux__ |
7c673cae | 25 | #include <sys/xattr.h> |
eafe8130 | 26 | #endif |
7c673cae FG |
27 | |
28 | static size_t acl_ea_size(int count) | |
29 | { | |
30 | return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); | |
31 | } | |
32 | ||
33 | static int acl_ea_count(size_t size) | |
34 | { | |
35 | if (size < sizeof(acl_ea_header)) | |
36 | return -1; | |
37 | size -= sizeof(acl_ea_header); | |
38 | if (size % sizeof(acl_ea_entry)) | |
39 | return -1; | |
40 | return size / sizeof(acl_ea_entry); | |
41 | } | |
42 | ||
43 | static int check_acl_and_mode(const void *buf, size_t size, mode_t mode) | |
44 | { | |
45 | const acl_ea_entry *group_entry = NULL, *mask_entry = NULL; | |
46 | const acl_ea_header *header = reinterpret_cast<const acl_ea_header*>(buf); | |
47 | const acl_ea_entry *entry = header->a_entries; | |
48 | int count = (size - sizeof(*header)) / sizeof(*entry); | |
49 | for (int i = 0; i < count; ++i) { | |
50 | __u16 tag = entry->e_tag; | |
51 | __u16 perm = entry->e_perm; | |
52 | switch(tag) { | |
53 | case ACL_USER_OBJ: | |
54 | if (perm != ((mode >> 6) & 7)) | |
55 | return -EINVAL; | |
56 | break; | |
57 | case ACL_USER: | |
58 | case ACL_GROUP: | |
59 | break; | |
60 | case ACL_GROUP_OBJ: | |
61 | group_entry = entry; | |
62 | break; | |
63 | case ACL_OTHER: | |
64 | if (perm != (mode & 7)) | |
65 | return -EINVAL; | |
66 | break; | |
67 | case ACL_MASK: | |
68 | mask_entry = entry; | |
69 | break; | |
70 | default: | |
71 | return -EIO; | |
72 | } | |
73 | ++entry; | |
74 | } | |
75 | if (mask_entry) { | |
76 | __u16 perm = mask_entry->e_perm; | |
77 | if (perm != ((mode >> 3) & 7)) | |
78 | return -EINVAL; | |
79 | } else { | |
80 | if (!group_entry) | |
81 | return -EIO; | |
82 | __u16 perm = group_entry->e_perm; | |
83 | if (perm != ((mode >> 3) & 7)) | |
84 | return -EINVAL; | |
85 | } | |
86 | return 0; | |
87 | } | |
88 | ||
89 | static int generate_test_acl(void *buf, size_t size, mode_t mode) | |
90 | { | |
91 | if (acl_ea_count(size) != 5) | |
92 | return -1; | |
93 | acl_ea_header *header = reinterpret_cast<acl_ea_header*>(buf); | |
94 | header->a_version = (__u32)ACL_EA_VERSION; | |
95 | acl_ea_entry *entry = header->a_entries; | |
96 | entry->e_tag = ACL_USER_OBJ; | |
97 | entry->e_perm = (mode >> 6) & 7; | |
98 | ++entry; | |
99 | entry->e_tag = ACL_USER; | |
100 | entry->e_perm = 7; | |
101 | entry->e_id = getuid(); | |
102 | ++entry; | |
103 | entry->e_tag = ACL_GROUP_OBJ; | |
104 | entry->e_perm = (mode >> 3) & 7; | |
105 | ++entry; | |
106 | entry->e_tag = ACL_MASK; | |
107 | entry->e_perm = 7; | |
108 | ++entry; | |
109 | entry->e_tag = ACL_OTHER; | |
110 | entry->e_perm = mode & 7; | |
111 | return 0; | |
112 | } | |
113 | ||
114 | static int generate_empty_acl(void *buf, size_t size, mode_t mode) | |
115 | { | |
116 | ||
117 | if (acl_ea_count(size) != 3) | |
118 | return -1; | |
119 | acl_ea_header *header = reinterpret_cast<acl_ea_header*>(buf); | |
120 | header->a_version = (__u32)ACL_EA_VERSION; | |
121 | acl_ea_entry *entry = header->a_entries; | |
122 | entry->e_tag = ACL_USER_OBJ; | |
123 | entry->e_perm = (mode >> 6) & 7; | |
124 | ++entry; | |
125 | entry->e_tag = ACL_GROUP_OBJ; | |
126 | entry->e_perm = (mode >> 3) & 7; | |
127 | ++entry; | |
128 | entry->e_tag = ACL_OTHER; | |
129 | entry->e_perm = mode & 7; | |
130 | return 0; | |
131 | } | |
132 | ||
133 | TEST(ACL, SetACL) { | |
134 | struct ceph_mount_info *cmount; | |
135 | ASSERT_EQ(0, ceph_create(&cmount, NULL)); | |
136 | ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); | |
137 | ASSERT_EQ(0, ceph_mount(cmount, "/")); | |
138 | ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl")); | |
139 | ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "0")); | |
140 | ||
141 | char test_file[256]; | |
142 | sprintf(test_file, "file1_setacl_%d", getpid()); | |
143 | ||
144 | int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600); | |
145 | ASSERT_GT(fd, 0); | |
146 | // change ownership to nobody -- we assume nobody exists and id is always 65534 | |
147 | ASSERT_EQ(ceph_fchown(cmount, fd, 65534, 65534), 0); | |
148 | ||
149 | ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "1")); | |
150 | ASSERT_EQ(ceph_open(cmount, test_file, O_RDWR, 0), -EACCES); | |
151 | ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "0")); | |
152 | ||
153 | size_t acl_buf_size = acl_ea_size(5); | |
154 | void *acl_buf = malloc(acl_buf_size); | |
155 | ASSERT_EQ(generate_test_acl(acl_buf, acl_buf_size, 0750), 0); | |
156 | ||
157 | // can't set default acl for non-directory | |
158 | ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_DEFAULT, acl_buf, acl_buf_size, 0), -EACCES); | |
159 | ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0); | |
160 | ||
161 | int tmpfd = ceph_open(cmount, test_file, O_RDWR, 0); | |
162 | ASSERT_GT(tmpfd, 0); | |
163 | ceph_close(cmount, tmpfd); | |
164 | ||
165 | struct ceph_statx stx; | |
166 | ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); | |
167 | // mode was modified according to ACL | |
168 | ASSERT_EQ(stx.stx_mode & 0777u, 0770u); | |
169 | ASSERT_EQ(check_acl_and_mode(acl_buf, acl_buf_size, stx.stx_mode), 0); | |
170 | ||
171 | acl_buf_size = acl_ea_size(3); | |
172 | // setting ACL that is equivalent to file mode | |
173 | ASSERT_EQ(generate_empty_acl(acl_buf, acl_buf_size, 0600), 0); | |
174 | ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0); | |
175 | // ACL was deleted | |
176 | ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, NULL, 0), -ENODATA); | |
177 | ||
178 | ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); | |
179 | // mode was modified according to ACL | |
180 | ASSERT_EQ(stx.stx_mode & 0777u, 0600u); | |
181 | ||
182 | free(acl_buf); | |
183 | ceph_close(cmount, fd); | |
184 | ceph_shutdown(cmount); | |
185 | } | |
186 | ||
187 | TEST(ACL, Chmod) { | |
188 | struct ceph_mount_info *cmount; | |
189 | ASSERT_EQ(0, ceph_create(&cmount, NULL)); | |
190 | ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); | |
191 | ASSERT_EQ(0, ceph_mount(cmount, "/")); | |
192 | ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl")); | |
193 | ||
194 | char test_file[256]; | |
195 | sprintf(test_file, "file1_acl_chmod_%d", getpid()); | |
196 | ||
197 | int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600); | |
198 | ASSERT_GT(fd, 0); | |
199 | ||
200 | int acl_buf_size = acl_ea_size(5); | |
201 | void *acl_buf = malloc(acl_buf_size); | |
202 | ASSERT_EQ(generate_test_acl(acl_buf, acl_buf_size, 0775), 0); | |
203 | ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0); | |
204 | ||
205 | struct ceph_statx stx; | |
206 | ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); | |
207 | // mode was updated according to ACL | |
208 | ASSERT_EQ(stx.stx_mode & 0777u, 0775u); | |
209 | ||
210 | // change mode | |
211 | ASSERT_EQ(ceph_fchmod(cmount, fd, 0640), 0); | |
212 | ||
213 | ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); | |
214 | ASSERT_EQ(stx.stx_mode & 0777u, 0640u); | |
215 | ||
216 | // ACL was updated according to mode | |
217 | ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size), acl_buf_size); | |
218 | ASSERT_EQ(check_acl_and_mode(acl_buf, acl_buf_size, stx.stx_mode), 0); | |
219 | ||
220 | free(acl_buf); | |
221 | ceph_close(cmount, fd); | |
222 | ceph_shutdown(cmount); | |
223 | } | |
224 | ||
225 | TEST(ACL, DefaultACL) { | |
226 | struct ceph_mount_info *cmount; | |
227 | ASSERT_EQ(0, ceph_create(&cmount, NULL)); | |
228 | ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); | |
229 | ASSERT_EQ(0, ceph_mount(cmount, "/")); | |
230 | ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl")); | |
231 | ||
232 | int acl_buf_size = acl_ea_size(5); | |
233 | void *acl1_buf = malloc(acl_buf_size); | |
234 | void *acl2_buf = malloc(acl_buf_size); | |
235 | ||
236 | ASSERT_EQ(generate_test_acl(acl1_buf, acl_buf_size, 0750), 0); | |
237 | ||
238 | char test_dir1[256]; | |
239 | sprintf(test_dir1, "dir1_acl_default_%d", getpid()); | |
240 | ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0750), 0); | |
241 | ||
242 | // set default acl | |
243 | ASSERT_EQ(ceph_setxattr(cmount, test_dir1, ACL_EA_DEFAULT, acl1_buf, acl_buf_size, 0), 0); | |
244 | ||
11fdf7f2 | 245 | char test_dir2[262]; |
7c673cae FG |
246 | sprintf(test_dir2, "%s/dir2", test_dir1); |
247 | ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0755), 0); | |
248 | ||
249 | // inherit default acl | |
250 | ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_DEFAULT, acl2_buf, acl_buf_size), acl_buf_size); | |
251 | ASSERT_EQ(memcmp(acl1_buf, acl2_buf, acl_buf_size), 0); | |
252 | ||
253 | // mode and ACL are updated | |
254 | ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size); | |
255 | { | |
256 | struct ceph_statx stx; | |
257 | ASSERT_EQ(ceph_statx(cmount, test_dir2, &stx, CEPH_STATX_MODE, 0), 0); | |
258 | // other bits of mode &= acl other perm | |
259 | ASSERT_EQ(stx.stx_mode & 0777u, 0750u); | |
260 | ASSERT_EQ(check_acl_and_mode(acl2_buf, acl_buf_size, stx.stx_mode), 0); | |
261 | } | |
262 | ||
11fdf7f2 | 263 | char test_file1[262]; |
7c673cae FG |
264 | sprintf(test_file1, "%s/file1", test_dir1); |
265 | int fd = ceph_open(cmount, test_file1, O_CREAT|O_RDWR, 0666); | |
266 | ASSERT_GT(fd, 0); | |
267 | ||
268 | // no default acl | |
269 | ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_DEFAULT, NULL, 0), -ENODATA); | |
270 | ||
271 | // mode and ACL are updated | |
272 | ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size); | |
273 | { | |
274 | struct ceph_statx stx; | |
275 | ASSERT_EQ(ceph_statx(cmount, test_file1, &stx, CEPH_STATX_MODE, 0), 0); | |
276 | // other bits of mode &= acl other perm | |
277 | ASSERT_EQ(stx.stx_mode & 0777u, 0660u); | |
278 | ASSERT_EQ(check_acl_and_mode(acl2_buf, acl_buf_size, stx.stx_mode), 0); | |
279 | } | |
280 | ||
281 | free(acl1_buf); | |
282 | free(acl2_buf); | |
283 | ceph_close(cmount, fd); | |
284 | ceph_shutdown(cmount); | |
285 | } | |
286 | ||
287 | TEST(ACL, Disabled) { | |
288 | struct ceph_mount_info *cmount; | |
289 | ASSERT_EQ(0, ceph_create(&cmount, NULL)); | |
290 | ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); | |
291 | ASSERT_EQ(0, ceph_mount(cmount, "/")); | |
292 | ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "")); | |
293 | ||
294 | size_t acl_buf_size = acl_ea_size(3); | |
295 | void *acl_buf = malloc(acl_buf_size); | |
296 | ASSERT_EQ(generate_empty_acl(acl_buf, acl_buf_size, 0755), 0); | |
297 | ||
298 | char test_dir[256]; | |
299 | sprintf(test_dir, "dir1_acl_disabled_%d", getpid()); | |
300 | ASSERT_EQ(ceph_mkdir(cmount, test_dir, 0750), 0); | |
301 | ||
302 | ASSERT_EQ(ceph_setxattr(cmount, test_dir, ACL_EA_DEFAULT, acl_buf, acl_buf_size, 0), -EOPNOTSUPP); | |
303 | ASSERT_EQ(ceph_setxattr(cmount, test_dir, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), -EOPNOTSUPP); | |
304 | ASSERT_EQ(ceph_getxattr(cmount, test_dir, ACL_EA_DEFAULT, acl_buf, acl_buf_size), -EOPNOTSUPP); | |
305 | ASSERT_EQ(ceph_getxattr(cmount, test_dir, ACL_EA_ACCESS, acl_buf, acl_buf_size), -EOPNOTSUPP); | |
306 | ||
307 | free(acl_buf); | |
308 | ceph_shutdown(cmount); | |
309 | } |