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

Commit 3554b8a1 authored by OGAWA Hirofumi's avatar OGAWA Hirofumi Committed by Greg Kroah-Hartman
Browse files

fat: work around race with userspace's read via blockdev while mounting

[ Upstream commit 07bfa4415ab607e459b69bd86aa7e7602ce10b4f ]

If userspace reads the buffer via blockdev while mounting,
sb_getblk()+modify can race with buffer read via blockdev.

For example,

            FS                               userspace
    bh = sb_getblk()
    modify bh->b_data
                                  read
				    ll_rw_block(bh)
				      fill bh->b_data by on-disk data
				      /* lost modified data by FS */
				      set_buffer_uptodate(bh)
    set_buffer_uptodate(bh)

Userspace should not use the blockdev while mounting though, the udev
seems to be already doing this.  Although I think the udev should try to
avoid this, workaround the race by small overhead.

Link: http://lkml.kernel.org/r/87pnk7l3sw.fsf_-_@mail.parknet.co.jp


Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Reported-by: default avatarJan Stancek <jstancek@redhat.com>
Tested-by: default avatarJan Stancek <jstancek@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 735ad033
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -1097,8 +1097,11 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
			err = -ENOMEM;
			goto error;
		}
		/* Avoid race with userspace read via bdev */
		lock_buffer(bhs[n]);
		memset(bhs[n]->b_data, 0, sb->s_blocksize);
		set_buffer_uptodate(bhs[n]);
		unlock_buffer(bhs[n]);
		mark_buffer_dirty_inode(bhs[n], dir);

		n++;
@@ -1155,6 +1158,8 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
	fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);

	de = (struct msdos_dir_entry *)bhs[0]->b_data;
	/* Avoid race with userspace read via bdev */
	lock_buffer(bhs[0]);
	/* filling the new directory slots ("." and ".." entries) */
	memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME);
	memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME);
@@ -1177,6 +1182,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
	de[0].size = de[1].size = 0;
	memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de));
	set_buffer_uptodate(bhs[0]);
	unlock_buffer(bhs[0]);
	mark_buffer_dirty_inode(bhs[0], dir);

	err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE);
@@ -1234,11 +1240,14 @@ static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots,

			/* fill the directory entry */
			copy = min(size, sb->s_blocksize);
			/* Avoid race with userspace read via bdev */
			lock_buffer(bhs[n]);
			memcpy(bhs[n]->b_data, slots, copy);
			slots += copy;
			size -= copy;
			set_buffer_uptodate(bhs[n]);
			unlock_buffer(bhs[n]);
			mark_buffer_dirty_inode(bhs[n], dir);
			slots += copy;
			size -= copy;
			if (!size)
				break;
			n++;
+3 −0
Original line number Diff line number Diff line
@@ -389,8 +389,11 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
				err = -ENOMEM;
				goto error;
			}
			/* Avoid race with userspace read via bdev */
			lock_buffer(c_bh);
			memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
			set_buffer_uptodate(c_bh);
			unlock_buffer(c_bh);
			mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
			if (sb->s_flags & MS_SYNCHRONOUS)
				err = sync_dirty_buffer(c_bh);