MF_GETATTR_BY_FID = BIT(5),
MF_QOS_MKDIR = BIT(6),
MF_RR_MKDIR = BIT(7),
+ MF_OPNAME_KMALLOCED = BIT(8),
};
enum md_cli_flags {
LUSTRE_OPC_MKNOD,
LUSTRE_OPC_CREATE,
LUSTRE_OPC_ANY,
+ LUSTRE_OPC_LOOKUP,
+ LUSTRE_OPC_OPEN,
+ LUSTRE_OPC_MIGR,
};
/**
#include "llite_internal.h"
#ifdef HAVE_LUSTRE_CRYPTO
+#include <libcfs/libcfs_crypto.h>
static int ll_get_context(struct inode *inode, void *ctx, size_t len)
{
return true;
}
+/**
+ * ll_setup_filename() - overlay to llcrypt_setup_filename
+ * @dir: the directory that will be searched
+ * @iname: the user-provided filename being searched for
+ * @lookup: 1 if we're allowed to proceed without the key because it's
+ * ->lookup() or we're finding the dir_entry for deletion; 0 if we cannot
+ * proceed without the key because we're going to create the dir_entry.
+ * @fname: the filename information to be filled in
+ *
+ * This overlay function is necessary to properly encode @fname after
+ * encryption, as it will be sent over the wire.
+ */
+int ll_setup_filename(struct inode *dir, const struct qstr *iname,
+ int lookup, struct llcrypt_name *fname)
+{
+ int rc;
+
+ rc = llcrypt_setup_filename(dir, iname, lookup, fname);
+ if (rc)
+ return rc;
+
+ if (IS_ENCRYPTED(dir) &&
+ !name_is_dot_or_dotdot(fname->disk_name.name,
+ fname->disk_name.len)) {
+ int presented_len = critical_chars(fname->disk_name.name,
+ fname->disk_name.len);
+ char *buf;
+
+ buf = kmalloc(presented_len + 1, GFP_NOFS);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ if (presented_len == fname->disk_name.len)
+ memcpy(buf, fname->disk_name.name, presented_len);
+ else
+ critical_encode(fname->disk_name.name,
+ fname->disk_name.len, buf);
+ buf[presented_len] = '\0';
+ kfree(fname->crypto_buf.name);
+ fname->crypto_buf.name = buf;
+ fname->crypto_buf.len = presented_len;
+ fname->disk_name.name = fname->crypto_buf.name;
+ fname->disk_name.len = fname->crypto_buf.len;
+ }
+
+ return rc;
+
+out_free:
+ llcrypt_free_filename(fname);
+ return rc;
+}
+
+/**
+ * ll_fname_disk_to_usr() - overlay to llcrypt_fname_disk_to_usr
+ * @inode: the inode to convert name
+ * @hash: major hash for inode
+ * @minor_hash: minor hash for inode
+ * @iname: the user-provided filename needing conversion
+ * @oname: the filename information to be filled in
+ *
+ * The caller must have allocated sufficient memory for the @oname string.
+ *
+ * This overlay function is necessary to properly decode @iname before
+ * decryption, as it comes from the wire.
+ */
+int ll_fname_disk_to_usr(struct inode *inode,
+ u32 hash, u32 minor_hash,
+ struct llcrypt_str *iname, struct llcrypt_str *oname)
+{
+ struct llcrypt_str lltr = LLTR_INIT(iname->name, iname->len);
+ char *buf = NULL;
+ int rc;
+
+ if (IS_ENCRYPTED(inode) &&
+ !name_is_dot_or_dotdot(lltr.name, lltr.len) &&
+ strnchr(lltr.name, lltr.len, '=')) {
+ /* Only proceed to critical decode if
+ * iname contains espace char '='.
+ */
+ int len = lltr.len;
+
+ buf = kmalloc(len, GFP_NOFS);
+ if (!buf)
+ return -ENOMEM;
+
+ len = critical_decode(lltr.name, len, buf);
+ lltr.name = buf;
+ lltr.len = len;
+ }
+
+ rc = llcrypt_fname_disk_to_usr(inode, hash, minor_hash, &lltr, oname);
+
+ kfree(buf);
+
+ return rc;
+}
+
+/* Copied from llcrypt_d_revalidate, as it is not exported */
+/*
+ * Validate dentries in encrypted directories to make sure we aren't potentially
+ * caching stale dentries after a key has been added.
+ */
+int ll_revalidate_d_crypto(struct dentry *dentry, unsigned int flags)
+{
+ struct dentry *dir;
+ int err;
+ int valid;
+
+ /*
+ * Plaintext names are always valid, since llcrypt doesn't support
+ * reverting to ciphertext names without evicting the directory's inode
+ * -- which implies eviction of the dentries in the directory.
+ */
+ if (!(dentry->d_flags & DCACHE_ENCRYPTED_NAME))
+ return 1;
+
+ /*
+ * Ciphertext name; valid if the directory's key is still unavailable.
+ *
+ * Although llcrypt forbids rename() on ciphertext names, we still must
+ * use dget_parent() here rather than use ->d_parent directly. That's
+ * because a corrupted fs image may contain directory hard links, which
+ * the VFS handles by moving the directory's dentry tree in the dcache
+ * each time ->lookup() finds the directory and it already has a dentry
+ * elsewhere. Thus ->d_parent can be changing, and we must safely grab
+ * a reference to some ->d_parent to prevent it from being freed.
+ */
+
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
+ dir = dget_parent(dentry);
+ err = llcrypt_get_encryption_info(d_inode(dir));
+ valid = !llcrypt_has_encryption_key(d_inode(dir));
+ dput(dir);
+
+ if (err < 0)
+ return err;
+
+ return valid;
+}
+
const struct llcrypt_operations lustre_cryptops = {
.key_prefix = "lustre:",
.get_context = ll_get_context,
void ll_sbi_set_encrypt(struct ll_sb_info *sbi, bool set)
{
}
+
+int ll_setup_filename(struct inode *dir, const struct qstr *iname,
+ int lookup, struct llcrypt_name *fname)
+{
+ return llcrypt_setup_filename(dir, iname, lookup, fname);
+}
+
+int ll_fname_disk_to_usr(struct inode *inode,
+ u32 hash, u32 minor_hash,
+ struct llcrypt_str *iname, struct llcrypt_str *oname)
+{
+ return llcrypt_fname_disk_to_usr(inode, hash, minor_hash, iname, oname);
+}
+
+int ll_revalidate_d_crypto(struct dentry *dentry, unsigned int flags)
+{
+ return 1;
+}
#endif
unsigned int lookup_flags)
{
struct inode *dir = dentry->d_parent->d_inode;
+ int rc;
CDEBUG(D_VFSTRACE, "VFS Op:name=%s, flags=%u\n",
dentry->d_name.name, lookup_flags);
+ rc = ll_revalidate_d_crypto(dentry, lookup_flags);
+ if (rc != 1)
+ return rc;
+
/* If this is intermediate component path lookup and we were able to get
* to this dentry, then its lock has not been revoked and the
* path component is valid. */
#include <lustre_fid.h>
#include <lustre_kernelcomm.h>
#include <lustre_swab.h>
+#include <libcfs/libcfs_crypto.h>
#include "llite_internal.h"
void *cookie, filldir_t filldir)
{
#endif
- struct ll_sb_info *sbi = ll_i2sbi(inode);
- __u64 pos = *ppos;
- bool is_api32 = ll_need_32bit_api(sbi);
- bool is_hash64 = sbi->ll_flags & LL_SBI_64BIT_HASH;
- struct page *page;
- bool done = false;
- int rc = 0;
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ __u64 pos = *ppos;
+ bool is_api32 = ll_need_32bit_api(sbi);
+ bool is_hash64 = sbi->ll_flags & LL_SBI_64BIT_HASH;
+ struct page *page;
+ bool done = false;
+ struct llcrypt_str lltr = LLTR_INIT(NULL, 0);
+ int rc = 0;
ENTRY;
+ if (IS_ENCRYPTED(inode)) {
+ rc = llcrypt_fname_alloc_buffer(inode, NAME_MAX, &lltr);
+ if (rc < 0)
+ RETURN(rc);
+ }
+
page = ll_get_dir_page(inode, op_data, pos);
while (rc == 0 && !done) {
* for 'filldir()' must be part of the 'ent'. */
#ifdef HAVE_DIR_CONTEXT
ctx->pos = lhash;
- done = !dir_emit(ctx, ent->lde_name, namelen, ino,
- type);
+ if (!IS_ENCRYPTED(inode)) {
+ done = !dir_emit(ctx, ent->lde_name, namelen,
+ ino, type);
+ } else {
+ /* Directory is encrypted */
+ int save_len = lltr.len;
+ struct llcrypt_str de_name =
+ LLTR_INIT(ent->lde_name, namelen);
+
+ rc = ll_fname_disk_to_usr(inode, 0, 0, &de_name,
+ &lltr);
+ de_name = lltr;
+ lltr.len = save_len;
+ if (rc) {
+ done = 1;
+ break;
+ }
+ done = !dir_emit(ctx, de_name.name, de_name.len,
+ ino, type);
+ }
#else
+ /* HAVE_DIR_CONTEXT is defined from kernel 3.11, whereas
+ * IS_ENCRYPTED is brought by kernel 4.14.
+ * So there is no need to handle encryption case here.
+ */
done = filldir(cookie, ent->lde_name, namelen, lhash,
ino, type);
#endif
#else
*ppos = pos;
#endif
+ llcrypt_fname_free_buffer(&lltr);
RETURN(rc);
}
PFID(ll_inode2fid(inode)),
inode, (unsigned long)pos, i_size_read(inode), api32);
+ if (IS_ENCRYPTED(inode)) {
+ rc = llcrypt_get_encryption_info(inode);
+ if (rc && rc != -ENOKEY)
+ GOTO(out, rc);
+ }
+
if (pos == MDS_DIR_END_OFF)
/*
* end-of-file.
}
op_data = ll_prep_md_op_data(NULL, parent->d_inode, de->d_inode,
- name, len, 0, LUSTRE_OPC_ANY, NULL);
+ name, len, 0, LUSTRE_OPC_OPEN, NULL);
if (IS_ERR(op_data)) {
kfree(name);
RETURN(PTR_ERR(op_data));
struct ptlrpc_request **request)
{
struct ll_sb_info *sbi = ll_i2sbi(inode);
- struct mdt_body *body;
+ struct mdt_body *body;
struct lov_mds_md *lmm = NULL;
struct ptlrpc_request *req = NULL;
struct md_op_data *op_data;
rc = md_getattr_name(sbi->ll_md_exp, op_data, &req);
ll_finish_md_op_data(op_data);
if (rc < 0) {
- CDEBUG(D_INFO, "md_getattr_name failed "
- "on %s: rc %d\n", filename, rc);
+ CDEBUG(D_INFO, "md_getattr_name failed on %s: rc %d\n",
+ filename, rc);
GOTO(out, rc);
}
int namelen, struct lu_fid *fid,
struct inode **inode)
{
- struct md_op_data *op_data = NULL;
- struct mdt_body *body;
- struct ptlrpc_request *req;
- int rc;
+ struct md_op_data *op_data = NULL;
+ struct mdt_body *body;
+ struct ptlrpc_request *req;
+ int rc;
ENTRY;
op_data = ll_prep_md_op_data(NULL, parent, NULL, name, namelen, 0,
}
op_data = ll_prep_md_op_data(NULL, parent, NULL, name, namelen,
- child_inode->i_mode, LUSTRE_OPC_ANY, NULL);
+ child_inode->i_mode, LUSTRE_OPC_MIGR,
+ NULL);
if (IS_ERR(op_data))
GOTO(out_iput, rc = PTR_ERR(op_data));
spin_unlock(&och->och_mod->mod_open_req->rq_lock);
}
- rc = md_rename(ll_i2sbi(parent)->ll_md_exp, op_data, name, namelen,
- name, namelen, &request);
+ rc = md_rename(ll_i2sbi(parent)->ll_md_exp, op_data,
+ op_data->op_name, op_data->op_namelen,
+ op_data->op_name, op_data->op_namelen, &request);
if (rc == 0) {
LASSERT(request != NULL);
ll_update_times(request, parent);
return ll_i2pccs(ll_info2i(lli));
}
-#ifdef HAVE_LUSTRE_CRYPTO
/* crypto.c */
+int ll_setup_filename(struct inode *dir, const struct qstr *iname,
+ int lookup, struct llcrypt_name *fname);
+int ll_fname_disk_to_usr(struct inode *inode,
+ u32 hash, u32 minor_hash,
+ struct llcrypt_str *iname, struct llcrypt_str *oname);
+int ll_revalidate_d_crypto(struct dentry *dentry, unsigned int flags);
+#ifdef HAVE_LUSTRE_CRYPTO
extern const struct llcrypt_operations lustre_cryptops;
#endif
+
/* llite/llite_foreign.c */
int ll_manage_foreign(struct inode *inode, struct lustre_md *lmd);
bool ll_foreign_is_openable(struct dentry *dentry, unsigned int flags);
__u32 mode, enum md_op_code opc,
void *data)
{
+ struct llcrypt_name fname = { 0 };
+ int rc;
+
LASSERT(i1 != NULL);
if (name == NULL) {
ll_i2gids(op_data->op_suppgids, i1, i2);
op_data->op_fid1 = *ll_inode2fid(i1);
- op_data->op_code = opc;
if (S_ISDIR(i1->i_mode)) {
down_read_non_owner(&ll_i2info(i1)->lli_lsm_sem);
if (ll_need_32bit_api(ll_i2sbi(i1)))
op_data->op_cli_flags |= CLI_API32;
- op_data->op_name = name;
- op_data->op_namelen = namelen;
+ if (opc == LUSTRE_OPC_LOOKUP || opc == LUSTRE_OPC_CREATE) {
+ /* In case of lookup, ll_setup_filename() has already been
+ * called in ll_lookup_it(), so just take provided name.
+ */
+ fname.disk_name.name = (unsigned char *)name;
+ fname.disk_name.len = namelen;
+ } else if (name && namelen) {
+ struct qstr dname = QSTR_INIT(name, namelen);
+ struct inode *dir;
+ int lookup;
+
+ if (!S_ISDIR(i1->i_mode) && i2 && S_ISDIR(i2->i_mode)) {
+ /* special case when called from ll_link() */
+ dir = i2;
+ lookup = 0;
+ } else {
+ dir = i1;
+ lookup = (int)(opc == LUSTRE_OPC_ANY);
+ }
+ rc = ll_setup_filename(dir, &dname, lookup, &fname);
+ if (rc) {
+ ll_finish_md_op_data(op_data);
+ return ERR_PTR(rc);
+ }
+ if (fname.disk_name.name &&
+ fname.disk_name.name != (unsigned char *)name)
+ /* op_data->op_name must be freed after use */
+ op_data->op_flags |= MF_OPNAME_KMALLOCED;
+ }
+
+ /* In fact LUSTRE_OPC_LOOKUP, LUSTRE_OPC_OPEN, LUSTRE_OPC_MIGR
+ * are LUSTRE_OPC_ANY
+ */
+ if (opc == LUSTRE_OPC_LOOKUP || opc == LUSTRE_OPC_OPEN ||
+ opc == LUSTRE_OPC_MIGR)
+ op_data->op_code = LUSTRE_OPC_ANY;
+ else
+ op_data->op_code = opc;
+ op_data->op_name = fname.disk_name.name;
+ op_data->op_namelen = fname.disk_name.len;
op_data->op_mode = mode;
op_data->op_mod_time = ktime_get_real_seconds();
op_data->op_fsuid = from_kuid(&init_user_ns, current_fsuid());
ll_unlock_md_op_lsm(op_data);
ll_security_release_secctx(op_data->op_file_secctx,
op_data->op_file_secctx_size);
+ if (op_data->op_flags & MF_OPNAME_KMALLOCED)
+ /* allocated via ll_setup_filename called
+ * from ll_prep_md_op_data
+ */
+ kfree(op_data->op_name);
llcrypt_free_ctx(op_data->op_file_encctx, op_data->op_file_encctx_size);
OBD_FREE_PTR(op_data);
}
__u32 opc;
int rc;
char secctx_name[XATTR_NAME_MAX + 1];
+ struct llcrypt_name fname;
ENTRY;
if (it->it_op & IT_CREAT)
opc = LUSTRE_OPC_CREATE;
else
- opc = LUSTRE_OPC_ANY;
+ opc = LUSTRE_OPC_LOOKUP;
+
+ /* Here we should be calling llcrypt_prepare_lookup(). But it installs a
+ * custom ->d_revalidate() method, so we lose ll_d_ops.
+ * To workaround this, call ll_setup_filename() and do the rest
+ * manually. Also make a copy of llcrypt_d_revalidate() (unfortunately
+ * not exported function) and call it from ll_revalidate_dentry(), to
+ * ensure we do not cache stale dentries after a key has been added.
+ */
+ rc = ll_setup_filename(parent, &dentry->d_name, 1, &fname);
+ if ((!rc || rc == -ENOENT) && fname.is_ciphertext_name) {
+ spin_lock(&dentry->d_lock);
+ dentry->d_flags |= DCACHE_ENCRYPTED_NAME;
+ spin_unlock(&dentry->d_lock);
+ }
+ if (rc == -ENOENT)
+ RETURN(NULL);
+ if (rc)
+ RETURN(ERR_PTR(rc));
- op_data = ll_prep_md_op_data(NULL, parent, NULL, dentry->d_name.name,
- dentry->d_name.len, 0, opc, NULL);
- if (IS_ERR(op_data))
- GOTO(out, retval = ERR_CAST(op_data));
+ op_data = ll_prep_md_op_data(NULL, parent, NULL, fname.disk_name.name,
+ fname.disk_name.len, 0, opc, NULL);
+ if (IS_ERR(op_data)) {
+ llcrypt_free_filename(&fname);
+ RETURN(ERR_CAST(op_data));
+ }
/* enforce umask if acl disabled or MDS doesn't support umask */
if (!IS_POSIXACL(parent) || !exp_connect_umask(ll_i2mdexp(parent)))
op_data->op_file_encctx = NULL;
op_data->op_file_encctx_size = 0;
}
+ llcrypt_free_filename(&fname);
ll_finish_md_op_data(op_data);
}
#endif
)
{
- struct qstr *src_name = &src_dchild->d_name;
- struct qstr *tgt_name = &tgt_dchild->d_name;
struct ptlrpc_request *request = NULL;
struct ll_sb_info *sbi = ll_i2sbi(src);
struct md_op_data *op_data;
ktime_t kstart = ktime_get();
umode_t mode = 0;
+ struct llcrypt_name foldname, fnewname;
int err;
ENTRY;
if (tgt_dchild->d_inode)
op_data->op_fid4 = *ll_inode2fid(tgt_dchild->d_inode);
+ err = ll_setup_filename(src, &src_dchild->d_name, 1, &foldname);
+ if (err)
+ RETURN(err);
+ err = ll_setup_filename(tgt, &tgt_dchild->d_name, 1, &fnewname);
+ if (err) {
+ llcrypt_free_filename(&foldname);
+ RETURN(err);
+ }
err = md_rename(sbi->ll_md_exp, op_data,
- src_name->name, src_name->len,
- tgt_name->name, tgt_name->len, &request);
+ foldname.disk_name.name, foldname.disk_name.len,
+ fnewname.disk_name.name, fnewname.disk_name.len,
+ &request);
+ llcrypt_free_filename(&foldname);
+ llcrypt_free_filename(&fnewname);
ll_finish_md_op_data(op_data);
if (!err) {
ll_update_times(request, src);
/* finish async stat RPC arguments */
static void sa_fini_data(struct md_enqueue_info *minfo)
{
+ struct md_op_data *op_data = &minfo->mi_data;
+
+ if (op_data->op_flags & MF_OPNAME_KMALLOCED)
+ /* allocated via ll_setup_filename called from sa_prep_data */
+ kfree(op_data->op_name);
ll_unlock_md_op_lsm(&minfo->mi_data);
iput(minfo->mi_dir);
OBD_FREE_PTR(minfo);
int namelen;
char *name;
struct lu_fid fid;
+ struct llcrypt_str lltr = LLTR_INIT(NULL, 0);
hash = le64_to_cpu(ent->lde_hash);
if (unlikely(hash < pos))
}
__set_current_state(TASK_RUNNING);
+ if (IS_ENCRYPTED(dir)) {
+ struct llcrypt_str de_name =
+ LLTR_INIT(ent->lde_name, namelen);
+
+ rc = llcrypt_fname_alloc_buffer(dir, NAME_MAX,
+ &lltr);
+ if (rc < 0)
+ continue;
+
+ if (ll_fname_disk_to_usr(dir, 0, 0, &de_name,
+ &lltr)) {
+ llcrypt_fname_free_buffer(&lltr);
+ continue;
+ }
+
+ name = lltr.name;
+ namelen = lltr.len;
+ }
+
sa_statahead(parent, name, namelen, &fid);
+ llcrypt_fname_free_buffer(&lltr);
}
pos = le64_to_cpu(dp->ldp_hash_end);
/* file is first dirent under @dir */
static int is_first_dirent(struct inode *dir, struct dentry *dentry)
{
- struct qstr *target = &dentry->d_name;
- struct md_op_data *op_data;
- int dot_de;
- struct page *page = NULL;
- int rc = LS_NOT_FIRST_DE;
- __u64 pos = 0;
+ struct qstr *target = &dentry->d_name;
+ struct md_op_data *op_data;
+ int dot_de;
+ struct page *page = NULL;
+ int rc = LS_NOT_FIRST_DE;
+ __u64 pos = 0;
+ struct llcrypt_str lltr = LLTR_INIT(NULL, 0);
ENTRY;
LUSTRE_OPC_ANY, dir);
if (IS_ERR(op_data))
RETURN(PTR_ERR(op_data));
+
+ if (IS_ENCRYPTED(dir)) {
+ int rc2 = llcrypt_fname_alloc_buffer(dir, NAME_MAX, &lltr);
+
+ if (rc2 < 0)
+ RETURN(rc2);
+ }
+
/**
*FIXME choose the start offset of the readdir
*/
continue;
}
+ if (IS_ENCRYPTED(dir)) {
+ struct llcrypt_str de_name =
+ LLTR_INIT(ent->lde_name, namelen);
+
+ if (ll_fname_disk_to_usr(dir, 0, 0, &de_name,
+ &lltr))
+ continue;
+ name = lltr.name;
+ namelen = lltr.len;
+ }
+
if (target->len != namelen ||
memcmp(target->name, name, namelen) != 0)
rc = LS_NOT_FIRST_DE;
}
EXIT;
out:
+ llcrypt_fname_free_buffer(&lltr);
ll_finish_md_op_data(op_data);
return rc;
buf = req_capsule_client_get(pill, field);
buf_size = req_capsule_get_size(pill, field, RCL_CLIENT);
- LASSERT(name != NULL && name_len != 0 &&
- buf != NULL && buf_size == name_len + 1);
+ LASSERT(buf != NULL && buf_size == name_len + 1);
+ if (!name) {
+ buf[name_len] = '\0';
+ return;
+ }
cpy_len = strlcpy(buf, name, buf_size);
LASSERT(lu_name_is_valid_2(buf, cpy_len));
};
static inline int
-mdd_name_check(struct mdd_device *m, const struct lu_name *ln)
+mdd_name_check(const struct lu_env *env, struct mdd_device *m,
+ const struct lu_name *ln)
{
+ struct mdd_thread_info *info = mdd_env_info(env);
+ bool enc = info->mdi_pattr.la_valid & LA_FLAGS &&
+ info->mdi_pattr.la_flags & LUSTRE_ENCRYPT_FL;
+
if (!lu_name_is_valid(ln))
return -EINVAL;
- else if (ln->ln_namelen > m->mdd_dt_conf.ddp_max_name_len)
+ else if (!enc && ln->ln_namelen > m->mdd_dt_conf.ddp_max_name_len)
return -ENAMETOOLONG;
else
return 0;
RETURN(-ESTALE);
/* Local ops, no lookup before link, check filename length here. */
- rc = mdd_name_check(m, lname);
+ rc = mdd_name_check(env, m, lname);
if (rc < 0)
RETURN(rc);
cattr->la_valid |= LA_PROJID;
}
- rc = mdd_name_check(m, lname);
+ rc = mdd_name_check(env, m, lname);
if (rc < 0)
RETURN(rc);
if (rc)
GOTO(out_pending, rc);
- rc = mdd_name_check(mdd, ltname);
+ rc = mdd_name_check(env, mdd, ltname);
if (rc < 0)
GOTO(out_pending, rc);
ENTRY;
- LASSERT(lu_name_is_valid(lname));
-
/* ignore tobj */
if (lu_fid_eq(tpfid, fid) && tname->ln_namelen == lname->ln_namelen &&
- !strncmp(tname->ln_name, lname->ln_name, lname->ln_namelen))
+ !memcmp(tname->ln_name, lname->ln_name, lname->ln_namelen))
RETURN(0);
CDEBUG(D_INFO, "update "DFID"/"DNAME":"DFID"\n",
/* ignore tobj */
if (lu_fid_eq(tpfid, fid) && tname->ln_namelen == lname->ln_namelen &&
- !strcmp(tname->ln_name, lname->ln_name))
+ !memcmp(tname->ln_name, lname->ln_name, lname->ln_namelen))
return 0;
rc = mdd_fld_lookup(env, mdd, fid, &link_mdt_index);
#include <lustre_linkea.h>
+/* encoding routines */
+#include <lustre_crypto.h>
+
/* Maximum EA size is limited by LNET_MTU for remote objects */
#define OSD_MAX_EA_SIZE 1048364
}
/**
+ * Utility function to get real name from object name
+ *
+ * \param[in] obj pointer to the object to be handled
+ * \param[in] name object name
+ * \param[in] len object name len
+ * \param[out]ln pointer to the struct lu_name to hold the real name
+ *
+ * If file is not encrypted, real name is just the object name.
+ * If file is encrypted, object name needs to be decoded. In
+ * this case a new buffer is allocated, and ln->ln_name needs to be freed by
+ * the caller.
+ *
+ * \retval 0, on success
+ * \retval -ve, on error
+ */
+static int obj_name2lu_name(struct osd_object *obj, const char *name,
+ int len, struct lu_name *ln)
+{
+ struct lu_fid namefid;
+
+ fid_zero(&namefid);
+
+ if (name[0] == '[')
+ sscanf(name + 1, SFID, RFID(&namefid));
+
+ if (fid_is_sane(&namefid) || name_is_dot_or_dotdot(name, len) ||
+ !(obj->oo_lma_flags & LUSTRE_ENCRYPT_FL)) {
+ ln->ln_name = name;
+ ln->ln_namelen = len;
+ } else {
+ char *buf = kmalloc(len, GFP_NOFS);
+
+ if (!buf)
+ return -ENOMEM;
+
+ len = critical_decode(name, len, buf);
+ ln->ln_name = buf;
+ ln->ln_namelen = len;
+ }
+
+ return 0;
+}
+
+/**
* Index delete function for interoperability mode (b11826).
* It will remove the directory entry added by osd_index_ea_insert().
* This entry is needed to maintain name->fid mapping.
struct buffer_head *bh;
struct htree_lock *hlock = NULL;
struct osd_device *osd = osd_dev(dt->do_lu.lo_dev);
+ struct lu_name ln;
int rc;
ENTRY;
LASSERT(!dt_object_remote(dt));
LASSERT(handle != NULL);
+ rc = obj_name2lu_name(obj, (char *)key, strlen((char *)key), &ln);
+ if (rc)
+ RETURN(rc);
+
osd_trans_exec_op(env, handle, OSD_OT_DELETE);
oh = container_of(handle, struct osd_thandle, ot_super);
LASSERT(oh->ot_handle->h_transaction != NULL);
dquot_initialize(dir);
- dentry = osd_child_dentry_get(env, obj,
- (char *)key, strlen((char *)key));
+ dentry = osd_child_dentry_get(env, obj, ln.ln_name, ln.ln_namelen);
if (obj->oo_hl_head != NULL) {
hlock = osd_oti_get(env)->oti_hlock;
out:
LASSERT(osd_invariant(obj));
osd_trans_exec_check(env, handle, OSD_OT_DELETE);
+ if (ln.ln_name != (char *)key)
+ kfree(ln.ln_name);
RETURN(rc);
}
struct ldiskfs_dentry_param *ldp;
struct dentry *child;
struct osd_thandle *oth;
+ struct lu_name ln;
int rc;
oth = container_of(th, struct osd_thandle, ot_super);
LASSERT(oth->ot_handle->h_transaction != NULL);
LASSERT(pobj->oo_inode);
+ rc = obj_name2lu_name(pobj, name, strlen(name), &ln);
+ if (rc)
+ RETURN(rc);
+
ldp = (struct ldiskfs_dentry_param *)info->oti_ldp;
if (unlikely(osd_object_is_root(pobj)))
ldp->edp_magic = 0;
else
osd_get_ldiskfs_dirent_param(ldp, fid);
- child = osd_child_dentry_get(info->oti_env, pobj, name, strlen(name));
+ child = osd_child_dentry_get(info->oti_env, pobj,
+ ln.ln_name, ln.ln_namelen);
child->d_fsdata = (void *)ldp;
dquot_initialize(pobj->oo_inode);
rc = osd_ldiskfs_add_entry(info, osd_obj2dev(pobj), oth->ot_handle,
}
}
+ if (ln.ln_name != name)
+ kfree(ln.ln_name);
RETURN(rc);
}
struct buffer_head *bh;
struct lu_fid *fid = (struct lu_fid *)rec;
struct htree_lock *hlock = NULL;
+ struct lu_name ln;
int ino;
int rc;
LASSERT(dir->i_op != NULL);
LASSERT(dir->i_op->lookup != NULL);
- dentry = osd_child_dentry_get(env, obj,
- (char *)key, strlen((char *)key));
+ rc = obj_name2lu_name(obj, (char *)key, strlen((char *)key), &ln);
+ if (rc)
+ RETURN(rc);
+
+ dentry = osd_child_dentry_get(env, obj, ln.ln_name, ln.ln_namelen);
if (obj->oo_hl_head != NULL) {
hlock = osd_oti_get(env)->oti_hlock;
ldiskfs_htree_unlock(hlock);
else
up_read(&obj->oo_ext_idx_sem);
- return rc;
+ if (ln.ln_name != (char *)key)
+ kfree(ln.ln_name);
+ RETURN(rc);
}
static int osd_index_declare_ea_insert(const struct lu_env *env,
struct osd_it_ea_dirent *ent = it->oie_dirent;
struct lu_fid *fid = &ent->oied_fid;
struct osd_fid_pack *rec;
+ struct lu_fid namefid;
ENTRY;
OSD_IT_EA_BUFSIZE)
RETURN(1);
+ fid_zero(&namefid);
+
/* "." is just the object itself. */
if (namelen == 1 && name[0] == '.') {
if (obj != NULL)
*fid = obj->oo_dt.do_lu.lo_header->loh_fid;
}
+ if (name[0] == '[')
+ sscanf(name + 1, SFID, RFID(&namefid));
+ if (fid_is_sane(&namefid) || name_is_dot_or_dotdot(name, namelen) ||
+ !obj || !(obj->oo_lma_flags & LUSTRE_ENCRYPT_FL)) {
+ memcpy(ent->oied_name, name, namelen);
+ } else {
+ int presented_len = critical_chars(name, namelen);
+
+ if (presented_len == namelen)
+ memcpy(ent->oied_name, name, namelen);
+ else
+ namelen = critical_encode(name, namelen,
+ ent->oied_name);
+
+ ent->oied_name[namelen] = '\0';
+ }
+
ent->oied_ino = ino;
ent->oied_off = offset;
ent->oied_namelen = namelen;
ent->oied_type = d_type;
- memcpy(ent->oied_name, name, namelen);
-
it->oie_rd_dirent++;
it->oie_dirent = (void *)ent + cfs_size_round(sizeof(*ent) + namelen);
RETURN(0);
int rc;
bool dotdot = false;
bool dirty = false;
+ struct lu_name ln;
ENTRY;
RETURN(rc);
}
- dentry = osd_child_dentry_by_inode(env, dir, ent->oied_name,
- ent->oied_namelen);
+ rc = obj_name2lu_name(obj, ent->oied_name, ent->oied_namelen, &ln);
+ if (rc)
+ RETURN(rc);
+
+ dentry = osd_child_dentry_by_inode(env, dir, ln.ln_name, ln.ln_namelen);
rc = osd_get_lma(info, inode, dentry, &info->oti_ost_attrs);
if (rc == -ENODATA || !fid_is_sane(&lma->lma_self_fid))
lma = NULL;
iput(inode);
if (rc >= 0 && !dirty)
dev->od_dirent_journal = 0;
+ if (ln.ln_name != ent->oied_name)
+ kfree(ln.ln_name);
return rc;
}
EXPORT_SYMBOL(RMF_FID_ARRAY);
struct req_msg_field RMF_SYMTGT =
- DEFINE_MSGF("symtgt", RMF_F_STRING, -1, NULL, NULL);
+ DEFINE_MSGF("symtgt", 0, -1, NULL, NULL);
EXPORT_SYMBOL(RMF_SYMTGT);
struct req_msg_field RMF_TGTUUID =
test_46() {
local testdir=$DIR/$tdir/mydir
local testfile=$testdir/myfile
+ local testdir2=$DIR/$tdir/mydir2
+ local testfile2=$testdir/myfile2
local lsfile=$TMP/lsfile
local scrambleddir
local scrambledfile
- local testfile2=$DIR/$tdir/${tfile}.2
- local tmpfile=$DIR/junk
-
$LCTL get_param mdc.*.import | grep -q client_encryption ||
skip "client encryption not supported"
stack_trap cleanup_for_enc_tests EXIT
setup_for_enc_tests
- touch $DIR/onefile
touch $DIR/$tdir/$tfile
mkdir $testdir
echo test > $testfile
+ echo othertest > $testfile2
sync ; echo 3 > /proc/sys/vm/drop_caches
# remove fscrypt key from keyring
keyctl revoke $(keyctl show | awk '$7 ~ "^fscrypt:" {print $1}')
keyctl reap
+ cancel_lru_locks
scrambleddir=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type d)
- ls -1 $scrambleddir > $lsfile || error "ls $testdir failed"
+ ls -1 $scrambleddir > $lsfile || error "ls $testdir failed (1)"
scrambledfile=$scrambleddir/$(head -n 1 $lsfile)
- stat $scrambledfile || error "stat $scrambledfile failed"
+ stat $scrambledfile || error "stat $scrambledfile failed (1)"
rm -f $lsfile
- cat $scrambledfile && error "cat $scrambledfile should have failed"
+ cat $scrambledfile && error "cat $scrambledfile should have failed (1)"
+ rm -f $scrambledfile || error "rm $scrambledfile failed (1)"
+
+ ls -1 $scrambleddir > $lsfile || error "ls $testdir failed (2)"
+ scrambledfile=$scrambleddir/$(head -n 1 $lsfile)
+ stat $scrambledfile || error "stat $scrambledfile failed (2)"
+ rm -f $lsfile
+ cat $scrambledfile && error "cat $scrambledfile should have failed (2)"
touch $scrambleddir/otherfile &&
error "touch otherfile should have failed"
error "mkdir otherdir should have failed"
ls -d $scrambleddir/otherdir && error "otherdir should not exist"
- rm -f $scrambledfile || error "rm $scrambledfile failed"
+ ls -R $DIR
+ rm -f $scrambledfile || error "rm $scrambledfile failed (2)"
rmdir $scrambleddir || error "rmdir $scrambleddir failed"
-
- rm -f $DIR/onefile
+ ls -R $DIR
}
run_test 46 "encrypted file access semantics without key"
error "rename from unencrypted to encrypted dir should fail"
ln $tmpfile $testfile &&
- error "link from unencrypted to encrypted dir should fail"
+ error "link from encrypted to unencrypted dir should fail"
cp $tmpfile $testfile ||
error "cp from unencrypted to encrypted dir should succeed"
rm -f $testfile
ln $testfile2 $tmpfile ||
- error "link from encrypted to unencrypted dir should succeed"
+ error "link from unencrypted to encrypted dir should succeed"
rm -f $tmpfile
# check we are limited in the number of hard links
ln $testfile2 ${testfile}_$i || break
done
[ $i -lt 160 ] || error "hard link $i should fail"
+ rm -f ${testfile}_*
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
# remove fscrypt key from keyring
keyctl revoke $(keyctl show | awk '$7 ~ "^fscrypt:" {print $1}')
keyctl reap
+ cancel_lru_locks
scrambleddir=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type d)
scrambledfile=$(find $DIR/$tdir/ -maxdepth 1 -type f)
local filecount=$($RUNAS find $testdir -type f | wc -l)
[ $filecount -eq 3 ] || error "found $filecount files"
- $RUNAS hexdump -C $testfile &&
- error "reading $testfile should have failed without key"
+ scrambledfiles=( $(find $testdir/ -maxdepth 1 -type f) )
+ $RUNAS hexdump -C ${scrambledfiles[0]} &&
+ error "reading ${scrambledfiles[0]} should fail without key"
$RUNAS touch ${testfile}.nokey &&
error "touch ${testfile}.nokey should have failed without key"
$RUNAS fscrypt lock --verbose $testdir ||
error "fscrypt lock $testdir failed (2)"
- $RUNAS hexdump -C $testfile2 &&
- error "reading $testfile2 should have failed without key"
+ $RUNAS hexdump -C ${scrambledfiles[1]} &&
+ error "reading ${scrambledfiles[1]} should fail without key"
echo mypass | $RUNAS fscrypt unlock --verbose $testdir ||
error "fscrypt unlock $testdir failed (2)"