Whamcloud - gitweb
LU-13717 sec: filename encryption - digest support
authorSebastien Buisson <sbuisson@ddn.com>
Fri, 22 Jan 2021 12:06:50 +0000 (21:06 +0900)
committerAndreas Dilger <adilger@whamcloud.com>
Mon, 21 Mar 2022 18:28:58 +0000 (18:28 +0000)
A number of operations are allowed on encrypted files without the key:
- read file metadata (stat);
- list directories;
- remove files and directories.
In order to present valid names to users, cipher text names are base64
encoded if they are short. Otherwise we compute a digested form of the
cipher text, made of the FID (16 bytes) followed by the second-to-last
cipher block (16 bytes), and we base64 encode this digested form for
presentation to user.
These transformations are carried out in the specific overlay
functions, that now need to know the fid of the file.

As the digested form does not contain the whole cipher text name,
server side needs to proceed to an operation by FID for requests such
as lookup and getattr. It also relies on the content of the LinkEA to
verify the digested form as received from client side.

Lustre-change: https://review.whamcloud.com/43392
Lustre-commit: ed4a625d88567a2498c3fe32fd340ae7985e6ad0

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I45d10a426373c2cfe0b92a58c351da452d085d7d
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Patrick Farrell <pfarrell@whamcloud.com>
Reviewed-on: https://review.whamcloud.com/45731
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
18 files changed:
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/llite/crypto.c
lustre/llite/dir.c
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/llite/namei.c
lustre/llite/statahead.c
lustre/mdc/mdc_lib.c
lustre/mdc/mdc_locks.c
lustre/mdc/mdc_request.c
lustre/mdt/mdt_handler.c
lustre/mdt/mdt_internal.h
lustre/mdt/mdt_lib.c
lustre/mdt/mdt_reint.c
lustre/ptlrpc/wiretest.c
lustre/tests/sanity-sec.sh
lustre/utils/wiretest.c

index db02d47..e12038d 100644 (file)
@@ -1360,6 +1360,9 @@ lov_mds_md_max_stripe_count(__kernel_size_t buf_size, __u32 lmm_magic)
 #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_NAMEHASH      (0x4000000000000000ULL) /* use hash instead of name
+                                                     * in case of encryption
+                                                     */
 
 #define OBD_MD_FLALLQUOTA (OBD_MD_FLUSRQUOTA | \
                           OBD_MD_FLGRPQUOTA | \
@@ -1986,7 +1989,7 @@ enum mds_op_bias {
        MDS_CLOSE_UPDATE_TIMES  = 1 << 20,
        /* setstripe create only, don't restripe if target exists */
        MDS_SETSTRIPE_CREATE    = 1 << 21,
-       MDS_OP_FID              = 1 << 22,
+       MDS_FID_OP              = 1 << 22,
        /* migrate dirent only */
        MDS_MIGRATE_NSONLY      = 1 << 23,
 };
index eaedc46..511a794 100644 (file)
@@ -1571,13 +1571,14 @@ enum la_valid {
 #define MDS_OPEN_RESYNC    04000000000000ULL /* FLR: file resync */
 #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 */
 
 /* lustre internal open flags, which should not be set from user space */
 #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 **********/
index 20ac1fb..d21e3f2 100644 (file)
@@ -171,19 +171,64 @@ 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 llcrypt_name *fname)
+                     int lookup, struct llcrypt_name *fname,
+                     struct lu_fid *fid)
 {
+       int digested = 0;
+       struct qstr dname;
        int rc;
 
-       rc = llcrypt_setup_filename(dir, iname, lookup, fname);
+       if (fid && IS_ENCRYPTED(dir) && !llcrypt_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 = llcrypt_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,40 +269,78 @@ out_free:
  * @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 llcrypt_str *iname, struct llcrypt_str *oname)
+                        struct llcrypt_str *iname, struct llcrypt_str *oname,
+                        struct lu_fid *fid)
 {
        struct llcrypt_str lltr = LLTR_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 &&
+                   !llcrypt_has_encryption_key(inode) &&
+                   likely(llcrypt_policy_has_filename_enc(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 = llcrypt_fname_disk_to_usr(inode, hash, minor_hash, &lltr, oname);
 
        kfree(buf);
+       oname->name = oname->name - digested;
+       oname->len = oname->len + digested;
 
        return rc;
 }
@@ -341,14 +424,22 @@ 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)
+                     int lookup, struct llcrypt_name *fname,
+                     struct lu_fid *fid)
 {
+       if (fid) {
+               fid->f_seq = 0;
+               fid->f_oid = 0;
+               fid->f_ver = 0;
+       }
+
        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)
