fs: Add freezing handling to mnt_want_write() / mnt_drop_write()

Most of places where we want freeze protection coincides with the places where
we also have remount-ro protection. So make mnt_want_write() and
mnt_drop_write() (and their _file alternative) prevent freezing as well.
For the few cases that are really interested only in remount-ro protection
provide new function variants.

BugLink: https://bugs.launchpad.net/bugs/897421
Tested-by: Kamal Mostafa <kamal@canonical.com>
Tested-by: Peter M. Petrakis <peter.petrakis@canonical.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
Tested-by: Massimo Morana <massimo.morana@canonical.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Jan Kara 2012-06-12 16:20:35 +02:00 committed by Al Viro
parent 5accdf82ba
commit eb04c28288
5 changed files with 86 additions and 25 deletions

View file

@ -217,7 +217,7 @@ static void drop_file_write_access(struct file *file)
return; return;
if (file_check_writeable(file) != 0) if (file_check_writeable(file) != 0)
return; return;
mnt_drop_write(mnt); __mnt_drop_write(mnt);
file_release_write(file); file_release_write(file);
} }

View file

@ -1660,11 +1660,11 @@ int file_update_time(struct file *file)
return 0; return 0;
/* Finally allowed to write? Takes lock. */ /* Finally allowed to write? Takes lock. */
if (mnt_want_write_file(file)) if (__mnt_want_write_file(file))
return 0; return 0;
ret = update_time(inode, &now, sync_it); ret = update_time(inode, &now, sync_it);
mnt_drop_write_file(file); __mnt_drop_write_file(file);
return ret; return ret;
} }

View file

