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>
This commit is contained in:
parent
619e17cf75
commit
07bfa4415a
13
fs/fat/dir.c
13
fs/fat/dir.c
|
@ -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)
|
||||||
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)
|
||||||
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,
|
||||||
|
|
||||||
/* 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);
|
||||||
|
set_buffer_uptodate(bhs[n]);
|
||||||
|
unlock_buffer(bhs[n]);
|
||||||
|
mark_buffer_dirty_inode(bhs[n], dir);
|
||||||
slots += copy;
|
slots += copy;
|
||||||
size -= copy;
|
size -= copy;
|
||||||
set_buffer_uptodate(bhs[n]);
|
|
||||||
mark_buffer_dirty_inode(bhs[n], dir);
|
|
||||||
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,
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue