@@ -178,19 +178,70 @@ static bool ll_empty_dir(struct inode *inode)
* ->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
+ * @fid: fid retrieved from user-provided filename
*
* This overlay function is necessary to properly encode @fname after
* encryption, as it will be sent over the wire.
+ * This overlay function is also necessary to handle the case of operations
+ * carried out without the key. Normally llcrypt makes use of digested names in
+ * that case. Having a digested name works for local file systems that can call
+ * llcrypt_match_name(), but Lustre server side is not aware of encryption.
+ * So for keyless @lookup operations on long names, for Lustre we choose to
+ * present to users the encoded struct ll_digest_filename, instead of a digested
+ * name. FID and name hash can then easily be extracted and put into the
+ * requests sent to servers.
*/
int ll_setup_filename(struct inode *dir, const struct qstr *iname,
- int lookup, struct fscrypt_name *fname)
+ int lookup, struct fscrypt_name *fname,
+ struct lu_fid *fid)
{
+ int digested = 0;
+ struct qstr dname;
int rc;
- rc = fscrypt_setup_filename(dir, iname, lookup, fname);
+ if (fid) {
+ fid->f_seq = 0;
+ fid->f_oid = 0;
+ fid->f_ver = 0;
+ }
+
+ if (fid && IS_ENCRYPTED(dir) && !fscrypt_has_encryption_key(dir) &&
+ iname->name[0] == '_')
+ digested = 1;
+
+ dname.name = iname->name + digested;
+ dname.len = iname->len - digested;
+
+ if (fid) {
+ fid->f_seq = 0;
+ fid->f_oid = 0;
+ fid->f_ver = 0;
+ }
+ rc = fscrypt_setup_filename(dir, &dname, lookup, fname);
if (rc)
return rc;
+ if (digested) {
+ /* Without the key, for long names user should have struct
+ * ll_digest_filename representation of the dentry instead of
+ * the name. So make sure it is valid, return fid and put
+ * excerpt of cipher text name in disk_name.
+ */
+ struct ll_digest_filename *digest;
+
+ if (fname->crypto_buf.len < sizeof(struct ll_digest_filename)) {
+ rc = -EINVAL;
+ goto out_free;
+ }
+ digest = (struct ll_digest_filename *)fname->crypto_buf.name;
+ *fid = digest->ldf_fid;
+ if (!fid_is_sane(fid)) {
+ rc = -EINVAL;
+ goto out_free;
+ }
+ fname->disk_name.name = digest->ldf_excerpt;
+ fname->disk_name.len = LLCRYPT_FNAME_DIGEST_SIZE;
+ }
if (IS_ENCRYPTED(dir) &&
!name_is_dot_or_dotdot(fname->disk_name.name,
fname->disk_name.len)) {
@@ -224,6 +275,11 @@ int ll_setup_filename(struct inode *dir, const struct qstr *iname,
return rc;
}
+#define LLCRYPT_FNAME_DIGEST(name, len) \
+ ((name) + round_down((len) - FS_CRYPTO_BLOCK_SIZE - 1, \
+ FS_CRYPTO_BLOCK_SIZE))
+#define LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE 32
+
/**
* ll_fname_disk_to_usr() - overlay to fscrypt_fname_disk_to_usr
* @inode: the inode to convert name
@@ -231,40 +287,76 @@ int ll_setup_filename(struct inode *dir, const struct qstr *iname,
* @minor_hash: minor hash for inode
* @iname: the user-provided filename needing conversion
* @oname: the filename information to be filled in
+ * @fid: the user-provided fid for filename
*
* 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.
+ * This overlay function is also necessary to handle the case of operations
+ * carried out without the key. Normally llcrypt makes use of digested names in
+ * that case. Having a digested name works for local file systems that can call
+ * llcrypt_match_name(), but Lustre server side is not aware of encryption.
+ * So for keyless @lookup operations on long names, for Lustre we choose to
+ * present to users the encoded struct ll_digest_filename, instead of a digested
+ * name. FID and name hash can then easily be extracted and put into the
+ * requests sent to servers.
*/
int ll_fname_disk_to_usr(struct inode *inode,
u32 hash, u32 minor_hash,
- struct fscrypt_str *iname, struct fscrypt_str *oname)
+ struct fscrypt_str *iname, struct fscrypt_str *oname,
+ struct lu_fid *fid)
{
struct fscrypt_str lltr = FSTR_INIT(iname->name, iname->len);
+ struct ll_digest_filename digest;
+ int digested = 0;
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;
+ if (IS_ENCRYPTED(inode)) {
+ if (!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;
+ }
+ if (lltr.len > LLCRYPT_FNAME_MAX_UNDIGESTED_SIZE &&
+ !fscrypt_has_encryption_key(inode)) {
+ digested = 1;
+ /* Without the key for long names, set the dentry name
+ * to the representing struct ll_digest_filename. It
+ * will be encoded by llcrypt for display, and will
+ * enable further lookup requests.
+ */
+ if (!fid)
+ return -EINVAL;
+ digest.ldf_fid = *fid;
+ memcpy(digest.ldf_excerpt,
+ LLCRYPT_FNAME_DIGEST(lltr.name, lltr.len),
+ LLCRYPT_FNAME_DIGEST_SIZE);
+
+ lltr.name = (char *)&digest;
+ lltr.len = sizeof(digest);
+
+ oname->name[0] = '_';
+ oname->name = oname->name + 1;
+ oname->len--;
+ }
}
-
rc = fscrypt_fname_disk_to_usr(inode, hash, minor_hash, &lltr, oname);
kfree(buf);
+ oname->name = oname->name - digested;
+ oname->len = oname->len + digested;
return rc;
}
@@ -250,7 +250,7 @@ int ll_dir_read(struct inode *inode, u64 *ppos, struct md_op_data *op_data,
= FSTR_INIT(ent->lde_name, namelen);
rc = ll_fname_disk_to_usr(inode, 0, 0, &de_name,
- &lltr);
+ &lltr, &fid);
de_name = lltr;
lltr.len = save_len;
if (rc) {
@@ -1705,11 +1705,22 @@ static inline struct pcc_super *ll_info2pccs(struct ll_inode_info *lli)
/* crypto.c */
#ifdef CONFIG_FS_ENCRYPTION
+/* The digested form is made of a FID (16 bytes) followed by the second-to-last
+ * ciphertext block (16 bytes), so a total length of 32 bytes.
+ * That way, llcrypt does not compute a digested form of this digest.
+ */
+struct ll_digest_filename {
+ struct lu_fid ldf_fid;
+ char ldf_excerpt[LLCRYPT_FNAME_DIGEST_SIZE];
+};
+
int ll_setup_filename(struct inode *dir, const struct qstr *iname,
- int lookup, struct fscrypt_name *fname);
+ int lookup, struct fscrypt_name *fname,
+ struct lu_fid *fid);
int ll_fname_disk_to_usr(struct inode *inode,
u32 hash, u32 minor_hash,
- struct fscrypt_str *iname, struct fscrypt_str *oname);
+ struct fscrypt_str *iname, struct fscrypt_str *oname,
+ struct lu_fid *fid);
int ll_revalidate_d_crypto(struct dentry *dentry, unsigned int flags);
#else
int ll_setup_filename(struct inode *dir, const struct qstr *iname,
@@ -3067,6 +3067,8 @@ struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data,
} else if (name && namelen) {
struct qstr dname = QSTR_INIT(name, namelen);
struct inode *dir;
+ struct lu_fid *pfid = NULL;
+ struct lu_fid fid;
int lookup;
if (!S_ISDIR(i1->i_mode) && i2 && S_ISDIR(i2->i_mode)) {
@@ -3077,11 +3079,18 @@ struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data,
dir = i1;
lookup = (int)(opc == LUSTRE_OPC_ANY);
}
- rc = ll_setup_filename(dir, &dname, lookup, &fname);
+ if (opc == LUSTRE_OPC_ANY && lookup)
+ pfid = &fid;
+ rc = ll_setup_filename(dir, &dname, lookup, &fname, pfid);
if (rc) {
ll_finish_md_op_data(op_data);
return ERR_PTR(rc);
}
+ if (pfid && !fid_is_zero(pfid)) {
+ if (i2 == NULL)
+ op_data->op_fid2 = fid;
+ op_data->op_bias = MDS_FID_OP;
+ }
if (fname.disk_name.name &&
fname.disk_name.name != (unsigned char *)name)
/* op_data->op_name must be freed after use */
@@ -814,6 +814,7 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
char secctx_name[XATTR_NAME_MAX + 1];
struct fscrypt_name fname;
struct inode *inode;
+ struct lu_fid fid;
u32 opc;
int rc;
@@ -856,7 +857,7 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
* 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);
+ rc = ll_setup_filename(parent, &dentry->d_name, 1, &fname, &fid);
if ((!rc || rc == -ENOENT) && fname.is_ciphertext_name) {
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_ENCRYPTED_NAME;
@@ -874,6 +875,12 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
return ERR_CAST(op_data);
goto out;
}
+ if (!fid_is_zero(&fid)) {
+ op_data->op_fid2 = fid;
+ op_data->op_bias = MDS_FID_OP;
+ if (it->it_op & IT_OPEN)
+ it->it_flags |= MDS_OPEN_BY_FID;
+ }
/* enforce umask if acl disabled or MDS doesn't support umask */
if (!IS_POSIXACL(parent) || !exp_connect_umask(ll_i2mdexp(parent)))
@@ -1856,7 +1863,8 @@ static int ll_unlink(struct inode *dir, struct dentry *dchild)
ll_i2info(dchild->d_inode)->lli_clob &&
dirty_cnt(dchild->d_inode))
op_data->op_cli_flags |= CLI_DIRTY_DATA;
- op_data->op_fid2 = op_data->op_fid3;
+ if (fid_is_zero(&op_data->op_fid2))
+ op_data->op_fid2 = op_data->op_fid3;
rc = md_unlink(ll_i2sbi(dir)->ll_md_exp, op_data, &request);
ll_finish_md_op_data(op_data);
if (rc)
@@ -1926,7 +1934,8 @@ static int ll_rmdir(struct inode *dir, struct dentry *dchild)
if (dchild->d_inode)
op_data->op_fid3 = *ll_inode2fid(dchild->d_inode);
- op_data->op_fid2 = op_data->op_fid3;
+ if (fid_is_zero(&op_data->op_fid2))
+ op_data->op_fid2 = op_data->op_fid3;
rc = md_unlink(ll_i2sbi(dir)->ll_md_exp, op_data, &request);
ll_finish_md_op_data(op_data);
if (rc == 0) {
@@ -2068,10 +2077,10 @@ static int ll_rename(struct inode *src, struct dentry *src_dchild,
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);
+ err = ll_setup_filename(src, &src_dchild->d_name, 1, &foldname, NULL);
if (err)
return err;
- err = ll_setup_filename(tgt, &tgt_dchild->d_name, 1, &fnewname);
+ err = ll_setup_filename(tgt, &tgt_dchild->d_name, 1, &fnewname, NULL);
if (err) {
fscrypt_free_filename(&foldname);
return err;
@@ -1141,14 +1141,16 @@ static int ll_statahead_thread(void *arg)
if (IS_ENCRYPTED(dir)) {
struct fscrypt_str de_name =
FSTR_INIT(ent->lde_name, namelen);
+ struct lu_fid fid;
rc = fscrypt_fname_alloc_buffer(dir, NAME_MAX,
&lltr);
if (rc < 0)
continue;
+ fid_le_to_cpu(&fid, &ent->lde_fid);
if (ll_fname_disk_to_usr(dir, 0, 0, &de_name,
- &lltr)) {
+ &lltr, &fid)) {
fscrypt_fname_free_buffer(&lltr);
continue;
}
@@ -1391,9 +1393,11 @@ static int is_first_dirent(struct inode *dir, struct dentry *dentry)
if (IS_ENCRYPTED(dir)) {
struct fscrypt_str de_name =
FSTR_INIT(ent->lde_name, namelen);
+ struct lu_fid fid;
+ fid_le_to_cpu(&fid, &ent->lde_fid);
if (ll_fname_disk_to_usr(dir, 0, 0, &de_name,
- &lltr))
+ &lltr, &fid))
continue;
name = lltr.name;
namelen = lltr.len;
@@ -621,6 +621,8 @@ void mdc_getattr_pack(struct req_capsule *pill, u64 valid, u32 flags,
b->mbo_valid = valid;
if (op_data->op_bias & MDS_CROSS_REF)
b->mbo_valid |= OBD_MD_FLCROSSREF;
+ if (op_data->op_bias & MDS_FID_OP)
+ b->mbo_valid |= OBD_MD_NAMEHASH;
b->mbo_eadatasize = ea_size;
b->mbo_flags = flags;
__mdc_pack_body(b, op_data->op_suppgids[0]);
@@ -1320,8 +1320,10 @@ int mdc_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
it->it_flags);
lockh.cookie = 0;
+ /* MDS_FID_OP is not a revalidate case */
if (fid_is_sane(&op_data->op_fid2) &&
- (it->it_op & (IT_LOOKUP | IT_GETATTR | IT_READDIR))) {
+ (it->it_op & (IT_LOOKUP | IT_GETATTR | IT_READDIR)) &&
+ !(op_data->op_bias & MDS_FID_OP)) {
/* We could just return 1 immediately, but since we should only
* be called in revalidate_it if we already have a lock, let's
* verify that.
@@ -287,6 +287,15 @@ static int mdc_getattr_name(struct obd_export *exp, struct md_op_data *op_data,
op_data->op_mode);
req_capsule_set_size(&req->rq_pill, &RMF_ACL, RCL_SERVER, acl_bufsize);
ptlrpc_request_set_replen(req);
+ if (op_data->op_bias & MDS_FID_OP) {
+ struct mdt_body *b = req_capsule_client_get(&req->rq_pill,
+ &RMF_MDT_BODY);
+
+ if (b) {
+ b->mbo_valid |= OBD_MD_NAMEHASH;
+ b->mbo_fid2 = op_data->op_fid2;
+ }
+ }
rc = mdc_getattr_common(exp, req);
if (rc) {
@@ -1197,11 +1197,14 @@ static inline __u32 lov_mds_md_size(__u16 stripes, __u32 lmm_magic)
#define OBD_MD_DEFAULT_MEA (0x0040000000000000ULL) /* default MEA */
#define OBD_MD_FLOSTLAYOUT (0x0080000000000000ULL) /* contain ost_layout */
#define OBD_MD_FLPROJID (0x0100000000000000ULL) /* project ID */
-#define OBD_MD_SECCTX (0x0200000000000000ULL) /* embed security xattr */
-#define OBD_MD_FLLAZYSIZE (0x0400000000000000ULL) /* Lazy size */
-#define OBD_MD_FLLAZYBLOCKS (0x0800000000000000ULL) /* Lazy blocks */
+#define OBD_MD_SECCTX (0x0200000000000000ULL) /* embed security xattr */
+#define OBD_MD_FLLAZYSIZE (0x0400000000000000ULL) /* Lazy size */
+#define OBD_MD_FLLAZYBLOCKS (0x0800000000000000ULL) /* Lazy blocks */
#define OBD_MD_FLBTIME (0x1000000000000000ULL) /* birth time */
-#define OBD_MD_ENCCTX (0x2000000000000000ULL) /* embed encryption ctx */
+#define OBD_MD_ENCCTX (0x2000000000000000ULL) /* embed encryption ctx */
+#define OBD_MD_NAMEHASH (0x4000000000000000ULL) /* use hash instead of name
+ * in case of encryption
+ */
#define OBD_MD_FLALLQUOTA (OBD_MD_FLUSRQUOTA | \
OBD_MD_FLGRPQUOTA | \
@@ -1705,7 +1708,8 @@ enum mds_op_bias {
MDS_PCC_ATTACH = 1 << 19,
MDS_CLOSE_UPDATE_TIMES = 1 << 20,
/* setstripe create only, don't restripe if target exists */
- MDS_SETSTRIPE_CREATE = 1 << 21,
+ MDS_SETSTRIPE_CREATE = 1 << 21,
+ MDS_FID_OP = 1 << 22,
};
#define MDS_CLOSE_INTENT (MDS_HSM_RELEASE | MDS_CLOSE_LAYOUT_SWAP | \
@@ -1221,12 +1221,13 @@ enum la_valid {
#define MDS_OPEN_PCC 010000000000000ULL /* PCC: auto RW-PCC cache attach
* for newly created file
*/
+#define MDS_OP_WITH_FID 020000000000000ULL /* operation carried out by FID */
#define MDS_OPEN_FL_INTERNAL (MDS_OPEN_HAS_EA | MDS_OPEN_HAS_OBJS | \
MDS_OPEN_OWNEROVERRIDE | MDS_OPEN_LOCK | \
MDS_OPEN_BY_FID | MDS_OPEN_LEASE | \
MDS_OPEN_RELEASE | MDS_OPEN_RESYNC | \
- MDS_OPEN_PCC)
+ MDS_OPEN_PCC | MDS_OP_WITH_FID)
/********* Changelogs **********/
/** Changelog record types */