+                        struct llcrypt_str *iname, struct llcrypt_str *oname,
+                        struct lu_fid *fid)
 {
        return llcrypt_fname_disk_to_usr(inode, hash, minor_hash, iname, oname);
 }
index 9ecfbe7..9034a93 100644 (file)
@@ -258,7 +258,7 @@ int ll_dir_read(struct inode *inode, __u64 *ppos, struct md_op_data *op_data,
                                        LLTR_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) {
index b6b3cc5..569c390 100644 (file)
@@ -1790,11 +1790,22 @@ static inline struct pcc_file *ll_file2pccf(struct file *file)
 }
 
 /* crypto.c */
+/* 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 llcrypt_name *fname);
+                     int lookup, struct llcrypt_name *fname,
+                     struct lu_fid *fid);
 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 *iname, struct llcrypt_str *oname,
+                        struct lu_fid *fid);
 int ll_revalidate_d_crypto(struct dentry *dentry, unsigned int flags);
 #ifdef HAVE_LUSTRE_CRYPTO
 extern const struct llcrypt_operations lustre_cryptops;
index 6c50eca..b4c7898 100644 (file)
@@ -3079,6 +3079,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)) {
@@ -3089,11 +3091,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 */
index 86cc832..f65c20b 100644 (file)
@@ -818,7 +818,7 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
        int rc;
        char secctx_name[XATTR_NAME_MAX + 1];
        struct llcrypt_name fname;
-
+       struct lu_fid fid;
        ENTRY;
 
        if (dentry->d_name.len > ll_i2sbi(parent)->ll_namelen)
@@ -855,7 +855,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;
@@ -872,6 +872,12 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
                llcrypt_free_filename(&fname);
                RETURN(ERR_CAST(op_data));
        }
+       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)))
@@ -1892,7 +1898,8 @@ static int ll_rmdir(struct inode *dir, struct dentry *dchild)
        if (dchild->d_inode != NULL)
                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) {
@@ -1980,7 +1987,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)
@@ -2064,10 +2072,10 @@ static int ll_rename(struct user_namespace *mnt_userns,
        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) {
                llcrypt_free_filename(&foldname);
                RETURN(err);
index 768a972..0d794e4 100644 (file)
@@ -1167,14 +1167,16 @@ static int ll_statahead_thread(void *arg)
                        if (IS_ENCRYPTED(dir)) {
                                struct llcrypt_str de_name =
                                        LLTR_INIT(ent->lde_name, namelen);
+                               struct lu_fid fid;
 
                                rc = llcrypt_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)) {
                                        llcrypt_fname_free_buffer(&lltr);
                                        continue;
                                }
@@ -1423,9 +1425,11 @@ static int is_first_dirent(struct inode *dir, struct dentry *dentry)
                        if (IS_ENCRYPTED(dir)) {
                                struct llcrypt_str de_name =
                                        LLTR_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;
index 2678dbd..c369cd6 100644 (file)
@@ -631,6 +631,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]);
index 1eabac8..d6bb7e1 100644 (file)
@@ -1329,8 +1329,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.
index 4982833..382b464 100644 (file)
@@ -296,6 +296,15 @@ again:
                             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) {
index 035c6fb..4c537bf 100644 (file)
@@ -64,6 +64,7 @@
 #include <lustre_barrier.h>
 #include <obd_cksum.h>
 #include <llog_swab.h>
+#include <lustre_crypto.h>
 
 #include "mdt_internal.h"
 
@@ -1896,6 +1897,97 @@ lookup:
        RETURN(rc);
 }
 
