This PR adds test for S_ENCRYPTED bit in the i_flags field of Inode.
The test implements 2 quering methods: using FS_IOC_GETFLAGS and STATX_ATTR_ENCRYPTED
Fixes: https://tracker.ceph.com/issues/64129
Author: Igor Golikov <igolikov@ibm.com>
Signed-off-by: Christopher Hoffman <choffman@redhat.com>
stx->stx_mask |= (CEPH_STATX_CTIME|CEPH_STATX_VERSION);
}
+ // Check that the flag has no garbage, if is_encrypted() is false
+ bool en = in->is_encrypted();
+ ceph_assert(en || !(stx->stx_attributes & STATX_ATTR_ENCRYPTED));
+ stx->stx_attributes |= en ? STATX_ATTR_ENCRYPTED : 0;
}
void Client::touch_dn(Dentry *dn)
int Client::_read_async(Fh *f, uint64_t off, uint64_t len, bufferlist *bl,
Context *onfinish)
{
- ceph_assert(ceph_mutex_is_locked_by_me(client_lock));
+ (ceph_mutex_is_locked_by_me(client_lock));
const auto& conf = cct->_conf;
Inode *in = f->inode.get();
}
// --- subvolume metrics tracking --- //
+int Client::get_inode_flags(const Inode* in, int* file_attr_out) {
+ if (!file_attr_out)
+ return -CEPHFS_EINVAL;
+
+ *file_attr_out = 0;
+ // set or clear the encryption flag depending on the inode status
+ if (in->is_encrypted()) {
+ *file_attr_out |= FS_ENCRYPT_FL;
+ } else {
+ *file_attr_out &= ~FS_ENCRYPT_FL;
+ }
+ return 0;
+}
+
+int Client::get_inode_flags(int fd, int* file_attr_out) {
+ Fh *fh = get_filehandle(fd);
+ if (!fh) {
+ return -CEPHFS_EBADF;
+ }
+ return get_inode_flags(fh->inode.get(), file_attr_out);
+}
+
StandaloneClient::StandaloneClient(Messenger *m, MonClient *mc,
boost::asio::io_context& ictx)
: Client(m, mc, new Objecter(m->cct, m, mc, ictx))
int remove_fscrypt_key(fscrypt_remove_key_arg* kid, int user = 0);
int get_fscrypt_key_status(fscrypt_get_key_status_arg* arg);
+ int get_inode_flags(const Inode* in, int* file_attr_out);
+ int get_inode_flags(int fd, int* file_attr_out);
+
int set_fscrypt_policy_v2(int fd, const struct fscrypt_policy_v2& policy);
int mds_command(
FSCryptContextRef Inode::init_fscrypt_ctx(FSCrypt *fscrypt)
{
- return fscrypt->init_ctx(fscrypt_auth);
+ auto res = fscrypt->init_ctx(fscrypt_auth);
+ // at this point, if fscrypt is enabled, the fscrypt_auth is not empty
+ // and we can set the S_ENCRYPTED flag
+ set_is_encrypted_flag();
+ return res;
}
void Inode::gen_inherited_fscrypt_auth(std::vector<uint8_t> *fsa)
uint32_t mode = 0;
uid_t uid = 0;
gid_t gid = 0;
- uint32_t i_flags;
+ uint32_t i_flags = 0;
// nlink
int32_t nlink = 0;
uint64_t effective_size() const;
void set_effective_size(uint64_t size);
+ // this method returns true if inode is de facto ecrypted.
+ // semantics of "enabled" is a bit confusing since it may mean
+ // "enabled but not encrypted de facto".
bool is_fscrypt_enabled() {
return !!fscrypt_auth.size();
}
// use i_flags as 1 << 14 will overlap with other mode bits.
bool is_encrypted() const { return (i_flags & S_ENCRYPTED) == S_ENCRYPTED; }
+ // this function sets S_ENCRYPTED bit in i_flag
+ // is called when the is_fscrypt_enabled
+ void set_is_encrypted_flag() {
+ bool en = is_fscrypt_enabled();
+ // just to make sure that no garbage is set in the flag, if fscrypt is disabled
+ ceph_assert(en || !(i_flags & S_ENCRYPTED));
+ i_flags |= en ? S_ENCRYPTED : 0;
+ }
bool has_dir_layout() const {
return layout != file_layout_t();
fuse_reply_ioctl(req, 0, arg, sizeof(*arg));
}
break;
+ case FS_IOC_GETFLAGS: {
+ generic_dout(0) << __FILE__ << ":" << __LINE__ << ": FS_IOC_GETFLAGS ioctl" << dendl;
+
+ int file_attr = 0;
+ if (out_bufsz < sizeof(file_attr)) {
+ fuse_reply_err(req, ERANGE);
+ break;
+ }
+
+ Fh *fh = (Fh*)fi->fh;
+ cfuse->client->get_inode_flags(fh->inode.get(), &file_attr);
+ fuse_reply_ioctl(req, 0, &file_attr, sizeof(file_attr));
+}
+break;
default:
fuse_reply_err(req, EINVAL);
}
struct timespec stx_mtime;
struct timespec stx_btime;
uint64_t stx_version;
+ uint64_t stx_attributes;
};
#define CEPH_STATX_MODE 0x00000001U /* Want/got stx_mode */
#define CEPH_STATX_VERSION 0x00001000U /* Want/got stx_version */
#define CEPH_STATX_ALL_STATS 0x00001fffU /* All supported stats */
+// the value of STATX_ATTR_ENCRYPTED on most systems is equal to FS_ENCRYPT_FL
+// to identify encrypted files uniformly,
+// simplifying operations that need to check encryption status.
+#ifndef STATX_ATTR_ENCRYPTED
+#define STATX_ATTR_ENCRYPTED 0x00000800 /* File requires key to decrypt in fs */
+#endif
+
/*
* Compatibility macros until these defines make their way into glibc
*/
#define FALLOC_FL_PUNCH_HOLE 0x02
#endif
+/* fscrypt IOCTL flags*/
+// the value of FS_ENCRYPT_FL on most systems is equal to STATX_ATTR_ENCRYPTED
+// to identify encrypted files uniformly,
+// simplifying operations that need to check encryption status.
+#ifndef FS_ENCRYPT_FL
+#define FS_ENCRYPT_FL 0x00000800 /* Encrypted file */
+#endif
+#ifndef FS_IOC_GETFLAGS
+#define FS_IOC_GETFLAGS _IOR('f', 1, int32_t)
+#endif
+
/** ceph_deleg_cb_t: Delegation recalls
*
* Called when there is an outstanding Delegation and there is conflicting
int ceph_set_fscrypt_policy_v2(struct ceph_mount_info *cmount,
int fd, const struct fscrypt_policy_v2 *policy);
+/**
+ * Fill file_attr_out with content of i_flags
+ * @param cmount the ceph mount handle to use.
+ * @param fd open directory file descriptor
+ * @param file_attr_out will have result bits set
+ * @returns zero on success, other returns a negative error code.
+ */
+int get_inode_flags(struct ceph_mount_info *cmount, int fd, int* file_attr_out);
+
/* Low Level */
struct Inode *ceph_ll_get_inode(struct ceph_mount_info *cmount,
vinodeno_t vino);
do_out_buffer(outbl, perf_dump, NULL);
return outbl.length();
}
+
+extern "C" int get_inode_flags(struct ceph_mount_info *cmount, int fd, int* file_attr_out) {
+ if (!cmount->is_mounted())
+ return -CEPHFS_ENOTCONN;
+
+ return cmount->get_client()->get_inode_flags(fd, file_attr_out);
+}
r = ceph_remove_fscrypt_key(cmount, &arg, 1299);
ASSERT_EQ(0, r);
ASSERT_EQ(2, arg.removal_status_flags);
-
ceph_shutdown(cmount);
}
r = ceph_remove_fscrypt_key(cmount, &arg, 1299);
ASSERT_EQ(-ENOKEY, r);
ASSERT_EQ(0, arg.removal_status_flags);
-
ceph_shutdown(cmount);
}
r = ceph_remove_fscrypt_key(cmount, &arg, 1299);
ASSERT_EQ(0, r);
ASSERT_EQ(0, arg.removal_status_flags);
-
ceph_shutdown(cmount);
}
r = ceph_set_fscrypt_policy_v2(cmount, fd, &policy);
ASSERT_EQ(-ENOTDIR, r);
ceph_close(cmount, fd);
-
ceph_shutdown(cmount);
}
ceph_unlink(cmount, src_path.c_str());
ceph_rmdir(cmount, dir_path.c_str());
+ ceph_unmount(cmount);
+ ceph_shutdown(cmount);
+}
+
+TEST(FSCrypt, QuerySEncryptFlag) {
+ struct ceph_fscrypt_key_identifier kid;
+
+ struct ceph_mount_info *cmount;
+ int r = init_mount(&cmount);
+ ASSERT_EQ(0, r);
+
+ //add dir
+ string dir_path = "sencrypt_test_dir1";
+ ceph_mkdir(cmount, dir_path.c_str(), 0777);
+ int fd1 = ceph_open(cmount, dir_path.c_str(), O_DIRECTORY, 0);
+
+ //query S_ENCRYPTED flag with statx on non encrypted directory
+ struct ceph_statx stx;
+ r = ceph_statx(cmount, dir_path.c_str(), &stx,
+ CEPH_STATX_ALL_STATS,
+ 0);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(0, stx.stx_attributes & STATX_ATTR_ENCRYPTED);
+
+ //query S_ENCRYPTED flag with ioctl on non encr directory
+ int file_attr_out;
+ r = get_inode_flags(cmount, fd1, &file_attr_out);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(0, file_attr_out & FS_ENCRYPT_FL);
+
+ // enable fscrypt
+ r = ceph_add_fscrypt_key(cmount, fscrypt_key, sizeof(fscrypt_key), &kid, 1299);
+ ASSERT_EQ(0, r);
+ struct fscrypt_policy_v2 policy;
+ policy.version = 2;
+ policy.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
+ policy.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
+ policy.flags = FSCRYPT_POLICY_FLAGS_PAD_32;
+ memcpy(policy.master_key_identifier, kid.raw, FSCRYPT_KEY_IDENTIFIER_SIZE);
+ r = ceph_set_fscrypt_policy_v2(cmount, fd1, &policy);
+ ASSERT_EQ(0, r);
+
+ //add file to encrypted directory
+ string file_path = "sencrypt_test_file";
+ string path = "";
+ path.append(dir_path);
+ path.append("/");
+ path.append(file_path);
+ int fd2 = ceph_open(cmount, path.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0600);
+ r = ceph_write(cmount, fd2, fscrypt_key, sizeof(fscrypt_key), 0);
+ ASSERT_EQ(32, r);
+ ceph_close(cmount, fd1);
+
+ //query S_ENCRYPTED flag with statx on encrypted file
+ r = ceph_statx(cmount, path.c_str(), &stx,
+ CEPH_STATX_ALL_STATS,
+ 0);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(STATX_ATTR_ENCRYPTED, stx.stx_attributes & STATX_ATTR_ENCRYPTED);
+
+ //query S_ENCRYPTED flag with ioctl on encrypted file
+ r = get_inode_flags(cmount, fd2, &file_attr_out);
+ ASSERT_EQ(FS_ENCRYPT_FL, file_attr_out & FS_ENCRYPT_FL);
+
+ // cleanup
+ ceph_close(cmount, fd2);
+ ceph_unlink(cmount, file_path.c_str());
+ ceph_rmdir(cmount, dir_path.c_str());
+ fscrypt_remove_key_arg arg;
+ generate_remove_key_arg(kid, &arg);
+ r = ceph_remove_fscrypt_key(cmount, &arg, 1299);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(0, arg.removal_status_flags);
ceph_shutdown(cmount);
}