From 09c558d16f0a80f436522edde89367c088fe2055 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Fri, 28 May 2021 18:11:53 +0200 Subject: [PATCH] LU-14677 sec: migrate/extend/split on encrypted file lfs migrate/extend/split makes use of volatile files to swap layouts. When operation is carried out on an encrypted file, the volatile file must be assigned the same encryption context as the original file, so that data moved/copied to different OSTs is identical to the original file's. Also update sanity-sec test_52 to exercise these commands. Signed-off-by: Sebastien Buisson Change-Id: I3878b5e9e6d3738dfee0ce0f89a3646e6a7b976f Reviewed-on: https://review.whamcloud.com/43878 Reviewed-by: Andreas Dilger Reviewed-by: Bobi Jam Tested-by: jenkins Tested-by: Maloo Reviewed-by: Oleg Drokin --- lustre/include/lustre/lustreapi.h | 1 + lustre/include/lustre_crypto.h | 3 ++ lustre/llite/llite_lib.c | 6 +-- lustre/llite/namei.c | 88 +++++++++++++++++++++++++++++++++++++-- lustre/tests/sanity-sec.sh | 62 +++++++++++++++++++++++++++ lustre/utils/lfs.c | 56 +++++++++++++++++++++++-- lustre/utils/liblustreapi.c | 12 ++++++ 7 files changed, 218 insertions(+), 10 deletions(-) diff --git a/lustre/include/lustre/lustreapi.h b/lustre/include/lustre/lustreapi.h index a938900..427cf6a 100644 --- a/lustre/include/lustre/lustreapi.h +++ b/lustre/include/lustre/lustreapi.h @@ -152,6 +152,7 @@ __u32 llapi_pattern_to_lov(uint64_t pattern); int llapi_file_open_param(const char *name, int flags, mode_t mode, const struct llapi_stripe_param *param); +int llapi_file_is_encrypted(int fd); int llapi_file_create_foreign(const char *name, mode_t mode, __u32 type, __u32 flags, char *foreign_lov); int llapi_file_create(const char *name, unsigned long long stripe_size, diff --git a/lustre/include/lustre_crypto.h b/lustre/include/lustre_crypto.h index f71ddf8..9f76f87 100644 --- a/lustre/include/lustre_crypto.h +++ b/lustre/include/lustre_crypto.h @@ -36,6 +36,9 @@ void llcrypt_free_ctx(void *encctx, __u32 size); bool ll_sbi_has_test_dummy_encryption(struct ll_sb_info *sbi); bool ll_sbi_has_encrypt(struct ll_sb_info *sbi); void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set); +/* sizeof(struct fscrypt_context_v2) = 40 */ +#define LLCRYPT_ENC_CTX_SIZE 40 + /* Encoding/decoding routines inspired from yEnc principles. * We just take care of a few critical characters: diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c index 75ee06a..d0597f2 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -2147,8 +2147,7 @@ int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, * it is necessary due to possible time * de-synchronization between MDT inode and OST objects */ - if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode) && - attr->ia_valid & ATTR_SIZE) { + if (S_ISREG(inode->i_mode) && IS_ENCRYPTED(inode)) { xvalid |= OP_XVALID_FLAGS; flags = LUSTRE_ENCRYPT_FL; /* Call to ll_io_zero_page is not necessary if @@ -2157,7 +2156,8 @@ int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, * In case of Direct IO, all we need is to set * new size. */ - if (attr->ia_size & ~PAGE_MASK && + if (attr->ia_valid & ATTR_SIZE && + attr->ia_size & ~PAGE_MASK && !(attr->ia_valid & ATTR_FILE && attr->ia_file->f_flags & O_DIRECT)) { pgoff_t offset = diff --git a/lustre/llite/namei.c b/lustre/llite/namei.c index cbe3aa6..29d4806 100644 --- a/lustre/llite/namei.c +++ b/lustre/llite/namei.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -882,9 +883,90 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry, *secctxlen = 0; } if (it->it_op & IT_CREAT && encrypt) { - rc = llcrypt_inherit_context(parent, NULL, op_data, false); - if (rc) - GOTO(out, retval = ERR_PTR(rc)); + /* 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))) { + 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); + if (rc) + GOTO(inherit, rc = -EINVAL); + + ref_file = fget(fd); + if (!ref_file) + GOTO(inherit, rc = -EINVAL); + + ref_inode = file_inode(ref_file); + if (!ref_inode) { + fput(ref_file); + GOTO(inherit, rc = -EINVAL); + } + + lsi = s2lsi(ref_inode->i_sb); + +getctx: + OBD_ALLOC(ctx, ctx_size); + if (!ctx) + GOTO(out, retval = ERR_PTR(-ENOMEM)); + +#ifdef CONFIG_LL_ENCRYPTION + rc = lsi->lsi_cop->get_context(ref_inode, + ctx, ctx_size); +#else + rc = -ENODATA; +#endif + if (rc == -ERANGE) { + OBD_FREE(ctx, ctx_size); + ctx_size *= 2; + goto getctx; + } + fput(ref_file); + if (rc < 0) { + OBD_FREE(ctx, ctx_size); + GOTO(inherit, rc); + } + + op_data->op_file_encctx_size = rc; + if (rc == ctx_size) { + op_data->op_file_encctx = ctx; + } else { + OBD_ALLOC(op_data->op_file_encctx, + op_data->op_file_encctx_size); + if (!op_data->op_file_encctx) { + OBD_FREE(ctx, ctx_size); + GOTO(out, retval = ERR_PTR(-ENOMEM)); + } + memcpy(op_data->op_file_encctx, ctx, + op_data->op_file_encctx_size); + OBD_FREE(ctx, ctx_size); + } + + } else { +inherit: + rc = llcrypt_inherit_context(parent, NULL, op_data, + false); + if (rc) + GOTO(out, retval = ERR_PTR(rc)); + } if (encctx != NULL) *encctx = op_data->op_file_encctx; if (encctxlen != NULL) diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index 9744b9a..d7221e2 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -3821,6 +3821,68 @@ test_52() { $LFS mirror verify -v $testfile && error "mirrors should be different" + rm -f $testfile $mirror1 $mirror2 + + $LFS setstripe -c1 -i0 $testfile + dd if=$tmpfile of=$testfile bs=9000 count=1 conv=fsync || + error "write to $testfile failed" + $LFS getstripe $testfile + cancel_lru_locks + + $LFS migrate -i1 $testfile || + error "migrate $testfile failed" + $LFS getstripe $testfile + stripe=$($LFS getstripe -i $testfile) + [ $stripe -eq 1 ] || error "migrate file $testfile failed" + + cancel_lru_locks + cmp -bl $tmpfile $testfile || + error "migrated file is corrupted" + + $LFS mirror extend -N -i0 $testfile || + error "mirror extend $testfile failed" + $LFS getstripe $testfile + mirror_count=$($LFS getstripe -N $testfile) + [ $mirror_count -eq 2 ] || + error "mirror extend file $testfile failed (1)" + stripe=$($LFS getstripe --mirror-id=1 -i $testfile) + [ $stripe -eq 1 ] || error "mirror extend file $testfile failed (2)" + stripe=$($LFS getstripe --mirror-id=2 -i $testfile) + [ $stripe -eq 0 ] || error "mirror extend file $testfile failed (3)" + + cancel_lru_locks + $LFS mirror verify -v $testfile || + error "mirror verify failed" + $LFS mirror read -N 1 -o $mirror1 $testfile || + error "read from mirror 1 failed" + cmp -bl $tmpfile $mirror1 || + error "corruption of mirror 1" + $LFS mirror read -N 2 -o $mirror2 $testfile || + error "read from mirror 2 failed" + cmp -bl $tmpfile $mirror2 || + error "corruption of mirror 2" + + $LFS mirror split --mirror-id 1 -f ${testfile}.mirror $testfile && + error "mirror split -f should fail" + + $LFS mirror split --mirror-id 1 $testfile && + error "mirror split without -d should fail" + + $LFS mirror split --mirror-id 1 -d $testfile || + error "mirror split failed" + $LFS getstripe $testfile + mirror_count=$($LFS getstripe -N $testfile) + [ $mirror_count -eq 1 ] || + error "mirror split file $testfile failed (1)" + stripe=$($LFS getstripe --mirror-id=1 -i $testfile) + [ -z "$stripe" ] || error "mirror extend file $testfile failed (2)" + stripe=$($LFS getstripe --mirror-id=2 -i $testfile) + [ $stripe -eq 0 ] || error "mirror extend file $testfile failed (3)" + + cancel_lru_locks + cmp -bl $tmpfile $testfile || + error "extended/split file is corrupted" + rm -f $tmpfile $mirror1 $mirror2 } run_test 52 "Mirrored encrypted file" diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 53b62f6..7db2850 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -839,8 +839,9 @@ migrate_open_files(const char *name, __u64 migration_flags, random_value = random(); rc = snprintf(volatile_file, sizeof(volatile_file), - "%s/%s:%.4X:%.4X", parent, LUSTRE_VOLATILE_HDR, - mdt_index, random_value); + "%s/%s:%.4X:%.4X:fd=%.2d", parent, + LUSTRE_VOLATILE_HDR, mdt_index, + random_value, fd); if (rc >= sizeof(volatile_file)) { rc = -ENAMETOOLONG; break; @@ -2202,6 +2203,7 @@ static int mirror_split(const char *fname, __u32 id, const char *pool, int mdt_index; int fd, fdv; bool purge = true; /* delete mirror by setting fdv=fd */ + bool is_encrypted; int rc; if (victim_file && (strcmp(fname, victim_file) == 0)) { @@ -2313,10 +2315,22 @@ static int mirror_split(const char *fname, __u32 id, const char *pool, goto close_fd; } + rc = llapi_file_is_encrypted(fd); + if (rc < 0) { + fprintf(stderr, "%s: cannot get flags of '%s': %d\n", + progname, fname, rc); + goto close_fd; + } + is_encrypted = rc; + again: if (!victim_file) { /* use a temp file to store the splitted layout */ if (mflags & MF_DESTROY) { + char file_path[PATH_MAX]; + unsigned int rnumber; + int open_flags; + if (last_non_stale_mirror(mirror_id, layout)) { rc = -EUCLEAN; fprintf(stderr, @@ -2333,16 +2347,50 @@ again: * try the old way to delete mirror using * volatile file. */ - fdv = llapi_create_volatile_idx(parent, - mdt_index, O_LOV_DELAY_CREATE); + do { + rnumber = random(); + rc = snprintf(file_path, + sizeof(file_path), + "%s/" LUSTRE_VOLATILE_HDR ":%.4X:%.4X:fd=%.2d", + parent, mdt_index, + rnumber, fd); + if (rc < 0 || + rc >= sizeof(file_path)) { + fdv = -ENAMETOOLONG; + break; + } + + open_flags = O_RDWR | + (O_LOV_DELAY_CREATE & ~O_ACCMODE) | + O_CREAT | O_EXCL | O_NOFOLLOW; + fdv = open(file_path, open_flags, + S_IRUSR | S_IWUSR); + if (fdv < 0) + rc = -errno; + } while (fdv < 0 && rc == -EEXIST); } } else { + if (is_encrypted) { + rc = -1; + fprintf(stderr, + "error %s: not permitted on encrypted file '%s': %d\n", + progname, fname, rc); + goto close_fd; + } + snprintf(victim, sizeof(victim), "%s.mirror~%u", fname, mirror_id); fdv = open(victim, flags, S_IRUSR | S_IWUSR); } } else { /* user specified victim file */ + if (is_encrypted) { + rc = -1; + fprintf(stderr, + "error %s: not permitted on encrypted file '%s': %d\n", + progname, fname, rc); + goto close_fd; + } fdv = open(victim_file, flags, S_IRUSR | S_IWUSR); } diff --git a/lustre/utils/liblustreapi.c b/lustre/utils/liblustreapi.c index 1371a12..88d39bf 100644 --- a/lustre/utils/liblustreapi.c +++ b/lustre/utils/liblustreapi.c @@ -776,6 +776,18 @@ retry_open: return fd; } +int llapi_file_is_encrypted(int fd) +{ + unsigned long flags; + int rc; + + rc = ioctl(fd, FS_IOC_GETFLAGS, &flags); + if (rc == -1) + return -errno; + + return !!(flags & LUSTRE_ENCRYPT_FL); +} + int llapi_file_open_pool(const char *name, int flags, int mode, unsigned long long stripe_size, int stripe_offset, int stripe_count, int stripe_pattern, char *pool_name) -- 1.8.3.1