+/**
+ * Find name matching hash
+ *
+ * We search \a child LinkEA for a name whose hash matches \a lname
+ * (it contains an encoded hash).
+ *
+ * \param info mdt thread info
+ * \param lname encoded hash to find
+ * \param parent parent object
+ * \param child object to search with LinkEA
+ * \param force_check true to check hash even if LinkEA has only one entry
+ *
+ * \retval 1 match found
+ * \retval 0 no match found
+ * \retval -ev negative errno upon error
+ */
+int find_name_matching_hash(struct mdt_thread_info *info, struct lu_name *lname,
+                           struct mdt_object *parent, struct mdt_object *child,
+                           bool force_check)
+{
+       /* Here, lname is an encoded hash of on-disk name, and
+        * client is doing access without encryption key.
+        * So we need to get LinkEA, check parent fid is correct and
+        * compare name hash with the one in the request.
+        */
+       struct lu_buf *buf = &info->mti_big_buf;
+       struct lu_name name;
+       struct lu_fid pfid;
+       struct linkea_data ldata = { NULL };
+       struct link_ea_header *leh;
+       struct link_ea_entry *lee;
+       struct lu_buf link = { 0 };
+       char *hash = NULL;
+       int reclen, count, rc;
+
+       ENTRY;
+
+       if (lname->ln_namelen < LLCRYPT_FNAME_DIGEST_SIZE)
+               RETURN(-EINVAL);
+
+       buf = lu_buf_check_and_alloc(buf, PATH_MAX);
+       if (!buf->lb_buf)
+               RETURN(-ENOMEM);
+
+       ldata.ld_buf = buf;
+       rc = mdt_links_read(info, child, &ldata);
+       if (rc < 0)
+               RETURN(rc);
+
+       leh = buf->lb_buf;
+       if (force_check || leh->leh_reccount > 1) {
+               hash = kmalloc(lname->ln_namelen, GFP_NOFS);
+               if (!hash)
+                       RETURN(-ENOMEM);
+               rc = critical_decode(lname->ln_name, lname->ln_namelen, hash);
+       }
+       lee = (struct link_ea_entry *)(leh + 1);
+       for (count = 0; count < leh->leh_reccount; count++) {
+               linkea_entry_unpack(lee, &reclen, &name, &pfid);
+               if (!force_check && leh->leh_reccount == 1) {
+                       /* if there is only one rec, it has to be it */
+                       *lname = name;
+                       break;
+               }
+               if (!parent || lu_fid_eq(&pfid, mdt_object_fid(parent))) {
+                       lu_buf_check_and_alloc(&link, name.ln_namelen);
+                       if (!link.lb_buf)
+                               GOTO(out_match, rc = -ENOMEM);
+                       rc = critical_decode(name.ln_name, name.ln_namelen,
+                                            link.lb_buf);
+
+                       if (memcmp(LLCRYPT_FNAME_DIGEST(link.lb_buf, rc),
+                                  hash, LLCRYPT_FNAME_DIGEST_SIZE) == 0) {
+                               *lname = name;
+                               break;
+                       }
+               }
+               lee = (struct link_ea_entry *) ((char *)lee + reclen);
+       }
+       if (count == leh->leh_reccount)
+               rc = 0;
+       else
+               rc = 1;
+
+out_match:
+       lu_buf_free(&link);
+       kfree(hash);
+
+       RETURN(rc);
+}
+
 /*
  * UPDATE lock should be taken against parent, and be released before exit;
  * child_bits lock should be taken against child, and be returned back:
@@ -1992,7 +2084,30 @@ static int mdt_getattr_name_lock(struct mdt_thread_info *info,
        lname = &info->mti_name;
        mdt_name_unpack(pill, &RMF_NAME, lname, MNF_FIX_ANON);
 
-       if (lu_name_is_valid(lname)) {
+       if (info->mti_body->mbo_valid & OBD_MD_NAMEHASH) {
+               reqbody = req_capsule_client_get(pill, &RMF_MDT_BODY);
+               if (unlikely(reqbody == NULL))
+                       RETURN(err_serious(-EPROTO));
+
+               *child_fid = reqbody->mbo_fid2;
+               if (unlikely(!fid_is_sane(child_fid)))
+                       RETURN(err_serious(-EINVAL));
+
+               if (lu_fid_eq(mdt_object_fid(parent), child_fid)) {
+                       mdt_object_get(info->mti_env, parent);
+                       child = parent;
+               } else {
+                       child = mdt_object_find(info->mti_env, info->mti_mdt,
+                                               child_fid);
+                       if (IS_ERR(child))
+                               RETURN(PTR_ERR(child));
+               }
+
+               CDEBUG(D_INODE, "getattr with lock for "DFID"/"DFID", "
+                      "ldlm_rep = %p\n",
+                      PFID(mdt_object_fid(parent)),
+                      PFID(&reqbody->mbo_fid2), ldlm_rep);
+       } else if (lu_name_is_valid(lname)) {
                if (mdt_object_remote(parent)) {
                        CERROR("%s: parent "DFID" is on remote target\n",
                               mdt_obd_name(info->mti_mdt),
@@ -2044,14 +2159,17 @@ static int mdt_getattr_name_lock(struct mdt_thread_info *info,
 
        mdt_set_disposition(info, ldlm_rep, DISP_LOOKUP_EXECD);
 
-       if (unlikely(!mdt_object_exists(parent)) && lu_name_is_valid(lname)) {
+       if (unlikely(!mdt_object_exists(parent)) &&
+           !(info->mti_body->mbo_valid & OBD_MD_NAMEHASH) &&
+           lu_name_is_valid(lname)) {
                LU_OBJECT_DEBUG(D_INODE, info->mti_env,
                                &parent->mot_obj,
                                "Parent doesn't exist!");
                GOTO(out_child, rc = -ESTALE);
        }
 
-       if (lu_name_is_valid(lname)) {
+       if (!(info->mti_body->mbo_valid & OBD_MD_NAMEHASH) &&
+           lu_name_is_valid(lname)) {
                if (info->mti_body->mbo_valid == OBD_MD_FLID) {
                        rc = mdt_raw_lookup(info, parent, lname);
 
@@ -2089,6 +2207,19 @@ static int mdt_getattr_name_lock(struct mdt_thread_info *info,
        /* step 3: lock child regardless if it is local or remote. */
        LASSERT(child);
 
