aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2016-12-16 05:02:56 -0500
committerMiklos Szeredi <mszeredi@redhat.com>2016-12-16 05:02:56 -0500
commita6c6065511411c57167a6cdae0c33263fb662b51 (patch)
treecd2c7904884c557a0d7d26a88efc737a8885407a
parent02b69b284cd7815239fabfe895bfef9a9eb5a3ce (diff)
ovl: redirect on rename-dir
Current code returns EXDEV when a directory would need to be copied up to move. We could copy up the directory tree in this case, but there's another, simpler solution: point to old lower directory from moved upper directory. This is achieved with a "trusted.overlay.redirect" xattr storing the path relative to the root of the overlay. After such attribute has been set, the directory can be moved without further actions required. This is a backward incompatible feature, old kernels won't be able to correctly mount an overlay containing redirected directories. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--Documentation/filesystems/overlayfs.txt21
-rw-r--r--fs/overlayfs/copy_up.c20
-rw-r--r--fs/overlayfs/dir.c138
-rw-r--r--fs/overlayfs/overlayfs.h4
-rw-r--r--fs/overlayfs/ovl_entry.h1
-rw-r--r--fs/overlayfs/super.c12
-rw-r--r--fs/overlayfs/util.c29
7 files changed, 195 insertions, 30 deletions
diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.txt
index 7aeb8e8d80cf..fb6f3070c6fe 100644
--- a/Documentation/filesystems/overlayfs.txt
+++ b/Documentation/filesystems/overlayfs.txt
@@ -130,6 +130,23 @@ directory.
130Readdir on directories that are not merged is simply handled by the 130Readdir on directories that are not merged is simply handled by the
131underlying directory (upper or lower). 131underlying directory (upper or lower).
132 132
133renaming directories
134--------------------
135
136When renaming a directory that is on the lower layer or merged (i.e. the
137directory was not created on the upper layer to start with) overlayfs can
138handle it in two different ways:
139
1401) return EXDEV error: this error is returned by rename(2) when trying to
141 move a file or directory across filesystem boundaries. Hence
142 applications are usually prepared to hande this error (mv(1) for example
143 recursively copies the directory tree). This is the default behavior.
144
1452) If the "redirect_dir" feature is enabled, then the directory will be
146 copied up (but not the contents). Then the "trusted.overlay.redirect"
147 extended attribute is set to the path of the original location from the
148 root of the overlay. Finally the directory is moved to the new
149 location.
133 150
134Non-directories 151Non-directories
135--------------- 152---------------
@@ -189,8 +206,8 @@ If a file with multiple hard links is copied up, then this will
189"break" the link. Changes will not be propagated to other names 206"break" the link. Changes will not be propagated to other names
190referring to the same inode. 207referring to the same inode.
191 208
192Directory trees are not copied up. If rename(2) is performed on a directory 209Unless "redirect_dir" feature is enabled, rename(2) on a lower or merged
193which is on the lower layer or is merged, then -EXDEV will be returned. 210directory will fail with EXDEV.
194 211
195Changes to underlying filesystems 212Changes to underlying filesystems
196--------------------------------- 213---------------------------------
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index f18c1a616e9e..e191c631b17f 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -324,17 +324,11 @@ out_cleanup:
324/* 324/*
325 * Copy up a single dentry 325 * Copy up a single dentry
326 * 326 *
327 * Directory renames only allowed on "pure upper" (already created on 327 * All renames start with copy up of source if necessary. The actual
328 * upper filesystem, never copied up). Directories which are on lower or 328 * rename will only proceed once the copy up was successful. Copy up uses
329 * are merged may not be renamed. For these -EXDEV is returned and 329 * upper parent i_mutex for exclusion. Since rename can change d_parent it
330 * userspace has to deal with it. This means, when copying up a 330 * is possible that the copy up will lock the old parent. At that point
331 * directory we can rely on it and ancestors being stable. 331 * the file will have already been copied up anyway.
332 *
333 * Non-directory renames start with copy up of source if necessary. The
334 * actual rename will only proceed once the copy up was successful. Copy
335 * up uses upper parent i_mutex for exclusion. Since rename can change
336 * d_parent it is possible that the copy up will lock the old parent. At
337 * that point the file will have already been copied up anyway.
338 */ 332 */
339int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, 333int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
340 struct path *lowerpath, struct kstat *stat) 334 struct path *lowerpath, struct kstat *stat)
@@ -346,7 +340,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
346 struct path parentpath; 340 struct path parentpath;
347 struct dentry *lowerdentry = lowerpath->dentry; 341 struct dentry *lowerdentry = lowerpath->dentry;
348 struct dentry *upperdir; 342 struct dentry *upperdir;
349 struct dentry *upperdentry;
350 const char *link = NULL; 343 const char *link = NULL;
351 344
352 if (WARN_ON(!workdir)) 345 if (WARN_ON(!workdir))
@@ -372,8 +365,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
372 pr_err("overlayfs: failed to lock workdir+upperdir\n"); 365 pr_err("overlayfs: failed to lock workdir+upperdir\n");
373 goto out_unlock; 366 goto out_unlock;
374 } 367 }
375 upperdentry = ovl_dentry_upper(dentry); 368 if (ovl_dentry_upper(dentry)) {
376 if (upperdentry) {
377 /* Raced with another copy-up? Nothing to do, then... */ 369 /* Raced with another copy-up? Nothing to do, then... */
378 err = 0; 370 err = 0;
379 goto out_unlock; 371 goto out_unlock;
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index f24b6b967901..c1de84c1c5ec 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -15,6 +15,7 @@
15#include <linux/posix_acl.h> 15#include <linux/posix_acl.h>
16#include <linux/posix_acl_xattr.h> 16#include <linux/posix_acl_xattr.h>
17#include <linux/atomic.h> 17#include <linux/atomic.h>
18#include <linux/ratelimit.h>
18#include "overlayfs.h" 19#include "overlayfs.h"
19 20
20void ovl_cleanup(struct inode *wdir, struct dentry *wdentry) 21void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
@@ -757,6 +758,104 @@ static bool ovl_type_merge_or_lower(struct dentry *dentry)
757 return OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type); 758 return OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type);
758} 759}
759 760
761static bool ovl_can_move(struct dentry *dentry)
762{
763 return ovl_redirect_dir(dentry->d_sb) ||
764 !d_is_dir(dentry) || !ovl_type_merge_or_lower(dentry);
765}
766
767#define OVL_REDIRECT_MAX 256
768
769static char *ovl_get_redirect(struct dentry *dentry, bool samedir)
770{
771 char *buf, *ret;
772 struct dentry *d, *tmp;
773 int buflen = OVL_REDIRECT_MAX + 1;
774
775 if (samedir) {
776 ret = kstrndup(dentry->d_name.name, dentry->d_name.len,
777 GFP_KERNEL);
778 goto out;
779 }
780
781 buf = ret = kmalloc(buflen, GFP_TEMPORARY);
782 if (!buf)
783 goto out;
784
785 buflen--;
786 buf[buflen] = '\0';
787 for (d = dget(dentry); !IS_ROOT(d);) {
788 const char *name;
789 int thislen;
790
791 spin_lock(&d->d_lock);
792 name = ovl_dentry_get_redirect(d);
793 if (name) {
794 thislen = strlen(name);
795 } else {
796 name = d->d_name.name;
797 thislen = d->d_name.len;
798 }
799
800 /* If path is too long, fall back to userspace move */
801 if (thislen + (name[0] != '/') > buflen) {
802 ret = ERR_PTR(-EXDEV);
803 spin_unlock(&d->d_lock);
804 goto out_put;
805 }
806
807 buflen -= thislen;
808 memcpy(&buf[buflen], name, thislen);
809 tmp = dget_dlock(d->d_parent);
810 spin_unlock(&d->d_lock);
811
812 dput(d);
813 d = tmp;
814
815 /* Absolute redirect: finished */
816 if (buf[buflen] == '/')
817 break;
818 buflen--;
819 buf[buflen] = '/';
820 }
821 ret = kstrdup(&buf[buflen], GFP_KERNEL);
822out_put:
823 dput(d);
824 kfree(buf);
825out:
826 return ret ? ret : ERR_PTR(-ENOMEM);
827}
828
829static int ovl_set_redirect(struct dentry *dentry, bool samedir)
830{
831 int err;
832 const char *redirect = ovl_dentry_get_redirect(dentry);
833
834 if (redirect && (samedir || redirect[0] == '/'))
835 return 0;
836
837 redirect = ovl_get_redirect(dentry, samedir);
838 if (IS_ERR(redirect))
839 return PTR_ERR(redirect);
840
841 err = ovl_do_setxattr(ovl_dentry_upper(dentry), OVL_XATTR_REDIRECT,
842 redirect, strlen(redirect), 0);
843 if (!err) {
844 spin_lock(&dentry->d_lock);
845 ovl_dentry_set_redirect(dentry, redirect);
846 spin_unlock(&dentry->d_lock);
847 } else {
848 kfree(redirect);
849 if (err == -EOPNOTSUPP)
850 ovl_clear_redirect_dir(dentry->d_sb);
851 else
852 pr_warn_ratelimited("overlay: failed to set redirect (%i)\n", err);
853 /* Fall back to userspace copy-up */
854 err = -EXDEV;
855 }
856 return err;
857}
858
760static int ovl_rename(struct inode *olddir, struct dentry *old, 859static int ovl_rename(struct inode *olddir, struct dentry *old,
761 struct inode *newdir, struct dentry *new, 860 struct inode *newdir, struct dentry *new,
762 unsigned int flags) 861 unsigned int flags)
@@ -773,6 +872,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
773 bool overwrite = !(flags & RENAME_EXCHANGE); 872 bool overwrite = !(flags & RENAME_EXCHANGE);
774 bool is_dir = d_is_dir(old); 873 bool is_dir = d_is_dir(old);
775 bool new_is_dir = d_is_dir(new); 874 bool new_is_dir = d_is_dir(new);
875 bool samedir = olddir == newdir;
776 struct dentry *opaquedir = NULL; 876 struct dentry *opaquedir = NULL;
777 const struct cred *old_cred = NULL; 877 const struct cred *old_cred = NULL;
778 878
@@ -784,9 +884,9 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
784 884
785 /* Don't copy up directory trees */ 885 /* Don't copy up directory trees */
786 err = -EXDEV; 886 err = -EXDEV;
787 if (is_dir && ovl_type_merge_or_lower(old)) 887 if (!ovl_can_move(old))
788 goto out; 888 goto out;
789 if (!overwrite && new_is_dir && ovl_type_merge_or_lower(new)) 889 if (!overwrite && !ovl_can_move(new))
790 goto out; 890 goto out;
791 891
792 err = ovl_want_write(old); 892 err = ovl_want_write(old);
@@ -837,7 +937,6 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
837 937
838 trap = lock_rename(new_upperdir, old_upperdir); 938 trap = lock_rename(new_upperdir, old_upperdir);
839 939
840
841 olddentry = lookup_one_len(old->d_name.name, old_upperdir, 940 olddentry = lookup_one_len(old->d_name.name, old_upperdir,
842 old->d_name.len); 941 old->d_name.len);
843 err = PTR_ERR(olddentry); 942 err = PTR_ERR(olddentry);
@@ -880,18 +979,29 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
880 if (WARN_ON(olddentry->d_inode == newdentry->d_inode)) 979 if (WARN_ON(olddentry->d_inode == newdentry->d_inode))
881 goto out_dput; 980 goto out_dput;
882 981
883 if (is_dir && !old_opaque && ovl_lower_positive(new)) { 982 if (is_dir) {
884 err = ovl_set_opaque(olddentry); 983 if (ovl_type_merge_or_lower(old)) {
885 if (err) 984 err = ovl_set_redirect(old, samedir);
886 goto out_dput; 985 if (err)
887 ovl_dentry_set_opaque(old, true); 986 goto out_dput;
987 } else if (!old_opaque && ovl_lower_positive(new)) {
988 err = ovl_set_opaque(olddentry);
989 if (err)
990 goto out_dput;
991 ovl_dentry_set_opaque(old, true);
992 }
888 } 993 }
889 if (!overwrite && 994 if (!overwrite && new_is_dir) {
890 new_is_dir && !new_opaque && ovl_lower_positive(old)) { 995 if (ovl_type_merge_or_lower(new)) {
891 err = ovl_set_opaque(newdentry); 996 err = ovl_set_redirect(new, samedir);
892 if (err) 997 if (err)
893 goto out_dput; 998 goto out_dput;
894 ovl_dentry_set_opaque(new, true); 999 } else if (!new_opaque && ovl_lower_positive(old)) {
1000 err = ovl_set_opaque(newdentry);
1001 if (err)
1002 goto out_dput;
1003 ovl_dentry_set_opaque(new, true);
1004 }
895 } 1005 }
896 1006
897 err = ovl_do_rename(old_upperdir->d_inode, olddentry, 1007 err = ovl_do_rename(old_upperdir->d_inode, olddentry,
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index e76d9d529e64..bdda37fa3f67 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -157,6 +157,10 @@ void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
157bool ovl_dentry_is_opaque(struct dentry *dentry); 157bool ovl_dentry_is_opaque(struct dentry *dentry);
158bool ovl_dentry_is_whiteout(struct dentry *dentry); 158bool ovl_dentry_is_whiteout(struct dentry *dentry);
159void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); 159void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
160bool ovl_redirect_dir(struct super_block *sb);
161void ovl_clear_redirect_dir(struct super_block *sb);
162const char *ovl_dentry_get_redirect(struct dentry *dentry);
163void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
160void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); 164void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
161void ovl_inode_init(struct inode *inode, struct inode *realinode, 165void ovl_inode_init(struct inode *inode, struct inode *realinode,
162 bool is_upper); 166 bool is_upper);
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index eb29882b6a54..d14bca1850d9 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -13,6 +13,7 @@ struct ovl_config {
13 char *upperdir; 13 char *upperdir;
14 char *workdir; 14 char *workdir;
15 bool default_permissions; 15 bool default_permissions;
16 bool redirect_dir;
16}; 17};
17 18
18/* private information held for overlayfs's superblock */ 19/* private information held for overlayfs's superblock */
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 4e44e865b716..520f9ab0e9ef 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -226,6 +226,8 @@ enum {
226 OPT_UPPERDIR, 226 OPT_UPPERDIR,
227 OPT_WORKDIR, 227 OPT_WORKDIR,
228 OPT_DEFAULT_PERMISSIONS, 228 OPT_DEFAULT_PERMISSIONS,
229 OPT_REDIRECT_DIR_ON,
230 OPT_REDIRECT_DIR_OFF,
229 OPT_ERR, 231 OPT_ERR,
230}; 232};
231 233
@@ -234,6 +236,8 @@ static const match_table_t ovl_tokens = {
234 {OPT_UPPERDIR, "upperdir=%s"}, 236 {OPT_UPPERDIR, "upperdir=%s"},
235 {OPT_WORKDIR, "workdir=%s"}, 237 {OPT_WORKDIR, "workdir=%s"},
236 {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, 238 {OPT_DEFAULT_PERMISSIONS, "default_permissions"},
239 {OPT_REDIRECT_DIR_ON, "redirect_dir=on"},
240 {OPT_REDIRECT_DIR_OFF, "redirect_dir=off"},
237 {OPT_ERR, NULL} 241 {OPT_ERR, NULL}
238}; 242};
239 243
@@ -298,6 +302,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
298 config->default_permissions = true; 302 config->default_permissions = true;
299 break; 303 break;
300 304
305 case OPT_REDIRECT_DIR_ON:
306 config->redirect_dir = true;
307 break;
308
309 case OPT_REDIRECT_DIR_OFF:
310 config->redirect_dir = false;
311 break;
312
301 default: 313 default:
302 pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p); 314 pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
303 return -EINVAL; 315 return -EINVAL;
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 0d45a84468d2..260b215852a3 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -176,6 +176,35 @@ void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque)
176 oe->opaque = opaque; 176 oe->opaque = opaque;
177} 177}
178 178
179bool ovl_redirect_dir(struct super_block *sb)
180{
181 struct ovl_fs *ofs = sb->s_fs_info;
182
183 return ofs->config.redirect_dir;
184}
185
186void ovl_clear_redirect_dir(struct super_block *sb)
187{
188 struct ovl_fs *ofs = sb->s_fs_info;
189
190 ofs->config.redirect_dir = false;
191}
192
193const char *ovl_dentry_get_redirect(struct dentry *dentry)
194{
195 struct ovl_entry *oe = dentry->d_fsdata;
196
197 return oe->redirect;
198}
199
200void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect)
201{
202 struct ovl_entry *oe = dentry->d_fsdata;
203
204 kfree(oe->redirect);
205 oe->redirect = redirect;
206}
207
179void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) 208void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry)
180{ 209{
181 struct ovl_entry *oe = dentry->d_fsdata; 210 struct ovl_entry *oe = dentry->d_fsdata;