@@ -284,6 +284,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);
@@ -2744,12 +2744,146 @@ static int ll_do_fiemap(struct inode *inode, struct fiemap *fiemap,
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 fscrypt_str lltr = FSTR_INIT(NULL, 0);
+ struct fscrypt_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 = fscrypt_get_encryption_info(parent);
+ if (rc && rc != -ENOKEY) {
+ dput(de_parent);
+ break;
+ }
+ }
+
+ if (enckey == -1) {
+ if (fscrypt_has_encryption_key(parent))
+ enckey = 1;
+ else
+ enckey = 0;
+ if (enckey == 1)
+ nameenc = true;
+ }
+
+ /* 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 = fscrypt_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) {
+ fscrypt_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;
+ }
+ fscrypt_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);
const struct getinfo_fid2path __user *gfin = arg;
struct getinfo_fid2path *gfout;
- u32 pathlen;
+ u32 pathlen, pathlen_orig;
size_t outsize;
int rc;
@@ -2763,7 +2897,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;
gfout = kzalloc(outsize, GFP_KERNEL);
@@ -2781,17 +2917,37 @@ int ll_fid2path(struct inode *inode, void __user *arg)
* old server without fileset mount support will ignore this.
*/
*gfout->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;
- if (copy_to_user(arg, gfout, outsize))
+ if (gfout->gf_pathlen && gfout->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_path,
+ gfout->gf_pathlen);
+ if (rc)
+ goto gf_free;
+ if (strlen(gfout->gf_path) > gfin->gf_pathlen) {
+ rc = -EOVERFLOW;
+ goto gf_free;
+ }
+ }
+
+ if (copy_to_user(arg, gfout, sizeof(*gfout) + pathlen_orig))
rc = -EFAULT;
gf_free:
kfree(gfout);
+ if (rc == -ENAMETOOLONG) {
+ pathlen += PATH_MAX;
+ goto gf_alloc;
+ }
return rc;
}
@@ -523,6 +523,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.
@@ -358,6 +358,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);
}
@@ -551,6 +551,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;
tgt = lmv_fid2tgt(lmv, &gf->gf_fid);
@@ -565,11 +567,23 @@ static int lmv_fid2path(struct obd_export *exp, int len, void *karg,
if (rc != 0 && rc != -EREMOTE)
goto out_fid2path;
+ if (gf->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 segment to gf
*/
if (remote_gf) {
struct getinfo_fid2path *ori_gf;
+ int oldisenc = 0;
char *ptr;
int len;
@@ -581,14 +595,22 @@ static int lmv_fid2path(struct obd_export *exp, int len, void *karg,
}
ptr = ori_gf->gf_path;
+ oldisenc = ptr[0] == '/';
len = strlen(gf->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_path));
- memcpy(ptr, gf->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_path) - oldisenc + 1);
+ if (globalisenc)
+ *(ptr++) = '/';
+ memcpy(ptr, gf->gf_path + currentisenc,
+ len - currentisenc);
+ ptr[len - currentisenc] = '/';
+ }
}
CDEBUG(D_INFO, "%s: get path %s " DFID " rec: %llu ln: %u\n",
@@ -601,13 +623,13 @@ static int lmv_fid2path(struct obd_export *exp, int len, void *karg,
/* sigh, has to go to another MDT to do path building further */
if (!remote_gf) {
- remote_gf_size = sizeof(*remote_gf) + PATH_MAX;
+ remote_gf_size = sizeof(*remote_gf) + len - sizeof(*gf);
remote_gf = kzalloc(remote_gf_size, GFP_NOFS);
if (!remote_gf) {
rc = -ENOMEM;
goto out_fid2path;
}
- remote_gf->gf_pathlen = PATH_MAX;
+ remote_gf->gf_pathlen = len - sizeof(*gf);
}
if (!fid_is_sane(&gf->gf_fid)) {
@@ -1707,8 +1707,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;
@@ -1746,12 +1744,10 @@ static int mdc_ioc_fid2path(struct obd_export *exp, struct getinfo_fid2path *gf)
goto out;
}
- 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_path :
- /* only log the last 512 characters of the path */
- gf->gf_path + gf->gf_pathlen - 512);
-
+ /* only log the first 512 characters of the path */
+ 512, gf->gf_path);
out:
kfree(key);
return rc;