Linux mnt_want_write是挂载层写权限的准入控制机制任何需要在文件系统上执行写操作的代码路径都必须在操作前调用它操作完成后调用mnt_drop_write。它维护了一个mount的mnt_writers计数器同时检测挂载的冻结状态和expiry过期状态。函数位于fs/namespace.c。// fs/namespace.cint mnt_want_write(struct vfsmount *m){int ret;sb_start_write(m-mnt_sb);ret __mnt_want_write(m);if (ret)sb_end_write(m-mnt_sb);return ret;}外层包裹了sb_start_write/sb_end_write的超级块级同步写入保护这是文件系统freeze冻结机制的一环。当文件系统被冻结时如调用ioctl(FIFREEZE)或LVM快照创建时sb_start_write会使写者阻塞等待冻结完成然后后续的写操作被阻止进入。// fs/namespace.cint __mnt_want_write(struct vfsmount *m){struct mount *p;if (unlikely(m-mnt_flags MNT_WRITE_HOLD))return -EBUSY;p real_mount(m);if (unlikely(p-mnt.mnt_flags MNT_READONLY))return -EROFS;if (unlikely(IS_MNT_EXPIRED(p)))return -EACCES;p-mnt_writers;smp_mb__after_atomic();if (unlikely(p-mnt.mnt_flags MNT_WRITE_HOLD)) {p-mnt_writers--;return -EBUSY;}return 0;}__mnt_want_write内部检查三个条件并以原子方式递增mnt_writers。MNT_WRITE_HOLD标志用于冻结期间的通知当文件系统冻结时MNT_WRITE_HOLD被设置同时等待mnt_writers降为零。此处有一个经典的TOCTOU处理模式先递增mnt_writers再检查MNT_WRITE_HOLD如果发现标志已被设置则回滚递减。通过smp_mb__after_atomic保证递增对其他CPU可见后再读取标志位。MNT_READONLY标志表明挂载是只读的直接返回-EROFS。这是一种快速检查实际挂载的读写状态可能通过remount动态切换所以每次写操作前都需要验证。IS_MNT_EXPIRED检查expiry状态。expiry机制用于自动挂载文件系统autofs和某些网络文件系统的超时卸载。当挂载过期时mnt_want_write返回-EACCES阻止写入。// include/linux/mount.hstatic inline int IS_MNT_EXPIRED(struct mount *mnt){return mnt-mnt_expiry_mark mnt-mnt_expiry_mark jiffies;}expiry判断依赖mnt_expiry_mark字段。autofs在定时器触发时对所有管理的挂载设置expiry标记后续任何访问会重置该标记防止过期。mnt_want_write返回-EACCES后VFS层通常会上报到autofs的d_automount方法触发重新挂载。mnt_writers是一个unsigned int类型它的递增和递减都不是原子的——但配合mnt_writer_lock锁机制保证正确性。实际上__mnt_want_write和__mnt_drop_write都在mnt_writer_mutex的保护范围内调用但这个mutex只保护冻结路径的协作非冻结路径的mnt_writers操作是直接读写int的因为持有inode_lock或文件系统内部锁已经提供了上下文互斥。// fs/namespace.cvoid mnt_drop_write(struct vfsmount *mnt){__mnt_drop_write(mnt);sb_end_write(mnt-mnt_sb);}void __mnt_drop_write(struct vfsmount *mnt){struct mount *p real_mount(mnt);p-mnt_writers--;}mnt_drop_write是mnt_want_write的逆操作递减mnt_writers并退出超级块的写保护。当mnt_writers降为0时可能触发等待中的冻结操作继续推进。// fs/namespace.cint mnt_want_write_file(struct file *file){int ret;sb_start_write(file_inode(file)-i_sb);ret __mnt_want_write_file(file);if (ret)sb_end_write(file_inode(file)-i_sb);return ret;}static inline int __mnt_want_write_file(struct file *file){if (file-f_mode FMODE_WRITER)return 0;return __mnt_want_write(file-f_path.mnt);}mnt_want_write_file是针对已打开文件的变体。如果文件打开时FMODE_WRITER标志已置位表示open时已经做了mnt_want_write直接返回0避免重复计数。这是为了防止内核代码路径在同一个文件上重复获取写权限。例如do_dentry_open已经为O_WRONLY/O_RDWR文件执行了mnt_want_write后续write系统调用中不要再重复调用。mnt_want_write的调用者覆盖了所有写操作的入口路径- vfs_write/vfs_writev每次写入前调用mnt_want_write_file- vfs_truncate截断文件前调用- vfs_unlink/vfs_rename/vfs_rmdir目录操作前调用- vfs_setattr修改inode属性前调用- 直接I/O和splice的写路径挂载冻结的完整流程依赖于mnt_writers的值。freeze_super同步设置MNT_WRITE_HOLD后等待所有mnt_writers降为0这意味着所有正在进行的写操作必须完成并调用mnt_drop_write后才能冻结完成。冻结完成后所有新的写操作在__mnt_want_write的MNT_WRITE_HOLD检查处被拒绝返回-EBUSY上层调用者通常直接返回-EIO或-ESTALE给用户态。
Linux mnt_want_write挂载写权限count递增与expiry
发布时间:2026/6/15 14:39:07
Linux mnt_want_write是挂载层写权限的准入控制机制任何需要在文件系统上执行写操作的代码路径都必须在操作前调用它操作完成后调用mnt_drop_write。它维护了一个mount的mnt_writers计数器同时检测挂载的冻结状态和expiry过期状态。函数位于fs/namespace.c。// fs/namespace.cint mnt_want_write(struct vfsmount *m){int ret;sb_start_write(m-mnt_sb);ret __mnt_want_write(m);if (ret)sb_end_write(m-mnt_sb);return ret;}外层包裹了sb_start_write/sb_end_write的超级块级同步写入保护这是文件系统freeze冻结机制的一环。当文件系统被冻结时如调用ioctl(FIFREEZE)或LVM快照创建时sb_start_write会使写者阻塞等待冻结完成然后后续的写操作被阻止进入。// fs/namespace.cint __mnt_want_write(struct vfsmount *m){struct mount *p;if (unlikely(m-mnt_flags MNT_WRITE_HOLD))return -EBUSY;p real_mount(m);if (unlikely(p-mnt.mnt_flags MNT_READONLY))return -EROFS;if (unlikely(IS_MNT_EXPIRED(p)))return -EACCES;p-mnt_writers;smp_mb__after_atomic();if (unlikely(p-mnt.mnt_flags MNT_WRITE_HOLD)) {p-mnt_writers--;return -EBUSY;}return 0;}__mnt_want_write内部检查三个条件并以原子方式递增mnt_writers。MNT_WRITE_HOLD标志用于冻结期间的通知当文件系统冻结时MNT_WRITE_HOLD被设置同时等待mnt_writers降为零。此处有一个经典的TOCTOU处理模式先递增mnt_writers再检查MNT_WRITE_HOLD如果发现标志已被设置则回滚递减。通过smp_mb__after_atomic保证递增对其他CPU可见后再读取标志位。MNT_READONLY标志表明挂载是只读的直接返回-EROFS。这是一种快速检查实际挂载的读写状态可能通过remount动态切换所以每次写操作前都需要验证。IS_MNT_EXPIRED检查expiry状态。expiry机制用于自动挂载文件系统autofs和某些网络文件系统的超时卸载。当挂载过期时mnt_want_write返回-EACCES阻止写入。// include/linux/mount.hstatic inline int IS_MNT_EXPIRED(struct mount *mnt){return mnt-mnt_expiry_mark mnt-mnt_expiry_mark jiffies;}expiry判断依赖mnt_expiry_mark字段。autofs在定时器触发时对所有管理的挂载设置expiry标记后续任何访问会重置该标记防止过期。mnt_want_write返回-EACCES后VFS层通常会上报到autofs的d_automount方法触发重新挂载。mnt_writers是一个unsigned int类型它的递增和递减都不是原子的——但配合mnt_writer_lock锁机制保证正确性。实际上__mnt_want_write和__mnt_drop_write都在mnt_writer_mutex的保护范围内调用但这个mutex只保护冻结路径的协作非冻结路径的mnt_writers操作是直接读写int的因为持有inode_lock或文件系统内部锁已经提供了上下文互斥。// fs/namespace.cvoid mnt_drop_write(struct vfsmount *mnt){__mnt_drop_write(mnt);sb_end_write(mnt-mnt_sb);}void __mnt_drop_write(struct vfsmount *mnt){struct mount *p real_mount(mnt);p-mnt_writers--;}mnt_drop_write是mnt_want_write的逆操作递减mnt_writers并退出超级块的写保护。当mnt_writers降为0时可能触发等待中的冻结操作继续推进。// fs/namespace.cint mnt_want_write_file(struct file *file){int ret;sb_start_write(file_inode(file)-i_sb);ret __mnt_want_write_file(file);if (ret)sb_end_write(file_inode(file)-i_sb);return ret;}static inline int __mnt_want_write_file(struct file *file){if (file-f_mode FMODE_WRITER)return 0;return __mnt_want_write(file-f_path.mnt);}mnt_want_write_file是针对已打开文件的变体。如果文件打开时FMODE_WRITER标志已置位表示open时已经做了mnt_want_write直接返回0避免重复计数。这是为了防止内核代码路径在同一个文件上重复获取写权限。例如do_dentry_open已经为O_WRONLY/O_RDWR文件执行了mnt_want_write后续write系统调用中不要再重复调用。mnt_want_write的调用者覆盖了所有写操作的入口路径- vfs_write/vfs_writev每次写入前调用mnt_want_write_file- vfs_truncate截断文件前调用- vfs_unlink/vfs_rename/vfs_rmdir目录操作前调用- vfs_setattr修改inode属性前调用- 直接I/O和splice的写路径挂载冻结的完整流程依赖于mnt_writers的值。freeze_super同步设置MNT_WRITE_HOLD后等待所有mnt_writers降为0这意味着所有正在进行的写操作必须完成并调用mnt_drop_write后才能冻结完成。冻结完成后所有新的写操作在__mnt_want_write的MNT_WRITE_HOLD检查处被拒绝返回-EBUSY上层调用者通常直接返回-EIO或-ESTALE给用户态。