Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb04c282 authored by Jan Kara's avatar Jan Kara Committed by Al Viro
Browse files

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: default avatarKamal Mostafa <kamal@canonical.com>
Tested-by: default avatarPeter M. Petrakis <peter.petrakis@canonical.com>
Tested-by: default avatarDann Frazier <dann.frazier@canonical.com>
Tested-by: default avatarMassimo Morana <massimo.morana@canonical.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 5accdf82
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -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);
}
}


+2 −2
Original line number Original line Diff line number Diff line
@@ -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;
}
}
+4 −0
Original line number Original line Diff line number Diff line
@@ -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
+77 −20
Original line number Original line Diff line number Diff line
@@ -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);
@@ -355,38 +374,76 @@ 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
 * __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
 *
 *
 * This is like mnt_want_write, but it takes a file and can
 * This is like __mnt_want_write, but it takes a file and can
 * do some optimisations if the file is open for write already
 * do some optimisations if the file is open for write already
 */
 */
int mnt_want_write_file(struct file *file)
int __mnt_want_write_file(struct file *file)
{
{
	struct inode *inode = file->f_dentry->d_inode;
	struct inode *inode = file->f_dentry->d_inode;

	if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
	if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
		return mnt_want_write(file->f_path.mnt);
		return __mnt_want_write(file->f_path.mnt);
	else
	else
		return mnt_clone_write(file->f_path.mnt);
		return mnt_clone_write(file->f_path.mnt);
}
}

/**
 * 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)
{
	int ret;

	sb_start_write(file->f_path.mnt->mnt_sb);
	ret = __mnt_want_write_file(file);
	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);
+1 −1
Original line number Original line Diff line number Diff line
@@ -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);
	}
	}