From 0af6d969c9a4666f936a889b3dbec2015d486f58 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Thu, 17 Jun 2021 15:31:44 +0200 Subject: [PATCH] LU-14677 sec: no encryption key migrate/extend/resync/split Allow some layout operations on encrypted files, even when the encryption key is not available: - lfs migrate - lfs mirror extend - lfs mirror resync - lfs mirror verify - lfs mirror split We allow these access patterns to applications that know what they are doing, by using the specific flags O_FILE_ENC or O_FILE_ENC_NONDIRECT. O_FILE_ENC_NONDIRECT has the same semantic as O_FILE_ENC, but it allows access without O_DIRECT. This is reserved for cases where cached data is wiped once IOs are finished, for instance via fadvise. Also update sanity-sec test_40 and test_52 to exercise these access patterns. Signed-off-by: Sebastien Buisson Change-Id: Ieaeee0e5bf7643f18d775fe6daa5e31c2f349f8c --- lustre/doc/lfs-migrate.1 | 5 + lustre/doc/lfs-mirror-split.1 | 10 + lustre/include/lustre/lustreapi.h | 7 + lustre/include/uapi/linux/lustre/lustre_user.h | 4 + lustre/llite/crypto.c | 33 ++++ lustre/llite/dir.c | 12 +- lustre/llite/file.c | 40 ++-- lustre/llite/llite_internal.h | 5 +- lustre/llite/llite_lib.c | 90 ++++++++- lustre/llite/namei.c | 57 +++--- lustre/osc/osc_request.c | 42 ++++- lustre/tests/sanity-sec.sh | 245 ++++++++++++++++++++++++- lustre/utils/lfs.c | 52 ++++-- lustre/utils/liblustreapi_layout.c | 18 +- 14 files changed, 539 insertions(+), 81 deletions(-) diff --git a/lustre/doc/lfs-migrate.1 b/lustre/doc/lfs-migrate.1 index 0725c1d..8c92bf8 100644 --- a/lustre/doc/lfs-migrate.1 +++ b/lustre/doc/lfs-migrate.1 @@ -69,6 +69,11 @@ write operations synchronous. Using the .B --non-direct option uses buffered read/write operations, which may improve migration speed at the cost of more CPU and memory overhead. +.br +This option cannot be used on encrypted files when the encryption key is not +available. It will result in +.B +-ENOKEY. .TP .BR -v , --verbose Print each filename as it is migrated. diff --git a/lustre/doc/lfs-mirror-split.1 b/lustre/doc/lfs-mirror-split.1 index fb74f18..9dd5daa 100644 --- a/lustre/doc/lfs-mirror-split.1 +++ b/lustre/doc/lfs-mirror-split.1 @@ -40,10 +40,20 @@ The pool storing a component contained within a mirror. .TP .BR \-\-destroy\fR|\fB\-d\fR This option indicates the split mirror will be destroyed. +.br +This option is mandatory on encrypted files when the encryption key is not +available. Otherwise operation results in +.B +-ENOKEY. .TP .BR \-f\fR\ <\fInew_file\fR> This option indicates the layout of the split mirror will be stored into <\fInew_file\fR>. +.br +This option cannot be used on encrypted files when the encryption key is not +available. It will result in +.B +-ENOKEY. .SH EXAMPLES .TP .B lfs mirror split --mirror-id 1 /mnt/lustre/file1 diff --git a/lustre/include/lustre/lustreapi.h b/lustre/include/lustre/lustreapi.h index 427cf6a..f70f641 100644 --- a/lustre/include/lustre/lustreapi.h +++ b/lustre/include/lustre/lustreapi.h @@ -148,6 +148,13 @@ struct llapi_stripe_param { #define lsp_tgts lsp_osts +enum { + MIGRATION_NONBLOCK = 0x0001, + MIGRATION_MIRROR = 0x0002, + MIGRATION_NONDIRECT = 0x0004, + MIGRATION_VERBOSE = 0x0008, +}; + __u32 llapi_pattern_to_lov(uint64_t pattern); int llapi_file_open_param(const char *name, int flags, mode_t mode, diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index 5bd1693..5c89b5a 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -682,6 +682,10 @@ struct fsxattr { #define O_LOV_DELAY_CREATE_MASK (O_NOCTTY | FASYNC) #define O_LOV_DELAY_CREATE (O_LOV_DELAY_CREATE_1_8 | \ O_LOV_DELAY_CREATE_MASK) +/* O_FILE_ENC principle is similar, for access to encrypted files without + * the encryption key. + */ +#define O_FILE_ENC (O_NOCTTY | O_NDELAY) #define LL_FILE_IGNORE_LOCK 0x00000001 #define LL_FILE_GROUP_LOCKED 0x00000002 diff --git a/lustre/llite/crypto.c b/lustre/llite/crypto.c index 3f69d39..640d7d9 100644 --- a/lustre/llite/crypto.c +++ b/lustre/llite/crypto.c @@ -114,6 +114,34 @@ static int ll_set_context(struct inode *inode, const void *ctx, size_t len, return ll_set_encflags(inode, (void *)ctx, len, false); } +/** + * ll_file_open_encrypt() - overlay to llcrypt_file_open + * @inode: the inode being opened + * @filp: the struct file being set up + * + * This overlay function is necessary to handle encrypted file open without + * the key. We allow this access pattern to applications that know what they + * are doing, by using the specific flag O_FILE_ENC. + * This flag is only compatible with O_DIRECT IOs, to make sure ciphertext + * data is wiped from page cache once IOs are finished. + */ +int ll_file_open_encrypt(struct inode *inode, struct file *filp) +{ + int rc; + + rc = llcrypt_file_open(inode, filp); + if (likely(rc != -ENOKEY)) + return rc; + + if (rc == -ENOKEY && + (filp->f_flags & O_FILE_ENC) == O_FILE_ENC && + filp->f_flags & O_DIRECT) + /* allow file open with O_FILE_ENC flag */ + rc = 0; + + return rc; +} + void llcrypt_free_ctx(void *encctx, __u32 size) { if (encctx) @@ -173,6 +201,11 @@ int ll_set_encflags(struct inode *inode, void *encctx, __u32 encctxlen, return 0; } +int ll_file_open_encrypt(struct inode *inode, struct file *filp) +{ + return llcrypt_file_open(inode, filp); +} + void llcrypt_free_ctx(void *encctx, __u32 size) { } diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index 95fd4b2..36b708d 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -1796,7 +1796,11 @@ out_rmdir: st.st_uid = body->mbo_uid; st.st_gid = body->mbo_gid; st.st_rdev = body->mbo_rdev; - st.st_size = body->mbo_size; + if (llcrypt_require_key(inode) == -ENOKEY) + st.st_size = round_up(st.st_size, + LUSTRE_ENCRYPTION_UNIT_SIZE); + else + st.st_size = body->mbo_size; st.st_blksize = PAGE_SIZE; st.st_blocks = body->mbo_blocks; st.st_atime = body->mbo_atime; @@ -1821,7 +1825,11 @@ out_rmdir: stx.stx_ino = cl_fid_build_ino(&body->mbo_fid1, sbi->ll_flags & LL_SBI_32BIT_API); - stx.stx_size = body->mbo_size; + if (llcrypt_require_key(inode) == -ENOKEY) + stx.stx_size = round_up(stx.stx_size, + LUSTRE_ENCRYPTION_UNIT_SIZE); + else + stx.stx_size = body->mbo_size; stx.stx_blocks = body->mbo_blocks; stx.stx_atime.tv_sec = body->mbo_atime; stx.stx_ctime.tv_sec = body->mbo_ctime; diff --git a/lustre/llite/file.c b/lustre/llite/file.c index 47fe6d5..ebab2b8 100644 --- a/lustre/llite/file.c +++ b/lustre/llite/file.c @@ -105,7 +105,11 @@ static void ll_prepare_close(struct inode *inode, struct md_op_data *op_data, op_data->op_attr.ia_atime = inode->i_atime; op_data->op_attr.ia_mtime = inode->i_mtime; op_data->op_attr.ia_ctime = inode->i_ctime; - op_data->op_attr.ia_size = i_size_read(inode); + if (llcrypt_require_key(inode) == -ENOKEY && + ll_i2info(inode)->lli_attr_valid & OBD_MD_FLLAZYSIZE) + op_data->op_attr.ia_size = ll_i2info(inode)->lli_lazysize; + else + op_data->op_attr.ia_size = i_size_read(inode); op_data->op_attr.ia_valid |= (ATTR_MODE | ATTR_ATIME | ATTR_ATIME_SET | ATTR_MTIME | ATTR_MTIME_SET | ATTR_CTIME); @@ -798,9 +802,12 @@ int ll_file_open(struct inode *inode, struct file *file) file->private_data = NULL; /* prevent ll_local_open assertion */ if (S_ISREG(inode->i_mode)) { - rc = llcrypt_file_open(inode, file); - if (rc) + rc = ll_file_open_encrypt(inode, file); + if (rc) { + if (it && it->it_disposition) + ll_release_openhandle(file_dentry(file), it); GOTO(out_nofiledata, rc); + } } fd = ll_file_data_get(); @@ -1449,6 +1456,16 @@ int ll_merge_attr(const struct lu_env *env, struct inode *inode) CDEBUG(D_VFSTRACE, DFID" updating i_size %llu\n", PFID(&lli->lli_fid), attr->cat_size); + if (llcrypt_require_key(inode) == -ENOKEY) { + /* Without the key, round up encrypted file size to next + * LUSTRE_ENCRYPTION_UNIT_SIZE. Clear text size is put in + * lli_lazysize for proper file size setting at close time. + */ + lli->lli_attr_valid |= OBD_MD_FLLAZYSIZE; + lli->lli_lazysize = attr->cat_size; + attr->cat_size = round_up(attr->cat_size, + LUSTRE_ENCRYPTION_UNIT_SIZE); + } i_size_write(inode, attr->cat_size); inode->i_blocks = attr->cat_blocks; @@ -4301,6 +4318,12 @@ loff_t ll_lseek(struct file *file, loff_t offset, int whence) cl_env_put(env, &refcheck); + /* Without the key, SEEK_HOLE return value has to be + * rounded up to next LUSTRE_ENCRYPTION_UNIT_SIZE. + */ + if (llcrypt_require_key(inode) == -ENOKEY && whence == SEEK_HOLE) + retval = round_up(retval, LUSTRE_ENCRYPTION_UNIT_SIZE); + RETURN(retval); } @@ -4736,17 +4759,6 @@ int ll_migrate(struct inode *parent, struct file *file, struct lmv_user_md *lum, if (is_root_inode(child_inode)) GOTO(out_iput, rc = -EINVAL); - if (IS_ENCRYPTED(child_inode)) { - rc = llcrypt_get_encryption_info(child_inode); - if (rc) - GOTO(out_iput, rc); - if (!llcrypt_has_encryption_key(child_inode)) { - CDEBUG(D_SEC, "no enc key for "DFID"\n", - PFID(ll_inode2fid(child_inode))); - GOTO(out_iput, rc = -ENOKEY); - } - } - op_data = ll_prep_md_op_data(NULL, parent, NULL, name, namelen, child_inode->i_mode, LUSTRE_OPC_ANY, NULL); if (IS_ERR(op_data)) diff --git a/lustre/llite/llite_internal.h b/lustre/llite/llite_internal.h index 6a86b6d..d9529bd 100644 --- a/lustre/llite/llite_internal.h +++ b/lustre/llite/llite_internal.h @@ -1202,6 +1202,8 @@ void ll_kill_super(struct super_block *sb); struct inode *ll_inode_from_resource_lock(struct ldlm_lock *lock); void ll_dir_clear_lsm_md(struct inode *inode); void ll_clear_inode(struct inode *inode); +int volatile_ref_file(const char *volatile_name, int volatile_len, + struct file **ref_file); int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, enum op_xvalid xvalid, bool hsm_import); int ll_setattr(struct dentry *de, struct iattr *attr); @@ -1715,8 +1717,9 @@ static inline struct pcc_super *ll_info2pccs(struct ll_inode_info *lli) return ll_i2pccs(ll_info2i(lli)); } -#ifdef HAVE_LUSTRE_CRYPTO /* crypto.c */ +int ll_file_open_encrypt(struct inode *inode, struct file *filp); +#ifdef HAVE_LUSTRE_CRYPTO extern const struct llcrypt_operations lustre_cryptops; #endif /* llite/llite_foreign.c */ diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c index d9f0a5c..8c73f4f 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1994,6 +1995,44 @@ putenv: RETURN(rc); } +/** + * Get reference file from volatile file name. + * Volatile file name may look like: + * /LUSTRE_VOLATILE_HDR:::fd= + * where fd is opened descriptor of reference file. + * + * \param[in] volatile_name volatile file name + * \param[in] volatile_len volatile file name length + * \param[out] ref_file pointer to struct file of reference file + * + * \retval 0 on success + * \retval negative errno on failure + */ +int volatile_ref_file(const char *volatile_name, int volatile_len, + struct file **ref_file) +{ + char *p, *q, *fd_str; + int fd, rc; + + p = strnstr(volatile_name, ":fd=", volatile_len); + if (!p || strlen(p + 4) == 0) + return -EINVAL; + + q = strchrnul(p + 4, ':'); + fd_str = kstrndup(p + 4, q - p - 4, GFP_NOFS); + if (!fd_str) + return -ENOMEM; + rc = kstrtouint(fd_str, 10, &fd); + kfree(fd_str); + if (rc) + return -EINVAL; + + *ref_file = fget(fd); + if (!ref_file) + return -EINVAL; + return 0; +} + /* If this inode has objects allocated to it (lsm != NULL), then the OST * object(s) determine the file size and mtime. Otherwise, the MDS will * keep these values until such a time that objects are allocated for it. @@ -2157,6 +2196,55 @@ int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, if (rc) GOTO(out, rc); } + /* If encrypted volatile file without the key, + * we need to fetch size from reference file, + * and set it on OST objects. This happens when + * migrating or extending an encrypted file + * without the key. + */ + if (filename_is_volatile(dentry->d_name.name, + dentry->d_name.len, + NULL) && + llcrypt_require_key(inode) == -ENOKEY) { + struct file *ref_file; + struct inode *ref_inode; + struct ll_inode_info *ref_lli; + struct cl_object *ref_obj; + struct cl_attr ref_attr = { 0 }; + struct lu_env *env; + __u16 refcheck; + + rc = volatile_ref_file( + dentry->d_name.name, + dentry->d_name.len, + &ref_file); + if (rc) + GOTO(out, rc); + + ref_inode = file_inode(ref_file); + if (!ref_inode) { + fput(ref_file); + GOTO(out, rc = -EINVAL); + } + + env = cl_env_get(&refcheck); + if (IS_ERR(env)) + GOTO(out, rc = PTR_ERR(env)); + + ref_lli = ll_i2info(ref_inode); + ref_obj = ref_lli->lli_clob; + cl_object_attr_lock(ref_obj); + rc = cl_object_attr_get(env, ref_obj, + &ref_attr); + cl_object_attr_unlock(ref_obj); + cl_env_put(env, &refcheck); + fput(ref_file); + if (rc) + GOTO(out, rc); + + attr->ia_valid |= ATTR_SIZE; + attr->ia_size = ref_attr.cat_size; + } } rc = cl_setattr_ost(lli->lli_clob, attr, xvalid, flags); } @@ -2215,7 +2303,7 @@ out: LPROC_LL_TRUNC : LPROC_LL_SETATTR, ktime_us_delta(ktime_get(), kstart)); - return rc; + RETURN(rc); } int ll_setattr(struct dentry *de, struct iattr *attr) diff --git a/lustre/llite/namei.c b/lustre/llite/namei.c index 29d4806..4633eda 100644 --- a/lustre/llite/namei.c +++ b/lustre/llite/namei.c @@ -49,7 +49,7 @@ static int ll_create_it(struct inode *dir, struct dentry *dentry, struct lookup_intent *it, void *secctx, __u32 secctxlen, bool encrypt, - void *encctx, __u32 encctxlen); + void *encctx, __u32 encctxlen, unsigned open_flags); /* called from iget5_locked->find_inode() under inode_lock spinlock */ static int ll_test_inode(struct inode *inode, void *opaque) @@ -883,37 +883,20 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry, *secctxlen = 0; } if (it->it_op & IT_CREAT && encrypt) { - /* Volatile file name may look like: - * /LUSTRE_VOLATILE_HDR:::fd= - * where fd is opened descriptor of reference file. - */ if (unlikely(filename_is_volatile(dentry->d_name.name, dentry->d_name.len, NULL))) { + /* get encryption context from reference file */ int ctx_size = LLCRYPT_ENC_CTX_SIZE; struct lustre_sb_info *lsi; struct file *ref_file; struct inode *ref_inode; - char *p, *q, *fd_str; void *ctx; - int fd; - - p = strnstr(dentry->d_name.name, ":fd=", - dentry->d_name.len); - if (!p || strlen(p + 4) == 0) - GOTO(out, retval = ERR_PTR(-EINVAL)); - q = strchrnul(p + 4, ':'); - fd_str = kstrndup(p + 4, q - p - 4, GFP_NOFS); - if (!fd_str) - GOTO(out, retval = ERR_PTR(-ENOMEM)); - rc = kstrtouint(fd_str, 10, &fd); - kfree(fd_str); + rc = volatile_ref_file(dentry->d_name.name, + dentry->d_name.len, + &ref_file); if (rc) - GOTO(inherit, rc = -EINVAL); - - ref_file = fget(fd); - if (!ref_file) - GOTO(inherit, rc = -EINVAL); + GOTO(out, retval = ERR_PTR(rc)); ref_inode = file_inode(ref_file); if (!ref_inode) { @@ -959,7 +942,6 @@ getctx: op_data->op_file_encctx_size); OBD_FREE(ctx, ctx_size); } - } else { inherit: rc = llcrypt_inherit_context(parent, NULL, op_data, @@ -1226,7 +1208,14 @@ static int ll_atomic_open(struct inode *dir, struct dentry *dentry, if (rc) GOTO(out_release, rc); if (open_flags & O_CREAT) { - if (!llcrypt_has_encryption_key(dir)) + /* For migration or mirroring without enc key, we still + * need to be able to create a volatile file. + */ + if (!llcrypt_has_encryption_key(dir) && + (!filename_is_volatile(dentry->d_name.name, + dentry->d_name.len, NULL) || + (open_flags & O_FILE_ENC) != O_FILE_ENC || + !(open_flags & O_DIRECT))) GOTO(out_release, rc = -ENOKEY); encrypt = true; } @@ -1257,7 +1246,8 @@ static int ll_atomic_open(struct inode *dir, struct dentry *dentry, if (it_disposition(it, DISP_OPEN_CREATE)) { /* Dentry instantiated in ll_create_it. */ rc = ll_create_it(dir, dentry, it, secctx, secctxlen, - encrypt, encctx, encctxlen); + encrypt, encctx, encctxlen, + open_flags); ll_security_release_secctx(secctx, secctxlen); llcrypt_free_ctx(encctx, encctxlen); if (rc) { @@ -1383,7 +1373,7 @@ static struct inode *ll_create_node(struct inode *dir, struct lookup_intent *it) static int ll_create_it(struct inode *dir, struct dentry *dentry, struct lookup_intent *it, void *secctx, __u32 secctxlen, bool encrypt, - void *encctx, __u32 encctxlen) + void *encctx, __u32 encctxlen, unsigned open_flags) { struct inode *inode; __u64 bits = 0; @@ -1418,7 +1408,18 @@ static int ll_create_it(struct inode *dir, struct dentry *dentry, d_instantiate(dentry, inode); if (encrypt) { - rc = ll_set_encflags(inode, encctx, encctxlen, true); + bool preload = true; + + /* For migration or mirroring without enc key, we + * create a volatile file without enc context. + */ + if (!llcrypt_has_encryption_key(dir) && + filename_is_volatile(dentry->d_name.name, + dentry->d_name.len, NULL) && + (open_flags & O_FILE_ENC) == O_FILE_ENC && + open_flags & O_DIRECT) + preload = false; + rc = ll_set_encflags(inode, encctx, encctxlen, preload); if (rc) RETURN(rc); } diff --git a/lustre/osc/osc_request.c b/lustre/osc/osc_request.c index 4683801..3f08a43 100644 --- a/lustre/osc/osc_request.c +++ b/lustre/osc/osc_request.c @@ -1424,7 +1424,8 @@ osc_brw_prep_request(int cmd, struct client_obd *cli, struct obdo *oa, if (req == NULL) RETURN(-ENOMEM); - if (opc == OST_WRITE && inode && IS_ENCRYPTED(inode)) { + if (opc == OST_WRITE && inode && IS_ENCRYPTED(inode) && + llcrypt_has_encryption_key(inode)) { for (i = 0; i < page_count; i++) { struct brw_page *pg = pga[i]; struct page *data_page = NULL; @@ -1435,9 +1436,7 @@ osc_brw_prep_request(int cmd, struct client_obd *cli, struct obdo *oa, pgoff_t index_orig; retry_encrypt: - if (nunits & ~LUSTRE_ENCRYPTION_MASK) - nunits = (nunits & LUSTRE_ENCRYPTION_MASK) + - LUSTRE_ENCRYPTION_UNIT_SIZE; + nunits = round_up(nunits, LUSTRE_ENCRYPTION_UNIT_SIZE); /* The page can already be locked when we arrive here. * This is possible when cl_page_assume/vvp_page_assume * is stuck on wait_on_page_writeback with page lock @@ -1494,14 +1493,38 @@ retry_encrypt: pg->bp_off_diff = pg->off & ~PAGE_MASK; pg->off = pg->off & PAGE_MASK; } - } else if (opc == OST_READ && inode && IS_ENCRYPTED(inode)) { + } else if (opc == OST_WRITE && inode && IS_ENCRYPTED(inode)) { + struct osc_async_page *oap = brw_page2oap(pga[0]); + struct cl_page *clpage = oap2cl_page(oap); + struct cl_object *clobj = clpage->cp_obj; + struct cl_attr attr = { 0 }; + struct lu_env *env; + __u16 refcheck; + + env = cl_env_get(&refcheck); + if (IS_ERR(env)) { + rc = PTR_ERR(env); + ptlrpc_request_free(req); + RETURN(rc); + } + + cl_object_attr_lock(clobj); + rc = cl_object_attr_get(env, clobj, &attr); + cl_object_attr_unlock(clobj); + cl_env_put(env, &refcheck); + if (rc != 0) { + ptlrpc_request_free(req); + RETURN(rc); + } + if (attr.cat_size) + oa->o_size = attr.cat_size; + } else if (opc == OST_READ && inode && IS_ENCRYPTED(inode) && + llcrypt_has_encryption_key(inode)) { for (i = 0; i < page_count; i++) { struct brw_page *pg = pga[i]; u32 nunits = (pg->off & ~PAGE_MASK) + pg->count; - if (nunits & ~LUSTRE_ENCRYPTION_MASK) - nunits = (nunits & LUSTRE_ENCRYPTION_MASK) + - LUSTRE_ENCRYPTION_UNIT_SIZE; + nunits = round_up(nunits, LUSTRE_ENCRYPTION_UNIT_SIZE); /* count/off are forced to cover the whole encryption * unit size so that all encrypted data is stored on the * OST, so adjust bp_{count,off}_diff for the size of @@ -1527,7 +1550,8 @@ retry_encrypt: for (i = 0; i < page_count; i++) { short_io_size += pga[i]->count; - if (!inode || !IS_ENCRYPTED(inode)) { + if (!inode || !IS_ENCRYPTED(inode) || + !llcrypt_has_encryption_key(inode)) { pga[i]->bp_count_diff = 0; pga[i]->bp_off_diff = 0; } diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index c04895b..45709dd 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -2676,6 +2676,18 @@ test_35() { } run_test 35 "Check permissions when accessing changelogs" +setup_dummy_key() { + local mode='\x00\x00\x00\x00' + local raw="$(printf ""\\\\x%02x"" {0..63})" + local size + local key + + [[ $(lscpu) =~ Byte\ Order.*Little ]] && size='\x40\x00\x00\x00' || + size='\x00\x00\x00\x40' + key="${mode}${raw}${size}" + echo -n -e "${key}" | keyctl padd logon fscrypt:4242424242424242 @s +} + setup_for_enc_tests() { # remount client with test_dummy_encryption option if is_mounted $MOUNT; then @@ -2689,6 +2701,8 @@ setup_for_enc_tests() { } cleanup_for_enc_tests() { + rm -f $* + # remount client normally if is_mounted $MOUNT; then umount_client $MOUNT || error "umount $MOUNT failed" @@ -2887,6 +2901,10 @@ test_40() { local tmpfile=$TMP/abc local tmpfile2=$TMP/abc2 local seek + local filesz + #define LUSTRE_ENCRYPTION_UNIT_SIZE (1 << 12) + local UNIT_SIZE=$((1 << 12)) + local scrambledfile $LCTL get_param mdc.*.import | grep -q client_encryption || skip "client encryption not supported" @@ -3011,6 +3029,19 @@ test_40() { cmp -bl $tmpfile $testfile || error "file $testfile is corrupted on server (5)" + filesz=$(stat --format=%s $testfile) + filesz=$(((filesz+UNIT_SIZE-1)/UNIT_SIZE * UNIT_SIZE)) + + sync ; echo 3 > /proc/sys/vm/drop_caches + + # remove fscrypt key from keyring + keyctl revoke $(keyctl show | awk '$7 ~ "^fscrypt:" {print $1}') + keyctl reap + + scrambledfile=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type f) + [ $(stat --format=%s $scrambledfile) -eq $filesz ] || + error "file size without key should be rounded up" + rm -f $tmpfile } run_test 40 "exercise size of encrypted file" @@ -3413,6 +3444,7 @@ test_47() { mrename $testfile2 $tmpfile && error "rename from encrypted to unencrypted dir should fail" + rm -f $testfile2 touch $tmpfile dd if=/dev/zero of=$testfile bs=512K count=1 @@ -3625,32 +3657,42 @@ test_49() { mkdir $dirname trace_cmd stat $dirname - trace_cmd touch $dirname/f1 + trace_cmd echo a > $dirname/f1 + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd stat $dirname/f1 + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd cat $dirname/f1 dd if=/dev/zero of=$dirname/f1 bs=1M count=10 conv=fsync + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd $TRUNCATE $dirname/f1 10240 trace_cmd $LFS setstripe -E -1 -S 4M $dirname/f2 + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd $LFS migrate -E -1 -S 256K $dirname/f2 if [[ $MDSCOUNT -gt 1 ]]; then trace_cmd $LFS setdirstripe -i 1 $dirname/d2 + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd $LFS migrate -m 0 $dirname/d2 - touch $dirname/d2/subf + echo b > $dirname/d2/subf + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches # migrate a non-empty encrypted dir trace_cmd $LFS migrate -m 1 $dirname/d2 $LFS setdirstripe -i 1 -c 1 $dirname/d3 dirname=$dirname/d3/subdir mkdir $dirname - + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd stat $dirname - trace_cmd touch $dirname/f1 + trace_cmd echo c > $dirname/f1 + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd stat $dirname/f1 + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd cat $dirname/f1 dd if=/dev/zero of=$dirname/f1 bs=1M count=10 conv=fsync + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd $TRUNCATE $dirname/f1 10240 trace_cmd $LFS setstripe -E -1 -S 4M $dirname/f2 + sync ; sync ; echo 3 > /proc/sys/vm/drop_caches trace_cmd $LFS migrate -E -1 -S 256K $dirname/f2 else skip_noexit "2nd part needs >= 2 MDTs" @@ -3848,7 +3890,7 @@ test_52() { [[ $OSTCOUNT -lt 2 ]] && skip_env "needs >= 2 OSTs" - stack_trap cleanup_for_enc_tests EXIT + stack_trap "cleanup_for_enc_tests $tmpfile $mirror1 $mirror2" EXIT setup_for_enc_tests dd if=/dev/urandom of=$tmpfile bs=5000 count=1 conv=fsync @@ -3947,8 +3989,6 @@ test_52() { cancel_lru_locks cmp -bl $tmpfile $testfile || error "extended/split file is corrupted" - - rm -f $tmpfile $mirror1 $mirror2 } run_test 52 "Mirrored encrypted file" @@ -4302,6 +4342,197 @@ test_57() { } run_test 57 "security.c xattr protection" +insert_enc_key() { + sync ; echo 3 > /proc/sys/vm/drop_caches + setup_dummy_key +} + +remove_env_key() { + cancel_lru_locks + sync ; echo 3 > /proc/sys/vm/drop_caches + keyctl revoke $(keyctl show | awk '$7 ~ "^fscrypt:" {print $1}') + keyctl reap +} + +verify_mirror() { + local mirror1=$TMP/$tfile.mirror1 + local mirror2=$TMP/$tfile.mirror2 + local testfile=$1 + local reffile=$2 + + $LFS mirror verify -v $testfile || + error "verifying mirror failed" + + $LFS mirror read -N 1 -o $mirror1 $testfile || + error "read from mirror 1 failed" + cmp -bl $reffile $mirror1 || + error "corruption of mirror 1" + $LFS mirror read -N 2 -o $mirror2 $testfile || + error "read from mirror 2 failed" + cmp -bl $reffile $mirror2 || + error "corruption of mirror 2" +} + +test_58a() { + local testfile=$DIR/$tdir/$tfile + local tmpfile=$TMP/$tfile + local mirror1=$TMP/$tfile.mirror1 + local mirror2=$TMP/$tfile.mirror2 + local scrambledfile + + $LCTL get_param mdc.*.import | grep -q client_encryption || + skip "client encryption not supported" + + mount.lustre --help |& grep -q "test_dummy_encryption:" || + skip "need dummy encryption support" + + [[ $OSTCOUNT -lt 2 ]] && skip_env "needs >= 2 OSTs" + + stack_trap "cleanup_for_enc_tests $tmpfile $mirror1 $mirror2" EXIT + setup_for_enc_tests + + dd if=/dev/urandom of=$tmpfile bs=5000 count=1 conv=fsync + + $LFS mirror create -N -i0 -N -i1 $testfile || + error "could not create mirror" + dd if=$tmpfile of=$testfile bs=5000 count=1 conv=fsync || + error "could not write to $testfile" + $LFS getstripe $testfile + + # now, without the key + remove_env_key + scrambledfile=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type f) + $LFS mirror resync $scrambledfile || + error "could not resync mirror" + + $LFS mirror verify -v $scrambledfile || + error "mirror verify failed" + + $LFS mirror read -N 1 -o $mirror1 $scrambledfile && + error "read from mirror should fail" + + # now, with the key + insert_enc_key + verify_mirror $testfile $tmpfile +} +run_test 58a "mirror resync of encrypted files without key" + +test_58b() { + local testfile=$DIR/$tdir/$tfile + local tmpfile=$TMP/$tfile + local mirror1=$TMP/$tfile.mirror1 + local mirror2=$TMP/$tfile.mirror2 + local scrambledfile + + $LCTL get_param mdc.*.import | grep -q client_encryption || + skip "client encryption not supported" + + mount.lustre --help |& grep -q "test_dummy_encryption:" || + skip "need dummy encryption support" + + [[ $OSTCOUNT -lt 2 ]] && skip_env "needs >= 2 OSTs" + + stack_trap "cleanup_for_enc_tests $tmpfile $mirror1 $mirror2" EXIT + setup_for_enc_tests + + tr '\0' '2' < /dev/zero | + dd of=$tmpfile bs=1 count=9000 conv=fsync + + $LFS setstripe -c1 -i0 $testfile + dd if=$tmpfile of=$testfile bs=9000 count=1 conv=fsync || + error "write to $testfile failed" + $LFS getstripe $testfile + + # now, without the key + remove_env_key + scrambledfile=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type f) + $LFS migrate -i1 $scrambledfile || + error "migrate $scrambledfile failed" + $LFS getstripe $scrambledfile + stripe=$($LFS getstripe -i $scrambledfile) + [ $stripe -eq 1 ] || error "migrate file $scrambledfile failed" + cancel_lru_locks + + # now, with the key + insert_enc_key + cmp -bl $tmpfile $testfile || + error "migrated file is corrupted" + + # now, without the key + remove_env_key + $LFS mirror extend -N -i0 $scrambledfile || + error "mirror extend $scrambledfile failed (1)" + $LFS getstripe $scrambledfile + mirror_count=$($LFS getstripe -N $scrambledfile) + [ $mirror_count -eq 2 ] || + error "mirror extend file $scrambledfile failed (2)" + stripe=$($LFS getstripe --mirror-id=1 -i $scrambledfile) + [ $stripe -eq 1 ] || + error "mirror extend file $scrambledfile failed (3)" + stripe=$($LFS getstripe --mirror-id=2 -i $scrambledfile) + [ $stripe -eq 0 ] || + error "mirror extend file $scrambledfile failed (4)" + + $LFS mirror verify -v $scrambledfile || + error "mirror verify failed" + + # now, with the key + insert_enc_key + verify_mirror $testfile $tmpfile + + # now, without the key + remove_env_key + $LFS mirror split --mirror-id 1 -d $scrambledfile || + error "mirror split file $scrambledfile failed (1)" + $LFS getstripe $scrambledfile + mirror_count=$($LFS getstripe -N $scrambledfile) + [ $mirror_count -eq 1 ] || + error "mirror split file $scrambledfile failed (2)" + stripe=$($LFS getstripe --mirror-id=1 -i $scrambledfile) + [ -z "$stripe" ] || error "mirror split file $scrambledfile failed (3)" + stripe=$($LFS getstripe --mirror-id=2 -i $scrambledfile) + [ $stripe -eq 0 ] || error "mirror split file $scrambledfile failed (4)" + + # now, with the key + insert_enc_key + cancel_lru_locks + cmp -bl $tmpfile $testfile || + error "extended/split file is corrupted" +} +run_test 58b "migrate/extend/split of encrypted files without key" + +test_58c() { + local dirname=$DIR/$tdir/subdir + local scrambleddir + + $LCTL get_param mdc.*.import | grep -q client_encryption || + skip "client encryption not supported" + + mount.lustre --help |& grep -q "test_dummy_encryption:" || + skip "need dummy encryption support" + + [[ $MDSCOUNT -ge 2 ]] || skip_env "needs >= 2 MDTs" + + stack_trap "cleanup_for_enc_tests" EXIT + setup_for_enc_tests + + $LFS setdirstripe -i 0 $dirname + echo b > $dirname/subf + + # now, without the key + remove_env_key + scrambleddir=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type d) + + # migrate a non-empty encrypted dir + $LFS migrate -m 1 $scrambleddir || + error "migrate $scrambleddir between MDTs failed (1)" + + stripe=$($LFS getdirstripe -i $scrambleddir) + [ $stripe -eq 1 ] || + error "migrate $scrambleddir between MDTs failed (2)" +} +run_test 58c "MDT migrate of encrypted files without key" + log "cleanup: ======================================================" sec_unsetup() { diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 7db2850..d175a67 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -765,13 +765,6 @@ static uint32_t check_foreign_type_name(const char *foreign_type_name) static const char *error_loc = "syserror"; -enum { - MIGRATION_NONBLOCK = 0x0001, - MIGRATION_MIRROR = 0x0002, - MIGRATION_NONDIRECT = 0x0004, - MIGRATION_VERBOSE = 0x0008, -}; - static int migrate_open_files(const char *name, __u64 migration_flags, const struct llapi_stripe_param *param, @@ -813,15 +806,24 @@ migrate_open_files(const char *name, __u64 migration_flags, *ptr = '\0'; } - /* open file, direct io */ /* even if the file is only read, WR mode is nedeed to allow * layout swap on fd */ - rflags = O_RDWR; + /* Allow migrating even without the key on encrypted files */ + rflags = O_RDWR | O_FILE_ENC; if (!(migration_flags & MIGRATION_NONDIRECT)) rflags |= O_DIRECT; +source_open: fd = open(name, rflags); if (fd < 0) { + /* If encrypted file without the key, + * retry mirror extend in O_DIRECT. + */ + if (errno == ENOKEY && !(rflags & O_DIRECT) && + migration_flags & MIGRATION_MIRROR) { + rflags |= O_DIRECT; + goto source_open; + } rc = -errno; error_loc = "cannot open source file"; return rc; @@ -834,9 +836,13 @@ migrate_open_files(const char *name, __u64 migration_flags, } do { - int open_flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW; + int open_flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | + /* Allow migrating without the key on encrypted files */ + O_FILE_ENC; mode_t open_mode = S_IRUSR | S_IWUSR; + if (rflags & O_DIRECT) + open_flags |= O_DIRECT; random_value = random(); rc = snprintf(volatile_file, sizeof(volatile_file), "%s/%s:%.4X:%.4X:fd=%.2d", parent, @@ -1964,8 +1970,8 @@ static int mirror_extend_layout(char *name, struct llapi_layout *m_layout, } } llapi_layout_comp_flags_set(m_layout, flags); - rc = migrate_open_files(name, MIGRATION_NONDIRECT, NULL, m_layout, &fd, - &fdv); + rc = migrate_open_files(name, MIGRATION_NONDIRECT | MIGRATION_MIRROR, + NULL, m_layout, &fd, &fdv); if (rc < 0) goto out; @@ -2278,7 +2284,15 @@ static int mirror_split(const char *fname, __u32 id, const char *pool, } } - fd = open(fname, O_RDWR); + if (!victim_file && mflags & MF_DESTROY) + /* Allow mirror split even without the key on encrypted files, + * and in this case of a 'split -d', open file with O_DIRECT + * (no IOs will be done). + */ + fd = open(fname, O_RDWR | O_DIRECT | O_FILE_ENC); + else + fd = open(fname, O_RDWR); + if (fd < 0) { fprintf(stderr, "error %s: open file '%s' failed: %s\n", @@ -2362,7 +2376,11 @@ again: open_flags = O_RDWR | (O_LOV_DELAY_CREATE & ~O_ACCMODE) | - O_CREAT | O_EXCL | O_NOFOLLOW; + O_CREAT | O_EXCL | O_NOFOLLOW | + /* O_DIRECT for mirror split -d */ + O_DIRECT | + /* Allow split without the key */ + O_FILE_ENC; fdv = open(file_path, open_flags, S_IRUSR | S_IWUSR); if (fdv < 0) @@ -10597,7 +10615,8 @@ int lfs_mirror_resync_file(const char *fname, struct ll_ioc_lease *ioc, goto error; } - fd = open(fname, O_DIRECT | O_RDWR); + /* Allow mirror resync even without the key on encrypted files */ + fd = open(fname, O_DIRECT | O_RDWR | O_FILE_ENC); if (fd < 0) { fprintf(stderr, "%s: cannot open '%s': %s.\n", progname, fname, strerror(errno)); @@ -11879,7 +11898,8 @@ int lfs_mirror_verify_file(const char *fname, __u16 *mirror_ids, int ids_nr, goto error; } - fd = open(fname, O_DIRECT | O_RDONLY); + /* Allow mirror verify even without the key on encrypted files */ + fd = open(fname, O_DIRECT | O_RDONLY | O_FILE_ENC); if (fd < 0) { fprintf(stderr, "%s: cannot open '%s': %s.\n", progname, fname, strerror(errno)); diff --git a/lustre/utils/liblustreapi_layout.c b/lustre/utils/liblustreapi_layout.c index aca20cc..aa48027 100644 --- a/lustre/utils/liblustreapi_layout.c +++ b/lustre/utils/liblustreapi_layout.c @@ -1069,15 +1069,27 @@ struct llapi_layout *llapi_layout_get_by_path(const char *path, enum llapi_layout_get_flags flags) { struct llapi_layout *layout = NULL; + bool failed = false; + int open_flags; int fd; int tmp; if (flags & LLAPI_LAYOUT_GET_EXPECTED) return llapi_layout_expected(path); - fd = open(path, O_RDONLY); - if (fd < 0) - return layout; + /* Always get layout in O_DIRECT */ + /* Allow fetching layout even without the key on encrypted files */ + open_flags = O_RDONLY | O_DIRECT | O_FILE_ENC; +do_open: + fd = open(path, open_flags); + if (fd < 0) { + if (errno != EINVAL || failed) + return layout; + /* EINVAL is because a directory cannot be opened in O_DIRECT */ + open_flags = O_RDONLY | O_FILE_ENC; + failed = true; + goto do_open; + } layout = llapi_layout_get_by_fd(fd, flags); tmp = errno; -- 1.8.3.1