Whamcloud - gitweb
LU-16205 sec: fid2path for encrypted files 30/48930/8
authorSebastien Buisson <sbuisson@ddn.com>
Thu, 3 Nov 2022 10:52:02 +0000 (11:52 +0100)
committerOleg Drokin <green@whamcloud.com>
Fri, 3 Feb 2023 06:50:12 +0000 (06:50 +0000)
Add support of fid2path for encrypted files. Server side returns raw
encrypted path name to client, which needs to process the returned
string. This is done from top to bottom, by iteratively decrypting
parent name and then doing a lookup on it, so that child can in turn
be decrypted.

For encrypted files that do not have their names encrypted, lookups
can be skipped. Indeed, name decryption is a no-op in this case, which
means it is not necessary to fetch the encryption key associated with
the parent inode.

Without the encryption key, lookups are skipped for the same reason.
But names have to be encoded and/or digested. So server needs to
insert FIDs of individual path components in the returned string.
These FIDs are interpreted by the client to build encoded/digested
names.

Add sanity-sec test_63 to exercise this new capability.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I165bf2e5657037ae2e25c9378e4713537ea94bec
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/48930
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: jsimmons <jsimmons@infradead.org>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/include/lustre_export.h
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/llite/file.c
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/lmv/lmv_obd.c
lustre/mdc/mdc_request.c
lustre/mdt/mdt_handler.c
lustre/tests/sanity-sec.sh

index 7a658d1..d3c0d3f 100644 (file)
@@ -484,6 +484,11 @@ static inline int exp_connect_encrypt(struct obd_export *exp)
        return !!(exp_connect_flags2(exp) & OBD_CONNECT2_ENCRYPT);
 }
 
+static inline int exp_connect_encrypt_fid2path(struct obd_export *exp)
+{
+       return !!(exp_connect_flags2(exp) & OBD_CONNECT2_ENCRYPT_FID2PATH);
+}
+
 static inline int exp_connect_lseek(struct obd_export *exp)
 {
        return !!(exp_connect_flags2(exp) & OBD_CONNECT2_LSEEK);
index 24f0da9..3e05502 100644 (file)
@@ -911,7 +911,8 @@ struct ptlrpc_body_v2 {
                                OBD_CONNECT2_REP_MBITS | \
                                OBD_CONNECT2_ATOMIC_OPEN_LOCK | \
                                OBD_CONNECT2_BATCH_RPC | \
-                               OBD_CONNECT2_ENCRYPT_NAME)
+                               OBD_CONNECT2_ENCRYPT_NAME | \
+                               OBD_CONNECT2_ENCRYPT_FID2PATH)
 
 #define OST_CONNECT_SUPPORTED  (OBD_CONNECT_SRVLOCK | OBD_CONNECT_GRANT | \
                                OBD_CONNECT_REQPORTAL | OBD_CONNECT_VERSION | \
index c09a0b7..0a7a88d 100644 (file)
@@ -2865,14 +2865,150 @@ out:
        RETURN(rc);
 }
 
