diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 862c368883c9..9da498ea75db 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -34,17 +34,35 @@ * -------------------------------- * Pure upper | U * Non-indexed upper | U - * Indexed upper | L - * Non-upper | L + * Indexed upper | L (*) + * Non-upper | L (*) * * U = upper file handle * L = lower file handle + * + * (*) Connecting an overlay dir from real lower dentry is not always + * possible when there are redirects in lower layers. To mitigate this case, + * we copy up the lower dir first and then encode an upper dir file handle. */ static bool ovl_should_encode_origin(struct dentry *dentry) { + struct ovl_fs *ofs = dentry->d_sb->s_fs_info; + if (!ovl_dentry_lower(dentry)) return false; + /* + * Decoding a merge dir, whose origin's parent is under a redirected + * lower dir is not always possible. As a simple aproximation, we do + * not encode lower dir file handles when overlay has multiple lower + * layers and origin is below the topmost lower layer. + * + * TODO: copy up only the parent that is under redirected lower. + */ + if (d_is_dir(dentry) && ofs->upper_mnt && + OVL_E(dentry)->lowerstack[0].layer->idx > 1) + return false; + /* Decoding a non-indexed upper from origin is not implemented */ if (ovl_dentry_upper(dentry) && !ovl_test_flag(OVL_INDEX, d_inode(dentry))) @@ -53,16 +71,43 @@ static bool ovl_should_encode_origin(struct dentry *dentry) return true; } +static int ovl_encode_maybe_copy_up(struct dentry *dentry) +{ + int err; + + if (ovl_dentry_upper(dentry)) + return 0; + + err = ovl_want_write(dentry); + if (err) + return err; + + err = ovl_copy_up(dentry); + + ovl_drop_write(dentry); + return err; +} + static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen) { - struct dentry *upper = ovl_dentry_upper(dentry); + struct dentry *upper; struct dentry *origin = ovl_dentry_lower(dentry); struct ovl_fh *fh = NULL; int err; - if (!ovl_should_encode_origin(dentry)) - origin = NULL; + /* + * If we should not encode a lower dir file handle, copy up and encode + * an upper dir file handle. + */ + if (!ovl_should_encode_origin(dentry)) { + err = ovl_encode_maybe_copy_up(dentry); + if (err) + goto fail; + origin = NULL; + } + + upper = ovl_dentry_upper(dentry); err = -EACCES; if (!upper || origin) goto fail;