diff options
| author | Namjae Jeon <linkinjeon@kernel.org> | 2026-07-20 17:43:34 +0900 |
|---|---|---|
| committer | Namjae Jeon <linkinjeon@kernel.org> | 2026-08-19 18:16:41 +0900 |
| commit | eb67671cd8149d563c04764d39fda16f17194eae (patch) | |
| tree | 527240a1027883be9e5ae0db63de943300350bf5 | |
| parent | 2bd734ee2cc8a9fe19ee96a34fc7038117c3b181 (diff) | |
| download | linux-eb67671cd8149d563c04764d39fda16f17194eae.tar.gz linux-eb67671cd8149d563c04764d39fda16f17194eae.zip | |
ntfs: write compressed data before replacing old clusters
ntfs_write_cb() punches the old compression unit and publishes the new
mapping before submitting the replacement data. An allocation or I/O
failure after the punch loses the previous contents and can leave the
mapping pointing at unwritten clusters.
Allocate and write the replacement clusters first. Replace the runlist only
after the synchronous write succeeds, and free new clusters on failure.
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
| -rw-r--r-- | fs/ntfs/compress.c | 63 |
1 files changed, 36 insertions, 27 deletions
diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c index 33ed0456bf7e..5b5cd494e5d8 100644 --- a/fs/ntfs/compress.c +++ b/fs/ntfs/compress.c @@ -1373,10 +1373,6 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages, new_length = ntfs_bytes_to_cluster(vol, round_up(bio_size, vol->cluster_size)); - err = ntfs_non_resident_attr_punch_hole(ni, new_vcn, ni->itype.compressed.block_clusters); - if (err < 0) - goto out; - rlc = ntfs_cluster_alloc(vol, new_vcn, new_length, -1, DATA_ZONE, false, true, true); if (IS_ERR(rlc)) { @@ -1385,28 +1381,6 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages, } bio_lcn = rlc->lcn; - down_write(&ni->runlist.lock); - rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, &new_rl_count); - if (IS_ERR(rl)) { - up_write(&ni->runlist.lock); - ntfs_error(vol->sb, "Failed to merge runlists"); - err = PTR_ERR(rl); - if (ntfs_cluster_free_from_rl(vol, rlc)) - ntfs_error(vol->sb, "Failed to free hot clusters."); - kvfree(rlc); - goto out; - } - - ni->runlist.count = new_rl_count; - ni->runlist.rl = rl; - - err = ntfs_attr_update_mapping_pairs(ni, 0); - up_write(&ni->runlist.lock); - if (err) { - err = -EIO; - goto out; - } - i = 0; while (bio_size > 0) { int page_size; @@ -1423,6 +1397,10 @@ setup_bio: if (!bio) { bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO); + if (!bio) { + err = -ENOMEM; + goto free_rlc; + } bio->bi_iter.bi_sector = ntfs_bytes_to_sector(vol, ntfs_cluster_to_bytes(vol, bio_lcn) + @@ -1433,7 +1411,7 @@ setup_bio: err = submit_bio_wait(bio); bio_put(bio); if (err) - goto out; + goto free_rlc; bio = NULL; goto setup_bio; } @@ -1442,6 +1420,37 @@ setup_bio: err = submit_bio_wait(bio); bio_put(bio); + if (err) + goto free_rlc; + + /* Do not discard the old compression block until the new one is safe. */ + err = ntfs_non_resident_attr_punch_hole(ni, new_vcn, cb_clusters); + if (err) + goto free_rlc; + + down_write(&ni->runlist.lock); + rl = ntfs_runlists_merge(&ni->runlist, rlc, 0, &new_rl_count); + if (IS_ERR(rl)) { + up_write(&ni->runlist.lock); + ntfs_error(vol->sb, "Failed to merge runlists"); + err = PTR_ERR(rl); + goto free_rlc; + } + + ni->runlist.count = new_rl_count; + ni->runlist.rl = rl; + rlc = NULL; + + err = ntfs_attr_update_mapping_pairs(ni, 0); + up_write(&ni->runlist.lock); + if (err) + err = -EIO; + goto out; + +free_rlc: + if (ntfs_cluster_free_from_rl(vol, rlc)) + ntfs_error(vol->sb, "Failed to free hot clusters."); + kvfree(rlc); out: if (outbuf) vunmap(outbuf); |
