ovl: decode pure lower dir file handles
authorAmir Goldstein <amir73il@gmail.com>
Wed, 17 Jan 2018 20:32:44 +0000 (22:32 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 24 Jan 2018 10:26:04 +0000 (11:26 +0100)
Similar to decoding a pure upper dir file handle, decoding a pure lower
dir file handle is implemented by looking an overlay dentry of the same
path as the pure lower path and verifying that the overlay dentry's
real lower matches the decoded real lower file handle.

Unlike the case of upper dir file handle, the lookup of overlay path by
lower real path can fail or find a mismatched overlay dentry if any of
the lower parents have been copied up and renamed. To address this case
we will need to check if any of the lower parents are indexed.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/export.c

index 7a4b6a0..3611748 100644 (file)
@@ -215,6 +215,23 @@ nomem:
        return ERR_PTR(-ENOMEM);
 }
 
+/* Get the upper or lower dentry in stach whose on layer @idx */
+static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx)
+{
+       struct ovl_entry *oe = dentry->d_fsdata;
+       int i;
+
+       if (!idx)
+               return ovl_dentry_upper(dentry);
+
+       for (i = 0; i < oe->numlower; i++) {
+               if (oe->lowerstack[i].layer->idx == idx)
+                       return oe->lowerstack[i].dentry;
+       }
+
+       return NULL;
+}
+
 /*
  * Lookup a child overlay dentry to get a connected overlay dentry whose real
  * dentry is @real. If @real is on upper layer, we lookup a child overlay
@@ -230,10 +247,6 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected,
        struct name_snapshot name;
        int err;
 
-       /* TODO: lookup by lower real dentry */
-       if (layer->idx)
-               return ERR_PTR(-EACCES);
-
        /*
         * Lookup child overlay dentry by real name. The dir mutex protects us
         * from racing with overlay rename. If the overlay dentry that is above
@@ -244,7 +257,7 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected,
        inode_lock_nested(dir, I_MUTEX_PARENT);
        err = -ECHILD;
        parent = dget_parent(real);
-       if (ovl_dentry_upper(connected) != parent)
+       if (ovl_dentry_real_at(connected, layer->idx) != parent)
                goto fail;
 
        /*
@@ -262,7 +275,7 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected,
                dput(this);
                err = -ENOENT;
                goto fail;
-       } else if (ovl_dentry_upper(this) != real) {
+       } else if (ovl_dentry_real_at(this, layer->idx) != real) {
                dput(this);
                err = -ESTALE;
                goto fail;
@@ -294,14 +307,13 @@ static struct dentry *ovl_lookup_real(struct super_block *sb,
        int err = 0;
 
        /* TODO: use index when looking up by lower real dentry */
-       if (layer->idx)
-               return ERR_PTR(-EACCES);
 
        connected = dget(sb->s_root);
        while (!err) {
                struct dentry *next, *this;
                struct dentry *parent = NULL;
-               struct dentry *real_connected = ovl_dentry_upper(connected);
+               struct dentry *real_connected = ovl_dentry_real_at(connected,
+                                                                  layer->idx);
 
                if (real_connected == real)
                        break;
@@ -391,6 +403,7 @@ static struct dentry *ovl_get_dentry(struct super_block *sb,
 {
        struct ovl_fs *ofs = sb->s_fs_info;
        struct ovl_layer upper_layer = { .mnt = ofs->upper_mnt };
+       struct ovl_layer *layer = upper ? &upper_layer : lowerpath->layer;
        struct dentry *real = upper ?: (index ?: lowerpath->dentry);
 
        /*
@@ -400,19 +413,15 @@ static struct dentry *ovl_get_dentry(struct super_block *sb,
        if (!d_is_dir(real))
                return ovl_obtain_alias(sb, upper, lowerpath, index);
 
-       /* TODO: lookup connected dir from real lower dir */
-       if (!upper)
-               return ERR_PTR(-EACCES);
-
        /* Removed empty directory? */
-       if ((upper->d_flags & DCACHE_DISCONNECTED) || d_unhashed(upper))
+       if ((real->d_flags & DCACHE_DISCONNECTED) || d_unhashed(real))
                return ERR_PTR(-ENOENT);
 
        /*
-        * If real upper dentry is connected and hashed, get a connected
-        * overlay dentry with the same path as the real upper dentry.
+        * If real dentry is connected and hashed, get a connected overlay
+        * dentry whose real dentry is @real.
         */
-       return ovl_lookup_real(sb, upper, &upper_layer);
+       return ovl_lookup_real(sb, real, layer);
 }
 
 static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,