From: Dave Hansen These should fix the bug that Erez Zadok reported: Stopping RPC idmapd: kernel: __fput() of writeable file with no mnt_want_write() kernel: WARNING: at fs/file_table.c:262 __fput() kernel: [] show_trace_log_lvl+0x12/0x25 kernel: [] show_trace+0xd/0x10 ... The actual bug was a missed mnt_want_write() when a filp was allocated with get_empty_filp() and then made writable. Using alloc_file(), instead, fixes that. -- If someone decides to demote a file from r/w to just r/o, they can use this same code as __fput(). NFS does just that, and will use this in the next patch. Signed-off-by: Dave Hansen Cc: Erez Zadok Cc: Trond Myklebust Cc: "J Bruce Fields" Signed-off-by: Andrew Morton --- fs/file_table.c | 46 +++++++++++++++++++++++++++-------------- include/linux/file.h | 1 2 files changed, 32 insertions(+), 15 deletions(-) diff -puN fs/file_table.c~create-file_drop_write_access-helper fs/file_table.c --- a/fs/file_table.c~create-file_drop_write_access-helper +++ a/fs/file_table.c @@ -223,6 +223,34 @@ void fastcall fput(struct file *file) EXPORT_SYMBOL(fput); +/** + * drop_file_write_access - give up ability to write to a file + * @file: the file to which we will stop writing + * + * This is a central place which will give up the ability + * to write to @file, along with access to write through + * its vfsmount. + */ +void drop_file_write_access(struct file *file) +{ + struct dentry *dentry = file->f_path.dentry; + struct vfsmount *mnt = file->f_path.mnt; + struct inode *inode = dentry->d_inode; + + put_write_access(inode); + if (!special_file(inode->i_mode)) { + if (file->f_mnt_write_state == FILE_MNT_WRITE_TAKEN) { + mnt_drop_write(mnt); + file->f_mnt_write_state |= FILE_MNT_WRITE_RELEASED; + } else { + printk(KERN_WARNING "dropping write access on " + "file with no mnt_want_write()\n"); + WARN_ON(1); + } + } +} +EXPORT_SYMBOL_GPL(drop_file_write_access); + /* __fput is called from task context when aio completion releases the last * last use of a struct file *. Do not use otherwise. */ @@ -248,21 +276,9 @@ void fastcall __fput(struct file *file) if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL)) cdev_put(inode->i_cdev); fops_put(file->f_op); - if (file->f_mode & FMODE_WRITE) { - put_write_access(inode); - if (!special_file(inode->i_mode)) { - if (file->f_mnt_write_state == FILE_MNT_WRITE_TAKEN) { - mnt_drop_write(mnt); - file->f_mnt_write_state |= - FILE_MNT_WRITE_RELEASED; - } else { - printk(KERN_WARNING "__fput() of writeable " - "file with no " - "mnt_want_write()\n"); - WARN_ON(1); - } - } - } + if (file->f_mode & FMODE_WRITE) + drop_file_write_access(file); + put_pid(file->f_owner.pid); file_kill(file); file->f_path.dentry = NULL; diff -puN include/linux/file.h~create-file_drop_write_access-helper include/linux/file.h --- a/include/linux/file.h~create-file_drop_write_access-helper +++ a/include/linux/file.h @@ -61,6 +61,7 @@ extern struct kmem_cache *filp_cachep; extern void FASTCALL(__fput(struct file *)); extern void FASTCALL(fput(struct file *)); +extern void drop_file_write_access(struct file *file); struct file_operations; struct vfsmount; _