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);
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 | \
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;
if (pathlen > PATH_MAX)
RETURN(-EINVAL);
+ pathlen_orig = pathlen;
+gf_alloc:
outsize = sizeof(*gfout) + pathlen;
OBD_ALLOC(gfout, outsize);
if (gfout == NULL)
* 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);
}
#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.
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);
}
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;
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;
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",
/* 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)) {
void *key;
int rc;
- if (gf->gf_pathlen > PATH_MAX)
- RETURN(-ENAMETOOLONG);
if (gf->gf_pathlen < 2)
RETURN(-EOVERFLOW);
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);
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;
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);
/* 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
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);
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);
}
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() {