+static int fid2path_for_enc_file(struct inode *parent, char *gfpath,
+                                __u32 gfpathlen)
+{
+       struct dentry *de = NULL, *de_parent = d_find_any_alias(parent);
+       struct llcrypt_str lltr = LLTR_INIT(NULL, 0);
+       struct llcrypt_str de_name;
+       char *p, *ptr = gfpath;
+       size_t len = 0, len_orig = 0;
+       int enckey = -1, nameenc = -1;
+       int rc = 0;
+
+       gfpath++;
+       while ((p = strsep(&gfpath, "/")) != NULL) {
+               struct lu_fid fid;
+
+               de = NULL;
+               if (!*p) {
+                       dput(de_parent);
+                       break;
+               }
+               len_orig = strlen(p);
+
+               rc = sscanf(p, "["SFID"]", RFID(&fid));
+               if (rc == 3)
+                       p = strchr(p, ']') + 1;
+               else
+                       fid_zero(&fid);
+               rc = 0;
+               len = strlen(p);
+
+               if (!IS_ENCRYPTED(parent)) {
+                       if (gfpathlen < len + 1) {
+                               dput(de_parent);
+                               rc = -EOVERFLOW;
+                               break;
+                       }
+                       memmove(ptr, p, len);
+                       p = ptr;
+                       ptr += len;
+                       *(ptr++) = '/';
+                       gfpathlen -= len + 1;
+                       goto lookup;
+               }
+
+               /* From here, we know parent is encrypted */
+
+               if (enckey != 0) {
+                       rc = llcrypt_get_encryption_info(parent);
+                       if (rc && rc != -ENOKEY) {
+                               dput(de_parent);
+                               break;
+                       }
+               }
+
+               if (enckey == -1) {
+                       if (llcrypt_has_encryption_key(parent))
+                               enckey = 1;
+                       else
+                               enckey = 0;
+                       if (enckey == 1)
+                               nameenc =
+                                       llcrypt_policy_has_filename_enc(parent);
+               }
+
+               /* Even if names are not encrypted, we still need to call
+                * ll_fname_disk_to_usr in order to decode names as they are
+                * coming from the wire.
+                */
+               rc = llcrypt_fname_alloc_buffer(parent, NAME_MAX + 1, &lltr);
+               if (rc < 0) {
+                       dput(de_parent);
+                       break;
+               }
+
+               de_name.name = p;
+               de_name.len = len;
+               rc = ll_fname_disk_to_usr(parent, 0, 0, &de_name,
+                                         &lltr, &fid);
+               if (rc) {
+                       llcrypt_fname_free_buffer(&lltr);
+                       dput(de_parent);
+                       break;
+               }
+               lltr.name[lltr.len] = '\0';
+
+               if (lltr.len <= len_orig && gfpathlen >= lltr.len + 1) {
+                       memcpy(ptr, lltr.name, lltr.len);
+                       p = ptr;
+                       len = lltr.len;
+                       ptr += lltr.len;
+                       *(ptr++) = '/';
+                       gfpathlen -= lltr.len + 1;
+               } else {
+                       rc = -EOVERFLOW;
+               }
+               llcrypt_fname_free_buffer(&lltr);
+
+               if (rc == -EOVERFLOW) {
+                       dput(de_parent);
+                       break;
+               }
+
+lookup:
+               if (!gfpath) {
+                       /* We reached the end of the string, which means
+                        * we are dealing with the last component in the path.
+                        * So save a useless lookup and exit.
+                        */
+                       dput(de_parent);
+                       break;
+               }
+
+               if (enckey == 0 || nameenc == 0)
+                       continue;
+
+               inode_lock(parent);
+               de = lookup_one_len(p, de_parent, len);
+               inode_unlock(parent);
+               if (IS_ERR_OR_NULL(de) || !de->d_inode) {
+                       dput(de_parent);
+                       rc = -ENODATA;
+                       break;
+               }
+
+               parent = de->d_inode;
+               dput(de_parent);
+               de_parent = de;
+       }
+
+       if (len)
+               *(ptr - 1) = '\0';
+       if (!IS_ERR_OR_NULL(de))
+               dput(de);
+       return rc;
+}
+
 int ll_fid2path(struct inode *inode, void __user *arg)
 {
-       struct obd_export       *exp = ll_i2mdexp(inode);
+       struct obd_export *exp = ll_i2mdexp(inode);
        const struct getinfo_fid2path __user *gfin = arg;
-       __u32                    pathlen;
-       struct getinfo_fid2path *gfout;
-       size_t                   outsize;
-       int                      rc;
+       __u32 pathlen, pathlen_orig;
+       struct getinfo_fid2path *gfout;
+       size_t outsize;
+       int rc = 0;
 
        ENTRY;
 
@@ -2886,7 +3022,9 @@ int ll_fid2path(struct inode *inode, void __user *arg)
 
        if (pathlen > PATH_MAX)
                RETURN(-EINVAL);
+       pathlen_orig = pathlen;
 
+gf_alloc:
        outsize = sizeof(*gfout) + pathlen;
        OBD_ALLOC(gfout, outsize);
        if (gfout == NULL)
@@ -2898,17 +3036,35 @@ int ll_fid2path(struct inode *inode, void __user *arg)
         * can lookup the correct path, this is mainly for fileset.
         * old server without fileset mount support will ignore this. */
        *gfout->gf_u.gf_root_fid = *ll_inode2fid(inode);
+       gfout->gf_pathlen = pathlen;
 
        /* Call mdc_iocontrol */
        rc = obd_iocontrol(OBD_IOC_FID2PATH, exp, outsize, gfout, NULL);
        if (rc != 0)
                GOTO(gf_free, rc);
 
-       if (copy_to_user(arg, gfout, outsize))
+       if (gfout->gf_pathlen && gfout->gf_u.gf_path[0] == '/') {
+               /* by convention, server side (mdt_path_current()) puts
+                * a leading '/' to tell client that we are dealing with
+                * an encrypted file
+                */
+               rc = fid2path_for_enc_file(inode, gfout->gf_u.gf_path,
+                                          gfout->gf_pathlen);
+               if (rc)
+                       GOTO(gf_free, rc);
+               if (strlen(gfout->gf_u.gf_path) > gfin->gf_pathlen)
+                       GOTO(gf_free, rc = -EOVERFLOW);
+       }
+
+       if (copy_to_user(arg, gfout, sizeof(*gfout) + pathlen_orig))
                rc = -EFAULT;
 
 gf_free:
        OBD_FREE(gfout, outsize);
+       if (rc == -ENAMETOOLONG) {
+               pathlen += PATH_MAX;
+               GOTO(gf_alloc, rc);
+       }
        RETURN(rc);
 }
 
