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