@ -61,6 +61,10 @@ extern void __init mnt_init(void);
extern struct lglock vfsmount_lock; extern struct lglock vfsmount_lock;
extern int __mnt_want_write(struct vfsmount *);
extern int __mnt_want_write_file(struct file *);
extern void __mnt_drop_write(struct vfsmount *);
extern void __mnt_drop_write_file(struct file *);
/* /*
* fs_struct.c * fs_struct.c

View file

@ -283,24 +283,22 @@ static int mnt_is_readonly(struct vfsmount *mnt)
} }
/* /*
* Most r/o checks on a fs are for operations that take * Most r/o & frozen checks on a fs are for operations that take discrete
* discrete amounts of time, like a write() or unlink(). * amounts of time, like a write() or unlink(). We must keep track of when
* We must keep track of when those operations start * those operations start (for permission checks) and when they end, so that we
* (for permission checks) and when they end, so that * can determine when writes are able to occur to a filesystem.
* we can determine when writes are able to occur to
* a filesystem.
*/ */
/** /**
* mnt_want_write - get write access to a mount * __mnt_want_write - get write access to a mount without freeze protection
* @m: the mount on which to take a write * @m: the mount on which to take a write
* *
* This tells the low-level filesystem that a write is * This tells the low-level filesystem that a write is about to be performed to
* about to be performed to it, and makes sure that * it, and makes sure that writes are allowed (mnt it read-write) before
* writes are allowed before returning success. When * returning success. This operation does not protect against filesystem being
* the write operation is finished, mnt_drop_write() * frozen. When the write operation is finished, __mnt_drop_write() must be
* must be called. This is effectively a refcount. * called. This is effectively a refcount.
*/ */
int mnt_want_write(struct vfsmount *m) int __mnt_want_write(struct vfsmount *m)
{ {
struct mount *mnt = real_mount(m); struct mount *mnt = real_mount(m);
int ret = 0; int ret = 0;
@ -326,6 +324,27 @@ int mnt_want_write(struct vfsmount *m)
ret = -EROFS; ret = -EROFS;
} }
preempt_enable(); preempt_enable();
return ret;
}
/**
* mnt_want_write - get write access to a mount
* @m: the mount on which to take a write
*
* This tells the low-level filesystem that a write is about to be performed to
* it, and makes sure that writes are allowed (mount is read-write, filesystem
* is not frozen) before returning success. When the write operation is
* finished, mnt_drop_write() must be called. This is effectively a refcount.
*/
int 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; return ret;
} }
EXPORT_SYMBOL_GPL(mnt_want_write); EXPORT_SYMBOL_GPL(mnt_want_write);
@ -354,6 +373,23 @@ int mnt_clone_write(struct vfsmount *mnt)
} }
EXPORT_SYMBOL_GPL(mnt_clone_write); EXPORT_SYMBOL_GPL(mnt_clone_write);
/**
* __mnt_want_write_file - get write access to a file's mount
* @file: the file who's mount on which to take a write
*
* This is like __mnt_want_write, but it takes a file and can
* do some optimisations if the file is open for write already
*/
int __mnt_want_write_file(struct file *file)
{
struct inode *inode = file->f_dentry->d_inode;
if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
return __mnt_want_write(file->f_path.mnt);
else
return mnt_clone_write(file->f_path.mnt);
}
/** /**
* mnt_want_write_file - get write access to a file's mount * mnt_want_write_file - get write access to a file's mount
* @file: the file who's mount on which to take a write * @file: the file who's mount on which to take a write
@ -363,30 +399,51 @@ EXPORT_SYMBOL_GPL(mnt_clone_write);
*/ */
int mnt_want_write_file(struct file *file) int mnt_want_write_file(struct file *file)
{ {
struct inode *inode = file->f_dentry->d_inode; int ret;
if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
return mnt_want_write(file->f_path.mnt); sb_start_write(file->f_path.mnt->mnt_sb);
else ret = __mnt_want_write_file(file);
return mnt_clone_write(file->f_path.mnt); if (ret)
sb_end_write(file->f_path.mnt->mnt_sb);
return ret;
} }
EXPORT_SYMBOL_GPL(mnt_want_write_file); EXPORT_SYMBOL_GPL(mnt_want_write_file);
/** /**
* mnt_drop_write - give up write access to a mount * __mnt_drop_write - give up write access to a mount
* @mnt: the mount on which to give up write access * @mnt: the mount on which to give up write access
* *
* Tells the low-level filesystem that we are done * Tells the low-level filesystem that we are done
* performing writes to it. Must be matched with * performing writes to it. Must be matched with
* mnt_want_write() call above. * __mnt_want_write() call above.
*/ */
void mnt_drop_write(struct vfsmount *mnt) void __mnt_drop_write(struct vfsmount *mnt)
{ {
preempt_disable(); preempt_disable();
mnt_dec_writers(real_mount(mnt)); mnt_dec_writers(real_mount(mnt));
preempt_enable(); preempt_enable();
} }
/**
* mnt_drop_write - give up write access to a mount
* @mnt: the mount on which to give up write access
*
* Tells the low-level filesystem that we are done performing writes to it and
* also allows filesystem to be frozen again. Must be matched with
* mnt_want_write() call above.
*/
void mnt_drop_write(struct vfsmount *mnt)
{
__mnt_drop_write(mnt);
sb_end_write(mnt->mnt_sb);
}
EXPORT_SYMBOL_GPL(mnt_drop_write); EXPORT_SYMBOL_GPL(mnt_drop_write);
void __mnt_drop_write_file(struct file *file)
{
__mnt_drop_write(file->f_path.mnt);
}
void mnt_drop_write_file(struct file *file) void mnt_drop_write_file(struct file *file)
{ {
mnt_drop_write(file->f_path.mnt); mnt_drop_write(file->f_path.mnt);

View file

@ -620,7 +620,7 @@ static inline int __get_file_write_access(struct inode *inode,
/* /*
* Balanced in __fput() * Balanced in __fput()
*/ */
error = mnt_want_write(mnt); error = __mnt_want_write(mnt);
if (error) if (error)
put_write_access(inode); put_write_access(inode);
} }