Commit 07bfa441 authored by OGAWA Hirofumi's avatar OGAWA Hirofumi Committed by Linus Torvalds

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.jpSigned-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>
parent 619e17cf
...@@ -1100,8 +1100,11 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, ...@@ -1100,8 +1100,11 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
err = -ENOMEM; err = -ENOMEM;
goto error; goto error;
} }
/* Avoid race with userspace read via bdev */
lock_buffer(bhs[n]);
memset(bhs[n]->b_data, 0, sb->s_blocksize); memset(bhs[n]->b_data, 0, sb->s_blocksize);
set_buffer_uptodate(bhs[n]); set_buffer_uptodate(bhs[n]);
unlock_buffer(bhs[n]);
mark_buffer_dirty_inode(bhs[n], dir); mark_buffer_dirty_inode(bhs[n], dir);
n++; n++;
...@@ -1158,6 +1161,8 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts) ...@@ -1158,6 +1161,8 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts)
fat_time_unix2fat(sbi, ts, &time, &date, &time_cs); fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
de = (struct msdos_dir_entry *)bhs[0]->b_data; 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) */ /* filling the new directory slots ("." and ".." entries) */
memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME); memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME);
memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME); memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME);
...@@ -1180,6 +1185,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts) ...@@ -1180,6 +1185,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts)
de[0].size = de[1].size = 0; de[0].size = de[1].size = 0;
memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de)); memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de));
set_buffer_uptodate(bhs[0]); set_buffer_uptodate(bhs[0]);
unlock_buffer(bhs[0]);
mark_buffer_dirty_inode(bhs[0], dir); mark_buffer_dirty_inode(bhs[0], dir);
err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE); err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE);
...@@ -1237,11 +1243,14 @@ static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, ...@@ -1237,11 +1243,14 @@ static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots,
/* fill the directory entry */ /* fill the directory entry */
copy = min(size, sb->s_blocksize); copy = min(size, sb->s_blocksize);
/* Avoid race with userspace read via bdev */
lock_buffer(bhs[n]);
memcpy(bhs[n]->b_data, slots, copy); memcpy(bhs[n]->b_data, slots, copy);
slots += copy;
size -= copy;
set_buffer_uptodate(bhs[n]); set_buffer_uptodate(bhs[n]);
unlock_buffer(bhs[n]);
mark_buffer_dirty_inode(bhs[n], dir); mark_buffer_dirty_inode(bhs[n], dir);
slots += copy;
size -= copy;
if (!size) if (!size)
break; break;
n++; n++;
......
...@@ -388,8 +388,11 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs, ...@@ -388,8 +388,11 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
err = -ENOMEM; err = -ENOMEM;
goto error; 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); memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
set_buffer_uptodate(c_bh); set_buffer_uptodate(c_bh);
unlock_buffer(c_bh);
mark_buffer_dirty_inode(c_bh, sbi->fat_inode); mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
if (sb->s_flags & SB_SYNCHRONOUS) if (sb->s_flags & SB_SYNCHRONOUS)
err = sync_dirty_buffer(c_bh); err = sync_dirty_buffer(c_bh);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment