aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChris Mason <clm@fb.com>2015-12-14 18:40:44 -0500
committerChris Mason <clm@fb.com>2015-12-15 12:09:38 -0500
commitbb1591b4ea1a1485ebc79be4e4748e94f96c670b (patch)
tree25dcd0b7dc78562d01c7865527d3fd46d6f99bb6 /fs
parent1b9b922a3a601b0c99a095ffafed61fcf6ebe0b7 (diff)
Btrfs: check prepare_uptodate_page() error code earlier
prepare_pages() may end up calling prepare_uptodate_page() twice if our write only spans a single page. But if the first call returns an error, our page will be unlocked and its not safe to call it again. This bug goes all the way back to 2011, and it's not something commonly hit. While we're here, add a more explicit check for the page being truncated away. The bare lock_page() alone is protected only by good thoughts and i_mutex, which we're sure to regret eventually. Reported-by: Dave Jones <dsj@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/file.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 8eb1f3c1b647..870150547f5c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1291,7 +1291,8 @@ out:
1291 * on error we return an unlocked page and the error value 1291 * on error we return an unlocked page and the error value
1292 * on success we return a locked page and 0 1292 * on success we return a locked page and 0
1293 */ 1293 */
1294static int prepare_uptodate_page(struct page *page, u64 pos, 1294static int prepare_uptodate_page(struct inode *inode,
1295 struct page *page, u64 pos,
1295 bool force_uptodate) 1296 bool force_uptodate)
1296{ 1297{
1297 int ret = 0; 1298 int ret = 0;
@@ -1306,6 +1307,10 @@ static int prepare_uptodate_page(struct page *page, u64 pos,
1306 unlock_page(page); 1307 unlock_page(page);
1307 return -EIO; 1308 return -EIO;
1308 } 1309 }
1310 if (page->mapping != inode->i_mapping) {
1311 unlock_page(page);
1312 return -EAGAIN;
1313 }
1309 } 1314 }
1310 return 0; 1315 return 0;
1311} 1316}
@@ -1324,6 +1329,7 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages,
1324 int faili; 1329 int faili;
1325 1330
1326 for (i = 0; i < num_pages; i++) { 1331 for (i = 0; i < num_pages; i++) {
1332again:
1327 pages[i] = find_or_create_page(inode->i_mapping, index + i, 1333 pages[i] = find_or_create_page(inode->i_mapping, index + i,
1328 mask | __GFP_WRITE); 1334 mask | __GFP_WRITE);
1329 if (!pages[i]) { 1335 if (!pages[i]) {
@@ -1333,13 +1339,17 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages,
1333 } 1339 }
1334 1340
1335 if (i == 0) 1341 if (i == 0)
1336 err = prepare_uptodate_page(pages[i], pos, 1342 err = prepare_uptodate_page(inode, pages[i], pos,
1337 force_uptodate); 1343 force_uptodate);
1338 if (i == num_pages - 1) 1344 if (!err && i == num_pages - 1)
1339 err = prepare_uptodate_page(pages[i], 1345 err = prepare_uptodate_page(inode, pages[i],
1340 pos + write_bytes, false); 1346 pos + write_bytes, false);
1341 if (err) { 1347 if (err) {
1342 page_cache_release(pages[i]); 1348 page_cache_release(pages[i]);
1349 if (err == -EAGAIN) {
1350 err = 0;
1351 goto again;
1352 }
1343 faili = i - 1; 1353 faili = i - 1;
1344 goto fail; 1354 goto fail;
1345 } 1355 }