// SPDX-License-Identifier: GPL-2.0-or-later
/*
* iomap callack functions
*
* Copyright (c) 2025 LG Electronics Co., Ltd.
*/
#include <linux/writeback.h>
#include "attrib.h"
#include "mft.h"
#include "ntfs.h"
#include "iomap.h"
static void ntfs_iomap_put_folio_non_resident(struct inode *inode, loff_t pos,
unsigned int len, struct folio *folio)
{
struct ntfs_inode *ni = NTFS_I(inode);
unsigned long sector_size = 1UL << inode->i_blkbits;
loff_t start_down, end_up, init;
start_down = round_down(pos, sector_size);
end_up = (pos + len - 1) | (sector_size - 1);
init = ni->initialized_size;
if (init >= start_down && init <= end_up) {
if (init < pos) {
loff_t offset = offset_in_folio(folio, pos + len);
if (offset == 0)
offset = folio_size(folio);
folio_zero_segments(folio,
offset_in_folio(folio, init),
offset_in_folio(folio, pos),
offset,
folio_size(folio));
} else {
loff_t offset = max_t(loff_t, pos + len, init);
offset = offset_in_folio(folio, offset);
if (offset == 0)
offset = folio_size(folio);
folio_zero_segment(folio,
offset,
folio_size(folio));
}
} else if (init <= pos) {
loff_t offset = 0, offset2 = offset_in_folio(folio, pos + len);
if ((init >> folio_shift(folio)) == (pos >> folio_shift(folio)))
offset = offset_in_folio(folio, init);
if (offset2 == 0)
offset2 = folio_size(folio);
folio_zero_segments(folio,
offset,
offset_in_folio(folio, pos),
offset2,
folio_size(folio));
}
folio_unlock(folio);
folio_put(folio);
}
/*
* iomap_zero_range is called for an area beyond the initialized size,
* garbage values can be read, so zeroing out is needed.
*/
static void ntfs_iomap_put_folio(struct inode *inode, loff_t pos,
unsigned int len, struct folio *folio)
{
if (NInoNonResident(NTFS_I(inode)))
return ntfs_iomap_put_folio_non_resident(inode, pos,
len, folio);
folio_unlock(folio);
folio_put(folio);
}
const struct iomap_write_ops ntfs_iomap_folio_ops = {
.put_folio = ntfs_iomap_put_folio,
};
static int ntfs_read_iomap_begin_resident(struct inode *inode, loff_t offset, loff_t length,
unsigned int flags, struct iomap *iomap)
{
struct ntfs_inode *base_ni, *ni = NTFS_I(inode);
struct ntfs_attr_search_ctx *ctx;
loff_t i_size;
u32 attr_len;
int err = 0;
char *kattr;
struct page *ipage;
if (NInoAttr(ni))
base_ni = ni->ext.base_ntfs_ino;
else
base_ni = ni;
ctx = ntfs_attr_get_search_ctx(base_ni, NULL);
if (!ctx) {
err = -ENOMEM;
goto out;
}
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx);
if (unlikely(err))
goto out;
attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
if (unlikely(attr_len > ni->initialized_size))
attr_len = ni->initialized_size;
i_size = i_size_read(inode);
if (unlikely(attr_len > i_size)) {
/* Race with shrinking truncate. */
attr_len = i_size;
}
if (offset >= attr_len) {
if (flags & IOMAP_REPORT)
err = -ENOENT;
else {
iomap->type = IOMAP_HOLE;
iomap->offset = offset;
iomap->length = length;
}
goto out;
}
kattr = (u8 *)ctx->attr + le16_to_cpu(ctx->attr->data.resident.value_offset);
ipage = alloc_page(GFP_NOFS | __GFP_ZERO);
if (!ipage) {
err = -ENOMEM;
goto out;
}
memcpy(page_address(ipage), kattr, attr_len);
iomap->type = IOMAP_INLINE;
iomap->inline_data = page_address