diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2016-07-29 06:05:24 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2016-07-29 06:05:24 -0400 |
commit | 39b681f8026c170a73972517269efc830db0d7ce (patch) | |
tree | 592a1c87e7b3f16d5422577ee9aa9bd6e93f49e9 | |
parent | a999d7e161a085e30181d0a88f049bd92112e172 (diff) |
ovl: store real inode pointer in ->i_private
To get from overlay inode to real inode we currently use 'struct
ovl_entry', which has lifetime connected to overlay dentry. This is okay,
since each overlay dentry had a new overlay inode allocated.
Following patch will break that assumption, so need to leave out ovl_entry.
This patch stores the real inode directly in i_private, with the lowest bit
used to indicate whether the inode is upper or lower.
Lifetime rules remain, using ovl_inode_real() must only be done while
caller holds ref on overlay dentry (and hence on real dentry), or within
RCU protected regions.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r-- | fs/overlayfs/copy_up.c | 1 | ||||
-rw-r--r-- | fs/overlayfs/dir.c | 3 | ||||
-rw-r--r-- | fs/overlayfs/inode.c | 11 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 18 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 44 |
5 files changed, 40 insertions, 37 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 80aa6f1eb336..54e5d6681786 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c | |||
@@ -292,6 +292,7 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, | |||
292 | goto out_cleanup; | 292 | goto out_cleanup; |
293 | 293 | ||
294 | ovl_dentry_update(dentry, newdentry); | 294 | ovl_dentry_update(dentry, newdentry); |
295 | ovl_inode_update(d_inode(dentry), d_inode(newdentry)); | ||
295 | newdentry = NULL; | 296 | newdentry = NULL; |
296 | 297 | ||
297 | /* | 298 | /* |
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b4eac8173f93..96b1bdcf3674 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
@@ -167,6 +167,7 @@ static void ovl_instantiate(struct dentry *dentry, struct inode *inode, | |||
167 | { | 167 | { |
168 | ovl_dentry_version_inc(dentry->d_parent); | 168 | ovl_dentry_version_inc(dentry->d_parent); |
169 | ovl_dentry_update(dentry, newdentry); | 169 | ovl_dentry_update(dentry, newdentry); |
170 | ovl_inode_update(inode, d_inode(newdentry)); | ||
170 | ovl_copyattr(newdentry->d_inode, inode); | 171 | ovl_copyattr(newdentry->d_inode, inode); |
171 | d_instantiate(dentry, inode); | 172 | d_instantiate(dentry, inode); |
172 | } | 173 | } |
@@ -416,7 +417,7 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, | |||
416 | }; | 417 | }; |
417 | 418 | ||
418 | err = -ENOMEM; | 419 | err = -ENOMEM; |
419 | inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata); | 420 | inode = ovl_new_inode(dentry->d_sb, mode); |
420 | if (!inode) | 421 | if (!inode) |
421 | goto out; | 422 | goto out; |
422 | 423 | ||
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 0598c169ce41..2bdd3cae0f71 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c | |||
@@ -117,15 +117,12 @@ static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry, | |||
117 | 117 | ||
118 | int ovl_permission(struct inode *inode, int mask) | 118 | int ovl_permission(struct inode *inode, int mask) |
119 | { | 119 | { |
120 | struct ovl_entry *oe = inode->i_private; | ||
121 | bool is_upper; | 120 | bool is_upper; |
122 | struct dentry *realdentry = ovl_entry_real(oe, &is_upper); | 121 | struct inode *realinode = ovl_inode_real(inode, &is_upper); |
123 | struct inode *realinode; | ||
124 | const struct cred *old_cred; | 122 | const struct cred *old_cred; |
125 | int err; | 123 | int err; |
126 | 124 | ||
127 | /* Careful in RCU walk mode */ | 125 | /* Careful in RCU walk mode */ |
128 | realinode = d_inode_rcu(realdentry); | ||
129 | if (!realinode) { | 126 | if (!realinode) { |
130 | WARN_ON(!(mask & MAY_NOT_BLOCK)); | 127 | WARN_ON(!(mask & MAY_NOT_BLOCK)); |
131 | return -ECHILD; | 128 | return -ECHILD; |
@@ -312,7 +309,7 @@ out: | |||
312 | 309 | ||
313 | struct posix_acl *ovl_get_acl(struct inode *inode, int type) | 310 | struct posix_acl *ovl_get_acl(struct inode *inode, int type) |
314 | { | 311 | { |
315 | struct inode *realinode = ovl_inode_real(inode); | 312 | struct inode *realinode = ovl_inode_real(inode, NULL); |
316 | const struct cred *old_cred; | 313 | const struct cred *old_cred; |
317 | struct posix_acl *acl; | 314 | struct posix_acl *acl; |
318 | 315 | ||
@@ -412,8 +409,7 @@ static const struct inode_operations ovl_symlink_inode_operations = { | |||
412 | .update_time = ovl_update_time, | 409 | .update_time = ovl_update_time, |
413 | }; | 410 | }; |
414 | 411 | ||
415 | struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, | 412 | struct inode *ovl_new_inode(struct super_block *sb, umode_t mode) |
416 | struct ovl_entry *oe) | ||
417 | { | 413 | { |
418 | struct inode *inode; | 414 | struct inode *inode; |
419 | 415 | ||
@@ -424,7 +420,6 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, | |||
424 | inode->i_ino = get_next_ino(); | 420 | inode->i_ino = get_next_ino(); |
425 | inode->i_mode = mode; | 421 | inode->i_mode = mode; |
426 | inode->i_flags |= S_NOCMTIME; | 422 | inode->i_flags |= S_NOCMTIME; |
427 | inode->i_private = oe; | ||
428 | 423 | ||
429 | mode &= S_IFMT; | 424 | mode &= S_IFMT; |
430 | switch (mode) { | 425 | switch (mode) { |
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index fb73c09a84e7..6410209ea616 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h | |||
@@ -27,6 +27,8 @@ enum ovl_path_type { | |||
27 | #define OVL_XATTR_PRE_LEN 16 | 27 | #define OVL_XATTR_PRE_LEN 16 |
28 | #define OVL_XATTR_OPAQUE OVL_XATTR_PRE_NAME"opaque" | 28 | #define OVL_XATTR_OPAQUE OVL_XATTR_PRE_NAME"opaque" |
29 | 29 | ||
30 | #define OVL_ISUPPER_MASK 1UL | ||
31 | |||
30 | static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) | 32 | static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) |
31 | { | 33 | { |
32 | int err = vfs_rmdir(dir, dentry); | 34 | int err = vfs_rmdir(dir, dentry); |
@@ -131,6 +133,16 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry) | |||
131 | return err; | 133 | return err; |
132 | } | 134 | } |
133 | 135 | ||
136 | static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper) | ||
137 | { | ||
138 | unsigned long x = (unsigned long) READ_ONCE(inode->i_private); | ||
139 | |||
140 | if (is_upper) | ||
141 | *is_upper = x & OVL_ISUPPER_MASK; | ||
142 | |||
143 | return (struct inode *) (x & ~OVL_ISUPPER_MASK); | ||
144 | } | ||
145 | |||
134 | enum ovl_path_type ovl_path_type(struct dentry *dentry); | 146 | enum ovl_path_type ovl_path_type(struct dentry *dentry); |
135 | u64 ovl_dentry_version_get(struct dentry *dentry); | 147 | u64 ovl_dentry_version_get(struct dentry *dentry); |
136 | void ovl_dentry_version_inc(struct dentry *dentry); | 148 | void ovl_dentry_version_inc(struct dentry *dentry); |
@@ -141,8 +153,6 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path); | |||
141 | struct dentry *ovl_dentry_upper(struct dentry *dentry); | 153 | struct dentry *ovl_dentry_upper(struct dentry *dentry); |
142 | struct dentry *ovl_dentry_lower(struct dentry *dentry); | 154 | struct dentry *ovl_dentry_lower(struct dentry *dentry); |
143 | struct dentry *ovl_dentry_real(struct dentry *dentry); | 155 | struct dentry *ovl_dentry_real(struct dentry *dentry); |
144 | struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper); | ||
145 | struct inode *ovl_inode_real(struct inode *inode); | ||
146 | struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode, | 156 | struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode, |
147 | bool is_upper); | 157 | bool is_upper); |
148 | struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry); | 158 | struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry); |
@@ -155,6 +165,7 @@ void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); | |||
155 | bool ovl_is_whiteout(struct dentry *dentry); | 165 | bool ovl_is_whiteout(struct dentry *dentry); |
156 | const struct cred *ovl_override_creds(struct super_block *sb); | 166 | const struct cred *ovl_override_creds(struct super_block *sb); |
157 | void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); | 167 | void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); |
168 | void ovl_inode_update(struct inode *inode, struct inode *upperinode); | ||
158 | struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | 169 | struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, |
159 | unsigned int flags); | 170 | unsigned int flags); |
160 | struct file *ovl_path_open(struct path *path, int flags); | 171 | struct file *ovl_path_open(struct path *path, int flags); |
@@ -183,8 +194,7 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type); | |||
183 | int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags); | 194 | int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags); |
184 | int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); | 195 | int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); |
185 | 196 | ||
186 | struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, | 197 | struct inode *ovl_new_inode(struct super_block *sb, umode_t mode); |
187 | struct ovl_entry *oe); | ||
188 | static inline void ovl_copyattr(struct inode *from, struct inode *to) | 198 | static inline void ovl_copyattr(struct inode *from, struct inode *to) |
189 | { | 199 | { |
190 | to->i_uid = from->i_uid; | 200 | to->i_uid = from->i_uid; |
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 058103c60f54..313f773652ff 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
@@ -145,25 +145,11 @@ struct dentry *ovl_dentry_real(struct dentry *dentry) | |||
145 | return realdentry; | 145 | return realdentry; |
146 | } | 146 | } |
147 | 147 | ||
148 | struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper) | 148 | static void ovl_inode_init(struct inode *inode, struct inode *realinode, |
149 | bool is_upper) | ||
149 | { | 150 | { |
150 | struct dentry *realdentry; | 151 | WRITE_ONCE(inode->i_private, (unsigned long) realinode | |
151 | 152 | (is_upper ? OVL_ISUPPER_MASK : 0)); | |
152 | realdentry = ovl_upperdentry_dereference(oe); | ||
153 | if (realdentry) { | ||
154 | *is_upper = true; | ||
155 | } else { | ||
156 | realdentry = __ovl_dentry_lower(oe); | ||
157 | *is_upper = false; | ||
158 | } | ||
159 | return realdentry; | ||
160 | } | ||
161 | |||
162 | struct inode *ovl_inode_real(struct inode *inode) | ||
163 | { | ||
164 | bool tmp; | ||
165 | |||
166 | return d_inode(ovl_entry_real(inode->i_private, &tmp)); | ||
167 | } | 153 | } |
168 | 154 | ||
169 | struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode, | 155 | struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode, |
@@ -235,7 +221,6 @@ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) | |||
235 | 221 | ||
236 | WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode)); | 222 | WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode)); |
237 | WARN_ON(oe->__upperdentry); | 223 | WARN_ON(oe->__upperdentry); |
238 | BUG_ON(!upperdentry->d_inode); | ||
239 | /* | 224 | /* |
240 | * Make sure upperdentry is consistent before making it visible to | 225 | * Make sure upperdentry is consistent before making it visible to |
241 | * ovl_upperdentry_dereference(). | 226 | * ovl_upperdentry_dereference(). |
@@ -244,6 +229,13 @@ void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) | |||
244 | oe->__upperdentry = upperdentry; | 229 | oe->__upperdentry = upperdentry; |
245 | } | 230 | } |
246 | 231 | ||
232 | void ovl_inode_update(struct inode *inode, struct inode *upperinode) | ||
233 | { | ||
234 | WARN_ON(!upperinode); | ||
235 | WRITE_ONCE(inode->i_private, | ||
236 | (unsigned long) upperinode | OVL_ISUPPER_MASK); | ||
237 | } | ||
238 | |||
247 | void ovl_dentry_version_inc(struct dentry *dentry) | 239 | void ovl_dentry_version_inc(struct dentry *dentry) |
248 | { | 240 | { |
249 | struct ovl_entry *oe = dentry->d_fsdata; | 241 | struct ovl_entry *oe = dentry->d_fsdata; |
@@ -574,14 +566,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
574 | 566 | ||
575 | if (upperdentry || ctr) { | 567 | if (upperdentry || ctr) { |
576 | struct dentry *realdentry; | 568 | struct dentry *realdentry; |
569 | struct inode *realinode; | ||
577 | 570 | ||
578 | realdentry = upperdentry ? upperdentry : stack[0].dentry; | 571 | realdentry = upperdentry ? upperdentry : stack[0].dentry; |
572 | realinode = d_inode(realdentry); | ||
579 | 573 | ||
580 | err = -ENOMEM; | 574 | err = -ENOMEM; |
581 | inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode, | 575 | inode = ovl_new_inode(dentry->d_sb, realinode->i_mode); |
582 | oe); | ||
583 | if (!inode) | 576 | if (!inode) |
584 | goto out_free_oe; | 577 | goto out_free_oe; |
578 | ovl_inode_init(inode, realinode, !!upperdentry); | ||
585 | ovl_copyattr(realdentry->d_inode, inode); | 579 | ovl_copyattr(realdentry->d_inode, inode); |
586 | } | 580 | } |
587 | 581 | ||
@@ -969,6 +963,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
969 | struct path upperpath = { NULL, NULL }; | 963 | struct path upperpath = { NULL, NULL }; |
970 | struct path workpath = { NULL, NULL }; | 964 | struct path workpath = { NULL, NULL }; |
971 | struct dentry *root_dentry; | 965 | struct dentry *root_dentry; |
966 | struct inode *realinode; | ||
972 | struct ovl_entry *oe; | 967 | struct ovl_entry *oe; |
973 | struct ovl_fs *ufs; | 968 | struct ovl_fs *ufs; |
974 | struct path *stack = NULL; | 969 | struct path *stack = NULL; |
@@ -1150,7 +1145,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1150 | if (!oe) | 1145 | if (!oe) |
1151 | goto out_put_cred; | 1146 | goto out_put_cred; |
1152 | 1147 | ||
1153 | root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); | 1148 | root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR)); |
1154 | if (!root_dentry) | 1149 | if (!root_dentry) |
1155 | goto out_free_oe; | 1150 | goto out_free_oe; |
1156 | 1151 | ||
@@ -1169,8 +1164,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1169 | 1164 | ||
1170 | root_dentry->d_fsdata = oe; | 1165 | root_dentry->d_fsdata = oe; |
1171 | 1166 | ||
1172 | ovl_copyattr(ovl_dentry_real(root_dentry)->d_inode, | 1167 | realinode = d_inode(ovl_dentry_real(root_dentry)); |
1173 | root_dentry->d_inode); | 1168 | ovl_inode_init(d_inode(root_dentry), realinode, !!upperpath.dentry); |
1169 | ovl_copyattr(realinode, d_inode(root_dentry)); | ||
1174 | 1170 | ||
1175 | sb->s_magic = OVERLAYFS_SUPER_MAGIC; | 1171 | sb->s_magic = OVERLAYFS_SUPER_MAGIC; |
1176 | sb->s_op = &ovl_super_operations; | 1172 | sb->s_op = &ovl_super_operations; |