diff options
author | OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> | 2019-09-23 15:32:53 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-24 15:54:06 -0700 |
commit | 07bfa4415ab607e459b69bd86aa7e7602ce10b4f (patch) | |
tree | 62a7bc60deca51a1a795c8a22293e658d88efc07 /fs/fat/fatent.c | |
parent | 619e17cf75dd58905aa67ccd494a6ba5f19d6cc6 (diff) | |
download | linux-stable-07bfa4415ab607e459b69bd86aa7e7602ce10b4f.tar.gz linux-stable-07bfa4415ab607e459b69bd86aa7e7602ce10b4f.tar.bz2 linux-stable-07bfa4415ab607e459b69bd86aa7e7602ce10b4f.zip |
fat: work around race with userspace's read via blockdev while mounting
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: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Reported-by: Jan Stancek <jstancek@redhat.com>
Tested-by: Jan Stancek <jstancek@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/fat/fatent.c')
-rw-r--r-- | fs/fat/fatent.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 265983635f2b..3647c65a0f48 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -388,8 +388,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 & SB_SYNCHRONOUS) err = sync_dirty_buffer(c_bh); |