mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 23:22:31 +02:00
fs/ntfs3: implement iomap-based file operations
This patch modifies the ntfs3 driver by replacing the buffer_head-based
operations with the iomap ones.
Implementation details:
- Implements core iomap operations (ntfs_iomap_begin/end) for block mapping:
Proper handling of resident attributes via IOMAP_INLINE.
Support for sparse files through IOMAP_HOLE semantics.
Correct unwritten extent handling for zeroing operations.
- Replaces custom implementations with standardized iomap helpers:
Converts buffered reads to use iomap_read_folio and iomap_readahead.
Implements iomap_file_buffered_write for write operations.
Uses iomap_dio_rw for direct I/O paths.
Migrates zero range operations to iomap_zero_range.
- Preserves special handling paths for compressed files
- Implements proper EOF/valid data size management during writes
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
parent
e37a75bb86
commit
099ef9ab92
|
|
@ -166,6 +166,12 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err == -ENOSPC && new_len && vcn - vcn0) {
|
||||||
|
/* Keep already allocated clusters. */
|
||||||
|
*alen = vcn - vcn0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
@ -886,7 +892,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||||
* - new allocated clusters are zeroed via blkdev_issue_zeroout.
|
* - new allocated clusters are zeroed via blkdev_issue_zeroout.
|
||||||
*/
|
*/
|
||||||
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
||||||
CLST *len, bool *new, bool zero)
|
CLST *len, bool *new, bool zero, void **res)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct runs_tree *run = &ni->file.run;
|
struct runs_tree *run = &ni->file.run;
|
||||||
|
|
@ -903,6 +909,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
||||||
|
|
||||||
if (new)
|
if (new)
|
||||||
*new = false;
|
*new = false;
|
||||||
|
if (res)
|
||||||
|
*res = NULL;
|
||||||
|
|
||||||
/* Try to find in cache. */
|
/* Try to find in cache. */
|
||||||
down_read(&ni->file.run_lock);
|
down_read(&ni->file.run_lock);
|
||||||
|
|
@ -939,8 +947,15 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attr_b->non_res) {
|
if (!attr_b->non_res) {
|
||||||
|
u32 data_size = le32_to_cpu(attr_b->res.data_size);
|
||||||
*lcn = RESIDENT_LCN;
|
*lcn = RESIDENT_LCN;
|
||||||
*len = le32_to_cpu(attr_b->res.data_size);
|
*len = data_size;
|
||||||
|
if (res && data_size) {
|
||||||
|
*res = kmemdup(resident_data(attr_b), data_size,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!*res)
|
||||||
|
err = -ENOMEM;
|
||||||
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1028,7 +1043,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
||||||
to_alloc = ((vcn0 + clen + clst_per_frame - 1) & cmask) - vcn;
|
to_alloc = ((vcn0 + clen + clst_per_frame - 1) & cmask) - vcn;
|
||||||
if (fr < clst_per_frame)
|
if (fr < clst_per_frame)
|
||||||
fr = clst_per_frame;
|
fr = clst_per_frame;
|
||||||
zero = true;
|
if (vcn != vcn0)
|
||||||
|
zero = true;
|
||||||
|
|
||||||
/* Check if 'vcn' and 'vcn0' in different attribute segments. */
|
/* Check if 'vcn' and 'vcn0' in different attribute segments. */
|
||||||
if (vcn < svcn || evcn1 <= vcn) {
|
if (vcn < svcn || evcn1 <= vcn) {
|
||||||
|
|
@ -1244,33 +1260,6 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio)
|
|
||||||
{
|
|
||||||
u64 vbo;
|
|
||||||
struct ATTRIB *attr;
|
|
||||||
u32 data_size;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, NULL);
|
|
||||||
if (!attr)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (attr->non_res)
|
|
||||||
return E_NTFS_NONRESIDENT;
|
|
||||||
|
|
||||||
vbo = folio->index << PAGE_SHIFT;
|
|
||||||
data_size = le32_to_cpu(attr->res.data_size);
|
|
||||||
if (vbo > data_size)
|
|
||||||
len = 0;
|
|
||||||
else
|
|
||||||
len = min(data_size - vbo, folio_size(folio));
|
|
||||||
|
|
||||||
folio_fill_tail(folio, 0, resident_data(attr) + vbo, len);
|
|
||||||
folio_mark_uptodate(folio);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
|
int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
|
||||||
{
|
{
|
||||||
u64 vbo;
|
u64 vbo;
|
||||||
|
|
@ -1287,7 +1276,7 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
|
||||||
return E_NTFS_NONRESIDENT;
|
return E_NTFS_NONRESIDENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
vbo = folio->index << PAGE_SHIFT;
|
vbo = folio_pos(folio);
|
||||||
data_size = le32_to_cpu(attr->res.data_size);
|
data_size = le32_to_cpu(attr->res.data_size);
|
||||||
if (vbo < data_size) {
|
if (vbo < data_size) {
|
||||||
char *data = resident_data(attr);
|
char *data = resident_data(attr);
|
||||||
|
|
@ -1360,21 +1349,20 @@ int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||||
int retry = 0;
|
int retry = 0;
|
||||||
|
|
||||||
for (vcn = from >> cluster_bits; vcn <= vcn_last; vcn += clen) {
|
for (vcn = from >> cluster_bits; vcn <= vcn_last; vcn += clen) {
|
||||||
if (!run_lookup_entry(run, vcn, &lcn, &clen, NULL)) {
|
if (run_lookup_entry(run, vcn, &lcn, &clen, NULL)) {
|
||||||
if (retry != 0) { /* Next run_lookup_entry(vcn) also failed. */
|
|
||||||
err = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
err = attr_load_runs_vcn(ni, type, name, name_len, run,
|
|
||||||
vcn);
|
|
||||||
if (err)
|
|
||||||
break;
|
|
||||||
|
|
||||||
clen = 0; /* Next run_lookup_entry(vcn) must be success. */
|
|
||||||
retry++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
retry = 0;
|
retry = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (retry) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
err = attr_load_runs_vcn(ni, type, name, name_len, run, vcn);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
|
||||||
|
clen = 0; /* Next run_lookup_entry(vcn) must be success. */
|
||||||
|
retry++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
368
fs/ntfs3/file.c
368
fs/ntfs3/file.c
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/falloc.h>
|
#include <linux/falloc.h>
|
||||||
#include <linux/fiemap.h>
|
#include <linux/fiemap.h>
|
||||||
#include <linux/fileattr.h>
|
#include <linux/fileattr.h>
|
||||||
|
#include <linux/iomap.h>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "ntfs.h"
|
#include "ntfs.h"
|
||||||
|
|
@ -189,9 +190,6 @@ static int ntfs_extend_initialized_size(struct file *file,
|
||||||
const loff_t new_valid)
|
const loff_t new_valid)
|
||||||
{
|
{
|
||||||
struct inode *inode = &ni->vfs_inode;
|
struct inode *inode = &ni->vfs_inode;
|
||||||
struct address_space *mapping = inode->i_mapping;
|
|
||||||
struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
|
|
||||||
loff_t pos = valid;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (valid >= new_valid)
|
if (valid >= new_valid)
|
||||||
|
|
@ -204,140 +202,41 @@ static int ntfs_extend_initialized_size(struct file *file,
|
||||||
|
|
||||||
WARN_ON(is_compressed(ni));
|
WARN_ON(is_compressed(ni));
|
||||||
|
|
||||||
for (;;) {
|
err = iomap_zero_range(inode, valid, new_valid - valid, NULL,
|
||||||
u32 zerofrom, len;
|
&ntfs_iomap_ops, &ntfs_iomap_folio_ops, NULL);
|
||||||
struct folio *folio;
|
if (err) {
|
||||||
u8 bits;
|
ni->i_valid = valid;
|
||||||
CLST vcn, lcn, clen;
|
ntfs_inode_warn(inode,
|
||||||
|
"failed to extend initialized size to %llx.",
|
||||||
if (is_sparsed(ni)) {
|
new_valid);
|
||||||
bits = sbi->cluster_bits;
|
return err;
|
||||||
vcn = pos >> bits;
|
|
||||||
|
|
||||||
err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL,
|
|
||||||
false);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (lcn == SPARSE_LCN) {
|
|
||||||
pos = ((loff_t)clen + vcn) << bits;
|
|
||||||
ni->i_valid = pos;
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zerofrom = pos & (PAGE_SIZE - 1);
|
|
||||||
len = PAGE_SIZE - zerofrom;
|
|
||||||
|
|
||||||
if (pos + len > new_valid)
|
|
||||||
len = new_valid - pos;
|
|
||||||
|
|
||||||
err = ntfs_write_begin(NULL, mapping, pos, len, &folio, NULL);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
folio_zero_range(folio, zerofrom, folio_size(folio) - zerofrom);
|
|
||||||
|
|
||||||
err = ntfs_write_end(NULL, mapping, pos, len, len, folio, NULL);
|
|
||||||
if (err < 0)
|
|
||||||
goto out;
|
|
||||||
pos += len;
|
|
||||||
|
|
||||||
next:
|
|
||||||
if (pos >= new_valid)
|
|
||||||
break;
|
|
||||||
|
|
||||||
balance_dirty_pages_ratelimited(mapping);
|
|
||||||
cond_resched();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
|
||||||
ni->i_valid = valid;
|
|
||||||
ntfs_inode_warn(inode, "failed to extend initialized size to %llx.",
|
|
||||||
new_valid);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void ntfs_filemap_close(struct vm_area_struct *vma)
|
||||||
* ntfs_zero_range - Helper function for punch_hole.
|
|
||||||
*
|
|
||||||
* It zeroes a range [vbo, vbo_to).
|
|
||||||
*/
|
|
||||||
static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
|
|
||||||
{
|
{
|
||||||
int err = 0;
|
struct inode *inode = file_inode(vma->vm_file);
|
||||||
struct address_space *mapping = inode->i_mapping;
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
u32 blocksize = i_blocksize(inode);
|
u64 from = (u64)vma->vm_pgoff << PAGE_SHIFT;
|
||||||
pgoff_t idx = vbo >> PAGE_SHIFT;
|
u64 to = min_t(u64, i_size_read(inode),
|
||||||
u32 from = vbo & (PAGE_SIZE - 1);
|
from + vma->vm_end - vma->vm_start);
|
||||||
pgoff_t idx_end = (vbo_to + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
||||||
loff_t page_off;
|
|
||||||
struct buffer_head *head, *bh;
|
|
||||||
u32 bh_next, bh_off, to;
|
|
||||||
sector_t iblock;
|
|
||||||
struct folio *folio;
|
|
||||||
bool dirty = false;
|
|
||||||
|
|
||||||
for (; idx < idx_end; idx += 1, from = 0) {
|
if (ni->i_valid < to) {
|
||||||
page_off = (loff_t)idx << PAGE_SHIFT;
|
ni->i_valid = to;
|
||||||
to = (page_off + PAGE_SIZE) > vbo_to ? (vbo_to - page_off) :
|
|
||||||
PAGE_SIZE;
|
|
||||||
iblock = page_off >> inode->i_blkbits;
|
|
||||||
|
|
||||||
folio = __filemap_get_folio(
|
|
||||||
mapping, idx, FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
|
|
||||||
mapping_gfp_constraint(mapping, ~__GFP_FS));
|
|
||||||
if (IS_ERR(folio))
|
|
||||||
return PTR_ERR(folio);
|
|
||||||
|
|
||||||
head = folio_buffers(folio);
|
|
||||||
if (!head)
|
|
||||||
head = create_empty_buffers(folio, blocksize, 0);
|
|
||||||
|
|
||||||
bh = head;
|
|
||||||
bh_off = 0;
|
|
||||||
do {
|
|
||||||
bh_next = bh_off + blocksize;
|
|
||||||
|
|
||||||
if (bh_next <= from || bh_off >= to)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!buffer_mapped(bh)) {
|
|
||||||
ntfs_get_block(inode, iblock, bh, 0);
|
|
||||||
/* Unmapped? It's a hole - nothing to do. */
|
|
||||||
if (!buffer_mapped(bh))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ok, it's mapped. Make sure it's up-to-date. */
|
|
||||||
if (folio_test_uptodate(folio))
|
|
||||||
set_buffer_uptodate(bh);
|
|
||||||
else if (bh_read(bh, 0) < 0) {
|
|
||||||
err = -EIO;
|
|
||||||
folio_unlock(folio);
|
|
||||||
folio_put(folio);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
mark_buffer_dirty(bh);
|
|
||||||
} while (bh_off = bh_next, iblock += 1,
|
|
||||||
head != (bh = bh->b_this_page));
|
|
||||||
|
|
||||||
folio_zero_segment(folio, from, to);
|
|
||||||
dirty = true;
|
|
||||||
|
|
||||||
folio_unlock(folio);
|
|
||||||
folio_put(folio);
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
if (dirty)
|
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
return err;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Copy of generic_file_vm_ops. */
|
||||||
|
static const struct vm_operations_struct ntfs_file_vm_ops = {
|
||||||
|
.close = ntfs_filemap_close,
|
||||||
|
.fault = filemap_fault,
|
||||||
|
.map_pages = filemap_map_pages,
|
||||||
|
.page_mkwrite = filemap_page_mkwrite,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ntfs_file_mmap_prepare - file_operations::mmap_prepare
|
* ntfs_file_mmap_prepare - file_operations::mmap_prepare
|
||||||
*/
|
*/
|
||||||
|
|
@ -346,7 +245,6 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
|
||||||
struct file *file = desc->file;
|
struct file *file = desc->file;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
u64 from = ((u64)desc->pgoff << PAGE_SHIFT);
|
|
||||||
bool rw = desc->vm_flags & VM_WRITE;
|
bool rw = desc->vm_flags & VM_WRITE;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
|
@ -378,7 +276,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rw) {
|
if (rw) {
|
||||||
u64 to = min_t(loff_t, i_size_read(inode),
|
u64 from = (u64)desc->pgoff << PAGE_SHIFT;
|
||||||
|
u64 to = min_t(u64, i_size_read(inode),
|
||||||
from + vma_desc_size(desc));
|
from + vma_desc_size(desc));
|
||||||
|
|
||||||
if (is_sparsed(ni)) {
|
if (is_sparsed(ni)) {
|
||||||
|
|
@ -391,7 +290,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
|
||||||
|
|
||||||
for (; vcn < end; vcn += len) {
|
for (; vcn < end; vcn += len) {
|
||||||
err = attr_data_get_block(ni, vcn, 1, &lcn,
|
err = attr_data_get_block(ni, vcn, 1, &lcn,
|
||||||
&len, &new, true);
|
&len, &new, true,
|
||||||
|
NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -411,6 +311,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = generic_file_mmap_prepare(desc);
|
err = generic_file_mmap_prepare(desc);
|
||||||
|
if (!err && rw)
|
||||||
|
desc->vm_ops = &ntfs_file_vm_ops;
|
||||||
out:
|
out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -465,7 +367,7 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
|
||||||
*/
|
*/
|
||||||
for (; vcn < cend_v; vcn += clen) {
|
for (; vcn < cend_v; vcn += clen) {
|
||||||
err = attr_data_get_block(ni, vcn, cend_v - vcn, &lcn,
|
err = attr_data_get_block(ni, vcn, cend_v - vcn, &lcn,
|
||||||
&clen, &new, true);
|
&clen, &new, true, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -474,7 +376,7 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
|
||||||
*/
|
*/
|
||||||
for (; vcn < cend; vcn += clen) {
|
for (; vcn < cend; vcn += clen) {
|
||||||
err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
|
err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
|
||||||
&clen, &new, false);
|
&clen, &new, false, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -503,25 +405,10 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
|
||||||
|
|
||||||
static int ntfs_truncate(struct inode *inode, loff_t new_size)
|
static int ntfs_truncate(struct inode *inode, loff_t new_size)
|
||||||
{
|
{
|
||||||
struct super_block *sb = inode->i_sb;
|
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
|
||||||
u64 new_valid;
|
|
||||||
int err;
|
int err;
|
||||||
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
|
u64 new_valid = min_t(u64, ni->i_valid, new_size);
|
||||||
|
|
||||||
if (!S_ISREG(inode->i_mode))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (is_compressed(ni)) {
|
|
||||||
if (ni->i_valid > new_size)
|
|
||||||
ni->i_valid = new_size;
|
|
||||||
} else {
|
|
||||||
err = block_truncate_page(inode->i_mapping, new_size,
|
|
||||||
ntfs_get_block);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size));
|
|
||||||
truncate_setsize(inode, new_size);
|
truncate_setsize(inode, new_size);
|
||||||
|
|
||||||
ni_lock(ni);
|
ni_lock(ni);
|
||||||
|
|
@ -531,11 +418,11 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
|
||||||
&new_valid, ni->mi.sbi->options->prealloc, NULL);
|
&new_valid, ni->mi.sbi->options->prealloc, NULL);
|
||||||
up_write(&ni->file.run_lock);
|
up_write(&ni->file.run_lock);
|
||||||
|
|
||||||
if (new_valid < ni->i_valid)
|
ni->i_valid = new_valid;
|
||||||
ni->i_valid = new_valid;
|
|
||||||
|
|
||||||
ni_unlock(ni);
|
ni_unlock(ni);
|
||||||
if (unlikely(err))
|
|
||||||
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
|
ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
|
||||||
|
|
@ -646,13 +533,17 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
|
||||||
|
|
||||||
tmp = min(vbo_a, end);
|
tmp = min(vbo_a, end);
|
||||||
if (tmp > vbo) {
|
if (tmp > vbo) {
|
||||||
err = ntfs_zero_range(inode, vbo, tmp);
|
err = iomap_zero_range(inode, vbo, tmp - vbo, NULL,
|
||||||
|
&ntfs_iomap_ops,
|
||||||
|
&ntfs_iomap_folio_ops, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vbo < end_a && end_a < end) {
|
if (vbo < end_a && end_a < end) {
|
||||||
err = ntfs_zero_range(inode, end_a, end);
|
err = iomap_zero_range(inode, end_a, end - end_a, NULL,
|
||||||
|
&ntfs_iomap_ops,
|
||||||
|
&ntfs_iomap_folio_ops, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -762,7 +653,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
|
||||||
for (; vcn < cend_v; vcn += clen) {
|
for (; vcn < cend_v; vcn += clen) {
|
||||||
err = attr_data_get_block(ni, vcn, cend_v - vcn,
|
err = attr_data_get_block(ni, vcn, cend_v - vcn,
|
||||||
&lcn, &clen, &new,
|
&lcn, &clen, &new,
|
||||||
true);
|
true, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -772,7 +663,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
|
||||||
for (; vcn < cend; vcn += clen) {
|
for (; vcn < cend; vcn += clen) {
|
||||||
err = attr_data_get_block(ni, vcn, cend - vcn,
|
err = attr_data_get_block(ni, vcn, cend - vcn,
|
||||||
&lcn, &clen, &new,
|
&lcn, &clen, &new,
|
||||||
false);
|
false, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -787,6 +678,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
|
||||||
ni_unlock(ni);
|
ni_unlock(ni);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
i_size_write(inode, i_size);
|
||||||
} else if (new_size > i_size) {
|
} else if (new_size > i_size) {
|
||||||
i_size_write(inode, new_size);
|
i_size_write(inode, new_size);
|
||||||
}
|
}
|
||||||
|
|
@ -923,12 +815,16 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
|
size_t bytes = iov_iter_count(iter);
|
||||||
ssize_t err;
|
ssize_t err;
|
||||||
|
|
||||||
err = check_read_restriction(inode);
|
err = check_read_restriction(inode);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (!bytes)
|
||||||
|
return 0; /* skip atime */
|
||||||
|
|
||||||
if (is_compressed(ni)) {
|
if (is_compressed(ni)) {
|
||||||
if (iocb->ki_flags & IOCB_DIRECT) {
|
if (iocb->ki_flags & IOCB_DIRECT) {
|
||||||
ntfs_inode_warn(
|
ntfs_inode_warn(
|
||||||
|
|
@ -940,13 +836,58 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check minimum alignment for dio. */
|
/* Check minimum alignment for dio. */
|
||||||
|
if ((iocb->ki_flags & IOCB_DIRECT) &&
|
||||||
|
(is_resident(ni) || ((iocb->ki_pos | iov_iter_alignment(iter)) &
|
||||||
|
ni->mi.sbi->bdev_blocksize_mask))) {
|
||||||
|
/* Fallback to buffered I/O */
|
||||||
|
iocb->ki_flags &= ~IOCB_DIRECT;
|
||||||
|
}
|
||||||
|
|
||||||
if (iocb->ki_flags & IOCB_DIRECT) {
|
if (iocb->ki_flags & IOCB_DIRECT) {
|
||||||
struct super_block *sb = inode->i_sb;
|
loff_t valid, i_size;
|
||||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
loff_t vbo = iocb->ki_pos;
|
||||||
if ((iocb->ki_pos | iov_iter_alignment(iter)) &
|
loff_t end = vbo + bytes;
|
||||||
sbi->bdev_blocksize_mask) {
|
unsigned int dio_flags = IOMAP_DIO_PARTIAL;
|
||||||
iocb->ki_flags &= ~IOCB_DIRECT;
|
|
||||||
|
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||||
|
if (!inode_trylock_shared(inode))
|
||||||
|
return -EAGAIN;
|
||||||
|
} else {
|
||||||
|
inode_lock_shared(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valid = ni->i_valid;
|
||||||
|
i_size = inode->i_size;
|
||||||
|
|
||||||
|
if (vbo < valid) {
|
||||||
|
if (valid < end) {
|
||||||
|
/* read cross 'valid' size. */
|
||||||
|
dio_flags |= IOMAP_DIO_FORCE_WAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iomap_dio_rw(iocb, iter, &ntfs_iomap_ops, NULL,
|
||||||
|
dio_flags, NULL, 0);
|
||||||
|
|
||||||
|
if (err > 0) {
|
||||||
|
end = vbo + err;
|
||||||
|
if (valid < end) {
|
||||||
|
size_t to_zero = end - valid;
|
||||||
|
/* Fix iter. */
|
||||||
|
iov_iter_revert(iter, to_zero);
|
||||||
|
iov_iter_zero(to_zero, iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (vbo < i_size) {
|
||||||
|
if (end > i_size)
|
||||||
|
bytes = i_size - vbo;
|
||||||
|
iov_iter_zero(bytes, iter);
|
||||||
|
iocb->ki_pos += bytes;
|
||||||
|
err = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
inode_unlock_shared(inode);
|
||||||
|
file_accessed(iocb->ki_filp);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return generic_file_read_iter(iocb, iter);
|
return generic_file_read_iter(iocb, iter);
|
||||||
|
|
@ -1070,7 +1011,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
|
||||||
off = valid & (frame_size - 1);
|
off = valid & (frame_size - 1);
|
||||||
|
|
||||||
err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 1, &lcn,
|
err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 1, &lcn,
|
||||||
&clen, NULL, false);
|
&clen, NULL, false, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
@ -1273,8 +1214,9 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||||
struct file *file = iocb->ki_filp;
|
struct file *file = iocb->ki_filp;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
ssize_t ret;
|
struct super_block *sb = inode->i_sb;
|
||||||
int err;
|
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||||
|
ssize_t ret, err;
|
||||||
|
|
||||||
if (!inode_trylock(inode)) {
|
if (!inode_trylock(inode)) {
|
||||||
if (iocb->ki_flags & IOCB_NOWAIT)
|
if (iocb->ki_flags & IOCB_NOWAIT)
|
||||||
|
|
@ -1312,15 +1254,73 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = is_compressed(ni) ? ntfs_compress_write(iocb, from) :
|
if (is_compressed(ni)) {
|
||||||
__generic_file_write_iter(iocb, from);
|
ret = ntfs_compress_write(iocb, from);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check minimum alignment for dio. */
|
||||||
|
if ((iocb->ki_flags & IOCB_DIRECT) &&
|
||||||
|
(is_resident(ni) || ((iocb->ki_pos | iov_iter_alignment(from)) &
|
||||||
|
sbi->bdev_blocksize_mask))) {
|
||||||
|
/* Fallback to buffered I/O */
|
||||||
|
iocb->ki_flags &= ~IOCB_DIRECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(iocb->ki_flags & IOCB_DIRECT)) {
|
||||||
|
ret = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
|
||||||
|
&ntfs_iomap_folio_ops, NULL);
|
||||||
|
inode_unlock(inode);
|
||||||
|
|
||||||
|
if (likely(ret > 0))
|
||||||
|
ret = generic_write_sync(iocb, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = iomap_dio_rw(iocb, from, &ntfs_iomap_ops, NULL, IOMAP_DIO_PARTIAL,
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
|
if (ret == -ENOTBLK) {
|
||||||
|
/* Returns -ENOTBLK in case of a page invalidation failure for writes.*/
|
||||||
|
/* The callers needs to fall back to buffered I/O in this case. */
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret >= 0 && iov_iter_count(from)) {
|
||||||
|
loff_t offset = iocb->ki_pos, endbyte;
|
||||||
|
|
||||||
|
iocb->ki_flags &= ~IOCB_DIRECT;
|
||||||
|
err = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
|
||||||
|
&ntfs_iomap_folio_ops, NULL);
|
||||||
|
if (err < 0) {
|
||||||
|
ret = err;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to ensure that the pages within the page cache for
|
||||||
|
* the range covered by this I/O are written to disk and
|
||||||
|
* invalidated. This is in attempt to preserve the expected
|
||||||
|
* direct I/O semantics in the case we fallback to buffered I/O
|
||||||
|
* to complete off the I/O request.
|
||||||
|
*/
|
||||||
|
ret += err;
|
||||||
|
endbyte = offset + err - 1;
|
||||||
|
err = filemap_write_and_wait_range(inode->i_mapping, offset,
|
||||||
|
endbyte);
|
||||||
|
if (err) {
|
||||||
|
ret = err;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate_mapping_pages(inode->i_mapping, offset >> PAGE_SHIFT,
|
||||||
|
endbyte >> PAGE_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
|
|
||||||
if (ret > 0)
|
|
||||||
ret = generic_write_sync(iocb, ret);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1359,6 +1359,8 @@ int ntfs_file_open(struct inode *inode, struct file *file)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
|
||||||
|
|
||||||
return generic_file_open(inode, file);
|
return generic_file_open(inode, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1408,16 +1410,30 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||||
if (unlikely(is_bad_ni(ni)))
|
if (unlikely(is_bad_ni(ni)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
err = fiemap_prep(inode, fieinfo, start, &len, ~FIEMAP_FLAG_XATTR);
|
if (is_compressed(ni)) {
|
||||||
if (err)
|
/* Unfortunately cp -r incorrectly treats compressed clusters. */
|
||||||
return err;
|
ntfs_inode_warn(inode,
|
||||||
|
"fiemap is not supported for compressed file");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
ni_lock(ni);
|
if (S_ISDIR(inode->i_mode)) {
|
||||||
|
/* TODO: add support for dirs (ATTR_ALLOC). */
|
||||||
|
ntfs_inode_warn(inode,
|
||||||
|
"fiemap is not supported for directories");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
err = ni_fiemap(ni, fieinfo, start, len);
|
if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
|
||||||
|
ntfs_inode_warn(inode, "fiemap(xattr) is not supported");
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
ni_unlock(ni);
|
inode_lock_shared(inode);
|
||||||
|
|
||||||
|
err = iomap_fiemap(inode, fieinfo, start, len, &ntfs_iomap_ops);
|
||||||
|
|
||||||
|
inode_unlock_shared(inode);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1463,7 +1479,7 @@ int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
|
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
|
||||||
ntfs_update_mftmirr(sbi, false);
|
ntfs_update_mftmirr(sbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sync_blockdev(sb->s_bdev);
|
err = sync_blockdev(sb->s_bdev);
|
||||||
|
|
|
||||||
|
|
@ -1850,183 +1850,11 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
|
||||||
return REPARSE_LINK;
|
return REPARSE_LINK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* ni_fiemap - Helper for file_fiemap().
|
|
||||||
*
|
|
||||||
* Assumed ni_lock.
|
|
||||||
* TODO: Less aggressive locks.
|
|
||||||
*/
|
|
||||||
int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
|
|
||||||
__u64 vbo, __u64 len)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
|
||||||
u8 cluster_bits = sbi->cluster_bits;
|
|
||||||
struct runs_tree run;
|
|
||||||
struct ATTRIB *attr;
|
|
||||||
CLST vcn = vbo >> cluster_bits;
|
|
||||||
CLST lcn, clen;
|
|
||||||
u64 valid = ni->i_valid;
|
|
||||||
u64 lbo, bytes;
|
|
||||||
u64 end, alloc_size;
|
|
||||||
size_t idx = -1;
|
|
||||||
u32 flags;
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
run_init(&run);
|
|
||||||
if (S_ISDIR(ni->vfs_inode.i_mode)) {
|
|
||||||
attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME,
|
|
||||||
ARRAY_SIZE(I30_NAME), NULL, NULL);
|
|
||||||
} else {
|
|
||||||
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL,
|
|
||||||
NULL);
|
|
||||||
if (!attr) {
|
|
||||||
err = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (is_attr_compressed(attr)) {
|
|
||||||
/* Unfortunately cp -r incorrectly treats compressed clusters. */
|
|
||||||
err = -EOPNOTSUPP;
|
|
||||||
ntfs_inode_warn(
|
|
||||||
&ni->vfs_inode,
|
|
||||||
"fiemap is not supported for compressed file (cp -r)");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!attr || !attr->non_res) {
|
|
||||||
err = fiemap_fill_next_extent(
|
|
||||||
fieinfo, 0, 0,
|
|
||||||
attr ? le32_to_cpu(attr->res.data_size) : 0,
|
|
||||||
FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_LAST |
|
|
||||||
FIEMAP_EXTENT_MERGED);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
end = vbo + len;
|
|
||||||
alloc_size = le64_to_cpu(attr->nres.alloc_size);
|
|
||||||
if (end > alloc_size)
|
|
||||||
end = alloc_size;
|
|
||||||
|
|
||||||
while (vbo < end) {
|
|
||||||
if (idx == -1) {
|
|
||||||
ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);
|
|
||||||
} else {
|
|
||||||
CLST vcn_next = vcn;
|
|
||||||
|
|
||||||
ok = run_get_entry(&run, ++idx, &vcn, &lcn, &clen) &&
|
|
||||||
vcn == vcn_next;
|
|
||||||
if (!ok)
|
|
||||||
vcn = vcn_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
err = attr_load_runs_vcn(ni, attr->type,
|
|
||||||
attr_name(attr),
|
|
||||||
attr->name_len, &run, vcn);
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
err = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!clen) {
|
|
||||||
err = -EINVAL; // ?
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lcn == SPARSE_LCN) {
|
|
||||||
vcn += clen;
|
|
||||||
vbo = (u64)vcn << cluster_bits;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
flags = FIEMAP_EXTENT_MERGED;
|
|
||||||
if (S_ISDIR(ni->vfs_inode.i_mode)) {
|
|
||||||
;
|
|
||||||
} else if (is_attr_compressed(attr)) {
|
|
||||||
CLST clst_data;
|
|
||||||
|
|
||||||
err = attr_is_frame_compressed(ni, attr,
|
|
||||||
vcn >> attr->nres.c_unit,
|
|
||||||
&clst_data, &run);
|
|
||||||
if (err)
|
|
||||||
break;
|
|
||||||
if (clst_data < NTFS_LZNT_CLUSTERS)
|
|
||||||
flags |= FIEMAP_EXTENT_ENCODED;
|
|
||||||
} else if (is_attr_encrypted(attr)) {
|
|
||||||
flags |= FIEMAP_EXTENT_DATA_ENCRYPTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
vbo = (u64)vcn << cluster_bits;
|
|
||||||
bytes = (u64)clen << cluster_bits;
|
|
||||||
lbo = (u64)lcn << cluster_bits;
|
|
||||||
|
|
||||||
vcn += clen;
|
|
||||||
|
|
||||||
if (vbo + bytes >= end)
|
|
||||||
bytes = end - vbo;
|
|
||||||
|
|
||||||
if (vbo + bytes <= valid) {
|
|
||||||
;
|
|
||||||
} else if (vbo >= valid) {
|
|
||||||
flags |= FIEMAP_EXTENT_UNWRITTEN;
|
|
||||||
} else {
|
|
||||||
/* vbo < valid && valid < vbo + bytes */
|
|
||||||
u64 dlen = valid - vbo;
|
|
||||||
|
|
||||||
if (vbo + dlen >= end)
|
|
||||||
flags |= FIEMAP_EXTENT_LAST;
|
|
||||||
|
|
||||||
err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen,
|
|
||||||
flags);
|
|
||||||
|
|
||||||
if (err < 0)
|
|
||||||
break;
|
|
||||||
if (err == 1) {
|
|
||||||
err = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vbo = valid;
|
|
||||||
bytes -= dlen;
|
|
||||||
if (!bytes)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
lbo += dlen;
|
|
||||||
flags |= FIEMAP_EXTENT_UNWRITTEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vbo + bytes >= end)
|
|
||||||
flags |= FIEMAP_EXTENT_LAST;
|
|
||||||
|
|
||||||
err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags);
|
|
||||||
if (err < 0)
|
|
||||||
break;
|
|
||||||
if (err == 1) {
|
|
||||||
err = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vbo += bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
run_close(&run);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct page *ntfs_lock_new_page(struct address_space *mapping,
|
static struct page *ntfs_lock_new_page(struct address_space *mapping,
|
||||||
pgoff_t index, gfp_t gfp)
|
pgoff_t index, gfp_t gfp)
|
||||||
{
|
{
|
||||||
struct folio *folio = __filemap_get_folio(mapping, index,
|
struct folio *folio = __filemap_get_folio(
|
||||||
FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
|
mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
|
||||||
if (IS_ERR(folio))
|
if (IS_ERR(folio))
|
||||||
|
|
@ -2186,7 +2014,7 @@ int ni_decompress_file(struct ntfs_inode *ni)
|
||||||
|
|
||||||
for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) {
|
for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) {
|
||||||
err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
|
err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
|
||||||
&clen, &new, false);
|
&clen, &new, false, NULL);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
@ -3017,7 +2845,8 @@ loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data)
|
||||||
|
|
||||||
/* Enumerate all fragments. */
|
/* Enumerate all fragments. */
|
||||||
for (vcn = offset >> cluster_bits;; vcn += clen) {
|
for (vcn = offset >> cluster_bits;; vcn += clen) {
|
||||||
err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL, false);
|
err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL, false,
|
||||||
|
NULL);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5130,7 +5130,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
|
||||||
|
|
||||||
undo_action_done:
|
undo_action_done:
|
||||||
|
|
||||||
ntfs_update_mftmirr(sbi, 0);
|
ntfs_update_mftmirr(sbi);
|
||||||
|
|
||||||
sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY;
|
sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -843,9 +843,8 @@ int ntfs_refresh_zone(struct ntfs_sb_info *sbi)
|
||||||
/*
|
/*
|
||||||
* ntfs_update_mftmirr - Update $MFTMirr data.
|
* ntfs_update_mftmirr - Update $MFTMirr data.
|
||||||
*/
|
*/
|
||||||
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
|
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
struct super_block *sb = sbi->sb;
|
struct super_block *sb = sbi->sb;
|
||||||
u32 blocksize, bytes;
|
u32 blocksize, bytes;
|
||||||
sector_t block1, block2;
|
sector_t block1, block2;
|
||||||
|
|
@ -884,12 +883,7 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
|
||||||
|
|
||||||
put_bh(bh1);
|
put_bh(bh1);
|
||||||
bh1 = NULL;
|
bh1 = NULL;
|
||||||
|
|
||||||
err = wait ? sync_dirty_buffer(bh2) : 0;
|
|
||||||
|
|
||||||
put_bh(bh2);
|
put_bh(bh2);
|
||||||
if (err)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sbi->flags &= ~NTFS_FLAGS_MFTMIRR;
|
sbi->flags &= ~NTFS_FLAGS_MFTMIRR;
|
||||||
|
|
@ -1357,9 +1351,7 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_on_buffer(bh);
|
wait_on_buffer(bh);
|
||||||
|
|
||||||
lock_buffer(bh);
|
lock_buffer(bh);
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
memset(bh->b_data, 0, blocksize);
|
memset(bh->b_data, 0, blocksize);
|
||||||
|
|
|
||||||
755
fs/ntfs3/inode.c
755
fs/ntfs3/inode.c
|
|
@ -12,6 +12,7 @@
|
||||||
#include <linux/nls.h>
|
#include <linux/nls.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include <linux/writeback.h>
|
#include <linux/writeback.h>
|
||||||
|
#include <linux/iomap.h>
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "ntfs.h"
|
#include "ntfs.h"
|
||||||
|
|
@ -166,9 +167,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
|
||||||
|
|
||||||
std5 = Add2Ptr(attr, roff);
|
std5 = Add2Ptr(attr, roff);
|
||||||
|
|
||||||
#ifdef STATX_BTIME
|
|
||||||
nt2kernel(std5->cr_time, &ni->i_crtime);
|
nt2kernel(std5->cr_time, &ni->i_crtime);
|
||||||
#endif
|
|
||||||
nt2kernel(std5->a_time, &ts);
|
nt2kernel(std5->a_time, &ts);
|
||||||
inode_set_atime_to_ts(inode, ts);
|
inode_set_atime_to_ts(inode, ts);
|
||||||
nt2kernel(std5->c_time, &ts);
|
nt2kernel(std5->c_time, &ts);
|
||||||
|
|
@ -555,168 +554,97 @@ struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref,
|
||||||
return inode;
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum get_block_ctx {
|
|
||||||
GET_BLOCK_GENERAL = 0,
|
|
||||||
GET_BLOCK_WRITE_BEGIN = 1,
|
|
||||||
GET_BLOCK_DIRECT_IO_R = 2,
|
|
||||||
GET_BLOCK_DIRECT_IO_W = 3,
|
|
||||||
GET_BLOCK_BMAP = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo,
|
|
||||||
struct buffer_head *bh, int create,
|
|
||||||
enum get_block_ctx ctx)
|
|
||||||
{
|
|
||||||
struct super_block *sb = inode->i_sb;
|
|
||||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
|
||||||
struct folio *folio = bh->b_folio;
|
|
||||||
u8 cluster_bits = sbi->cluster_bits;
|
|
||||||
u32 block_size = sb->s_blocksize;
|
|
||||||
u64 bytes, lbo, valid;
|
|
||||||
u32 off;
|
|
||||||
int err;
|
|
||||||
CLST vcn, lcn, len;
|
|
||||||
bool new;
|
|
||||||
|
|
||||||
/* Clear previous state. */
|
|
||||||
clear_buffer_new(bh);
|
|
||||||
clear_buffer_uptodate(bh);
|
|
||||||
|
|
||||||
if (is_resident(ni)) {
|
|
||||||
bh->b_blocknr = RESIDENT_LCN;
|
|
||||||
bh->b_size = block_size;
|
|
||||||
if (!folio) {
|
|
||||||
/* direct io (read) or bmap call */
|
|
||||||
err = 0;
|
|
||||||
} else {
|
|
||||||
ni_lock(ni);
|
|
||||||
err = attr_data_read_resident(ni, folio);
|
|
||||||
ni_unlock(ni);
|
|
||||||
|
|
||||||
if (!err)
|
|
||||||
set_buffer_uptodate(bh);
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
vcn = vbo >> cluster_bits;
|
|
||||||
off = vbo & sbi->cluster_mask;
|
|
||||||
new = false;
|
|
||||||
|
|
||||||
err = attr_data_get_block(ni, vcn, 1, &lcn, &len, create ? &new : NULL,
|
|
||||||
create && sbi->cluster_size > PAGE_SIZE);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (!len)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
bytes = ((u64)len << cluster_bits) - off;
|
|
||||||
|
|
||||||
if (lcn >= sbi->used.bitmap.nbits) {
|
|
||||||
/* This case includes resident/compressed/sparse. */
|
|
||||||
if (!create) {
|
|
||||||
if (bh->b_size > bytes)
|
|
||||||
bh->b_size = bytes;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
WARN_ON(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new)
|
|
||||||
set_buffer_new(bh);
|
|
||||||
|
|
||||||
lbo = ((u64)lcn << cluster_bits) + off;
|
|
||||||
|
|
||||||
set_buffer_mapped(bh);
|
|
||||||
bh->b_bdev = sb->s_bdev;
|
|
||||||
bh->b_blocknr = lbo >> sb->s_blocksize_bits;
|
|
||||||
|
|
||||||
valid = ni->i_valid;
|
|
||||||
|
|
||||||
if (ctx == GET_BLOCK_DIRECT_IO_W) {
|
|
||||||
/* ntfs_direct_IO will update ni->i_valid. */
|
|
||||||
if (vbo >= valid)
|
|
||||||
set_buffer_new(bh);
|
|
||||||
} else if (create) {
|
|
||||||
/* Normal write. */
|
|
||||||
if (bytes > bh->b_size)
|
|
||||||
bytes = bh->b_size;
|
|
||||||
|
|
||||||
if (vbo >= valid)
|
|
||||||
set_buffer_new(bh);
|
|
||||||
|
|
||||||
if (vbo + bytes > valid) {
|
|
||||||
ni->i_valid = vbo + bytes;
|
|
||||||
mark_inode_dirty(inode);
|
|
||||||
}
|
|
||||||
} else if (vbo >= valid) {
|
|
||||||
/* Read out of valid data. */
|
|
||||||
clear_buffer_mapped(bh);
|
|
||||||
} else if (vbo + bytes <= valid) {
|
|
||||||
/* Normal read. */
|
|
||||||
} else if (vbo + block_size <= valid) {
|
|
||||||
/* Normal short read. */
|
|
||||||
bytes = block_size;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Read across valid size: vbo < valid && valid < vbo + block_size
|
|
||||||
*/
|
|
||||||
bytes = block_size;
|
|
||||||
|
|
||||||
if (folio) {
|
|
||||||
u32 voff = valid - vbo;
|
|
||||||
|
|
||||||
bh->b_size = block_size;
|
|
||||||
off = vbo & (PAGE_SIZE - 1);
|
|
||||||
folio_set_bh(bh, folio, off);
|
|
||||||
|
|
||||||
if (bh_read(bh, 0) < 0) {
|
|
||||||
err = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
folio_zero_segment(folio, off + voff, off + block_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bh->b_size > bytes)
|
|
||||||
bh->b_size = bytes;
|
|
||||||
|
|
||||||
#ifndef __LP64__
|
|
||||||
if (ctx == GET_BLOCK_DIRECT_IO_W || ctx == GET_BLOCK_DIRECT_IO_R) {
|
|
||||||
static_assert(sizeof(size_t) < sizeof(loff_t));
|
|
||||||
if (bytes > 0x40000000u)
|
|
||||||
bh->b_size = 0x40000000u;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ntfs_get_block(struct inode *inode, sector_t vbn,
|
|
||||||
struct buffer_head *bh_result, int create)
|
|
||||||
{
|
|
||||||
return ntfs_get_block_vbo(inode, (u64)vbn << inode->i_blkbits,
|
|
||||||
bh_result, create, GET_BLOCK_GENERAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntfs_get_block_bmap(struct inode *inode, sector_t vsn,
|
|
||||||
struct buffer_head *bh_result, int create)
|
|
||||||
{
|
|
||||||
return ntfs_get_block_vbo(inode,
|
|
||||||
(u64)vsn << inode->i_sb->s_blocksize_bits,
|
|
||||||
bh_result, create, GET_BLOCK_BMAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
static sector_t ntfs_bmap(struct address_space *mapping, sector_t block)
|
static sector_t ntfs_bmap(struct address_space *mapping, sector_t block)
|
||||||
{
|
{
|
||||||
return generic_block_bmap(mapping, block, ntfs_get_block_bmap);
|
return iomap_bmap(mapping, block, &ntfs_iomap_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ntfs_iomap_read_end_io(struct bio *bio)
|
||||||
|
{
|
||||||
|
int error = blk_status_to_errno(bio->bi_status);
|
||||||
|
struct folio_iter fi;
|
||||||
|
|
||||||
|
bio_for_each_folio_all(fi, bio) {
|
||||||
|
struct folio *folio = fi.folio;
|
||||||
|
struct inode *inode = folio->mapping->host;
|
||||||
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
|
u64 valid = ni->i_valid;
|
||||||
|
u32 f_size = folio_size(folio);
|
||||||
|
loff_t f_pos = folio_pos(folio);
|
||||||
|
|
||||||
|
|
||||||
|
if (valid < f_pos + f_size) {
|
||||||
|
u32 z_from = valid <= f_pos ?
|
||||||
|
0 :
|
||||||
|
offset_in_folio(folio, valid);
|
||||||
|
/* The only thing ntfs_iomap_read_end_io used for. */
|
||||||
|
folio_zero_segment(folio, z_from, f_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
iomap_finish_folio_read(folio, fi.offset, fi.length, error);
|
||||||
|
}
|
||||||
|
bio_put(bio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copied from iomap/bio.c.
|
||||||
|
*/
|
||||||
|
static int ntfs_iomap_bio_read_folio_range(const struct iomap_iter *iter,
|
||||||
|
struct iomap_read_folio_ctx *ctx,
|
||||||
|
size_t plen)
|
||||||
|
{
|
||||||
|
struct folio *folio = ctx->cur_folio;
|
||||||
|
const struct iomap *iomap = &iter->iomap;
|
||||||
|
loff_t pos = iter->pos;
|
||||||
|
size_t poff = offset_in_folio(folio, pos);
|
||||||
|
loff_t length = iomap_length(iter);
|
||||||
|
sector_t sector;
|
||||||
|
struct bio *bio = ctx->read_ctx;
|
||||||
|
|
||||||
|
sector = iomap_sector(iomap, pos);
|
||||||
|
if (!bio || bio_end_sector(bio) != sector ||
|
||||||
|
!bio_add_folio(bio, folio, plen, poff)) {
|
||||||
|
gfp_t gfp = mapping_gfp_constraint(folio->mapping, GFP_KERNEL);
|
||||||
|
gfp_t orig_gfp = gfp;
|
||||||
|
unsigned int nr_vecs = DIV_ROUND_UP(length, PAGE_SIZE);
|
||||||
|
|
||||||
|
if (bio)
|
||||||
|
submit_bio(bio);
|
||||||
|
|
||||||
|
if (ctx->rac) /* same as readahead_gfp_mask */
|
||||||
|
gfp |= __GFP_NORETRY | __GFP_NOWARN;
|
||||||
|
bio = bio_alloc(iomap->bdev, bio_max_segs(nr_vecs), REQ_OP_READ,
|
||||||
|
gfp);
|
||||||
|
/*
|
||||||
|
* If the bio_alloc fails, try it again for a single page to
|
||||||
|
* avoid having to deal with partial page reads. This emulates
|
||||||
|
* what do_mpage_read_folio does.
|
||||||
|
*/
|
||||||
|
if (!bio)
|
||||||
|
bio = bio_alloc(iomap->bdev, 1, REQ_OP_READ, orig_gfp);
|
||||||
|
if (ctx->rac)
|
||||||
|
bio->bi_opf |= REQ_RAHEAD;
|
||||||
|
bio->bi_iter.bi_sector = sector;
|
||||||
|
bio->bi_end_io = ntfs_iomap_read_end_io;
|
||||||
|
bio_add_folio_nofail(bio, folio, plen, poff);
|
||||||
|
ctx->read_ctx = bio;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ntfs_iomap_bio_submit_read(struct iomap_read_folio_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct bio *bio = ctx->read_ctx;
|
||||||
|
|
||||||
|
if (bio)
|
||||||
|
submit_bio(bio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iomap_read_ops ntfs_iomap_bio_read_ops = {
|
||||||
|
.read_folio_range = ntfs_iomap_bio_read_folio_range,
|
||||||
|
.submit_read = ntfs_iomap_bio_submit_read,
|
||||||
|
};
|
||||||
|
|
||||||
static int ntfs_read_folio(struct file *file, struct folio *folio)
|
static int ntfs_read_folio(struct file *file, struct folio *folio)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -724,6 +652,10 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
loff_t vbo = folio_pos(folio);
|
loff_t vbo = folio_pos(folio);
|
||||||
|
struct iomap_read_folio_ctx ctx = {
|
||||||
|
.cur_folio = folio,
|
||||||
|
.ops = &ntfs_iomap_bio_read_ops,
|
||||||
|
};
|
||||||
|
|
||||||
if (unlikely(is_bad_ni(ni))) {
|
if (unlikely(is_bad_ni(ni))) {
|
||||||
folio_unlock(folio);
|
folio_unlock(folio);
|
||||||
|
|
@ -737,24 +669,14 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_resident(ni)) {
|
|
||||||
ni_lock(ni);
|
|
||||||
err = attr_data_read_resident(ni, folio);
|
|
||||||
ni_unlock(ni);
|
|
||||||
if (err != E_NTFS_NONRESIDENT) {
|
|
||||||
folio_unlock(folio);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_compressed(ni)) {
|
if (is_compressed(ni)) {
|
||||||
/* ni_lock is taken inside ni_read_folio_cmpr after page locks */
|
/* ni_lock is taken inside ni_read_folio_cmpr after page locks */
|
||||||
err = ni_read_folio_cmpr(ni, folio);
|
err = ni_read_folio_cmpr(ni, folio);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Normal + sparse files. */
|
iomap_read_folio(&ntfs_iomap_ops, &ctx);
|
||||||
return mpage_read_folio(folio, ntfs_get_block);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ntfs_readahead(struct readahead_control *rac)
|
static void ntfs_readahead(struct readahead_control *rac)
|
||||||
|
|
@ -762,8 +684,10 @@ static void ntfs_readahead(struct readahead_control *rac)
|
||||||
struct address_space *mapping = rac->mapping;
|
struct address_space *mapping = rac->mapping;
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
u64 valid;
|
struct iomap_read_folio_ctx ctx = {
|
||||||
loff_t pos;
|
.ops = &ntfs_iomap_bio_read_ops,
|
||||||
|
.rac = rac,
|
||||||
|
};
|
||||||
|
|
||||||
if (is_resident(ni)) {
|
if (is_resident(ni)) {
|
||||||
/* No readahead for resident. */
|
/* No readahead for resident. */
|
||||||
|
|
@ -775,80 +699,7 @@ static void ntfs_readahead(struct readahead_control *rac)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = ni->i_valid;
|
iomap_readahead(&ntfs_iomap_ops, &ctx);
|
||||||
pos = readahead_pos(rac);
|
|
||||||
|
|
||||||
if (valid < i_size_read(inode) && pos <= valid &&
|
|
||||||
valid < pos + readahead_length(rac)) {
|
|
||||||
/* Range cross 'valid'. Read it page by page. */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mpage_readahead(rac, ntfs_get_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntfs_get_block_direct_IO_R(struct inode *inode, sector_t iblock,
|
|
||||||
struct buffer_head *bh_result, int create)
|
|
||||||
{
|
|
||||||
return ntfs_get_block_vbo(inode, (u64)iblock << inode->i_blkbits,
|
|
||||||
bh_result, create, GET_BLOCK_DIRECT_IO_R);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntfs_get_block_direct_IO_W(struct inode *inode, sector_t iblock,
|
|
||||||
struct buffer_head *bh_result, int create)
|
|
||||||
{
|
|
||||||
return ntfs_get_block_vbo(inode, (u64)iblock << inode->i_blkbits,
|
|
||||||
bh_result, create, GET_BLOCK_DIRECT_IO_W);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t ntfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
|
||||||
{
|
|
||||||
struct file *file = iocb->ki_filp;
|
|
||||||
struct address_space *mapping = file->f_mapping;
|
|
||||||
struct inode *inode = mapping->host;
|
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
|
||||||
loff_t vbo = iocb->ki_pos;
|
|
||||||
loff_t end;
|
|
||||||
int wr = iov_iter_rw(iter) & WRITE;
|
|
||||||
size_t iter_count = iov_iter_count(iter);
|
|
||||||
loff_t valid;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
if (is_resident(ni)) {
|
|
||||||
/* Switch to buffered write. */
|
|
||||||
ret = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (is_compressed(ni)) {
|
|
||||||
ret = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = blockdev_direct_IO(iocb, inode, iter,
|
|
||||||
wr ? ntfs_get_block_direct_IO_W :
|
|
||||||
ntfs_get_block_direct_IO_R);
|
|
||||||
|
|
||||||
if (ret > 0)
|
|
||||||
end = vbo + ret;
|
|
||||||
else if (wr && ret == -EIOCBQUEUED)
|
|
||||||
end = vbo + iter_count;
|
|
||||||
else
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
valid = ni->i_valid;
|
|
||||||
if (wr) {
|
|
||||||
if (end > valid && !S_ISBLK(inode->i_mode)) {
|
|
||||||
ni->i_valid = end;
|
|
||||||
mark_inode_dirty(inode);
|
|
||||||
}
|
|
||||||
} else if (vbo < valid && valid < end) {
|
|
||||||
/* Fix page. */
|
|
||||||
iov_iter_revert(iter, end - valid);
|
|
||||||
iov_iter_zero(end - valid, iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ntfs_set_size(struct inode *inode, u64 new_size)
|
int ntfs_set_size(struct inode *inode, u64 new_size)
|
||||||
|
|
@ -861,12 +712,10 @@ int ntfs_set_size(struct inode *inode, u64 new_size)
|
||||||
/* Check for maximum file size. */
|
/* Check for maximum file size. */
|
||||||
if (is_sparsed(ni) || is_compressed(ni)) {
|
if (is_sparsed(ni) || is_compressed(ni)) {
|
||||||
if (new_size > sbi->maxbytes_sparse) {
|
if (new_size > sbi->maxbytes_sparse) {
|
||||||
err = -EFBIG;
|
return -EFBIG;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
} else if (new_size > sbi->maxbytes) {
|
} else if (new_size > sbi->maxbytes) {
|
||||||
err = -EFBIG;
|
return -EFBIG;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ni_lock(ni);
|
ni_lock(ni);
|
||||||
|
|
@ -875,15 +724,256 @@ int ntfs_set_size(struct inode *inode, u64 new_size)
|
||||||
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
|
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
|
||||||
&ni->i_valid, true, NULL);
|
&ni->i_valid, true, NULL);
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
i_size_write(inode, new_size);
|
||||||
|
mark_inode_dirty(inode);
|
||||||
|
}
|
||||||
|
|
||||||
up_write(&ni->file.run_lock);
|
up_write(&ni->file.run_lock);
|
||||||
ni_unlock(ni);
|
ni_unlock(ni);
|
||||||
|
|
||||||
mark_inode_dirty(inode);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to get mapping vbo -> lbo.
|
||||||
|
* used with:
|
||||||
|
* - iomap_zero_range
|
||||||
|
* - iomap_truncate_page
|
||||||
|
* - iomap_dio_rw
|
||||||
|
* - iomap_file_buffered_write
|
||||||
|
* - iomap_bmap
|
||||||
|
* - iomap_fiemap
|
||||||
|
* - iomap_bio_read_folio
|
||||||
|
* - iomap_bio_readahead
|
||||||
|
*/
|
||||||
|
static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
|
||||||
|
unsigned int flags, struct iomap *iomap,
|
||||||
|
struct iomap *srcmap)
|
||||||
|
{
|
||||||
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
|
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||||
|
u8 cluster_bits = sbi->cluster_bits;
|
||||||
|
CLST vcn = offset >> cluster_bits;
|
||||||
|
u32 off = offset & sbi->cluster_mask;
|
||||||
|
bool rw = flags & IOMAP_WRITE;
|
||||||
|
loff_t endbyte = offset + length;
|
||||||
|
void *res = NULL;
|
||||||
|
int err;
|
||||||
|
CLST lcn, clen, clen_max;
|
||||||
|
bool new_clst = false;
|
||||||
|
if (unlikely(ntfs3_forced_shutdown(sbi->sb)))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if ((flags & IOMAP_REPORT) && offset > ntfs_get_maxbytes(ni)) {
|
||||||
|
/* called from fiemap/bmap. */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
clen_max = rw ? (bytes_to_cluster(sbi, endbyte) - vcn) : 1;
|
||||||
|
|
||||||
|
err = attr_data_get_block(
|
||||||
|
ni, vcn, clen_max, &lcn, &clen, rw ? &new_clst : NULL,
|
||||||
|
flags == IOMAP_WRITE && (off || (endbyte & sbi->cluster_mask)),
|
||||||
|
&res);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lcn == EOF_LCN) {
|
||||||
|
/* request out of file. */
|
||||||
|
if (flags & IOMAP_REPORT) {
|
||||||
|
/* special code for report. */
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rw) {
|
||||||
|
/* should never be here. */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
lcn = SPARSE_LCN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lcn == RESIDENT_LCN) {
|
||||||
|
if (offset >= clen) {
|
||||||
|
kfree(res);
|
||||||
|
if (flags & IOMAP_REPORT) {
|
||||||
|
/* special code for report. */
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
iomap->private = iomap->inline_data = res;
|
||||||
|
iomap->type = IOMAP_INLINE;
|
||||||
|
iomap->offset = 0;
|
||||||
|
iomap->length = clen; /* resident size in bytes. */
|
||||||
|
iomap->flags = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clen) {
|
||||||
|
/* broken file? */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lcn == COMPRESSED_LCN) {
|
||||||
|
/* should never be here. */
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
iomap->flags = new_clst ? IOMAP_F_NEW : 0;
|
||||||
|
iomap->bdev = inode->i_sb->s_bdev;
|
||||||
|
|
||||||
|
/* Translate clusters into bytes. */
|
||||||
|
iomap->offset = offset;
|
||||||
|
iomap->addr = ((loff_t)lcn << cluster_bits) + off;
|
||||||
|
iomap->length = ((loff_t)clen << cluster_bits) - off;
|
||||||
|
if (length && iomap->length > length)
|
||||||
|
iomap->length = length;
|
||||||
|
else
|
||||||
|
endbyte = offset + iomap->length;
|
||||||
|
|
||||||
|
if (lcn == SPARSE_LCN) {
|
||||||
|
iomap->addr = IOMAP_NULL_ADDR;
|
||||||
|
iomap->type = IOMAP_HOLE;
|
||||||
|
} else if (endbyte <= ni->i_valid) {
|
||||||
|
iomap->type = IOMAP_MAPPED;
|
||||||
|
} else if (offset < ni->i_valid) {
|
||||||
|
iomap->type = IOMAP_MAPPED;
|
||||||
|
if (flags & IOMAP_REPORT)
|
||||||
|
iomap->length = ni->i_valid - offset;
|
||||||
|
} else if (rw || (flags & IOMAP_ZERO)) {
|
||||||
|
iomap->type = IOMAP_MAPPED;
|
||||||
|
} else {
|
||||||
|
iomap->type = IOMAP_UNWRITTEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & IOMAP_ZERO) && iomap->type == IOMAP_MAPPED) {
|
||||||
|
/* Avoid too large requests. */
|
||||||
|
u32 tail;
|
||||||
|
u32 off_a = iomap->addr & (PAGE_SIZE - 1);
|
||||||
|
if (off_a)
|
||||||
|
tail = PAGE_SIZE - off_a;
|
||||||
|
else
|
||||||
|
tail = PAGE_SIZE;
|
||||||
|
|
||||||
|
if (iomap->length > tail)
|
||||||
|
iomap->length = tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ntfs_iomap_end(struct inode *inode, loff_t pos, loff_t length,
|
||||||
|
ssize_t written, unsigned int flags,
|
||||||
|
struct iomap *iomap)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
|
loff_t endbyte = pos + written;
|
||||||
|
|
||||||
|
if ((flags & IOMAP_WRITE) || (flags & IOMAP_ZERO)) {
|
||||||
|
if (iomap->type == IOMAP_INLINE) {
|
||||||
|
u32 data_size;
|
||||||
|
struct ATTRIB *attr;
|
||||||
|
struct mft_inode *mi;
|
||||||
|
|
||||||
|
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0,
|
||||||
|
NULL, &mi);
|
||||||
|
if (!attr || attr->non_res) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_size = le32_to_cpu(attr->res.data_size);
|
||||||
|
if (!(pos < data_size && endbyte <= data_size)) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update resident data. */
|
||||||
|
memcpy(resident_data(attr) + pos,
|
||||||
|
iomap_inline_data(iomap, pos), written);
|
||||||
|
mi->dirty = true;
|
||||||
|
ni->i_valid = data_size;
|
||||||
|
} else if (ni->i_valid < endbyte) {
|
||||||
|
ni->i_valid = endbyte;
|
||||||
|
mark_inode_dirty(inode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & IOMAP_ZERO) && iomap->type == IOMAP_MAPPED) {
|
||||||
|
balance_dirty_pages_ratelimited(inode->i_mapping);
|
||||||
|
cond_resched();
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (iomap->type == IOMAP_INLINE) {
|
||||||
|
kfree(iomap->private);
|
||||||
|
iomap->private = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* write_begin + put_folio + write_end.
|
||||||
|
* iomap_zero_range
|
||||||
|
* iomap_truncate_page
|
||||||
|
* iomap_file_buffered_write
|
||||||
|
*/
|
||||||
|
static void ntfs_iomap_put_folio(struct inode *inode, loff_t pos,
|
||||||
|
unsigned int len, struct folio *folio)
|
||||||
|
{
|
||||||
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
|
loff_t end = pos + len;
|
||||||
|
u32 f_size = folio_size(folio);
|
||||||
|
loff_t f_pos = folio_pos(folio);
|
||||||
|
loff_t f_end = f_pos + f_size;
|
||||||
|
|
||||||
|
if (ni->i_valid < end && end < f_end) {
|
||||||
|
/* zero range [end - f_end). */
|
||||||
|
/* The only thing ntfs_iomap_put_folio used for. */
|
||||||
|
folio_zero_segment(folio, offset_in_folio(folio, end), f_size);
|
||||||
|
}
|
||||||
|
folio_unlock(folio);
|
||||||
|
folio_put(folio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ntfs_writeback_range(struct iomap_writepage_ctx *wpc,
|
||||||
|
struct folio *folio, u64 offset,
|
||||||
|
unsigned int len, u64 end_pos)
|
||||||
|
{
|
||||||
|
struct iomap *iomap = &wpc->iomap;
|
||||||
|
struct inode *inode = wpc->inode;
|
||||||
|
|
||||||
|
/* Check iomap position. */
|
||||||
|
if (!(iomap->offset <= offset &&
|
||||||
|
offset < iomap->offset + iomap->length)) {
|
||||||
|
int err;
|
||||||
|
struct ntfs_sb_info *sbi = ntfs_sb(inode->i_sb);
|
||||||
|
loff_t i_size_up = ntfs_up_cluster(sbi, inode->i_size);
|
||||||
|
loff_t len_max = i_size_up - offset;
|
||||||
|
|
||||||
|
err = ntfs_iomap_begin(inode, offset, len_max, IOMAP_WRITE,
|
||||||
|
iomap, NULL);
|
||||||
|
if (err) {
|
||||||
|
ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iomap_add_to_ioend(wpc, folio, offset, end_pos, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const struct iomap_writeback_ops ntfs_writeback_ops = {
|
||||||
|
.writeback_range = ntfs_writeback_range,
|
||||||
|
.writeback_submit = iomap_ioend_writeback_submit,
|
||||||
|
};
|
||||||
|
|
||||||
static int ntfs_resident_writepage(struct folio *folio,
|
static int ntfs_resident_writepage(struct folio *folio,
|
||||||
struct writeback_control *wbc)
|
struct writeback_control *wbc)
|
||||||
{
|
{
|
||||||
|
|
@ -911,40 +1001,15 @@ static int ntfs_resident_writepage(struct folio *folio,
|
||||||
|
|
||||||
static int ntfs_writepages(struct address_space *mapping,
|
static int ntfs_writepages(struct address_space *mapping,
|
||||||
struct writeback_control *wbc)
|
struct writeback_control *wbc)
|
||||||
{
|
|
||||||
struct inode *inode = mapping->host;
|
|
||||||
|
|
||||||
/* Avoid any operation if inode is bad. */
|
|
||||||
if (unlikely(is_bad_ni(ntfs_i(inode))))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
if (is_resident(ntfs_i(inode))) {
|
|
||||||
struct folio *folio = NULL;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
while ((folio = writeback_iter(mapping, wbc, folio, &error)))
|
|
||||||
error = ntfs_resident_writepage(folio, wbc);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
return mpage_writepages(mapping, wbc, ntfs_get_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntfs_get_block_write_begin(struct inode *inode, sector_t vbn,
|
|
||||||
struct buffer_head *bh_result, int create)
|
|
||||||
{
|
|
||||||
return ntfs_get_block_vbo(inode, (u64)vbn << inode->i_blkbits,
|
|
||||||
bh_result, create, GET_BLOCK_WRITE_BEGIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ntfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
|
|
||||||
loff_t pos, u32 len, struct folio **foliop, void **fsdata)
|
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
struct ntfs_inode *ni = ntfs_i(inode);
|
||||||
|
struct iomap_writepage_ctx wpc = {
|
||||||
|
.inode = mapping->host,
|
||||||
|
.wbc = wbc,
|
||||||
|
.ops = &ntfs_writeback_ops,
|
||||||
|
};
|
||||||
|
|
||||||
/* Avoid any operation if inode is bad. */
|
/* Avoid any operation if inode is bad. */
|
||||||
if (unlikely(is_bad_ni(ni)))
|
if (unlikely(is_bad_ni(ni)))
|
||||||
|
|
@ -954,100 +1019,15 @@ int ntfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (is_resident(ni)) {
|
if (is_resident(ni)) {
|
||||||
struct folio *folio = __filemap_get_folio(
|
struct folio *folio;
|
||||||
mapping, pos >> PAGE_SHIFT, FGP_WRITEBEGIN,
|
|
||||||
mapping_gfp_mask(mapping));
|
|
||||||
|
|
||||||
if (IS_ERR(folio)) {
|
while ((folio = writeback_iter(mapping, wbc, folio, &err)))
|
||||||
err = PTR_ERR(folio);
|
err = ntfs_resident_writepage(folio, wbc);
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ni_lock(ni);
|
return err;
|
||||||
err = attr_data_read_resident(ni, folio);
|
|
||||||
ni_unlock(ni);
|
|
||||||
|
|
||||||
if (!err) {
|
|
||||||
*foliop = folio;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
folio_unlock(folio);
|
|
||||||
folio_put(folio);
|
|
||||||
|
|
||||||
if (err != E_NTFS_NONRESIDENT)
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = block_write_begin(mapping, pos, len, foliop,
|
return iomap_writepages(&wpc);
|
||||||
ntfs_get_block_write_begin);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ntfs_write_end - Address_space_operations::write_end.
|
|
||||||
*/
|
|
||||||
int ntfs_write_end(const struct kiocb *iocb, struct address_space *mapping,
|
|
||||||
loff_t pos, u32 len, u32 copied, struct folio *folio,
|
|
||||||
void *fsdata)
|
|
||||||
{
|
|
||||||
struct inode *inode = mapping->host;
|
|
||||||
struct ntfs_inode *ni = ntfs_i(inode);
|
|
||||||
u64 valid = ni->i_valid;
|
|
||||||
bool dirty = false;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (is_resident(ni)) {
|
|
||||||
ni_lock(ni);
|
|
||||||
err = attr_data_write_resident(ni, folio);
|
|
||||||
ni_unlock(ni);
|
|
||||||
if (!err) {
|
|
||||||
struct buffer_head *head = folio_buffers(folio);
|
|
||||||
dirty = true;
|
|
||||||
/* Clear any buffers in folio. */
|
|
||||||
if (head) {
|
|
||||||
struct buffer_head *bh = head;
|
|
||||||
|
|
||||||
do {
|
|
||||||
clear_buffer_dirty(bh);
|
|
||||||
clear_buffer_mapped(bh);
|
|
||||||
set_buffer_uptodate(bh);
|
|
||||||
} while (head != (bh = bh->b_this_page));
|
|
||||||
}
|
|
||||||
folio_mark_uptodate(folio);
|
|
||||||
err = copied;
|
|
||||||
}
|
|
||||||
folio_unlock(folio);
|
|
||||||
folio_put(folio);
|
|
||||||
} else {
|
|
||||||
err = generic_write_end(iocb, mapping, pos, len, copied, folio,
|
|
||||||
fsdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err >= 0) {
|
|
||||||
if (!(ni->std_fa & FILE_ATTRIBUTE_ARCHIVE)) {
|
|
||||||
inode_set_mtime_to_ts(inode,
|
|
||||||
inode_set_ctime_current(inode));
|
|
||||||
ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid != ni->i_valid) {
|
|
||||||
/* ni->i_valid is changed in ntfs_get_block_vbo. */
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos + err > inode->i_size) {
|
|
||||||
i_size_write(inode, pos + err);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirty)
|
|
||||||
mark_inode_dirty(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc)
|
int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||||
|
|
@ -1062,6 +1042,7 @@ int ntfs_sync_inode(struct inode *inode)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper function to read file.
|
* Helper function to read file.
|
||||||
|
* Used to read $AttrDef and $UpCase
|
||||||
*/
|
*/
|
||||||
int inode_read_data(struct inode *inode, void *data, size_t bytes)
|
int inode_read_data(struct inode *inode, void *data, size_t bytes)
|
||||||
{
|
{
|
||||||
|
|
@ -2107,18 +2088,26 @@ const struct address_space_operations ntfs_aops = {
|
||||||
.read_folio = ntfs_read_folio,
|
.read_folio = ntfs_read_folio,
|
||||||
.readahead = ntfs_readahead,
|
.readahead = ntfs_readahead,
|
||||||
.writepages = ntfs_writepages,
|
.writepages = ntfs_writepages,
|
||||||
.write_begin = ntfs_write_begin,
|
|
||||||
.write_end = ntfs_write_end,
|
|
||||||
.direct_IO = ntfs_direct_IO,
|
|
||||||
.bmap = ntfs_bmap,
|
.bmap = ntfs_bmap,
|
||||||
.dirty_folio = block_dirty_folio,
|
.dirty_folio = iomap_dirty_folio,
|
||||||
.migrate_folio = buffer_migrate_folio,
|
.migrate_folio = filemap_migrate_folio,
|
||||||
.invalidate_folio = block_invalidate_folio,
|
.release_folio = iomap_release_folio,
|
||||||
|
.invalidate_folio = iomap_invalidate_folio,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct address_space_operations ntfs_aops_cmpr = {
|
const struct address_space_operations ntfs_aops_cmpr = {
|
||||||
.read_folio = ntfs_read_folio,
|
.read_folio = ntfs_read_folio,
|
||||||
.dirty_folio = block_dirty_folio,
|
.dirty_folio = iomap_dirty_folio,
|
||||||
.direct_IO = ntfs_direct_IO,
|
.release_folio = iomap_release_folio,
|
||||||
|
.invalidate_folio = iomap_invalidate_folio,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct iomap_ops ntfs_iomap_ops = {
|
||||||
|
.iomap_begin = ntfs_iomap_begin,
|
||||||
|
.iomap_end = ntfs_iomap_end,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct iomap_write_ops ntfs_iomap_folio_ops = {
|
||||||
|
.put_folio = ntfs_iomap_put_folio,
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
|
||||||
|
|
@ -442,8 +442,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||||
u64 new_size, const u64 *new_valid, bool keep_prealloc,
|
u64 new_size, const u64 *new_valid, bool keep_prealloc,
|
||||||
struct ATTRIB **ret);
|
struct ATTRIB **ret);
|
||||||
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
||||||
CLST *len, bool *new, bool zero);
|
CLST *len, bool *new, bool zero, void **res);
|
||||||
int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio);
|
|
||||||
int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio);
|
int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio);
|
||||||
int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||||
const __le16 *name, u8 name_len, struct runs_tree *run,
|
const __le16 *name, u8 name_len, struct runs_tree *run,
|
||||||
|
|
@ -568,8 +567,6 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
|
||||||
struct REPARSE_DATA_BUFFER *buffer);
|
struct REPARSE_DATA_BUFFER *buffer);
|
||||||
int ni_write_inode(struct inode *inode, int sync, const char *hint);
|
int ni_write_inode(struct inode *inode, int sync, const char *hint);
|
||||||
#define _ni_write_inode(i, w) ni_write_inode(i, w, __func__)
|
#define _ni_write_inode(i, w) ni_write_inode(i, w, __func__)
|
||||||
int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
|
|
||||||
__u64 vbo, __u64 len);
|
|
||||||
int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio);
|
int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio);
|
||||||
int ni_decompress_file(struct ntfs_inode *ni);
|
int ni_decompress_file(struct ntfs_inode *ni);
|
||||||
int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
|
int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
|
||||||
|
|
@ -614,7 +611,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft,
|
||||||
void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno, bool is_mft);
|
void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno, bool is_mft);
|
||||||
int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to);
|
int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to);
|
||||||
int ntfs_refresh_zone(struct ntfs_sb_info *sbi);
|
int ntfs_refresh_zone(struct ntfs_sb_info *sbi);
|
||||||
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait);
|
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi);
|
||||||
void ntfs_bad_inode(struct inode *inode, const char *hint);
|
void ntfs_bad_inode(struct inode *inode, const char *hint);
|
||||||
#define _ntfs_bad_inode(i) ntfs_bad_inode(i, __func__)
|
#define _ntfs_bad_inode(i) ntfs_bad_inode(i, __func__)
|
||||||
enum NTFS_DIRTY_FLAGS {
|
enum NTFS_DIRTY_FLAGS {
|
||||||
|
|
@ -745,13 +742,6 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi,
|
||||||
struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref,
|
struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref,
|
||||||
const struct cpu_str *name);
|
const struct cpu_str *name);
|
||||||
int ntfs_set_size(struct inode *inode, u64 new_size);
|
int ntfs_set_size(struct inode *inode, u64 new_size);
|
||||||
int ntfs_get_block(struct inode *inode, sector_t vbn,
|
|
||||||
struct buffer_head *bh_result, int create);
|
|
||||||
int ntfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
|
|
||||||
loff_t pos, u32 len, struct folio **foliop, void **fsdata);
|
|
||||||
int ntfs_write_end(const struct kiocb *iocb, struct address_space *mapping,
|
|
||||||
loff_t pos, u32 len, u32 copied, struct folio *folio,
|
|
||||||
void *fsdata);
|
|
||||||
int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc);
|
int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc);
|
||||||
int ntfs_sync_inode(struct inode *inode);
|
int ntfs_sync_inode(struct inode *inode);
|
||||||
int inode_read_data(struct inode *inode, void *data, size_t bytes);
|
int inode_read_data(struct inode *inode, void *data, size_t bytes);
|
||||||
|
|
@ -762,6 +752,8 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
int ntfs_link_inode(struct inode *inode, struct dentry *dentry);
|
int ntfs_link_inode(struct inode *inode, struct dentry *dentry);
|
||||||
int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry);
|
int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry);
|
||||||
void ntfs_evict_inode(struct inode *inode);
|
void ntfs_evict_inode(struct inode *inode);
|
||||||
|
extern const struct iomap_ops ntfs_iomap_ops;
|
||||||
|
extern const struct iomap_write_ops ntfs_iomap_folio_ops;
|
||||||
extern const struct inode_operations ntfs_link_inode_operations;
|
extern const struct inode_operations ntfs_link_inode_operations;
|
||||||
extern const struct address_space_operations ntfs_aops;
|
extern const struct address_space_operations ntfs_aops;
|
||||||
extern const struct address_space_operations ntfs_aops_cmpr;
|
extern const struct address_space_operations ntfs_aops_cmpr;
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,9 @@
|
||||||
#include <linux/buffer_head.h>
|
#include <linux/buffer_head.h>
|
||||||
#include <linux/exportfs.h>
|
#include <linux/exportfs.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/fs_struct.h>
|
|
||||||
#include <linux/fs_context.h>
|
#include <linux/fs_context.h>
|
||||||
#include <linux/fs_parser.h>
|
#include <linux/fs_parser.h>
|
||||||
|
#include <linux/fs_struct.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/minmax.h>
|
#include <linux/minmax.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
@ -674,7 +674,7 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)
|
||||||
sbi->volume.ni = NULL;
|
sbi->volume.ni = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ntfs_update_mftmirr(sbi, 0);
|
ntfs_update_mftmirr(sbi);
|
||||||
|
|
||||||
indx_clear(&sbi->security.index_sii);
|
indx_clear(&sbi->security.index_sii);
|
||||||
indx_clear(&sbi->security.index_sdh);
|
indx_clear(&sbi->security.index_sdh);
|
||||||
|
|
@ -821,7 +821,12 @@ static int ntfs_sync_fs(struct super_block *sb, int wait)
|
||||||
if (!err)
|
if (!err)
|
||||||
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
|
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
|
||||||
|
|
||||||
ntfs_update_mftmirr(sbi, wait);
|
ntfs_update_mftmirr(sbi);
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
sync_blockdev(sb->s_bdev);
|
||||||
|
blkdev_issue_flush(sb->s_bdev);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user