f2fs: fix to avoid reading out encrypted data in page cache
Subsystems (2)
Chao Yu
maintainerPATCH 1/4 f2fs: fix to avoid reading out encrypted data in page cache
From: Chao Yu <[email protected]>
For encrypted inode, if user overwrites data of the inode, f2fs will read encrypted data into page cache, and then do the decryption.
However reader can race with overwriter, and it will see encrypted data which has not been decrypted by overwriter yet. Fix it by moving decrypting work to background and keep page non-uptodated until data is decrypted.
Thread A Thread B
- f2fs_file_write_iter
- __generic_file_write_iter
- generic_perform_write
- f2fs_write_begin
- f2fs_submit_page_bio
- generic_file_read_iter
- do_generic_file_read
- lock_page_killable
- unlock_page
- copy_page_to_iter
hit the encrypted data in updated page
- lock_page
- fscrypt_decrypt_page
--- fs/f2fs/data.c | 90 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 43 deletions(-)
fs/f2fs/data.c +47 -43
@@ -955,6 +955,37 @@ out:return ret; } +struct bio *f2fs_grab_bio (struct inode *inode,block_t blkaddr, +unsigned nr_pages) +{ +struct f2fs_sb_info *sbi =F2FS_I_SB (inode); +struct fscrypt_ctx *ctx =NULL ; +struct block_device *bdev = sbi->sb->s_bdev; +struct bio *bio; + +if (f2fs_encrypted_inode (inode) &&S_ISREG (inode->i_mode)) { + ctx =fscrypt_get_ctx (inode,GFP_NOFS ); +if (IS_ERR (ctx)) +return ERR_CAST (ctx); + +/* wait the page to be moved by cleaning */ +f2fs_wait_on_encrypted_page_writeback (sbi, blkaddr); + } + + bio =bio_alloc (GFP_KERNEL ,min_t (int, nr_pages,BIO_MAX_PAGES )); +if (!bio) { +if (ctx) +fscrypt_release_ctx (ctx); +return ERR_PTR (-ENOMEM ); + } + bio->bi_bdev = bdev; + bio->bi_iter.bi_sector =SECTOR_FROM_BLOCK (blkaddr); + bio->bi_end_io = f2fs_read_end_io; + bio->bi_private = ctx; + +return bio; +} +/* * This function was originally taken from fs/mpage.c, and customized for f2fs. * Major change was from block_size == page_size in f2fs by default.
@@ -973,7 +1004,6 @@ static int f2fs_mpage_readpages(struct address_space *mapping,sector_t last_block;sector_t last_block_in_file;sector_t block_nr; -struct block_device *bdev = inode->i_sb->s_bdev;struct f2fs_map_blocks map; map.m_pblk =0 ;
@@ -1048,31 +1078,9 @@ submit_and_realloc: bio =NULL ; }if (bio ==NULL ) { -struct fscrypt_ctx *ctx =NULL ; - -if (f2fs_encrypted_inode (inode) && -S_ISREG (inode->i_mode)) { - - ctx =fscrypt_get_ctx (inode,GFP_NOFS ); -if (IS_ERR (ctx)) - goto set_error_page; - -/* wait the page to be moved by cleaning */ -f2fs_wait_on_encrypted_page_writeback ( -F2FS_I_SB (inode), block_nr); - } - - bio =bio_alloc (GFP_KERNEL , -min_t (int, nr_pages,BIO_MAX_PAGES )); -if (!bio) { -if (ctx) -fscrypt_release_ctx (ctx); + bio =f2fs_grab_bio (inode, block_nr, nr_pages); +if (IS_ERR (bio)) goto set_error_page; - } - bio->bi_bdev = bdev; - bio->bi_iter.bi_sector =SECTOR_FROM_BLOCK (block_nr); - bio->bi_end_io = f2fs_read_end_io; - bio->bi_private = ctx; }if (bio_add_page (bio, page, blocksize,0 ) < blocksize)
@@ -1622,18 +1630,21 @@ repeat:if (blkaddr ==NEW_ADDR ) {zero_user_segment (page,0 ,PAGE_SIZE ); }else { -struct f2fs_io_info fio = { - .sbi = sbi, - .type =DATA , - .rw =READ_SYNC , - .old_blkaddr = blkaddr, - .new_blkaddr = blkaddr, - .page = page, - .encrypted_page =NULL , - }; - err =f2fs_submit_page_bio (&fio); -if (err) +struct bio *bio; + + bio =f2fs_grab_bio (inode, blkaddr,1 ); +if (IS_ERR (bio)) { + err =PTR_ERR (bio); goto fail; + } + +if (bio_add_page (bio, page,PAGE_SIZE ,0 ) <PAGE_SIZE ) { +bio_put (bio); + err = -EFAULT ; + goto fail; + } + +__submit_bio (sbi,READ_SYNC , bio,DATA );lock_page (page);if (unlikely (!PageUptodate (page))) {
@@ -1644,13 +1655,6 @@ repeat:f2fs_put_page (page,1 ); goto repeat; } - -/* avoid symlink page */ -if (f2fs_encrypted_inode (inode) &&S_ISREG (inode->i_mode)) { - err =fscrypt_decrypt_page (page); -if (err) - goto fail; - } } out_update:SetPageUptodate (page);
Signature · 1 line
2.7.2
Chao Yu
maintainerPATCH 2/4 f2fs: fix to detect truncation prior rather than EIO during read
From: Chao Yu <[email protected]>
In procedure of synchonized read, after sending out the read request, reader will try to lock the page for waiting device to finish the read jobs and unlock the page, but meanwhile, truncater will race with reader, so after reader get lock of the page, it should check page's mapping to detect whether someone has truncated the page in advance, then reader has the chance to do the retry if truncation was done, otherwise read can be failed due to previous condition check.
--- fs/f2fs/data.c | 16 ++++++++-------- fs/f2fs/gc.c | 4 ++-- fs/f2fs/node.c | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-)
fs/f2fs/data.c +8 -8
@@ -500,14 +500,14 @@ repeat:/* wait for read completion */ lock_page (page); -if (unlikely (!PageUptodate (page))) { -f2fs_put_page (page,1 ); -return ERR_PTR (-EIO ); - }if (unlikely (page->mapping != mapping)) {f2fs_put_page (page,1 ); goto repeat; } +if (unlikely (!PageUptodate (page))) { +f2fs_put_page (page,1 ); +return ERR_PTR (-EIO ); + }return page; }
@@ -1647,14 +1647,14 @@ repeat:__submit_bio (sbi,READ_SYNC , bio,DATA );lock_page (page); -if (unlikely (!PageUptodate (page))) { - err = -EIO ; - goto fail; - }if (unlikely (page->mapping != mapping)) {f2fs_put_page (page,1 ); goto repeat; } +if (unlikely (!PageUptodate (page))) { + err = -EIO ; + goto fail; + } } out_update:SetPageUptodate (page);
fs/f2fs/gc.c +2 -2
@@ -593,11 +593,11 @@ static void move_encrypted_block(struct inode *inode, block_t bidx)/* write page */ lock_page (fio.encrypted_page); -if (unlikely (!PageUptodate (fio.encrypted_page))) { +if (unlikely (fio.encrypted_page->mapping !=META_MAPPING (fio.sbi))) { err = -EIO ; goto put_page_out; } -if (unlikely (fio.encrypted_page->mapping !=META_MAPPING (fio.sbi))) { +if (unlikely (!PageUptodate (fio.encrypted_page))) { err = -EIO ; goto put_page_out; }
fs/f2fs/node.c +3 -3
@@ -1146,13 +1146,13 @@ repeat:lock_page (page); -if (unlikely (!PageUptodate (page))) - goto out_err; -if (unlikely (page->mapping !=NODE_MAPPING (sbi))) {f2fs_put_page (page,1 ); goto repeat; } + +if (unlikely (!PageUptodate (page))) + goto out_err; page_hit:if (unlikely (nid !=nid_of_node (page))) {f2fs_bug_on (sbi,1 );
Signature · 1 line
2.7.2
Chao Yu
maintainerPATCH 3/4 f2fs: fix to redirty page if fail to gc data page
In reply to Chao YuFrom: Chao Yu <[email protected]>
If we fail to move data page during foreground GC, we should give another chance to writeback that page which was set dirty previously by writer.
--- fs/f2fs/gc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
fs/f2fs/gc.c +12 -1
@@ -653,12 +653,23 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type) .page = page, .encrypted_page = NULL, }; +bool is_dirty =PageDirty (page); +int err; + +retry:set_page_dirty (page);f2fs_wait_on_page_writeback (page,DATA , true);if (clear_page_dirty_for_io (page))inode_dec_dirty_pages (inode); +set_cold_data (page); -do_write_data_page (&fio); + + err =do_write_data_page (&fio); +if (err == -ENOMEM && is_dirty) { +congestion_wait (BLK_RW_ASYNC ,HZ /50 ); + goto retry; + } +clear_cold_data (page); } out:
Signature · 1 line
2.7.2
Chao Yu
maintainerPATCH 4/4 f2fs: add nodiscard mount option
In reply to Chao YuFrom: Chao Yu <[email protected]>
This patch adds 'nodiscard' mount option.
--- Documentation/filesystems/f2fs.txt | 4 +++- fs/f2fs/super.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-)
Documentation/filesystems/f2fs.txt +3 -1
@@ -109,7 +109,9 @@ background_gc=%s Turn on/off cleaning operations, namely garbage disable_roll_forward Disable the roll-forward recovery routine norecovery Disable the roll-forward recovery routine, mounted read- only (i.e., -o ro,disable_roll_forward) -discard Issue discard/TRIM commands when a segment is cleaned. +discard/nodiscard Enable/disable real-time discard in f2fs, if discard is + enabled, f2fs will issue discard/TRIM commands when a + segment is cleaned. no_heap Disable heap-style segment allocation which finds free segments for data from the beginning of main area, while for node from the end of main area.
fs/f2fs/super.c +4 -0
@@ -76,6 +76,7 @@ enum { Opt_disable_roll_forward, Opt_norecovery, Opt_discard, + Opt_nodiscard, Opt_noheap, Opt_user_xattr, Opt_nouser_xattr,
@@ -106,6 +107,7 @@ static match_table_t f2fs_tokens = { {Opt_disable_roll_forward, "disable_roll_forward"}, {Opt_norecovery, "norecovery"}, {Opt_discard, "discard"}, + {Opt_nodiscard, "nodiscard"}, {Opt_noheap, "no_heap"}, {Opt_user_xattr, "user_xattr"}, {Opt_nouser_xattr, "nouser_xattr"},
@@ -426,6 +428,8 @@ static int parse_options(struct super_block *sb, char *options) "the device does not support discard"); }break ; +case Opt_nodiscard: +clear_opt (sbi,DISCARD );case Opt_noheap:set_opt (sbi,NOHEAP );break ;
Signature · 9 lines
2.7.2 ------------------------------------------------------------------------------ Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today. http://sdm.link/attshape