index 87de8fd..781ea86 100644 (file)
@@ -538,6 +538,23 @@ static inline void obd_connect_set_name_enc(struct obd_connect_data *data)
 #endif
 }
 
+static inline bool obd_connect_has_enc_fid2path(struct obd_connect_data *data)
+{
+#ifdef HAVE_LUSTRE_CRYPTO
+       return data->ocd_connect_flags & OBD_CONNECT_FLAGS2 &&
+               data->ocd_connect_flags2 & OBD_CONNECT2_ENCRYPT_FID2PATH;
+#else
+       return false;
+#endif
+}
+
+static inline void obd_connect_set_enc_fid2path(struct obd_connect_data *data)
+{
+#ifdef HAVE_LUSTRE_CRYPTO
+       data->ocd_connect_flags2 |= OBD_CONNECT2_ENCRYPT_FID2PATH;
+#endif
+}
+
 /*
  * Locking to guarantee consistency of non-atomic updates to long long i_size,
  * consistency between file size and KMS.
index 407adc3..b6f98e1 100644 (file)
@@ -381,6 +381,7 @@ static int client_common_fill_super(struct super_block *sb, char *md, char *dt)
 
        obd_connect_set_secctx(data);
        if (ll_sbi_has_encrypt(sbi)) {
+               obd_connect_set_enc_fid2path(data);
                obd_connect_set_name_enc(data);
                obd_connect_set_enc(data);
        }
index 7801b3b..84cc639 100644 (file)
@@ -571,6 +571,8 @@ static int lmv_fid2path(struct obd_export *exp, int len, void *karg,
        struct getinfo_fid2path *remote_gf = NULL;
        struct lu_fid root_fid;
        int remote_gf_size = 0;
+       int currentisenc = 0;
+       int globalisenc = 0;
        int rc;
 
        gf = karg;
@@ -586,10 +588,23 @@ repeat_fid2path:
        if (rc != 0 && rc != -EREMOTE)
                GOTO(out_fid2path, rc);
 
+       if (gf->gf_u.gf_path[0] == '/') {
+               /* by convention, server side (mdt_path_current()) puts
+                * a leading '/' to tell client that we are dealing with
+                * an encrypted file
+                */
+               currentisenc = 1;
+               globalisenc = 1;
+       } else {
+               currentisenc = 0;
+       }
+
        /* If remote_gf != NULL, it means just building the
-        * path on the remote MDT, copy this path segement to gf */
+        * path on the remote MDT, copy this path segment to gf.
+        */
        if (remote_gf != NULL) {
                struct getinfo_fid2path *ori_gf;
+               int oldisenc = 0;
                char *ptr;
                int len;
 
@@ -599,13 +614,22 @@ repeat_fid2path:
                        GOTO(out_fid2path, rc = -EOVERFLOW);
 
                ptr = ori_gf->gf_u.gf_path;
+               oldisenc = ptr[0] == '/';
 
                len = strlen(gf->gf_u.gf_path);
-               /* move the current path to the right to release space
-                * for closer-to-root part */
-               memmove(ptr + len + 1, ptr, strlen(ori_gf->gf_u.gf_path));
-               memcpy(ptr, gf->gf_u.gf_path, len);
-               ptr[len] = '/';
+               if (len) {
+                       /* move the current path to the right to release space
+                        * for closer-to-root part
+                        */
+                       memmove(ptr + len - currentisenc + 1 + globalisenc,
+                               ptr + oldisenc,
+                               strlen(ori_gf->gf_u.gf_path) - oldisenc + 1);
+                       if (globalisenc)
+                               *(ptr++) = '/';
+                       memcpy(ptr, gf->gf_u.gf_path + currentisenc,
+                              len - currentisenc);
+                       ptr[len - currentisenc] = '/';
+               }
        }
 
        CDEBUG(D_INFO, "%s: get path %s "DFID" rec: %llu ln: %u\n",
@@ -618,11 +642,11 @@ repeat_fid2path:
 
        /* sigh, has to go to another MDT to do path building further */
        if (remote_gf == NULL) {
-               remote_gf_size = sizeof(*remote_gf) + PATH_MAX;
+               remote_gf_size = sizeof(*remote_gf) + len - sizeof(*gf);
                OBD_ALLOC(remote_gf, remote_gf_size);
                if (remote_gf == NULL)
                        GOTO(out_fid2path, rc = -ENOMEM);
-               remote_gf->gf_pathlen = PATH_MAX;
+               remote_gf->gf_pathlen = len - sizeof(*gf);
        }
 
        if (!fid_is_sane(&gf->gf_fid)) {
index 66c8954..db75544 100644 (file)
@@ -1698,8 +1698,6 @@ static int mdc_ioc_fid2path(struct obd_export *exp, struct getinfo_fid2path *gf)
        void *key;
        int rc;
 
-       if (gf->gf_pathlen > PATH_MAX)
-               RETURN(-ENAMETOOLONG);
        if (gf->gf_pathlen < 2)
                RETURN(-EOVERFLOW);
 
@@ -1731,11 +1729,10 @@ static int mdc_ioc_fid2path(struct obd_export *exp, struct getinfo_fid2path *gf)
        if (vallen > sizeof(*gf) + gf->gf_pathlen)
                GOTO(out, rc = -EOVERFLOW);
 
-       CDEBUG(D_IOCTL, "path got "DFID" from %llu #%d: %s\n",
+       CDEBUG(D_IOCTL, "path got "DFID" from %llu #%d: %.*s\n",
               PFID(&gf->gf_fid), gf->gf_recno, gf->gf_linkno,
-              gf->gf_pathlen < 512 ? gf->gf_u.gf_path :
-              /* only log the last 512 characters of the path */
-              gf->gf_u.gf_path + gf->gf_pathlen - 512);
+              /* only log the first 512 characters of the path */
+              512, gf->gf_u.gf_path);
 
 out:
        OBD_FREE(key, keylen);
index 19e255a..7a17d54 100644 (file)
@@ -7039,6 +7039,10 @@ static int mdt_path_current(struct mdt_thread_info *info,
        struct mdt_object *mdt_obj;
        struct link_ea_header *leh;
        struct link_ea_entry *lee;
+       bool worthchecking = true;
+       bool needsfid = false;
+       bool supported = false;
+       int isenc = -1;
        char *ptr;
        int reclen;
        int rc = 0;
@@ -7081,6 +7085,39 @@ static int mdt_path_current(struct mdt_thread_info *info,
                        GOTO(remote_out, rc = -EREMOTE);
                }
 
+               if (worthchecking) {
+                       /* we need to know if the FID being
+                        * looked up is encrypted
+                        */
+                       struct lu_attr la = { 0 };
+                       struct dt_object *dt = mdt_obj2dt(mdt_obj);
+
+                       if (dt && dt->do_ops && dt->do_ops->do_attr_get)
+                               dt_attr_get(info->mti_env, dt, &la);
+                       if (la.la_valid & LA_FLAGS &&
+                           la.la_flags & LUSTRE_ENCRYPT_FL) {
+                               if (!supported && mdt_info_req(info) &&
+                                   !exp_connect_encrypt_fid2path(
+                                           mdt_info_req(info)->rq_export)) {
+                                       /* client does not support fid2path
+                                        * for encrypted files
+                                        */
+                                       mdt_object_put(info->mti_env, mdt_obj);
+                                       GOTO(out, rc = -ENODATA);
+                               } else {
+                                       supported = true;
+                               }
+                               needsfid = true;
+                               if (isenc == -1)
+                                       isenc = 1;
+                       } else {
+                               worthchecking = false;
+                               needsfid = false;
+                               if (isenc == -1)
+                                       isenc = 0;
+                       }
+               }
+
                rc = mdt_links_read(info, mdt_obj, &ldata);
                if (rc != 0) {
                        mdt_object_put(info->mti_env, mdt_obj);
@@ -7118,8 +7155,21 @@ static int mdt_path_current(struct mdt_thread_info *info,
                /* Pack the name in the end of the buffer */
                ptr -= tmpname->ln_namelen;
                if (ptr - 1 <= fp->gf_u.gf_path)
-                       GOTO(out, rc = -EOVERFLOW);
+                       GOTO(out, rc = -ENAMETOOLONG);
                strncpy(ptr, tmpname->ln_name, tmpname->ln_namelen);
+               if (needsfid) {
+                       /* Pack FID before file name, so that client can build
+                        * encoded/digested form.
+                        */
+                       char fidstr[FID_LEN + 1];
+
+                       snprintf(fidstr, sizeof(fidstr), DFID,
+                                PFID(&fp->gf_fid));
+                       ptr -= strlen(fidstr);
+                       if (ptr - 1 <= fp->gf_u.gf_path)
+                               GOTO(out, rc = -ENAMETOOLONG);
+                       strncpy(ptr, fidstr, strlen(fidstr));
+               }
                *(--ptr) = '/';
 
                /* keep the last resolved fid to the client, so the
@@ -7134,7 +7184,8 @@ static int mdt_path_current(struct mdt_thread_info *info,
        rc = 0;
 
 remote_out:
-       ptr++; /* skip leading / */
+       if (isenc != 1)
+               ptr++; /* skip leading / unless this is an encrypted file */
        memmove(fp->gf_u.gf_path, ptr,
                fp->gf_u.gf_path + fp->gf_pathlen - ptr);
 
@@ -7229,24 +7280,12 @@ 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 {
-               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;
-       }
+       else
+               rc = 0;
 
        if (rc < 0) {
                mdt_object_put(info->mti_env, obj);
index ad53adb..57f6ecd 100755 (executable)
@@ -5181,6 +5181,147 @@ test_62() {
 }
 run_test 62 "e2fsck with encrypted files"
 
+create_files() {
+       local path
+
+       for path in "${paths[@]}"; do
+               touch $path
+       done
+}
+
+build_fids() {
+       local path
+
+       for path in "${paths[@]}"; do
+               fids+=("$(lfs path2fid $path)")
+       done
+}
+
+check_fids() {
+       for fid in "${fids[@]}"; do
+               echo $fid
+               respath=$(lfs fid2path $MOUNT $fid)
+               echo -e "\t" $respath
+               ls -li $respath >/dev/null
+               [ $? -eq 0 ] || error "fid2path $fid failed"
+       done
+}
+
+test_63() {
+       declare -a fids
+       declare -a paths
+       local vaultdir1=$DIR/$tdir/vault1==dir
+       local vaultdir2=$DIR/$tdir/vault2==dir
+       local longfname1="longfilenamewitha=inthemiddletotestbehaviorregardingthedigestedform"
+       local longdname="longdirectorynamewitha=inthemiddletotestbehaviorregardingthedigestedform"
+       local longfname2="$longdname/${longfname1}2"
+
+       (( $MDS1_VERSION > $(version_code 2.15.53) )) ||
+               skip "Need MDS version at least 2.15.53"
+
+       $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"
+
+       which fscrypt || skip "This test needs fscrypt userspace tool"
+
+       yes | fscrypt setup --force --verbose ||
+               echo "fscrypt global setup already done"
+       sed -i 's/\(.*\)policy_version\(.*\):\(.*\)\"[0-9]*\"\(.*\)/\1policy_version\2:\3"2"\4/' \
+               /etc/fscrypt.conf
+       yes | fscrypt setup --verbose $MOUNT ||
+               echo "fscrypt setup $MOUNT already done"
+
+       # enable_filename_encryption tunable only available for client
+       # built against embedded llcrypt. If client is built against in-kernel
+       # fscrypt, file names are always encrypted.
+       $LCTL get_param mdc.*.connect_flags | grep -q name_encryption &&
+         nameenc=$(lctl get_param -n llite.*.enable_filename_encryption |
+                       head -n1)
+       if [ -n "$nameenc" ]; then
+               do_facet mgs $LCTL set_param -P \
+                       llite.*.enable_filename_encryption=1
+               [ $? -eq 0 ] ||
+                       error "set_param -P \
+                               llite.*.enable_filename_encryption=1 failed"
+
+               wait_update_facet --verbose client \
+                       "$LCTL get_param -n llite.*.enable_filename_encryption \
+                       | head -n1" 1 30 ||
+                       error "enable_filename_encryption not set on client"
+       fi
+
+       mkdir -p $vaultdir1
+       echo -e 'mypass\nmypass' | fscrypt encrypt --verbose \
+               --source=custom_passphrase --name=protector_63_1 $vaultdir1 ||
+               error "fscrypt encrypt $vaultdir1 failed"
+
+       mkdir $vaultdir1/dirA
+       mkdir $vaultdir1/$longdname
+       paths=("$vaultdir1/fileA")
+       paths+=("$vaultdir1/dirA/fileB")
+       paths+=("$vaultdir1/$longfname1")
+       paths+=("$vaultdir1/$longfname2")
+       create_files
+
+       paths+=("$vaultdir1/dirA")
+       paths+=("$vaultdir1/$longdname")
+
+       build_fids
+       check_fids
+
+       fscrypt lock --verbose $vaultdir1 ||
+               error "fscrypt lock $vaultdir1 failed (1)"
+
+       check_fids
+
+       if [ -z "$nameenc" ]; then
+               echo "Rest of the test requires disabling name encryption"
+               exit 0
+       fi
+
+       # disable name encryption
+       do_facet mgs $LCTL set_param -P llite.*.enable_filename_encryption=0
+       [ $? -eq 0 ] ||
+               error "set_param -P llite.*.enable_filename_encryption=0 failed"
+
+       wait_update_facet --verbose client \
+               "$LCTL get_param -n llite.*.enable_filename_encryption \
+               | head -n1" 0 30 ||
+               error "enable_filename_encryption not set back to default"
+
+       mkdir -p $vaultdir2
+       echo -e 'mypass\nmypass' | fscrypt encrypt --verbose \
+               --source=custom_passphrase --name=protector_63_2 $vaultdir2 ||
+               error "fscrypt encrypt $vaultdir2 failed"
+
+       mkdir $vaultdir2/dirA
+       mkdir $vaultdir2/$longdname
+       paths=()
+       fids=()
+       paths=("$vaultdir2/fileA")
+       paths+=("$vaultdir2/dirA/fileB")
+       paths+=("$vaultdir2/$longfname1")
+       paths+=("$vaultdir2/$longfname2")
+       create_files
+
+       paths+=("$vaultdir2/dirA")
+       paths+=("$vaultdir2/$longdname")
+
+       build_fids
+       check_fids
+
+       fscrypt lock --verbose $vaultdir2 ||
+               error "fscrypt lock $vaultdir2 failed (2)"
+
+       check_fids
+
+       rm -rf $MOUNT/.fscrypt
+}
+run_test 63 "fid2path with encrypted files"
+
 log "cleanup: ======================================================"
 
 sec_unsetup() {