diff options
author | Anton Altaparmakov <aia21@cantab.net> | 2005-03-10 06:06:19 -0500 |
---|---|---|
committer | Anton Altaparmakov <aia21@cantab.net> | 2005-05-05 06:26:01 -0400 |
commit | 905685f68fc72844b8c2689c39a5c6c35e840152 (patch) | |
tree | 0ff1d145a7771b24643c1b685ecbb3f791cda6fb /fs/ntfs | |
parent | 43b01fda8b17b2b63e7dcdeed11c2ebba56b1fc9 (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/ntfs')
-rw-r--r-- | fs/ntfs/ChangeLog | 3 | ||||
-rw-r--r-- | fs/ntfs/aops.c | 18 | ||||
-rw-r--r-- | fs/ntfs/attrib.c | 41 |
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 | ||
104 | 2.1.22 - Many bug and race fixes and error handling improvements. | 107 | 2.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 | ||
358 | retry_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 | ||
1260 | retry_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); |