aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2017-06-21 08:28:36 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2017-07-04 16:03:17 -0400
commit02bcd1577400b0b2eab806ccb9f72d6b5ec7bcca (patch)
tree92c54e46aa6ae552eefac194f25d7faccdd00d5c
parent6b8aa129dcbe0e9825109b35c4b967f984e8fb13 (diff)
ovl: introduce the inodes index dir feature
Create the index dir on mount. The index dir will contain hardlinks to upper inodes, named after the hex representation of their origin lower inodes. The index dir is going to be used to prevent breaking lower hardlinks on copy up and to implement overlayfs NFS export. Because the feature is not fully backward compat, enabling the feature is opt-in by config/module/mount option. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/Kconfig20
-rw-r--r--fs/overlayfs/copy_up.c9
-rw-r--r--fs/overlayfs/overlayfs.h2
-rw-r--r--fs/overlayfs/ovl_entry.h3
-rw-r--r--fs/overlayfs/super.c66
-rw-r--r--fs/overlayfs/util.c15
6 files changed, 108 insertions, 7 deletions
diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig
index c0c9683934b7..cbfc196e5dc5 100644
--- a/fs/overlayfs/Kconfig
+++ b/fs/overlayfs/Kconfig
@@ -23,3 +23,23 @@ config OVERLAY_FS_REDIRECT_DIR
23 Note, that redirects are not backward compatible. That is, mounting 23 Note, that redirects are not backward compatible. That is, mounting
24 an overlay which has redirects on a kernel that doesn't support this 24 an overlay which has redirects on a kernel that doesn't support this
25 feature will have unexpected results. 25 feature will have unexpected results.
26
27config OVERLAY_FS_INDEX
28 bool "Overlayfs: turn on inodes index feature by default"
29 depends on OVERLAY_FS
30 help
31 If this config option is enabled then overlay filesystems will use
32 the inodes index dir to map lower inodes to upper inodes by default.
33 In this case it is still possible to turn off index globally with the
34 "index=off" module option or on a filesystem instance basis with the
35 "index=off" mount option.
36
37 The inodes index feature prevents breaking of lower hardlinks on copy
38 up.
39
40 Note, that the inodes index feature is read-only backward compatible.
41 That is, mounting an overlay which has an index dir on a kernel that
42 doesn't support this feature read-only, will not have any negative
43 outcomes. However, mounting the same overlay with an old kernel
44 read-write and then mounting it again with a new kernel, will have
45 unexpected results.
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 87289b9a152c..f9f51cce3c18 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -233,12 +233,13 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
233 return err; 233 return err;
234} 234}
235 235
236static struct ovl_fh *ovl_encode_fh(struct dentry *lower, uuid_t *uuid) 236static struct ovl_fh *ovl_encode_fh(struct dentry *lower)
237{ 237{
238 struct ovl_fh *fh; 238 struct ovl_fh *fh;
239 int fh_type, fh_len, dwords; 239 int fh_type, fh_len, dwords;
240 void *buf; 240 void *buf;
241 int buflen = MAX_HANDLE_SZ; 241 int buflen = MAX_HANDLE_SZ;
242 uuid_t *uuid = &lower->d_sb->s_uuid;
242 243
243 buf = kmalloc(buflen, GFP_TEMPORARY); 244 buf = kmalloc(buflen, GFP_TEMPORARY);
244 if (!buf) 245 if (!buf)
@@ -283,7 +284,6 @@ out:
283static int ovl_set_origin(struct dentry *dentry, struct dentry *lower, 284static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
284 struct dentry *upper) 285 struct dentry *upper)
285{ 286{
286 struct super_block *sb = lower->d_sb;
287 const struct ovl_fh *fh = NULL; 287 const struct ovl_fh *fh = NULL;
288 int err; 288 int err;
289 289
@@ -292,9 +292,8 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
292 * so we can use the overlay.origin xattr to distignuish between a copy 292 * so we can use the overlay.origin xattr to distignuish between a copy
293 * up and a pure upper inode. 293 * up and a pure upper inode.
294 */ 294 */
295 if (sb->s_export_op && sb->s_export_op->fh_to_dentry && 295 if (ovl_can_decode_fh(lower->d_sb)) {
296 !uuid_is_null(&sb->s_uuid)) { 296 fh = ovl_encode_fh(lower);
297 fh = ovl_encode_fh(lower, &sb->s_uuid);
298 if (IS_ERR(fh)) 297 if (IS_ERR(fh))
299 return PTR_ERR(fh); 298 return PTR_ERR(fh);
300 } 299 }
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 5e958427463d..4e7a74e99d3c 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -183,6 +183,8 @@ void ovl_drop_write(struct dentry *dentry);
183struct dentry *ovl_workdir(struct dentry *dentry); 183struct dentry *ovl_workdir(struct dentry *dentry);
184const struct cred *ovl_override_creds(struct super_block *sb); 184const struct cred *ovl_override_creds(struct super_block *sb);
185struct super_block *ovl_same_sb(struct super_block *sb); 185struct super_block *ovl_same_sb(struct super_block *sb);
186bool ovl_can_decode_fh(struct super_block *sb);
187struct dentry *ovl_indexdir(struct super_block *sb);
186struct ovl_entry *ovl_alloc_entry(unsigned int numlower); 188struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
187bool ovl_dentry_remote(struct dentry *dentry); 189bool ovl_dentry_remote(struct dentry *dentry);
188bool ovl_dentry_weird(struct dentry *dentry); 190bool ovl_dentry_weird(struct dentry *dentry);
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index 5b5a32116424..9642ec64467b 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -14,6 +14,7 @@ struct ovl_config {
14 char *workdir; 14 char *workdir;
15 bool default_permissions; 15 bool default_permissions;
16 bool redirect_dir; 16 bool redirect_dir;
17 bool index;
17}; 18};
18 19
19/* private information held for overlayfs's superblock */ 20/* private information held for overlayfs's superblock */
@@ -25,6 +26,8 @@ struct ovl_fs {
25 struct dentry *workbasedir; 26 struct dentry *workbasedir;
26 /* workdir is the 'work' directory under workbasedir */ 27 /* workdir is the 'work' directory under workbasedir */
27 struct dentry *workdir; 28 struct dentry *workdir;
29 /* index directory listing overlay inodes by origin file handle */
30 struct dentry *indexdir;
28 long namelen; 31 long namelen;
29 /* pathnames of lower and upper dirs, for show_options */ 32 /* pathnames of lower and upper dirs, for show_options */
30 struct ovl_config config; 33 struct ovl_config config;
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index fea7bd496f2e..fa83b3245124 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -34,6 +34,11 @@ module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);
34MODULE_PARM_DESC(ovl_redirect_dir_def, 34MODULE_PARM_DESC(ovl_redirect_dir_def,
35 "Default to on or off for the redirect_dir feature"); 35 "Default to on or off for the redirect_dir feature");
36 36
37static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX);
38module_param_named(index, ovl_index_def, bool, 0644);
39MODULE_PARM_DESC(ovl_index_def,
40 "Default to on or off for the inodes index feature");
41
37static void ovl_dentry_release(struct dentry *dentry) 42static void ovl_dentry_release(struct dentry *dentry)
38{ 43{
39 struct ovl_entry *oe = dentry->d_fsdata; 44 struct ovl_entry *oe = dentry->d_fsdata;
@@ -203,6 +208,7 @@ static void ovl_put_super(struct super_block *sb)
203 struct ovl_fs *ufs = sb->s_fs_info; 208 struct ovl_fs *ufs = sb->s_fs_info;
204 unsigned i; 209 unsigned i;
205 210
211 dput(ufs->indexdir);
206 dput(ufs->workdir); 212 dput(ufs->workdir);
207 ovl_inuse_unlock(ufs->workbasedir); 213 ovl_inuse_unlock(ufs->workbasedir);
208 dput(ufs->workbasedir); 214 dput(ufs->workbasedir);
@@ -265,6 +271,12 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
265 return err; 271 return err;
266} 272}
267 273
274/* Will this overlay be forced to mount/remount ro? */
275static bool ovl_force_readonly(struct ovl_fs *ufs)
276{
277 return (!ufs->upper_mnt || !ufs->workdir);
278}
279
268/** 280/**
269 * ovl_show_options 281 * ovl_show_options
270 * 282 *
@@ -286,6 +298,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
286 if (ufs->config.redirect_dir != ovl_redirect_dir_def) 298 if (ufs->config.redirect_dir != ovl_redirect_dir_def)
287 seq_printf(m, ",redirect_dir=%s", 299 seq_printf(m, ",redirect_dir=%s",
288 ufs->config.redirect_dir ? "on" : "off"); 300 ufs->config.redirect_dir ? "on" : "off");
301 if (ufs->config.index != ovl_index_def)
302 seq_printf(m, ",index=%s",
303 ufs->config.index ? "on" : "off");
289 return 0; 304 return 0;
290} 305}
291 306
@@ -293,7 +308,7 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)
293{ 308{
294 struct ovl_fs *ufs = sb->s_fs_info; 309 struct ovl_fs *ufs = sb->s_fs_info;
295 310
296 if (!(*flags & MS_RDONLY) && (!ufs->upper_mnt || !ufs->workdir)) 311 if (!(*flags & MS_RDONLY) && ovl_force_readonly(ufs))
297 return -EROFS; 312 return -EROFS;
298 313
299 return 0; 314 return 0;
@@ -317,6 +332,8 @@ enum {
317 OPT_DEFAULT_PERMISSIONS, 332 OPT_DEFAULT_PERMISSIONS,
318 OPT_REDIRECT_DIR_ON, 333 OPT_REDIRECT_DIR_ON,
319 OPT_REDIRECT_DIR_OFF, 334 OPT_REDIRECT_DIR_OFF,
335 OPT_INDEX_ON,
336 OPT_INDEX_OFF,
320 OPT_ERR, 337 OPT_ERR,
321}; 338};
322 339
@@ -327,6 +344,8 @@ static const match_table_t ovl_tokens = {
327 {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, 344 {OPT_DEFAULT_PERMISSIONS, "default_permissions"},
328 {OPT_REDIRECT_DIR_ON, "redirect_dir=on"}, 345 {OPT_REDIRECT_DIR_ON, "redirect_dir=on"},
329 {OPT_REDIRECT_DIR_OFF, "redirect_dir=off"}, 346 {OPT_REDIRECT_DIR_OFF, "redirect_dir=off"},
347 {OPT_INDEX_ON, "index=on"},
348 {OPT_INDEX_OFF, "index=off"},
330 {OPT_ERR, NULL} 349 {OPT_ERR, NULL}
331}; 350};
332 351
@@ -399,6 +418,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
399 config->redirect_dir = false; 418 config->redirect_dir = false;
400 break; 419 break;
401 420
421 case OPT_INDEX_ON:
422 config->index = true;
423 break;
424
425 case OPT_INDEX_OFF:
426 config->index = false;
427 break;
428
402 default: 429 default:
403 pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p); 430 pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
404 return -EINVAL; 431 return -EINVAL;
@@ -417,6 +444,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
417} 444}
418 445
419#define OVL_WORKDIR_NAME "work" 446#define OVL_WORKDIR_NAME "work"
447#define OVL_INDEXDIR_NAME "index"
420 448
421static struct dentry *ovl_workdir_create(struct super_block *sb, 449static struct dentry *ovl_workdir_create(struct super_block *sb,
422 struct ovl_fs *ufs, 450 struct ovl_fs *ufs,
@@ -610,6 +638,15 @@ static int ovl_lower_dir(const char *name, struct path *path,
610 if (ovl_dentry_remote(path->dentry)) 638 if (ovl_dentry_remote(path->dentry))
611 *remote = true; 639 *remote = true;
612 640
641 /*
642 * The inodes index feature needs to encode and decode file
643 * handles, so it requires that all layers support them.
644 */
645 if (ofs->config.index && !ovl_can_decode_fh(path->dentry->d_sb)) {
646 ofs->config.index = false;
647 pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off.\n", name);
648 }
649
613 return 0; 650 return 0;
614 651
615out_put: 652out_put:
@@ -807,6 +844,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
807 goto out; 844 goto out;
808 845
809 ufs->config.redirect_dir = ovl_redirect_dir_def; 846 ufs->config.redirect_dir = ovl_redirect_dir_def;
847 ufs->config.index = ovl_index_def;
810 err = ovl_parse_opt((char *) data, &ufs->config); 848 err = ovl_parse_opt((char *) data, &ufs->config);
811 if (err) 849 if (err)
812 goto out_free_config; 850 goto out_free_config;
@@ -965,6 +1003,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
965 } else { 1003 } else {
966 vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE); 1004 vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE);
967 } 1005 }
1006
1007 /* Check if upper/work fs supports file handles */
1008 if (ufs->config.index &&
1009 !ovl_can_decode_fh(ufs->workdir->d_sb)) {
1010 ufs->config.index = false;
1011 pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
1012 }
968 } 1013 }
969 } 1014 }
970 1015
@@ -1002,6 +1047,21 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
1002 else if (ufs->upper_mnt->mnt_sb != ufs->same_sb) 1047 else if (ufs->upper_mnt->mnt_sb != ufs->same_sb)
1003 ufs->same_sb = NULL; 1048 ufs->same_sb = NULL;
1004 1049
1050 if (!(ovl_force_readonly(ufs)) && ufs->config.index) {
1051 ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry,
1052 OVL_INDEXDIR_NAME, true);
1053 err = PTR_ERR(ufs->indexdir);
1054 if (IS_ERR(ufs->indexdir))
1055 goto out_put_lower_mnt;
1056
1057 if (!ufs->indexdir)
1058 pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
1059 }
1060
1061 /* Show index=off/on in /proc/mounts for any of the reasons above */
1062 if (!ufs->indexdir)
1063 ufs->config.index = false;
1064
1005 if (remote) 1065 if (remote)
1006 sb->s_d_op = &ovl_reval_dentry_operations; 1066 sb->s_d_op = &ovl_reval_dentry_operations;
1007 else 1067 else
@@ -1009,7 +1069,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
1009 1069
1010 ufs->creator_cred = cred = prepare_creds(); 1070 ufs->creator_cred = cred = prepare_creds();
1011 if (!cred) 1071 if (!cred)
1012 goto out_put_lower_mnt; 1072 goto out_put_indexdir;
1013 1073
1014 /* Never override disk quota limits or use reserved space */ 1074 /* Never override disk quota limits or use reserved space */
1015 cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); 1075 cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
@@ -1058,6 +1118,8 @@ out_free_oe:
1058 kfree(oe); 1118 kfree(oe);
1059out_put_cred: 1119out_put_cred:
1060 put_cred(ufs->creator_cred); 1120 put_cred(ufs->creator_cred);
1121out_put_indexdir:
1122 dput(ufs->indexdir);
1061out_put_lower_mnt: 1123out_put_lower_mnt:
1062 for (i = 0; i < ufs->numlower; i++) 1124 for (i = 0; i < ufs->numlower; i++)
1063 mntput(ufs->lower_mnt[i]); 1125 mntput(ufs->lower_mnt[i]);
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index adccd74162d6..90b50b8e75ab 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -12,6 +12,8 @@
12#include <linux/slab.h> 12#include <linux/slab.h>
13#include <linux/cred.h> 13#include <linux/cred.h>
14#include <linux/xattr.h> 14#include <linux/xattr.h>
15#include <linux/exportfs.h>
16#include <linux/uuid.h>
15#include "overlayfs.h" 17#include "overlayfs.h"
16#include "ovl_entry.h" 18#include "ovl_entry.h"
17 19
@@ -47,6 +49,19 @@ struct super_block *ovl_same_sb(struct super_block *sb)
47 return ofs->same_sb; 49 return ofs->same_sb;
48} 50}
49 51
52bool ovl_can_decode_fh(struct super_block *sb)
53{
54 return (sb->s_export_op && sb->s_export_op->fh_to_dentry &&
55 !uuid_is_null(&sb->s_uuid));
56}
57
58struct dentry *ovl_indexdir(struct super_block *sb)
59{
60 struct ovl_fs *ofs = sb->s_fs_info;
61
62 return ofs->indexdir;
63}
64
50struct ovl_entry *ovl_alloc_entry(unsigned int numlower) 65struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
51{ 66{
52 size_t size = offsetof(struct ovl_entry, lowerstack[numlower]); 67 size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);