diff options
Diffstat (limited to 'src/test/libcephfs/acl.cc')
-rw-r--r-- | src/test/libcephfs/acl.cc | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/src/test/libcephfs/acl.cc b/src/test/libcephfs/acl.cc new file mode 100644 index 000000000..e263ef2fb --- /dev/null +++ b/src/test/libcephfs/acl.cc @@ -0,0 +1,367 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ +#include "include/types.h" +#include "gtest/gtest.h" +#include "include/cephfs/libcephfs.h" +#include "include/fs_types.h" +#include "include/ceph_fs.h" +#include "client/posix_acl.h" +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef __linux__ +#include <sys/xattr.h> +#endif + +static size_t acl_ea_size(int count) +{ + return sizeof(acl_ea_header) + count * sizeof(acl_ea_entry); +} + +static int acl_ea_count(size_t size) +{ + if (size < sizeof(acl_ea_header)) + return -1; + size -= sizeof(acl_ea_header); + if (size % sizeof(acl_ea_entry)) + return -1; + return size / sizeof(acl_ea_entry); +} + +static int check_acl_and_mode(const void *buf, size_t size, mode_t mode) +{ + const acl_ea_entry *group_entry = NULL, *mask_entry = NULL; + const acl_ea_header *header = reinterpret_cast<const acl_ea_header*>(buf); + const acl_ea_entry *entry = header->a_entries; + int count = (size - sizeof(*header)) / sizeof(*entry); + for (int i = 0; i < count; ++i) { + __u16 tag = entry->e_tag; + __u16 perm = entry->e_perm; + switch(tag) { + case ACL_USER_OBJ: + if (perm != ((mode >> 6) & 7)) + return -CEPHFS_EINVAL; + break; + case ACL_USER: + case ACL_GROUP: + break; + case ACL_GROUP_OBJ: + group_entry = entry; + break; + case ACL_OTHER: + if (perm != (mode & 7)) + return -CEPHFS_EINVAL; + break; + case ACL_MASK: + mask_entry = entry; + break; + default: + return -CEPHFS_EIO; + } + ++entry; + } + if (mask_entry) { + __u16 perm = mask_entry->e_perm; + if (perm != ((mode >> 3) & 7)) + return -CEPHFS_EINVAL; + } else { + if (!group_entry) + return -CEPHFS_EIO; + __u16 perm = group_entry->e_perm; + if (perm != ((mode >> 3) & 7)) + return -CEPHFS_EINVAL; + } + return 0; +} + +static int generate_test_acl(void *buf, size_t size, mode_t mode) +{ + if (acl_ea_count(size) != 5) + return -1; + acl_ea_header *header = reinterpret_cast<acl_ea_header*>(buf); + header->a_version = (__u32)ACL_EA_VERSION; + acl_ea_entry *entry = header->a_entries; + entry->e_tag = ACL_USER_OBJ; + entry->e_perm = (mode >> 6) & 7; + ++entry; + entry->e_tag = ACL_USER; + entry->e_perm = 7; + entry->e_id = getuid(); + ++entry; + entry->e_tag = ACL_GROUP_OBJ; + entry->e_perm = (mode >> 3) & 7; + ++entry; + entry->e_tag = ACL_MASK; + entry->e_perm = 7; + ++entry; + entry->e_tag = ACL_OTHER; + entry->e_perm = mode & 7; + return 0; +} + +static int generate_empty_acl(void *buf, size_t size, mode_t mode) +{ + + if (acl_ea_count(size) != 3) + return -1; + acl_ea_header *header = reinterpret_cast<acl_ea_header*>(buf); + header->a_version = (__u32)ACL_EA_VERSION; + acl_ea_entry *entry = header->a_entries; + entry->e_tag = ACL_USER_OBJ; + entry->e_perm = (mode >> 6) & 7; + ++entry; + entry->e_tag = ACL_GROUP_OBJ; + entry->e_perm = (mode >> 3) & 7; + ++entry; + entry->e_tag = ACL_OTHER; + entry->e_perm = mode & 7; + return 0; +} + +TEST(ACL, SetACL) { + struct ceph_mount_info *cmount; + ASSERT_EQ(0, ceph_create(&cmount, NULL)); + ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); + ASSERT_EQ(0, ceph_mount(cmount, "/")); + ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl")); + ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "0")); + + char test_file[256]; + sprintf(test_file, "file1_setacl_%d", getpid()); + + int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600); + ASSERT_GT(fd, 0); + // change ownership to nobody -- we assume nobody exists and id is always 65534 + ASSERT_EQ(ceph_fchown(cmount, fd, 65534, 65534), 0); + + ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "1")); + // "nobody" will be ignored on Windows + #ifndef _WIN32 + ASSERT_EQ(ceph_open(cmount, test_file, O_RDWR, 0), -CEPHFS_EACCES); + #endif + ASSERT_EQ(0, ceph_conf_set(cmount, "client_permissions", "0")); + + size_t acl_buf_size = acl_ea_size(5); + void *acl_buf = malloc(acl_buf_size); + ASSERT_EQ(generate_test_acl(acl_buf, acl_buf_size, 0750), 0); + + // can't set default acl for non-directory + ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_DEFAULT, acl_buf, acl_buf_size, 0), -CEPHFS_EACCES); + ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0); + + int tmpfd = ceph_open(cmount, test_file, O_RDWR, 0); + ASSERT_GT(tmpfd, 0); + ceph_close(cmount, tmpfd); + + struct ceph_statx stx; + ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); + // mode was modified according to ACL + ASSERT_EQ(stx.stx_mode & 0777u, 0770u); + ASSERT_EQ(check_acl_and_mode(acl_buf, acl_buf_size, stx.stx_mode), 0); + + acl_buf_size = acl_ea_size(3); + // setting ACL that is equivalent to file mode + ASSERT_EQ(generate_empty_acl(acl_buf, acl_buf_size, 0600), 0); + ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0); + // ACL was deleted + ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, NULL, 0), -CEPHFS_ENODATA); + + ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); + // mode was modified according to ACL + ASSERT_EQ(stx.stx_mode & 0777u, 0600u); + + free(acl_buf); + ceph_close(cmount, fd); + ceph_shutdown(cmount); +} + +TEST(ACL, Chmod) { + struct ceph_mount_info *cmount; + ASSERT_EQ(0, ceph_create(&cmount, NULL)); + ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); + ASSERT_EQ(0, ceph_mount(cmount, "/")); + ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl")); + + char test_file[256]; + sprintf(test_file, "file1_acl_chmod_%d", getpid()); + + int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600); + ASSERT_GT(fd, 0); + + int acl_buf_size = acl_ea_size(5); + void *acl_buf = malloc(acl_buf_size); + ASSERT_EQ(generate_test_acl(acl_buf, acl_buf_size, 0775), 0); + ASSERT_EQ(ceph_fsetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), 0); + + struct ceph_statx stx; + ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); + // mode was updated according to ACL + ASSERT_EQ(stx.stx_mode & 0777u, 0775u); + + // change mode + ASSERT_EQ(ceph_fchmod(cmount, fd, 0640), 0); + + ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_MODE, 0), 0); + ASSERT_EQ(stx.stx_mode & 0777u, 0640u); + + // ACL was updated according to mode + ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, acl_buf, acl_buf_size), acl_buf_size); + ASSERT_EQ(check_acl_and_mode(acl_buf, acl_buf_size, stx.stx_mode), 0); + + free(acl_buf); + ceph_close(cmount, fd); + ceph_shutdown(cmount); +} + +TEST(ACL, DefaultACL) { + struct ceph_mount_info *cmount; + ASSERT_EQ(0, ceph_create(&cmount, NULL)); + ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); + ASSERT_EQ(0, ceph_mount(cmount, "/")); + ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl")); + + int acl_buf_size = acl_ea_size(5); + void *acl1_buf = malloc(acl_buf_size); + void *acl2_buf = malloc(acl_buf_size); + + ASSERT_EQ(generate_test_acl(acl1_buf, acl_buf_size, 0750), 0); + + char test_dir1[256]; + sprintf(test_dir1, "dir1_acl_default_%d", getpid()); + ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0750), 0); + + // set default acl + ASSERT_EQ(ceph_setxattr(cmount, test_dir1, ACL_EA_DEFAULT, acl1_buf, acl_buf_size, 0), 0); + + char test_dir2[262]; + sprintf(test_dir2, "%s/dir2", test_dir1); + ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0755), 0); + + // inherit default acl + ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_DEFAULT, acl2_buf, acl_buf_size), acl_buf_size); + ASSERT_EQ(memcmp(acl1_buf, acl2_buf, acl_buf_size), 0); + + // mode and ACL are updated + ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size); + { + struct ceph_statx stx; + ASSERT_EQ(ceph_statx(cmount, test_dir2, &stx, CEPH_STATX_MODE, 0), 0); + // other bits of mode &= acl other perm + ASSERT_EQ(stx.stx_mode & 0777u, 0750u); + ASSERT_EQ(check_acl_and_mode(acl2_buf, acl_buf_size, stx.stx_mode), 0); + } + + char test_file1[262]; + sprintf(test_file1, "%s/file1", test_dir1); + int fd = ceph_open(cmount, test_file1, O_CREAT|O_RDWR, 0666); + ASSERT_GT(fd, 0); + + // no default acl + ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_DEFAULT, NULL, 0), -CEPHFS_ENODATA); + + // mode and ACL are updated + ASSERT_EQ(ceph_fgetxattr(cmount, fd, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size); + { + struct ceph_statx stx; + ASSERT_EQ(ceph_statx(cmount, test_file1, &stx, CEPH_STATX_MODE, 0), 0); + // other bits of mode &= acl other perm + ASSERT_EQ(stx.stx_mode & 0777u, 0660u); + ASSERT_EQ(check_acl_and_mode(acl2_buf, acl_buf_size, stx.stx_mode), 0); + } + + free(acl1_buf); + free(acl2_buf); + ASSERT_EQ(ceph_unlink(cmount, test_file1), 0); + ASSERT_EQ(ceph_rmdir(cmount, test_dir2), 0); + ASSERT_EQ(ceph_rmdir(cmount, test_dir1), 0); + ceph_close(cmount, fd); + ceph_shutdown(cmount); +} + +TEST(ACL, Disabled) { + struct ceph_mount_info *cmount; + ASSERT_EQ(0, ceph_create(&cmount, NULL)); + ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); + ASSERT_EQ(0, ceph_mount(cmount, "/")); + ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "")); + + size_t acl_buf_size = acl_ea_size(3); + void *acl_buf = malloc(acl_buf_size); + ASSERT_EQ(generate_empty_acl(acl_buf, acl_buf_size, 0755), 0); + + char test_dir[256]; + sprintf(test_dir, "dir1_acl_disabled_%d", getpid()); + ASSERT_EQ(ceph_mkdir(cmount, test_dir, 0750), 0); + + ASSERT_EQ(ceph_setxattr(cmount, test_dir, ACL_EA_DEFAULT, acl_buf, acl_buf_size, 0), -CEPHFS_EOPNOTSUPP); + ASSERT_EQ(ceph_setxattr(cmount, test_dir, ACL_EA_ACCESS, acl_buf, acl_buf_size, 0), -CEPHFS_EOPNOTSUPP); + ASSERT_EQ(ceph_getxattr(cmount, test_dir, ACL_EA_DEFAULT, acl_buf, acl_buf_size), -CEPHFS_EOPNOTSUPP); + ASSERT_EQ(ceph_getxattr(cmount, test_dir, ACL_EA_ACCESS, acl_buf, acl_buf_size), -CEPHFS_EOPNOTSUPP); + + free(acl_buf); + ceph_shutdown(cmount); +} + +TEST(ACL, SnapdirACL) { + struct ceph_mount_info *cmount; + ASSERT_EQ(0, ceph_create(&cmount, NULL)); + ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); + ASSERT_EQ(0, ceph_mount(cmount, "/")); + ASSERT_EQ(0, ceph_conf_set(cmount, "client_acl_type", "posix_acl")); + + int acl_buf_size = acl_ea_size(5); + void *acl1_buf = malloc(acl_buf_size); + void *acl2_buf = malloc(acl_buf_size); + void *acl3_buf = malloc(acl_buf_size); + + ASSERT_EQ(generate_test_acl(acl1_buf, acl_buf_size, 0750), 0); + + char test_dir1[256]; + sprintf(test_dir1, "dir1_acl_default_%d", getpid()); + ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0750), 0); + + // set default acl + ASSERT_EQ(ceph_setxattr(cmount, test_dir1, ACL_EA_DEFAULT, acl1_buf, acl_buf_size, 0), 0); + + char test_dir2[262]; + sprintf(test_dir2, "%s/dir2", test_dir1); + ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0755), 0); + + // inherit default acl + ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_DEFAULT, acl2_buf, acl_buf_size), acl_buf_size); + ASSERT_EQ(memcmp(acl1_buf, acl2_buf, acl_buf_size), 0); + + char test_dir2_snapdir[512]; + sprintf(test_dir2_snapdir, "%s/dir2/.snap", test_dir1); + + // inherit default acl + ASSERT_EQ(ceph_getxattr(cmount, test_dir2_snapdir, ACL_EA_DEFAULT, acl3_buf, acl_buf_size), acl_buf_size); + ASSERT_EQ(memcmp(acl2_buf, acl3_buf, acl_buf_size), 0); + + memset(acl2_buf, 0, acl_buf_size); + memset(acl3_buf, 0, acl_buf_size); + + ASSERT_EQ(ceph_getxattr(cmount, test_dir2, ACL_EA_ACCESS, acl2_buf, acl_buf_size), acl_buf_size); + ASSERT_EQ(ceph_getxattr(cmount, test_dir2_snapdir, ACL_EA_ACCESS, acl3_buf, acl_buf_size), acl_buf_size); + ASSERT_EQ(memcmp(acl2_buf, acl3_buf, acl_buf_size), 0); + + free(acl1_buf); + free(acl2_buf); + free(acl3_buf); + ASSERT_EQ(ceph_rmdir(cmount, test_dir2), 0); + ASSERT_EQ(ceph_rmdir(cmount, test_dir1), 0); + ceph_shutdown(cmount); +} |