Pull fuse updates from Miklos Szeredi: - Fix an issue with reusing the bdi in case of block based filesystems - Allow root (in init namespace) to access fuse filesystems in user namespaces if expicitly enabled with a module param - Misc fixes * tag 'fuse-update-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: retire block-device-based superblock on force unmount vfs: function to prevent re-use of block-device-based superblocks virtio_fs: Modify format for virtio_fs_direct_access virtiofs: delete unused parameter for virtio_fs_cleanup_vqs fuse: Add module param for CAP_SYS_ADMIN access bypassing allow_other fuse: Remove the control interface for virtio-fs fuse: ioctl: translate ENOSYS fuse: limit nsec fuse: avoid unnecessary spinlock bump fuse: fix deadlock between atomic O_TRUNC and page invalidation fuse: write inode in fuse_release()
Unified · Split
| @@ -279,7 +279,7 @@ How are requirements fulfilled? · 278 unmodified lines | |||
| 279 | the filesystem or not. | 279 | the filesystem or not. |
| 280 | 280 | ||
| 281 | Note that the *ptrace* check is not strictly necessary to | 281 | Note that the *ptrace* check is not strictly necessary to |
| 282 | prevent B/2/i, it is enough to check if mount owner has enough | 282 | prevent C/2/i, it is enough to check if mount owner has enough |
| 283 | privilege to send signal to the process accessing the | 283 | privilege to send signal to the process accessing the |
| 284 | filesystem, since *SIGSTOP* can be used to get a similar effect. | 284 | filesystem, since *SIGSTOP* can be used to get a similar effect. |
| 285 | 285 | ||
| @@ -288,10 +288,29 @@ I think these limitations are unacceptable? · 2 unmodified lines | |||
| 288 | 288 | ||
| 289 | If a sysadmin trusts the users enough, or can ensure through other | 289 | If a sysadmin trusts the users enough, or can ensure through other |
| 290 | measures, that system processes will never enter non-privileged | 290 | measures, that system processes will never enter non-privileged |
| 291 | mounts, it can relax the last limitation with a 'user_allow_other' | 291 | mounts, it can relax the last limitation in several ways: |
| 292 | config option. If this config option is set, the mounting user can | 292 | |
| 293 | add the 'allow_other' mount option which disables the check for other | 293 | - With the 'user_allow_other' config option. If this config option is |
| 294 | users' processes. | 294 | set, the mounting user can add the 'allow_other' mount option which |
| 295 | disables the check for other users' processes. | ||
| 296 | |||
| 297 | User namespaces have an unintuitive interaction with 'allow_other': | ||
| 298 | an unprivileged user - normally restricted from mounting with | ||
| 299 | 'allow_other' - could do so in a user namespace where they're | ||
| 300 | privileged. If any process could access such an 'allow_other' mount | ||
| 301 | this would give the mounting user the ability to manipulate | ||
| 302 | processes in user namespaces where they're unprivileged. For this | ||
| 303 | reason 'allow_other' restricts access to users in the same userns | ||
| 304 | or a descendant. | ||
| 305 | |||
| 306 | - With the 'allow_sys_admin_access' module option. If this option is | ||
| 307 | set, super user's processes have unrestricted access to mounts | ||
| 308 | irrespective of allow_other setting or user namespace of the | ||
| 309 | mounting user. | ||
| 310 | |||
| 311 | Note that both of these relaxations expose the system to potential | ||
| 312 | information leak or *DoS* as described in points B and C/2/i-ii in the | ||
| 313 | preceding section. | ||
| 295 | 314 | ||
| 296 | Kernel - userspace interface | 315 | Kernel - userspace interface |
| 297 | ============================ | 316 | ============================ |
| @@ -258,7 +258,7 @@ int fuse_ctl_add_conn(struct fuse_conn *fc) · 257 unmodified lines | |||
| 258 | struct dentry *parent; | 258 | struct dentry *parent; |
| 259 | char name[32]; | 259 | char name[32]; |
| 260 | 260 | ||
| 261 | if (!fuse_control_sb) | 261 | if (!fuse_control_sb || fc->no_control) |
| 262 | return 0; | 262 | return 0; |
| 263 | 263 | ||
| 264 | parent = fuse_control_sb->s_root; | 264 | parent = fuse_control_sb->s_root; |
| @@ -296,7 +296,7 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc) · 31 unmodified lines | |||
| 296 | { | 296 | { |
| 297 | int i; | 297 | int i; |
| 298 | 298 | ||
| 299 | if (!fuse_control_sb) | 299 | if (!fuse_control_sb || fc->no_control) |
| 300 | return; | 300 | return; |
| 301 | 301 | ||
| 302 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { | 302 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { |
| @@ -138,9 +138,9 @@ static struct fuse_dax_mapping *alloc_dax_mapping(struct fuse_conn_dax *fcd) · 137 unmodified lines | |||
| 138 | WARN_ON(fcd->nr_free_ranges <= 0); | 138 | WARN_ON(fcd->nr_free_ranges <= 0); |
| 139 | fcd->nr_free_ranges--; | 139 | fcd->nr_free_ranges--; |
| 140 | } | 140 | } |
| 141 | __kick_dmap_free_worker(fcd, 0); | ||
| 141 | spin_unlock(&fcd->lock); | 142 | spin_unlock(&fcd->lock); |
| 142 | 143 | ||
| 143 | kick_dmap_free_worker(fcd, 0); | ||
| 144 | return dmap; | 144 | return dmap; |
| 145 | } | 145 | } |
| 146 | 146 | ||
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | #include <linux/pagemap.h> | 11 | #include <linux/pagemap.h> |
| 12 | #include <linux/file.h> | 12 | #include <linux/file.h> |
| 13 | #include <linux/fs_context.h> | 13 | #include <linux/fs_context.h> |
| 14 | #include <linux/moduleparam.h> | ||
| 14 | #include <linux/sched.h> | 15 | #include <linux/sched.h> |
| 15 | #include <linux/namei.h> | 16 | #include <linux/namei.h> |
| 16 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
| @@ -21,6 +22,11 @@ · 4 unmodified lines | |||
| 21 | #include <linux/types.h> | 22 | #include <linux/types.h> |
| 22 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
| 23 | 24 | ||
| 25 | static bool __read_mostly allow_sys_admin_access; | ||
| 26 | module_param(allow_sys_admin_access, bool, 0644); | ||
| 27 | MODULE_PARM_DESC(allow_sys_admin_access, | ||
| 28 | "Allow users with CAP_SYS_ADMIN in initial userns to bypass allow_other access check"); | ||
| 29 | |||
| 24 | static void fuse_advise_use_readdirplus(struct inode *dir) | 30 | static void fuse_advise_use_readdirplus(struct inode *dir) |
| 25 | { | 31 | { |
| 26 | struct fuse_inode *fi = get_fuse_inode(dir); | 32 | struct fuse_inode *fi = get_fuse_inode(dir); |
| @@ -537,6 +543,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, · 510 unmodified lines | |||
| 537 | struct fuse_file *ff; | 543 | struct fuse_file *ff; |
| 538 | void *security_ctx = NULL; | 544 | void *security_ctx = NULL; |
| 539 | u32 security_ctxlen; | 545 | u32 security_ctxlen; |
| 546 | bool trunc = flags & O_TRUNC; | ||
| 540 | 547 | ||
| 541 | /* Userspace expects S_IFREG in create mode */ | 548 | /* Userspace expects S_IFREG in create mode */ |
| 542 | BUG_ON((mode & S_IFMT) != S_IFREG); | 549 | BUG_ON((mode & S_IFMT) != S_IFREG); |
| @@ -561,7 +568,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, · 18 unmodified lines | |||
| 561 | inarg.mode = mode; | 568 | inarg.mode = mode; |
| 562 | inarg.umask = current_umask(); | 569 | inarg.umask = current_umask(); |
| 563 | 570 | ||
| 564 | if (fm->fc->handle_killpriv_v2 && (flags & O_TRUNC) && | 571 | if (fm->fc->handle_killpriv_v2 && trunc && |
| 565 | !(flags & O_EXCL) && !capable(CAP_FSETID)) { | 572 | !(flags & O_EXCL) && !capable(CAP_FSETID)) { |
| 566 | inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID; | 573 | inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID; |
| 567 | } | 574 | } |
| @@ -623,6 +630,10 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, · 55 unmodified lines | |||
| 623 | } else { | 630 | } else { |
| 624 | file->private_data = ff; | 631 | file->private_data = ff; |
| 625 | fuse_finish_open(inode, file); | 632 | fuse_finish_open(inode, file); |
| 633 | if (fm->fc->atomic_o_trunc && trunc) | ||
| 634 | truncate_pagecache(inode, 0); | ||
| 635 | else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) | ||
| 636 | invalidate_inode_pages2(inode->i_mapping); | ||
| 626 | } | 637 | } |
| 627 | return err; | 638 | return err; |
| 628 | 639 | ||
| @@ -1224,6 +1235,9 @@ int fuse_allow_current_process(struct fuse_conn *fc) · 595 unmodified lines | |||
| 1224 | { | 1235 | { |
| 1225 | const struct cred *cred; | 1236 | const struct cred *cred; |
| 1226 | 1237 | ||
| 1238 | if (allow_sys_admin_access && capable(CAP_SYS_ADMIN)) | ||
| 1239 | return 1; | ||
| 1240 | |||
| 1227 | if (fc->allow_other) | 1241 | if (fc->allow_other) |
| 1228 | return current_in_userns(fc->user_ns); | 1242 | return current_in_userns(fc->user_ns); |
| 1229 | 1243 | ||
| @@ -210,13 +210,9 @@ void fuse_finish_open(struct inode *inode, struct file *file) · 209 unmodified lines | |||
| 210 | fi->attr_version = atomic64_inc_return(&fc->attr_version); | 210 | fi->attr_version = atomic64_inc_return(&fc->attr_version); |
| 211 | i_size_write(inode, 0); | 211 | i_size_write(inode, 0); |
| 212 | spin_unlock(&fi->lock); | 212 | spin_unlock(&fi->lock); |
| 213 | truncate_pagecache(inode, 0); | ||
| 214 | file_update_time(file); | 213 | file_update_time(file); |
| 215 | fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE); | 214 | fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE); |
| 216 | } else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) { | ||
| 217 | invalidate_inode_pages2(inode->i_mapping); | ||
| 218 | } | 215 | } |
| 219 | |||
| 220 | if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache) | 216 | if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache) |
| 221 | fuse_link_write_file(file); | 217 | fuse_link_write_file(file); |
| 222 | } | 218 | } |
| @@ -239,30 +235,38 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir) · 16 unmodified lines | |||
| 239 | if (err) | 235 | if (err) |
| 240 | return err; | 236 | return err; |
| 241 | 237 | ||
| 242 | if (is_wb_truncate || dax_truncate) { | 238 | if (is_wb_truncate || dax_truncate) |
| 243 | inode_lock(inode); | 239 | inode_lock(inode); |
| 244 | fuse_set_nowrite(inode); | ||
| 245 | } | ||
| 246 | 240 | ||
| 247 | if (dax_truncate) { | 241 | if (dax_truncate) { |
| 248 | filemap_invalidate_lock(inode->i_mapping); | 242 | filemap_invalidate_lock(inode->i_mapping); |
| 249 | err = fuse_dax_break_layouts(inode, 0, 0); | 243 | err = fuse_dax_break_layouts(inode, 0, 0); |
| 250 | if (err) | 244 | if (err) |
| 251 | goto out; | 245 | goto out_inode_unlock; |
| 252 | } | 246 | } |
| 253 | 247 | ||
| 248 | if (is_wb_truncate || dax_truncate) | ||
| 249 | fuse_set_nowrite(inode); | ||
| 250 | |||
| 254 | err = fuse_do_open(fm, get_node_id(inode), file, isdir); | 251 | err = fuse_do_open(fm, get_node_id(inode), file, isdir); |
| 255 | if (!err) | 252 | if (!err) |
| 256 | fuse_finish_open(inode, file); | 253 | fuse_finish_open(inode, file); |
| 257 | 254 | ||
| 258 | out: | 255 | if (is_wb_truncate || dax_truncate) |
| 256 | fuse_release_nowrite(inode); | ||
| 257 | if (!err) { | ||
| 258 | struct fuse_file *ff = file->private_data; | ||
| 259 | |||
| 260 | if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) | ||
| 261 | truncate_pagecache(inode, 0); | ||
| 262 | else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) | ||
| 263 | invalidate_inode_pages2(inode->i_mapping); | ||
| 264 | } | ||
| 259 | if (dax_truncate) | 265 | if (dax_truncate) |
| 260 | filemap_invalidate_unlock(inode->i_mapping); | 266 | filemap_invalidate_unlock(inode->i_mapping); |
| 261 | 267 | out_inode_unlock: | |
| 262 | if (is_wb_truncate | dax_truncate) { | 268 | if (is_wb_truncate || dax_truncate) |
| 263 | fuse_release_nowrite(inode); | ||
| 264 | inode_unlock(inode); | 269 | inode_unlock(inode); |
| 265 | } | ||
| 266 | 270 | ||
| 267 | return err; | 271 | return err; |
| 268 | } | 272 | } |
| @@ -338,6 +342,15 @@ static int fuse_open(struct inode *inode, struct file *file) · 69 unmodified lines | |||
| 338 | 342 | ||
| 339 | static int fuse_release(struct inode *inode, struct file *file) | 343 | static int fuse_release(struct inode *inode, struct file *file) |
| 340 | { | 344 | { |
| 345 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
| 346 | |||
| 347 | /* | ||
| 348 | * Dirty pages might remain despite write_inode_now() call from | ||
| 349 | * fuse_flush() due to writes racing with the close. | ||
| 350 | */ | ||
| 351 | if (fc->writeback_cache) | ||
| 352 | write_inode_now(inode, 1); | ||
| 353 | |||
| 341 | fuse_release_common(file, false); | 354 | fuse_release_common(file, false); |
| 342 | 355 | ||
| 343 | /* return value is ignored by VFS */ | 356 | /* return value is ignored by VFS */ |
| @@ -180,6 +180,12 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, · 179 unmodified lines | |||
| 180 | inode->i_uid = make_kuid(fc->user_ns, attr->uid); | 180 | inode->i_uid = make_kuid(fc->user_ns, attr->uid); |
| 181 | inode->i_gid = make_kgid(fc->user_ns, attr->gid); | 181 | inode->i_gid = make_kgid(fc->user_ns, attr->gid); |
| 182 | inode->i_blocks = attr->blocks; | 182 | inode->i_blocks = attr->blocks; |
| 183 | |||
| 184 | /* Sanitize nsecs */ | ||
| 185 | attr->atimensec = min_t(u32, attr->atimensec, NSEC_PER_SEC - 1); | ||
| 186 | attr->mtimensec = min_t(u32, attr->mtimensec, NSEC_PER_SEC - 1); | ||
| 187 | attr->ctimensec = min_t(u32, attr->ctimensec, NSEC_PER_SEC - 1); | ||
| 188 | |||
| 183 | inode->i_atime.tv_sec = attr->atime; | 189 | inode->i_atime.tv_sec = attr->atime; |
| 184 | inode->i_atime.tv_nsec = attr->atimensec; | 190 | inode->i_atime.tv_nsec = attr->atimensec; |
| 185 | /* mtime from server may be stale due to local buffered write */ | 191 | /* mtime from server may be stale due to local buffered write */ |
| @@ -476,8 +482,14 @@ static void fuse_umount_begin(struct super_block *sb) · 290 unmodified lines | |||
| 476 | { | 482 | { |
| 477 | struct fuse_conn *fc = get_fuse_conn_super(sb); | 483 | struct fuse_conn *fc = get_fuse_conn_super(sb); |
| 478 | 484 | ||
| 479 | if (!fc->no_force_umount) | 485 | if (fc->no_force_umount) |
| 480 | fuse_abort_conn(fc); | 486 | return; |
| 487 | |||
| 488 | fuse_abort_conn(fc); | ||
| 489 | |||
| 490 | // Only retire block-device-based superblocks. | ||
| 491 | if (sb->s_bdev != NULL) | ||
| 492 | retire_super(sb); | ||
| 481 | } | 493 | } |
| 482 | 494 | ||
| 483 | static void fuse_send_destroy(struct fuse_mount *fm) | 495 | static void fuse_send_destroy(struct fuse_mount *fm) |
| @@ -9,6 +9,17 @@ · 8 unmodified lines | |||
| 9 | #include <linux/compat.h> | 9 | #include <linux/compat.h> |
| 10 | #include <linux/fileattr.h> | 10 | #include <linux/fileattr.h> |
| 11 | 11 | ||
| 12 | static ssize_t fuse_send_ioctl(struct fuse_mount *fm, struct fuse_args *args) | ||
| 13 | { | ||
| 14 | ssize_t ret = fuse_simple_request(fm, args); | ||
| 15 | |||
| 16 | /* Translate ENOSYS, which shouldn't be returned from fs */ | ||
| 17 | if (ret == -ENOSYS) | ||
| 18 | ret = -ENOTTY; | ||
| 19 | |||
| 20 | return ret; | ||
| 21 | } | ||
| 22 | |||
| 12 | /* | 23 | /* |
| 13 | * CUSE servers compiled on 32bit broke on 64bit kernels because the | 24 | * CUSE servers compiled on 32bit broke on 64bit kernels because the |
| 14 | * ABI was defined to be 'struct iovec' which is different on 32bit | 25 | * ABI was defined to be 'struct iovec' which is different on 32bit |
| @@ -259,7 +270,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, · 244 unmodified lines | |||
| 259 | ap.args.out_pages = true; | 270 | ap.args.out_pages = true; |
| 260 | ap.args.out_argvar = true; | 271 | ap.args.out_argvar = true; |
| 261 | 272 | ||
| 262 | transferred = fuse_simple_request(fm, &ap.args); | 273 | transferred = fuse_send_ioctl(fm, &ap.args); |
| 263 | err = transferred; | 274 | err = transferred; |
| 264 | if (transferred < 0) | 275 | if (transferred < 0) |
| 265 | goto out; | 276 | goto out; |
| @@ -393,7 +404,7 @@ static int fuse_priv_ioctl(struct inode *inode, struct fuse_file *ff, · 127 unmodified lines | |||
| 393 | args.out_args[1].size = inarg.out_size; | 404 | args.out_args[1].size = inarg.out_size; |
| 394 | args.out_args[1].value = ptr; | 405 | args.out_args[1].value = ptr; |
| 395 | 406 | ||
| 396 | err = fuse_simple_request(fm, &args); | 407 | err = fuse_send_ioctl(fm, &args); |
| 397 | if (!err) { | 408 | if (!err) { |
| 398 | if (outarg.result < 0) | 409 | if (outarg.result < 0) |
| 399 | err = outarg.result; | 410 | err = outarg.result; |
| @@ -741,8 +741,7 @@ out: · 740 unmodified lines | |||
| 741 | } | 741 | } |
| 742 | 742 | ||
| 743 | /* Free virtqueues (device must already be reset) */ | 743 | /* Free virtqueues (device must already be reset) */ |
| 744 | static void virtio_fs_cleanup_vqs(struct virtio_device *vdev, | 744 | static void virtio_fs_cleanup_vqs(struct virtio_device *vdev) |
| 745 | struct virtio_fs *fs) | ||
| 746 | { | 745 | { |
| 747 | vdev->config->del_vqs(vdev); | 746 | vdev->config->del_vqs(vdev); |
| 748 | } | 747 | } |
| @@ -757,7 +756,7 @@ static long virtio_fs_direct_access(struct dax_device *dax_dev, pgoff_t pgoff, · 8 unmodified lines | |||
| 757 | { | 756 | { |
| 758 | struct virtio_fs *fs = dax_get_private(dax_dev); | 757 | struct virtio_fs *fs = dax_get_private(dax_dev); |
| 759 | phys_addr_t offset = PFN_PHYS(pgoff); | 758 | phys_addr_t offset = PFN_PHYS(pgoff); |
| 760 | size_t max_nr_pages = fs->window_len/PAGE_SIZE - pgoff; | 759 | size_t max_nr_pages = fs->window_len / PAGE_SIZE - pgoff; |
| 761 | 760 | ||
| 762 | if (kaddr) | 761 | if (kaddr) |
| 763 | *kaddr = fs->window_kaddr + offset; | 762 | *kaddr = fs->window_kaddr + offset; |
| @@ -895,7 +894,7 @@ static int virtio_fs_probe(struct virtio_device *vdev) · 131 unmodified lines | |||
| 895 | 894 | ||
| 896 | out_vqs: | 895 | out_vqs: |
| 897 | virtio_reset_device(vdev); | 896 | virtio_reset_device(vdev); |
| 898 | virtio_fs_cleanup_vqs(vdev, fs); | 897 | virtio_fs_cleanup_vqs(vdev); |
| 899 | kfree(fs->vqs); | 898 | kfree(fs->vqs); |
| 900 | 899 | ||
| 901 | out: | 900 | out: |
| @@ -927,7 +926,7 @@ static void virtio_fs_remove(struct virtio_device *vdev) · 25 unmodified lines | |||
| 927 | virtio_fs_stop_all_queues(fs); | 926 | virtio_fs_stop_all_queues(fs); |
| 928 | virtio_fs_drain_all_queues_locked(fs); | 927 | virtio_fs_drain_all_queues_locked(fs); |
| 929 | virtio_reset_device(vdev); | 928 | virtio_reset_device(vdev); |
| 930 | virtio_fs_cleanup_vqs(vdev, fs); | 929 | virtio_fs_cleanup_vqs(vdev); |
| 931 | 930 | ||
| 932 | vdev->priv = NULL; | 931 | vdev->priv = NULL; |
| 933 | /* Put device reference on virtio_fs object */ | 932 | /* Put device reference on virtio_fs object */ |
| @@ -422,6 +422,35 @@ bool trylock_super(struct super_block *sb) · 421 unmodified lines | |||
| 422 | return false; | 422 | return false; |
| 423 | } | 423 | } |
| 424 | 424 | ||
| 425 | /** | ||
| 426 | * retire_super - prevents superblock from being reused | ||
| 427 | * @sb: superblock to retire | ||
| 428 | * | ||
| 429 | * The function marks superblock to be ignored in superblock test, which | ||
| 430 | * prevents it from being reused for any new mounts. If the superblock has | ||
| 431 | * a private bdi, it also unregisters it, but doesn't reduce the refcount | ||
| 432 | * of the superblock to prevent potential races. The refcount is reduced | ||
| 433 | * by generic_shutdown_super(). The function can not be called | ||
| 434 | * concurrently with generic_shutdown_super(). It is safe to call the | ||
| 435 | * function multiple times, subsequent calls have no effect. | ||
| 436 | * | ||
| 437 | * The marker will affect the re-use only for block-device-based | ||
| 438 | * superblocks. Other superblocks will still get marked if this function | ||
| 439 | * is used, but that will not affect their reusability. | ||
| 440 | */ | ||
| 441 | void retire_super(struct super_block *sb) | ||
| 442 | { | ||
| 443 | WARN_ON(!sb->s_bdev); | ||
| 444 | down_write(&sb->s_umount); | ||
| 445 | if (sb->s_iflags & SB_I_PERSB_BDI) { | ||
| 446 | bdi_unregister(sb->s_bdi); | ||
| 447 | sb->s_iflags &= ~SB_I_PERSB_BDI; | ||
| 448 | } | ||
| 449 | sb->s_iflags |= SB_I_RETIRED; | ||
| 450 | up_write(&sb->s_umount); | ||
| 451 | } | ||
| 452 | EXPORT_SYMBOL(retire_super); | ||
| 453 | |||
| 425 | /** | 454 | /** |
| 426 | * generic_shutdown_super - common helper for ->kill_sb() | 455 | * generic_shutdown_super - common helper for ->kill_sb() |
| 427 | * @sb: superblock to kill | 456 | * @sb: superblock to kill |
| @@ -1216,7 +1245,7 @@ static int set_bdev_super_fc(struct super_block *s, struct fs_context *fc) · 788 unmodified lines | |||
| 1216 | 1245 | ||
| 1217 | static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc) | 1246 | static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc) |
| 1218 | { | 1247 | { |
| 1219 | return s->s_bdev == fc->sget_key; | 1248 | return !(s->s_iflags & SB_I_RETIRED) && s->s_bdev == fc->sget_key; |
| 1220 | } | 1249 | } |
| 1221 | 1250 | ||
| 1222 | /** | 1251 | /** |
| @@ -1309,7 +1338,7 @@ EXPORT_SYMBOL(get_tree_bdev); · 86 unmodified lines | |||
| 1309 | 1338 | ||
| 1310 | static int test_bdev_super(struct super_block *s, void *data) | 1339 | static int test_bdev_super(struct super_block *s, void *data) |
| 1311 | { | 1340 | { |
| 1312 | return (void *)s->s_bdev == data; | 1341 | return !(s->s_iflags & SB_I_RETIRED) && (void *)s->s_bdev == data; |
| 1313 | } | 1342 | } |
| 1314 | 1343 | ||
| 1315 | struct dentry *mount_bdev(struct file_system_type *fs_type, | 1344 | struct dentry *mount_bdev(struct file_system_type *fs_type, |
| @@ -1433,6 +1433,7 @@ extern int send_sigurg(struct fown_struct *fown); · 1432 unmodified lines | |||
| 1433 | #define SB_I_SKIP_SYNC 0x00000100 /* Skip superblock at global sync */ | 1433 | #define SB_I_SKIP_SYNC 0x00000100 /* Skip superblock at global sync */ |
| 1434 | #define SB_I_PERSB_BDI 0x00000200 /* has a per-sb bdi */ | 1434 | #define SB_I_PERSB_BDI 0x00000200 /* has a per-sb bdi */ |
| 1435 | #define SB_I_TS_EXPIRY_WARNED 0x00000400 /* warned about timestamp range expiry */ | 1435 | #define SB_I_TS_EXPIRY_WARNED 0x00000400 /* warned about timestamp range expiry */ |
| 1436 | #define SB_I_RETIRED 0x00000800 /* superblock shouldn't be reused */ | ||
| 1436 | 1437 | ||
| 1437 | /* Possible states of 'frozen' field */ | 1438 | /* Possible states of 'frozen' field */ |
| 1438 | enum { | 1439 | enum { |
| @@ -2565,6 +2566,7 @@ extern struct dentry *mount_nodev(struct file_system_type *fs_type, · 1126 unmodified lines | |||
| 2565 | int flags, void *data, | 2566 | int flags, void *data, |
| 2566 | int (*fill_super)(struct super_block *, void *, int)); | 2567 | int (*fill_super)(struct super_block *, void *, int)); |
| 2567 | extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path); | 2568 | extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path); |
| 2569 | void retire_super(struct super_block *sb); | ||
| 2568 | void generic_shutdown_super(struct super_block *sb); | 2570 | void generic_shutdown_super(struct super_block *sb); |
| 2569 | void kill_block_super(struct super_block *sb); | 2571 | void kill_block_super(struct super_block *sb); |
| 2570 | void kill_anon_super(struct super_block *sb); | 2572 | void kill_anon_super(struct super_block *sb); |