mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
ntfs: do not map an unmappable runlist fragment as a hole
When the extent mft record holding part of a file's runlist cannot be
read, ntfs_attr_vcn_to_rl() ignores the failed ntfs_map_runlist_nolock()
retry and returns with *lcn == LCN_RL_NOT_MAPPED. The iomap read path
only rejects lcn < LCN_ENOENT, so it maps the range as a hole and read()
returns zeros with no error. The first read already does this:
ntfs_attr_map_whole_runlist() keeps the fragments it could read,
readahead drops its error, and the next lookup finds the unmapped tail.
On a file whose runlist is split between its base record (vcn 0-214) and
one extent record (vcn 215-1499), breaking only the extent record's FILE
magic makes the kernel log "Failed to map extent mft record", yet read()
returns 1285 clusters of zeros for vcn 215-1499. A damaged attribute
list entry, which makes the retry fail with -ENOENT, gives the same
zeros.
Return -ENOMEM if the retry ran out of memory and -EIO otherwise. Both
callers already handle an ERR_PTR; other negative lcns, including
LCN_ENOENT, are returned as before, and so is LCN_RL_NOT_MAPPED at or
beyond allocated_size, where nothing is mapped: the runlist ends there
with LCN_RL_NOT_MAPPED when only the last extent is mapped, as after a
write into it, and with clusters smaller than a page every read of a
file's last folio looks up such vcns.
Reads of vcn 215-1499 now fail with -EIO. An intact volume exercised
with buffered, mmap and O_DIRECT I/O, fallocate, truncate, sparse and
compressed files behaves as before.
Fixes: 495e90fa33 ("ntfs: update attrib operations")
Cc: stable@vger.kernel.org
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
87a548005f
commit
2b451a6565
|
|
@ -317,7 +317,7 @@ int ntfs_map_runlist(struct ntfs_inode *ni, s64 vcn)
|
|||
struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64 *lcn)
|
||||
{
|
||||
struct runlist_element *rl = ni->runlist.rl;
|
||||
int err;
|
||||
int err = 0;
|
||||
bool is_retry = false;
|
||||
|
||||
if (!rl) {
|
||||
|
|
@ -335,12 +335,32 @@ struct runlist_element *ntfs_attr_vcn_to_rl(struct ntfs_inode *ni, s64 vcn, s64
|
|||
|
||||
if (*lcn <= LCN_RL_NOT_MAPPED && is_retry == false) {
|
||||
is_retry = true;
|
||||
if (!ntfs_map_runlist_nolock(ni, vcn, NULL)) {
|
||||
err = ntfs_map_runlist_nolock(ni, vcn, NULL);
|
||||
if (!err) {
|
||||
rl = ni->runlist.rl;
|
||||
goto remap_rl;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The runlist fragment containing @vcn could not be mapped, e.g.
|
||||
* because the extent mft record holding it is corrupt. Do not hand
|
||||
* LCN_RL_NOT_MAPPED back to callers, which would treat it as a hole.
|
||||
* At or beyond the allocated size nothing is mapped, and the runlist
|
||||
* ends there with LCN_RL_NOT_MAPPED if only a later extent has been
|
||||
* mapped, so return that end as it is.
|
||||
*/
|
||||
if (*lcn == LCN_RL_NOT_MAPPED) {
|
||||
unsigned long flags;
|
||||
s64 allocated_size;
|
||||
|
||||
read_lock_irqsave(&ni->size_lock, flags);
|
||||
allocated_size = ni->allocated_size;
|
||||
read_unlock_irqrestore(&ni->size_lock, flags);
|
||||
if ((s64)ntfs_cluster_to_bytes(ni->vol, vcn) < allocated_size)
|
||||
return ERR_PTR(err == -ENOMEM ? -ENOMEM : -EIO);
|
||||
}
|
||||
|
||||
return rl;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user