#include <linux/writeback.h>
#include "attrib.h"
#include "mft.h"
#include "ntfs.h"
#include "debug.h"
#include "iomap.h"
static void ntfs_iomap_read_end_io(struct bio *bio)
{
int error = blk_status_to_errno(bio->bi_status);
struct folio_iter iter;
bio_for_each_folio_all(iter, bio) {
struct folio *folio = iter.folio;
struct ntfs_inode *ni = NTFS_I(folio->mapping->host);
s64 init_size;
loff_t pos = folio_pos(folio);
init_size = ni->initialized_size;
if (pos + iter.offset < init_size &&
pos + iter.offset + iter.length > init_size)
folio_zero_segment(folio, offset_in_folio(folio, init_size),
iter.offset + iter.length);
iomap_finish_folio_read(folio, iter.offset, iter.length, error);
}
bio_put(bio);
}
static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter,
struct iomap_read_folio_ctx *ctx)
{
iomap_bio_submit_read_endio(iter, ctx, ntfs_iomap_read_end_io);
}
static const struct iomap_read_ops ntfs_iomap_bio_read_ops = {
.read_folio_range = iomap_bio_read_folio_range,
.submit_read = ntfs_iomap_bio_submit_read,
};
static int ntfs_read_folio(struct file *file, struct folio *folio)
{
struct ntfs_inode *ni = NTFS_I(folio->mapping->host);
struct iomap_read_folio_ctx ctx = {
.cur_folio = folio,
.ops = &ntfs_iomap_bio_read_ops,
};
if (ni->type != AT_INDEX_ALLOCATION) {
if (NInoEncrypted(ni)) {
folio_unlock(folio);
return -EOPNOTSUPP;
}
if (NInoNonResident(ni) && NInoCompressed(ni))
return ntfs_read_compressed_block(folio);
}
iomap_read_folio(&ntfs_read_iomap_ops, &ctx, NULL);
return 0;
}
static sector_t ntfs_bmap(struct address_space *mapping, sector_t block)
{
s64 ofs, size;
loff_t i_size;
s64 lcn;
unsigned long blocksize, flags;
struct ntfs_inode *ni = NTFS_I(mapping->host);
struct ntfs_volume *vol = ni->vol;
unsigned int delta;
unsigned char blocksize_bits;
ntfs_debug("Entering for mft_no 0x%llx, logical block 0x%llx.",
ni->mft_no, (unsigned long long)block);
if (ni->type != AT_DATA || !NInoNonResident(ni) || NInoEncrypted(ni) ||
NInoMstProtected(ni)) {
ntfs_error(vol->sb, "BMAP does not make sense for %s attributes, returning 0.",
(ni->type != AT_DATA) ? "non-data" :
(!NInoNonResident(ni) ? "resident" :
"encrypted"));
return 0;
}
blocksize = vol->sb->s_blocksize;
blocksize_bits = vol->sb->s_blocksize_bits;
ofs = (s64)block << blocksize_bits;
read_lock_irqsave(&ni->size_lock, flags);
size = ni->initialized_size;
i_size = i_size_read(VFS_I(ni));
read_unlock_irqrestore(&ni->size_lock, flags);
if (unlikely(ofs >= size || (ofs + blocksize > size && size < i_size)))
goto hole;
down_read(&ni->runlist.lock);
lcn = ntfs_attr_vcn_to_lcn_nolock(ni, ntfs_bytes_to_cluster(vol, ofs),
false);
up_read(&ni->runlist.lock);
if (unlikely(lcn < LCN_HOLE)) {
switch ((int)lcn) {
case LCN_ENOENT:
goto hole;
case LCN_ENOMEM:
ntfs_error(vol->sb,
"Not enough memory to complete mapping for inode 0x%llx. Returning 0.",
ni->mft_no);
break;
default:
ntfs_error(vol->sb,
"Failed to complete mapping for inode 0x%llx. Run chkdsk. Returning 0.",
ni->mft_no);
break;
}
return 0;
}
if (lcn < 0) {
hole:
ntfs_debug("Done (returning hole).");
return 0;
}
delta = ofs & vol->cluster_size_mask;
if (unlikely(sizeof(block) < sizeof(lcn))) {
block = lcn = (ntfs_cluster_to_bytes(vol, lcn) + delta) >>
blocksize_bits;
if (unlikely(block != lcn)) {
ntfs_error(vol->sb,
"Physical block 0x%llx is too large to be returned, returning 0.",
(long long)lcn);
return 0;
}
} else
block = (ntfs_cluster_to_bytes(vol, lcn) + delta) >>
blocksize_bits;
ntfs_debug("Done (returning block 0x%llx).", (unsigned long long)lcn);
return block;
}
static void ntfs_readahead(struct readahead_control *rac)
{
struct address_space *mapping = rac->mapping;
struct inode *inode = mapping->host;
struct ntfs_inode *ni = NTFS_I(inode);
struct iomap_read_folio_ctx ctx = {
.ops = &ntfs_iomap_bio_read_ops,
.rac = rac,
};
if (!NInoNonResident(ni) || NInoCompressed(ni))
return;
iomap_readahead(&ntfs_read_iomap_ops, &ctx, NULL);
}
static int ntfs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct inode *inode = mapping->host;
struct ntfs_inode *ni = NTFS_I(inode);
struct iomap_writepage_ctx wpc = {
.inode = mapping->host,
.wbc = wbc,
.ops = &ntfs_writeback_ops,
};
bool need_iput = false;
int ret;
if (NVolShutdown(ni->vol))
return -EIO;
if (!NInoNonResident(ni))
return 0;
if (NInoEncrypted(ni)) {
ntfs_debug("Encrypted I/O not supported");
return -EOPNOTSUPP;
}
if ((ni->type == AT_DATA || ni->type == AT_INDEX_ALLOCATION) &&
igrab(inode))
need_iput = true;
ret = iomap_writepages(&wpc);
if (need_iput)
iput(inode);
return ret;
}
static int ntfs_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span)
{
return iomap_swapfile_activate(sis, swap_file, span,
&ntfs_read_iomap_ops);
}
const struct address_space_operations ntfs_aops = {
.read_folio = ntfs_read_folio,
.readahead = ntfs_readahead,
.writepages = ntfs_writepages,
.dirty_folio = iomap_dirty_folio,
.bmap = ntfs_bmap,
.migrate_folio = filemap_migrate_folio,
.is_partially_uptodate = iomap_is_partially_uptodate,
.error_remove_folio = generic_error_remove_folio,
.release_folio = iomap_release_folio,
.invalidate_folio = iomap_invalidate_folio,
.swap_activate = ntfs_swap_activate,
};
const struct address_space_operations ntfs_mft_aops = {
.read_folio = ntfs_read_folio,
.readahead = ntfs_readahead,
.writepages = ntfs_mft_writepages,
.dirty_folio = iomap_dirty_folio,
.bmap = ntfs_bmap,
.migrate_folio = filemap_migrate_folio,
.is_partially_uptodate = iomap_is_partially_uptodate,
.error_remove_folio = generic_error_remove_folio,
.release_folio = iomap_release_folio,
.invalidate_folio = iomap_invalidate_folio,
};