.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.
.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
#define lsp_tgts lsp_osts
+enum {
+ LLAPI_MIGRATION_NONBLOCK = 0x0001,
+ LLAPI_MIGRATION_MIRROR = 0x0002,
+ LLAPI_MIGRATION_NONDIRECT = 0x0004,
+ LLAPI_MIGRATION_VERBOSE = 0x0008,
+};
+
__u32 llapi_pattern_to_lov(uint64_t pattern);
int llapi_file_open_param(const char *name, int flags, mode_t mode,
LUSTRE_OPC_ANY,
LUSTRE_OPC_LOOKUP,
LUSTRE_OPC_OPEN,
- LUSTRE_OPC_MIGR,
};
/**
#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 to O_LOV_DELAY_CREATE above,
+ * 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
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 when we have O_DIRECT */
+ rc = 0;
+
+ return rc;
+}
+
void llcrypt_free_ctx(void *encctx, __u32 size)
{
if (encctx)
fid->f_ver = 0;
}
rc = llcrypt_setup_filename(dir, &dname, lookup, fname);
+ if (rc == -ENOENT && lookup &&
+ !llcrypt_has_encryption_key(dir) &&
+ unlikely(filename_is_volatile(iname->name, iname->len, NULL))) {
+ /* For purpose of migration or mirroring without enc key, we
+ * allow lookup of volatile file without enc context.
+ */
+ memset(fname, 0, sizeof(struct llcrypt_name));
+ fname->disk_name.name = (unsigned char *)iname->name;
+ fname->disk_name.len = iname->len;
+ rc = 0;
+ }
if (rc)
return rc;
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)
{
}
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;
stx.stx_mode = body->mbo_mode;
stx.stx_ino = cl_fid_build_ino(&body->mbo_fid1,
api32);
- 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;
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);
+ /* In case of encrypted file without the key, visible size was rounded
+ * up to next LUSTRE_ENCRYPTION_UNIT_SIZE, and clear text size was
+ * stored into lli_lazysize in ll_merge_attr(), so set proper file size
+ * now that we are closing.
+ */
+ 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);
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();
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;
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);
}
__u64 data_version = 0;
size_t namelen = strlen(name);
int lumlen = lmv_user_md_size(lum->lum_stripe_count, lum->lum_magic);
+ bool oldformat = false;
int rc;
ENTRY;
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);
- }
- if (unlikely(!llcrypt_policy_has_filename_enc(child_inode))) {
- CDEBUG(D_SEC,
- "cannot migrate old format encrypted "DFID", please move to new enc dir first\n",
- PFID(ll_inode2fid(child_inode)));
- GOTO(out_iput, rc = -EUCLEAN);
- }
+ if (IS_ENCRYPTED(parent)) {
+ if (unlikely(!llcrypt_policy_has_filename_enc(parent)))
+ oldformat = true;
+ } else if (IS_ENCRYPTED(child_inode) &&
+ unlikely(!llcrypt_policy_has_filename_enc(child_inode))) {
+ oldformat = true;
+ }
+ if (unlikely(oldformat)) {
+ CDEBUG(D_SEC,
+ "cannot migrate old format encrypted "DFID", please move to new enc dir first\n",
+ PFID(ll_inode2fid(child_inode)));
+ GOTO(out_iput, rc = -EUCLEAN);
}
op_data = ll_prep_md_op_data(NULL, parent, NULL, name, namelen,
- child_inode->i_mode, LUSTRE_OPC_MIGR,
- NULL);
+ child_inode->i_mode, LUSTRE_OPC_ANY, NULL);
if (IS_ERR(op_data))
GOTO(out_iput, rc = PTR_ERR(op_data));
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);
struct llcrypt_str *iname, struct llcrypt_str *oname,
struct lu_fid *fid);
int ll_revalidate_d_crypto(struct dentry *dentry, unsigned int flags);
+int ll_file_open_encrypt(struct inode *inode, struct file *filp);
#ifdef HAVE_LUSTRE_CRYPTO
extern const struct llcrypt_operations lustre_cryptops;
#endif
#include <linux/random.h>
#include <linux/statfs.h>
#include <linux/time.h>
+#include <linux/file.h>
#include <linux/types.h>
#include <libcfs/linux/linux-uuid.h>
#include <linux/version.h>
RETURN(rc);
}
+/**
+ * Get reference file from volatile file name.
+ * Volatile file name may look like:
+ * <parent>/LUSTRE_VOLATILE_HDR:<mdt_index>:<random>:fd=<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.
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);
}
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)
LASSERT(fid_seq(&lli->lli_fid) != 0);
- lli->lli_attr_valid = body->mbo_valid;
+ /* In case of encrypted file without the key, please do not lose
+ * clear text size stored into lli_lazysize in ll_merge_attr(),
+ * we will need it in ll_prepare_close().
+ */
+ if (lli->lli_attr_valid & OBD_MD_FLLAZYSIZE && lli->lli_lazysize &&
+ llcrypt_require_key(inode) == -ENOKEY)
+ lli->lli_attr_valid = body->mbo_valid | OBD_MD_FLLAZYSIZE;
+ else
+ lli->lli_attr_valid = body->mbo_valid;
if (body->mbo_valid & OBD_MD_FLSIZE) {
i_size_write(inode, body->mbo_size);
op_data->op_flags |= MF_OPNAME_KMALLOCED;
}
- /* In fact LUSTRE_OPC_LOOKUP, LUSTRE_OPC_OPEN, LUSTRE_OPC_MIGR
+ /* In fact LUSTRE_OPC_LOOKUP, LUSTRE_OPC_OPEN
* are LUSTRE_OPC_ANY
*/
- if (opc == LUSTRE_OPC_LOOKUP || opc == LUSTRE_OPC_OPEN ||
- opc == LUSTRE_OPC_MIGR)
+ if (opc == LUSTRE_OPC_LOOKUP || opc == LUSTRE_OPC_OPEN)
op_data->op_code = LUSTRE_OPC_ANY;
else
op_data->op_code = opc;
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 int open_flags);
/* called from iget5_locked->find_inode() under inode_lock spinlock */
static int ll_test_inode(struct inode *inode, void *opaque)
*secctxlen = 0;
}
if (it->it_op & IT_CREAT && encrypt) {
- /* Volatile file name may look like:
- * <parent>/LUSTRE_VOLATILE_HDR:<mdt_index>:<random>:fd=<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) {
op_data->op_file_encctx_size);
OBD_FREE(ctx, ctx_size);
}
-
} else {
inherit:
rc = llcrypt_inherit_context(parent, NULL, op_data,
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;
}
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) {
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 int open_flags)
{
struct inode *inode;
__u64 bits = 0;
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);
}
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;
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
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
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;
}
}
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
}
cleanup_for_enc_tests() {
- rm -rf $DIR/$tdir
+ rm -rf $DIR/$tdir $*
# remount client normally
if is_mounted $MOUNT; then
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"
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"
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
MATCHING_STRING="get xattr 'security.c'" \
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
MATCHING_STRING="get xattr 'security.c'" \
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"
[[ $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
cancel_lru_locks
cmp -bl $tmpfile $testfile ||
error "extended/split file is corrupted"
-
- rm -f $tmpfile $mirror1 $mirror2
}
run_test 52 "Mirrored encrypted file"
}
run_test 58 "access to enc file's xattrs"
+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 -vvv $testfile ||
+ error "verifying mirror failed (1)"
+ if [ $($LFS mirror verify -v $testfile 2>&1 |
+ grep -ci "only valid") -ne 0 ]; then
+ error "verifying mirror failed (2)"
+ fi
+
+ $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_59a() {
+ 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 -vvv $scrambledfile ||
+ error "mirror verify failed (1)"
+ if [ $($LFS mirror verify -v $scrambledfile 2>&1 |
+ grep -ci "only valid") -ne 0 ]; then
+ error "mirror verify failed (2)"
+ fi
+
+ $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 59a "mirror resync of encrypted files without key"
+
+test_59b() {
+ 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 -vvv $scrambledfile ||
+ error "mirror verify failed (1)"
+ if [ $($LFS mirror verify -v $scrambledfile 2>&1 |
+ grep -ci "only valid") -ne 0 ]; then
+ error "mirror verify failed (2)"
+ fi
+
+ # 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 59b "migrate/extend/split of encrypted files without key"
+
+test_59c() {
+ 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 59c "MDT migrate of encrypted files without key"
+
log "cleanup: ======================================================"
sec_unsetup() {
static const char *error_loc = "syserror";
-enum {
- MIGRATION_NONBLOCK = 0x0001,
- MIGRATION_MIRROR = 0x0002,
- MIGRATION_NONDIRECT = 0x0004,
- MIGRATION_VERBOSE = 0x0008,
-};
+#define MIGRATION_NONBLOCK LLAPI_MIGRATION_NONBLOCK
+#define MIGRATION_MIRROR LLAPI_MIGRATION_MIRROR
+#define MIGRATION_NONDIRECT LLAPI_MIGRATION_NONDIRECT
+#define MIGRATION_VERBOSE LLAPI_MIGRATION_VERBOSE
static int
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 | O_NOATIME;
+ /* Allow migrating even without the key on encrypted files */
+ rflags = O_RDWR | O_NOATIME | 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;
}
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,
}
}
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;
}
}
- 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",
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)
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));
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));
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;
if (comp_array[i].lrc_synced && pos & (page_size - 1)) {
rc = llapi_mirror_truncate(fd,
comp_array[i].lrc_mirror_id, pos);
- if (rc < 0)
+ /* Ignore truncate error on encrypted file without the
+ * key if tried on LUSTRE_ENCRYPTION_UNIT_SIZE boundary.
+ */
+ if (rc < 0 && (rc != -ENOKEY ||
+ pos & ~LUSTRE_ENCRYPTION_MASK))
comp_array[i].lrc_synced = false;
}
}
#define WANT_INDEX 0x8
#define WANT_ERROR 0x10
+/* Define a fixed 4096-byte encryption unit size */
+#define LUSTRE_ENCRYPTION_BLOCKBITS 12
+#define LUSTRE_ENCRYPTION_UNIT_SIZE ((size_t)1 << LUSTRE_ENCRYPTION_BLOCKBITS)
+#define LUSTRE_ENCRYPTION_MASK (~(LUSTRE_ENCRYPTION_UNIT_SIZE - 1))
+
/* mount point listings in /proc/mounts */
#ifndef PROC_MOUNTS
#define PROC_MOUNTS "/proc/mounts"