+       if (info->mti_body->mbo_valid & OBD_MD_NAMEHASH) {
+               /* Here, lname is an encoded hash of on-disk name, and
+                * client is doing access without encryption key.
+                * So we need to compare name hash with the one in the request.
+                */
+               if (!find_name_matching_hash(info, lname, parent,
+                                            child, true)) {
+                       mdt_set_disposition(info, ldlm_rep, DISP_LOOKUP_NEG);
+                       mdt_clear_disposition(info, ldlm_rep, DISP_LOOKUP_POS);
+                       GOTO(out_child, rc = -ENOENT);
+               }
+       }
+
        OBD_FAIL_TIMEOUT(OBD_FAIL_MDS_RESEND, obd_timeout * 2);
        if (!mdt_object_exists(child)) {
                LU_OBJECT_DEBUG(D_INODE, info->mti_env,
@@ -7091,12 +7222,24 @@ static int mdt_fid2path(struct mdt_thread_info *info,
                RETURN(rc);
        }
 
-       if (mdt_object_remote(obj))
+       if (mdt_object_remote(obj)) {
                rc = -EREMOTE;
-       else if (!mdt_object_exists(obj))
+       } else if (!mdt_object_exists(obj)) {
                rc = -ENOENT;
-       else
-               rc = 0;
+       } else {
+               struct lu_attr la = { 0 };
+               struct dt_object *dt = mdt_obj2dt(obj);
+
+               if (dt && dt->do_ops && dt->do_ops->do_attr_get)
+                       dt_attr_get(info->mti_env, mdt_obj2dt(obj), &la);
+               if (la.la_valid & LA_FLAGS && la.la_flags & LUSTRE_ENCRYPT_FL)
+                       /* path resolution cannot be carried out on server
+                        * side for encrypted files
+                        */
+                       rc = -ENODATA;
+               else
+                       rc = 0;
+       }
 
        if (rc < 0) {
                mdt_object_put(info->mti_env, obj);
index b921ecf..96c5e19 100644 (file)
@@ -926,6 +926,9 @@ void mdt_reconstruct_open(struct mdt_thread_info *, struct mdt_lock_handle *);
 int mdt_layout_change(struct mdt_thread_info *info, struct mdt_object *obj,
                      struct mdt_lock_handle *lhc,
                      struct md_layout_change *spec);
+int find_name_matching_hash(struct mdt_thread_info *info, struct lu_name *lname,
+                           struct mdt_object *parent, struct mdt_object *child,
+                           bool force_check);
 int mdt_device_sync(const struct lu_env *env, struct mdt_device *mdt);
 
 struct lu_buf *mdt_buf(const struct lu_env *env, void *area, ssize_t len);
index 421f4fe..5e42e2e 100644 (file)
@@ -1418,6 +1418,10 @@ static int mdt_unlink_unpack(struct mdt_thread_info *info)
        attr->la_mtime = rec->ul_time;
        attr->la_mode  = rec->ul_mode;
        attr->la_valid = LA_UID | LA_GID | LA_CTIME | LA_MTIME | LA_MODE;
+       if (rec->ul_bias & MDS_FID_OP)
+               info->mti_spec.sp_cr_flags |= MDS_OP_WITH_FID;
+       else
+               info->mti_spec.sp_cr_flags &= ~MDS_OP_WITH_FID;
 
        rc = mdt_name_unpack(pill, &RMF_NAME, &rr->rr_name, 0);
        if (rc < 0)
index f7d7770..2c87123 100644 (file)
@@ -45,6 +45,7 @@
 #include <lprocfs_status.h>
 #include "mdt_internal.h"
 #include <lustre_lmv.h>
+#include <lustre_crypto.h>
 
 static inline void mdt_reint_init_ma(struct mdt_thread_info *info,
                                     struct md_attr *ma)
@@ -1141,31 +1142,36 @@ relock:
        if (rc != 0)
                GOTO(put_parent, rc);
 
-       /* lookup child object along with version checking */
-       fid_zero(child_fid);
-       rc = mdt_lookup_version_check(info, mp, &rr->rr_name, child_fid, 1);
-       if (rc != 0) {
-               /* Name might not be able to find during resend of
-                * remote unlink, considering following case.
-                * dir_A is a remote directory, the name entry of
-                * dir_A is on MDT0, the directory is on MDT1,
-                *
-                * 1. client sends unlink req to MDT1.
-                * 2. MDT1 sends name delete update to MDT0.
-                * 3. name entry is being deleted in MDT0 synchronously.
-                * 4. MDT1 is restarted.
-                * 5. client resends unlink req to MDT1. So it can not
-                *    find the name entry on MDT0 anymore.
-                * In this case, MDT1 only needs to destory the local
-                * directory.
-                */
-               if (mdt_object_remote(mp) && rc == -ENOENT &&
-                   !fid_is_zero(rr->rr_fid2) &&
-                   lustre_msg_get_flags(req->rq_reqmsg) & MSG_RESENT) {
-                       no_name = 1;
-                       *child_fid = *rr->rr_fid2;
-               } else {
-                       GOTO(unlock_parent, rc);
+       if (info->mti_spec.sp_cr_flags & MDS_OP_WITH_FID) {
+               *child_fid = *rr->rr_fid2;
+       } else {
+               /* lookup child object along with version checking */
+               fid_zero(child_fid);
+               rc = mdt_lookup_version_check(info, mp, &rr->rr_name, child_fid,
+                                             1);
+               if (rc != 0) {
+                       /* Name might not be able to find during resend of
+                        * remote unlink, considering following case.
+                        * dir_A is a remote directory, the name entry of
+                        * dir_A is on MDT0, the directory is on MDT1,
+                        *
+                        * 1. client sends unlink req to MDT1.
+                        * 2. MDT1 sends name delete update to MDT0.
+                        * 3. name entry is being deleted in MDT0 synchronously.
+                        * 4. MDT1 is restarted.
+                        * 5. client resends unlink req to MDT1. So it can not
+                        *    find the name entry on MDT0 anymore.
+                        * In this case, MDT1 only needs to destory the local
+                        * directory.
+                        */
+                       if (mdt_object_remote(mp) && rc == -ENOENT &&
+                           !fid_is_zero(rr->rr_fid2) &&
+                           lustre_msg_get_flags(req->rq_reqmsg) & MSG_RESENT) {
+                               no_name = 1;
+                               *child_fid = *rr->rr_fid2;
+                       } else {
+                               GOTO(unlock_parent, rc);
+                       }
                }
        }
 
@@ -1177,6 +1183,16 @@ relock:
        if (IS_ERR(mc))
                GOTO(unlock_parent, rc = PTR_ERR(mc));
 
+       if (info->mti_spec.sp_cr_flags & MDS_OP_WITH_FID) {
+               /* In this case, child fid is embedded in the request, and we do
+                * not have a proper name as rr_name contains an encoded
+                * hash. So find name that matches provided hash.
+                */
+               if (!find_name_matching_hash(info, &rr->rr_name,
+                                            NULL, mc, false))
+                       GOTO(put_child, rc = -ENOENT);
+       }
+
        if (!cos_incompat) {
                rc = mdt_object_striped(info, mc);
                if (rc < 0)
@@ -1318,6 +1334,9 @@ out_stat:
 unlock_child:
        mdt_reint_striped_unlock(info, mc, child_lh, einfo, rc);
 put_child:
+       if (info->mti_spec.sp_cr_flags & MDS_OP_WITH_FID &&
+           info->mti_big_buf.lb_buf)
+               lu_buf_free(&info->mti_big_buf);
        mdt_object_put(info->mti_env, mc);
 unlock_parent:
        mdt_object_unlock(info, mp, parent_lh, rc);
index 412720d..c9b93ae 100644 (file)
@@ -2397,8 +2397,8 @@ void lustre_assert_wire_constants(void)
                (unsigned)MDS_CLOSE_UPDATE_TIMES);
        LASSERTF(MDS_SETSTRIPE_CREATE == 0x00200000UL, "found 0x%.8xUL\n",
                (unsigned)MDS_SETSTRIPE_CREATE);
-       LASSERTF(MDS_OP_FID == 0x00400000UL, "found 0x%.8xUL\n",
-               (unsigned)MDS_OP_FID);
+       LASSERTF(MDS_FID_OP == 0x00400000UL, "found 0x%.8xUL\n",
+               (unsigned)MDS_FID_OP);
        LASSERTF(MDS_MIGRATE_NSONLY == 0x00800000UL, "found 0x%.8xUL\n",
                (unsigned)MDS_MIGRATE_NSONLY);
 
index 06d51e8..3b10db4 100755 (executable)
@@ -3342,8 +3342,8 @@ run_test 45 "encrypted file access semantics: MMAP"
 test_46() {
        local testdir=$DIR/$tdir/mydir
        local testfile=$testdir/myfile
-       local testdir2=$DIR/$tdir/mydir2
-       local testfile2=$testdir/myfile2
+       local testdir2=$DIR/$tdir/mydirwithaveryverylongnametotestcodebehaviour0
+       local testfile2=$testdir/myfilewithaveryverylongnametotestcodebehaviour0
        local lsfile=$TMP/lsfile
        local scrambleddir
        local scrambledfile
@@ -3361,6 +3361,11 @@ test_46() {
        mkdir $testdir
        echo test > $testfile
        echo othertest > $testfile2
+       if [[ $MDSCOUNT -gt 1 ]]; then
+               $LFS setdirstripe -c1 -i1 $testdir2
+       else
+               mkdir $testdir2
+       fi
        sync ; echo 3 > /proc/sys/vm/drop_caches
 
        # remove fscrypt key from keyring
@@ -3368,6 +3373,11 @@ test_46() {
        keyctl reap
        cancel_lru_locks
 
+       # this is $testdir2
+       scrambleddir=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type d| grep _)
+       stat $scrambleddir || error "stat $scrambleddir failed"
+       rmdir $scrambleddir || error "rmdir $scrambleddir failed"
+
        scrambleddir=$(find $DIR/$tdir/ -maxdepth 1 -mindepth 1 -type d)
        ls -1 $scrambleddir > $lsfile || error "ls $testdir failed (1)"
 
@@ -4093,7 +4103,7 @@ run_test 53 "Mixed PAGE_SIZE clients"
 test_54() {
        local testdir=$DIR/$tdir/$ID0
        local testfile=$testdir/$tfile
-       local testfile2=$testdir/${tfile}2
+       local testfile2=$testdir/${tfile}withveryverylongnametoexercisecode
        local tmpfile=$TMP/${tfile}.tmp
        local resfile=$TMP/${tfile}.res
 
index 8c3e77f..c7ebce7 100644 (file)
@@ -2432,8 +2432,8 @@ void lustre_assert_wire_constants(void)
                (unsigned)MDS_CLOSE_UPDATE_TIMES);
        LASSERTF(MDS_SETSTRIPE_CREATE == 0x00200000UL, "found 0x%.8xUL\n",
                (unsigned)MDS_SETSTRIPE_CREATE);
-       LASSERTF(MDS_OP_FID == 0x00400000UL, "found 0x%.8xUL\n",
-               (unsigned)MDS_OP_FID);
+       LASSERTF(MDS_FID_OP == 0x00400000UL, "found 0x%.8xUL\n",
+               (unsigned)MDS_FID_OP);
        LASSERTF(MDS_MIGRATE_NSONLY == 0x00800000UL, "found 0x%.8xUL\n",
                (unsigned)MDS_MIGRATE_NSONLY);