Skip to content

Commit dbf00d8

Browse files
committed
Merge tag 'fs_for_v7.0-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
Pull udf fix from Jan Kara: "Fix for a race in UDF that can lead to memory corruption" * tag 'fs_for_v7.0-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: udf: Fix race between file type conversion and writeback mpage: Provide variant of mpage_writepages() with own optional folio handler
2 parents d0c3bcd + 102e57d commit dbf00d8

File tree

3 files changed

+48
-26
lines changed

3 files changed

+48
-26
lines changed

fs/mpage.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -646,17 +646,24 @@ static int mpage_write_folio(struct writeback_control *wbc, struct folio *folio,
646646
}
647647

648648
/**
649-
* mpage_writepages - walk the list of dirty pages of the given address space & writepage() all of them
649+
* __mpage_writepages - walk the list of dirty pages of the given address space
650+
* & writepage() all of them
650651
* @mapping: address space structure to write
651652
* @wbc: subtract the number of written pages from *@wbc->nr_to_write
652653
* @get_block: the filesystem's block mapper function.
654+
* @write_folio: handler to call for each folio before calling
655+
* mpage_write_folio()
653656
*
654657
* This is a library function, which implements the writepages()
655-
* address_space_operation.
658+
* address_space_operation. It calls @write_folio handler for each folio. If
659+
* the handler returns value > 0, it calls mpage_write_folio() to do the
660+
* folio writeback.
656661
*/
657662
int
658-
mpage_writepages(struct address_space *mapping,
659-
struct writeback_control *wbc, get_block_t get_block)
663+
__mpage_writepages(struct address_space *mapping,
664+
struct writeback_control *wbc, get_block_t get_block,
665+
int (*write_folio)(struct folio *folio,
666+
struct writeback_control *wbc))
660667
{
661668
struct mpage_data mpd = {
662669
.get_block = get_block,
@@ -666,11 +673,22 @@ mpage_writepages(struct address_space *mapping,
666673
int error;
667674

668675
blk_start_plug(&plug);
669-
while ((folio = writeback_iter(mapping, wbc, folio, &error)))
676+
while ((folio = writeback_iter(mapping, wbc, folio, &error))) {
677+
if (write_folio) {
678+
error = write_folio(folio, wbc);
679+
/*
680+
* == 0 means folio is handled, < 0 means error. In
681+
* both cases hand back control to writeback_iter()
682+
*/
683+
if (error <= 0)
684+
continue;
685+
/* Let mpage_write_folio() handle the folio. */
686+
}
670687
error = mpage_write_folio(wbc, folio, &mpd);
688+
}
671689
if (mpd.bio)
672690
mpage_bio_submit_write(mpd.bio);
673691
blk_finish_plug(&plug);
674692
return error;
675693
}
676-
EXPORT_SYMBOL(mpage_writepages);
694+
EXPORT_SYMBOL(__mpage_writepages);

fs/udf/inode.c

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -181,35 +181,32 @@ static void udf_write_failed(struct address_space *mapping, loff_t to)
181181
}
182182
}
183183

184-
static int udf_adinicb_writepages(struct address_space *mapping,
185-
struct writeback_control *wbc)
184+
static int udf_handle_page_wb(struct folio *folio,
185+
struct writeback_control *wbc)
186186
{
187-
struct inode *inode = mapping->host;
187+
struct inode *inode = folio->mapping->host;
188188
struct udf_inode_info *iinfo = UDF_I(inode);
189-
struct folio *folio = NULL;
190-
int error = 0;
191189

192-
while ((folio = writeback_iter(mapping, wbc, folio, &error))) {
193-
BUG_ON(!folio_test_locked(folio));
194-
BUG_ON(folio->index != 0);
195-
memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio,
196-
0, i_size_read(inode));
197-
folio_unlock(folio);
198-
}
190+
/*
191+
* Inodes in the normal format are handled by the generic code. This
192+
* check is race-free as the folio lock protects us from inode type
193+
* conversion.
194+
*/
195+
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB)
196+
return 1;
199197

198+
memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio,
199+
0, i_size_read(inode));
200+
folio_unlock(folio);
200201
mark_inode_dirty(inode);
201202
return 0;
202203
}
203204

204205
static int udf_writepages(struct address_space *mapping,
205206
struct writeback_control *wbc)
206207
{
207-
struct inode *inode = mapping->host;
208-
struct udf_inode_info *iinfo = UDF_I(inode);
209-
210-
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
211-
return udf_adinicb_writepages(mapping, wbc);
212-
return mpage_writepages(mapping, wbc, udf_get_block_wb);
208+
return __mpage_writepages(mapping, wbc, udf_get_block_wb,
209+
udf_handle_page_wb);
213210
}
214211

215212
static void udf_adinicb_read_folio(struct folio *folio)

include/linux/mpage.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ struct readahead_control;
1717

1818
void mpage_readahead(struct readahead_control *, get_block_t get_block);
1919
int mpage_read_folio(struct folio *folio, get_block_t get_block);
20-
int mpage_writepages(struct address_space *mapping,
21-
struct writeback_control *wbc, get_block_t get_block);
20+
int __mpage_writepages(struct address_space *mapping,
21+
struct writeback_control *wbc, get_block_t get_block,
22+
int (*write_folio)(struct folio *folio,
23+
struct writeback_control *wbc));
24+
static inline int mpage_writepages(struct address_space *mapping,
25+
struct writeback_control *wbc, get_block_t get_block)
26+
{
27+
return __mpage_writepages(mapping, wbc, get_block, NULL);
28+
}
2229

2330
#endif

0 commit comments

Comments
 (0)