Whamcloud - gitweb
LU-15176 sec: present .fscrypt in subdir mount 67/46167/3
authorSebastien Buisson <sbuisson@ddn.com>
Wed, 12 Jan 2022 10:13:44 +0000 (11:13 +0100)
committerOleg Drokin <green@whamcloud.com>
Mon, 31 Jan 2022 01:34:42 +0000 (01:34 +0000)
fscrypt userspace tool works with a .fscrypt directory at the root of
the file system. In case of subdirectory mount, we virtually present
this .fscrypt directory at the root of the mount point so that fscrypt
can be used. This makes it possible to even do a subdirectory mount of
an encrypted directory, making clients access encrypted content only.
Internally, the .fscrypt directory is always stored at the root of
Lustre.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I2a0ee360f724da1df49b2be0df986d52e06f45fd
Reviewed-on: https://review.whamcloud.com/46167
Reviewed-by: Patrick Farrell <pfarrell@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Reviewed-by: James Simmons <jsimmons@infradead.org>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/include/uapi/linux/lustre/lustre_user.h
lustre/llite/crypto.c
lustre/llite/llite_lib.c
lustre/llite/namei.c
lustre/tests/sanity-sec.sh

index e9be1a6..4e3b720 100644 (file)
@@ -2172,6 +2172,7 @@ enum ioc_data_version_flags {
 #endif
 
 #define dot_lustre_name ".lustre"
+#define dot_fscrypt_name ".fscrypt"
 
 
 /********* HSM **********/
index 3d7b63e..797fa3d 100644 (file)
@@ -235,8 +235,13 @@ int ll_setup_filename(struct inode *dir, const struct qstr *iname,
        }
        rc = llcrypt_setup_filename(dir, &dname, lookup, fname);
        if (rc == -ENOENT && lookup &&
-           !llcrypt_has_encryption_key(dir) &&
-           unlikely(filename_is_volatile(iname->name, iname->len, NULL))) {
+           ((is_root_inode(dir) && iname->len == strlen(dot_fscrypt_name) &&
+             strncmp(iname->name, dot_fscrypt_name, iname->len) == 0) ||
+            (!llcrypt_has_encryption_key(dir) &&
+             unlikely(filename_is_volatile(iname->name, iname->len, NULL))))) {
+               /* In case of subdir mount of an encrypted directory, we allow
+                * lookup of /.fscrypt directory.
+                */
                /* For purpose of migration or mirroring without enc key, we
                 * allow lookup of volatile file without enc context.
                 */
index 88d193d..77aa3a7 100644 (file)
@@ -3268,7 +3268,16 @@ struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data,
                return ERR_PTR(-ENOMEM);
 
        ll_i2gids(op_data->op_suppgids, i1, i2);
-       op_data->op_fid1 = *ll_inode2fid(i1);
+       /* If the client is using a subdir mount and looks at what it sees as
+        * /.fscrypt, interpret it as the .fscrypt dir at the root of the fs.
+        */
+       if (unlikely(i1->i_sb && i1->i_sb->s_root && is_root_inode(i1) &&
+                    !fid_is_root(ll_inode2fid(i1)) &&
+                    name && namelen == strlen(dot_fscrypt_name) &&
+                    strncmp(name, dot_fscrypt_name, namelen) == 0))
+               lu_root_fid(&op_data->op_fid1);
+       else
+               op_data->op_fid1 = *ll_inode2fid(i1);
 
        if (S_ISDIR(i1->i_mode)) {
                down_read_non_owner(&ll_i2info(i1)->lli_lsm_sem);
index 7ca34c1..6681993 100644 (file)
@@ -2147,6 +2147,15 @@ static int ll_rename(struct user_namespace *mnt_userns,
        if (IS_ERR(op_data))
                RETURN(PTR_ERR(op_data));
 
+       /* If the client is using a subdir mount and does a rename to what it
+        * sees as /.fscrypt, interpret it as the .fscrypt dir at fs root.
+        */
+       if (unlikely(is_root_inode(tgt) && !fid_is_root(ll_inode2fid(tgt)) &&
+                    tgt_dchild->d_name.len == strlen(dot_fscrypt_name) &&
+                    strncmp(tgt_dchild->d_name.name, dot_fscrypt_name,
+                            tgt_dchild->d_name.len) == 0))
+               lu_root_fid(&op_data->op_fid2);
+
        if (src_dchild->d_inode)
                op_data->op_fid3 = *ll_inode2fid(src_dchild->d_inode);
 
index ee4fd1a..2e4c5b1 100755 (executable)
@@ -4234,6 +4234,8 @@ test_54() {
        local testfile2=$testdir/${tfile}withveryverylongnametoexercisecode
        local tmpfile=$TMP/${tfile}.tmp
        local resfile=$TMP/${tfile}.res
+       local fid1
+       local fid2
 
        $LCTL get_param mdc.*.import | grep -q client_encryption ||
                skip "client encryption not supported"
@@ -4309,7 +4311,75 @@ test_54() {
        $RUNAS fscrypt lock --verbose $testdir ||
                error "fscrypt lock $testdir failed (3)"
 
-       rm -f $tmpfile $resfile
+       rm -rf $tmpfile $resfile $testdir ${testdir}2 $MOUNT/.fscrypt
+
+       # remount client with subdirectory mount
+       umount_client $MOUNT || error "umount $MOUNT failed (1)"
+       export FILESET=/$tdir
+       mount_client $MOUNT ${MOUNT_OPTS} || error "remount failed (1)"
+       export FILESET=""
+
+       # setup encryption from inside this subdir mount
+       # the .fscrypt directory is going to be created at the real fs root
+       fscrypt setup --verbose $MOUNT ||
+               error "fscrypt setup $MOUNT failed (2)"
+       testdir=$MOUNT/vault
+       mkdir $testdir
+       chown -R $ID0:$ID0 $testdir
+       fid1=$(path2fid $MOUNT/.fscrypt)
+       echo "With FILESET $tdir, .fscrypt FID is $fid1"
+
+       # encrypt 'vault' dir inside the subdir mount
+       echo -e 'mypass\nmypass' | su - $USER0 -c "fscrypt encrypt --verbose \
+               --source=custom_passphrase --name=protector $testdir" ||
+               error "fscrypt encrypt failed"
+
+       echo abc > $tmpfile
+       chmod 666 $tmpfile
+       $RUNAS cp $tmpfile $testdir/encfile
+
+       $RUNAS fscrypt lock --verbose $testdir ||
+               error "fscrypt lock $testdir failed (4)"
+
+       # remount client with encrypted dir as subdirectory mount
+       umount_client $MOUNT || error "umount $MOUNT failed (2)"
+       export FILESET=/$tdir/vault
+       mount_client $MOUNT ${MOUNT_OPTS} || error "remount failed (2)"
+       export FILESET=""
+       ls -laR $MOUNT
+       fid2=$(path2fid $MOUNT/.fscrypt)
+       echo "With FILESET $tdir/vault, .fscrypt FID is $fid2"
+       [ "$fid1" == "$fid2" ] || error "fid1 $fid1 != fid2 $fid2 (1)"
+
+       # all content seen by this mount is encrypted, but .fscrypt is virtually
+       # presented, letting us call fscrypt lock/unlock
+       echo mypass | $RUNAS fscrypt unlock --verbose $MOUNT ||
+               error "fscrypt unlock $MOUNT failed (3)"
+
+       ls -laR $MOUNT
+       [ $(cat $MOUNT/encfile) == "abc" ] || error "cat encfile failed"
+
+       # remount client without subdir mount
+       umount_client $MOUNT || error "umount $MOUNT failed (3)"
+       mount_client $MOUNT ${MOUNT_OPTS} || error "remount failed (3)"
+       ls -laR $MOUNT
+       fid2=$(path2fid $MOUNT/.fscrypt)
+       echo "Without FILESET, .fscrypt FID is $fid2"
+       [ "$fid1" == "$fid2" ] || error "fid1 $fid1 != fid2 $fid2 (2)"
+
+       # because .fscrypt was actually created at the real root of the fs,
+       # we can call fscrypt lock/unlock on the encrypted dir
+       echo mypass | $RUNAS fscrypt unlock --verbose $DIR/$tdir/vault ||
+               error "fscrypt unlock $$DIR/$tdir/vault failed (4)"
+
+       ls -laR $MOUNT
+       echo c >> $DIR/$tdir/vault/encfile || error "write to encfile failed"
+
+       rm -rf $DIR/$tdir/vault/*
+       $RUNAS fscrypt lock --verbose $DIR/$tdir/vault ||
+               error "fscrypt lock $DIR/$tdir/vault failed (5)"
+
+       rm -rf $tmpfile $MOUNT/.fscrypt
 }
 run_test 54 "Encryption policies with fscrypt"