aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAnton Altaparmakov <aia21@cantab.net>2005-03-10 06:06:19 -0500
committerAnton Altaparmakov <aia21@cantab.net>2005-05-05 06:26:01 -0400
commit905685f68fc72844b8c2689c39a5c6c35e840152 (patch)
tree0ff1d145a7771b24643c1b685ecbb3f791cda6fb /fs
parent43b01fda8b17b2b63e7dcdeed11c2ebba56b1fc9 (diff)
NTFS: - Modify ->readpage and ->writepage (fs/ntfs/aops.c) so they detect
and handle the case where an attribute is converted from resident to non-resident by a concurrent file write. - Reorder some operations when converting an attribute from resident to non-resident (fs/ntfs/attrib.c) so it is safe wrt concurrent ->readpage and ->writepage. Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
Diffstat (limited to 'fs')
-rw-r--r--fs/ntfs/ChangeLog3
-rw-r--r--fs/ntfs/aops.c18
-rw-r--r--fs/ntfs/attrib.c41
3 files changed, 46 insertions, 16 deletions
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
index e0b4adf5adce..b29e0618f358 100644
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -100,6 +100,9 @@ ToDo/Notes:
100 - Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident(). 100 - Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
101 - Fix sign of various error return values to be negative in 101 - Fix sign of various error return values to be negative in
102 fs/ntfs/lcnalloc.c. 102 fs/ntfs/lcnalloc.c.
103 - Modify ->readpage and ->writepage (fs/ntfs/aops.c) so they detect and
104 handle the case where an attribute is converted from resident to
105 non-resident by a concurrent file write.
103 106
1042.1.22 - Many bug and race fixes and error handling improvements. 1072.1.22 - Many bug and race fixes and error handling improvements.
105 108
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index 2a7cba258cca..6241c4cfbe28 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -355,6 +355,7 @@ static int ntfs_readpage(struct file *file, struct page *page)
355 u32 attr_len; 355 u32 attr_len;
356 int err = 0; 356 int err = 0;
357 357
358retry_readpage:
358 BUG_ON(!PageLocked(page)); 359 BUG_ON(!PageLocked(page));
359 /* 360 /*
360 * This can potentially happen because we clear PageUptodate() during 361 * This can potentially happen because we clear PageUptodate() during
@@ -408,6 +409,14 @@ static int ntfs_readpage(struct file *file, struct page *page)
408 err = PTR_ERR(mrec); 409 err = PTR_ERR(mrec);
409 goto err_out; 410 goto err_out;
410 } 411 }
412 /*
413 * If a parallel write made the attribute non-resident, drop the mft
414 * record and retry the readpage.
415 */
416 if (unlikely(NInoNonResident(ni))) {
417 unmap_mft_record(base_ni);
418 goto retry_readpage;
419 }
411 ctx = ntfs_attr_get_search_ctx(base_ni, mrec); 420 ctx = ntfs_attr_get_search_ctx(base_ni, mrec);
412 if (unlikely(!ctx)) { 421 if (unlikely(!ctx)) {
413 err = -ENOMEM; 422 err = -ENOMEM;
@@ -1248,6 +1257,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
1248 u32 attr_len; 1257 u32 attr_len;
1249 int err; 1258 int err;
1250 1259
1260retry_writepage:
1251 BUG_ON(!PageLocked(page)); 1261 BUG_ON(!PageLocked(page));
1252 i_size = i_size_read(vi); 1262 i_size = i_size_read(vi);
1253 /* Is the page fully outside i_size? (truncate in progress) */ 1263 /* Is the page fully outside i_size? (truncate in progress) */
@@ -1338,6 +1348,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
1338 ctx = NULL; 1348 ctx = NULL;
1339 goto err_out; 1349 goto err_out;
1340 } 1350 }
1351 /*
1352 * If a parallel write made the attribute non-resident, drop the mft
1353 * record and retry the writepage.
1354 */
1355 if (unlikely(NInoNonResident(ni))) {
1356 unmap_mft_record(base_ni);
1357 goto retry_writepage;
1358 }
1341 ctx = ntfs_attr_get_search_ctx(base_ni, m); 1359 ctx = ntfs_attr_get_search_ctx(base_ni, m);
1342 if (unlikely(!ctx)) { 1360 if (unlikely(!ctx)) {
1343 err = -ENOMEM; 1361 err = -ENOMEM;
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 3b9de4040216..41859343a0c8 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -1376,19 +1376,6 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
1376 err = ntfs_attr_record_resize(m, a, arec_size); 1376 err = ntfs_attr_record_resize(m, a, arec_size);
1377 if (unlikely(err)) 1377 if (unlikely(err))
1378 goto err_out; 1378 goto err_out;
1379 /* Setup the in-memory attribute structure to be non-resident. */
1380 NInoSetNonResident(ni);
1381 ni->runlist.rl = rl;
1382 write_lock_irqsave(&ni->size_lock, flags);
1383 ni->allocated_size = new_size;
1384 write_unlock_irqrestore(&ni->size_lock, flags);
1385 /*
1386 * FIXME: For now just clear all of these as we do not support them
1387 * when writing.
1388 */
1389 NInoClearCompressed(ni);
1390 NInoClearSparse(ni);
1391 NInoClearEncrypted(ni);
1392 /* 1379 /*
1393 * Convert the resident part of the attribute record to describe a 1380 * Convert the resident part of the attribute record to describe a
1394 * non-resident attribute. 1381 * non-resident attribute.
@@ -1399,7 +1386,10 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
1399 memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), 1386 memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
1400 a->name_length * sizeof(ntfschar)); 1387 a->name_length * sizeof(ntfschar));
1401 a->name_offset = cpu_to_le16(name_ofs); 1388 a->name_offset = cpu_to_le16(name_ofs);
1402 /* Update the flags to match the in-memory ones. */ 1389 /*
1390 * FIXME: For now just clear all of these as we do not support them
1391 * when writing.
1392 */
1403 a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE | 1393 a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE |
1404 ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK)); 1394 ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK));
1405 /* Setup the fields specific to non-resident attributes. */ 1395 /* Setup the fields specific to non-resident attributes. */
@@ -1422,6 +1412,25 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
1422 err); 1412 err);
1423 goto undo_err_out; 1413 goto undo_err_out;
1424 } 1414 }
1415 /* Setup the in-memory attribute structure to be non-resident. */
1416 /*
1417 * FIXME: For now just clear all of these as we do not support them
1418 * when writing.
1419 */
1420 NInoClearSparse(ni);
1421 NInoClearEncrypted(ni);
1422 NInoClearCompressed(ni);
1423 ni->runlist.rl = rl;
1424 write_lock_irqsave(&ni->size_lock, flags);
1425 ni->allocated_size = new_size;
1426 write_unlock_irqrestore(&ni->size_lock, flags);
1427 /*
1428 * This needs to be last since the address space operations ->readpage
1429 * and ->writepage can run concurrently with us as they are not
1430 * serialized on i_sem. Note, we are not allowed to fail once we flip
1431 * this switch, which is another reason to do this last.
1432 */
1433 NInoSetNonResident(ni);
1425 /* Mark the mft record dirty, so it gets written back. */ 1434 /* Mark the mft record dirty, so it gets written back. */
1426 flush_dcache_mft_record_page(ctx->ntfs_ino); 1435 flush_dcache_mft_record_page(ctx->ntfs_ino);
1427 mark_mft_record_dirty(ctx->ntfs_ino); 1436 mark_mft_record_dirty(ctx->ntfs_ino);
@@ -1431,6 +1440,7 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
1431 if (page) { 1440 if (page) {
1432 set_page_dirty(page); 1441 set_page_dirty(page);
1433 unlock_page(page); 1442 unlock_page(page);
1443 mark_page_accessed(page);
1434 page_cache_release(page); 1444 page_cache_release(page);
1435 } 1445 }
1436 ntfs_debug("Done."); 1446 ntfs_debug("Done.");
@@ -1492,11 +1502,10 @@ undo_err_out:
1492 memcpy((u8*)a + mp_ofs, kaddr, attr_size); 1502 memcpy((u8*)a + mp_ofs, kaddr, attr_size);
1493 kunmap_atomic(kaddr, KM_USER0); 1503 kunmap_atomic(kaddr, KM_USER0);
1494 } 1504 }
1495 /* Finally setup the ntfs inode appropriately. */ 1505 /* Setup the allocated size in the ntfs inode in case it changed. */
1496 write_lock_irqsave(&ni->size_lock, flags); 1506 write_lock_irqsave(&ni->size_lock, flags);
1497 ni->allocated_size = arec_size - mp_ofs; 1507 ni->allocated_size = arec_size - mp_ofs;
1498 write_unlock_irqrestore(&ni->size_lock, flags); 1508 write_unlock_irqrestore(&ni->size_lock, flags);
1499 NInoClearNonResident(ni);
1500 /* Mark the mft record dirty, so it gets written back. */ 1509 /* Mark the mft record dirty, so it gets written back. */
1501 flush_dcache_mft_record_page(ctx->ntfs_ino); 1510 flush_dcache_mft_record_page(ctx->ntfs_ino);
1502 mark_mft_record_dirty(ctx->ntfs_ino); 1511 mark_mft_record_dirty(ctx->ntfs_ino);