mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'ntfs3_for_7.0' of https://github.com/Paragon-Software-Group/linux-ntfs3
Pull ntfs3 updates from Konstantin Komarov: "New code: - improve readahead for bitmap initialization and large directory scans - fsync files by syncing parent inodes - drop of preallocated clusters for sparse and compressed files - zero-fill folios beyond i_valid in ntfs_read_folio() - implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs - implement iomap-based file operations - allow explicit boolean acl/prealloc mount options - fall-through between switch labels - delayed-allocation (delalloc) support Fixes: - check return value of indx_find to avoid infinite loop - initialize new folios before use - infinite loop in attr_load_runs_range on inconsistent metadata - infinite loop triggered by zero-sized ATTR_LIST - ntfs_mount_options leak in ntfs_fill_super() - deadlock in ni_read_folio_cmpr - circular locking dependency in run_unpack_ex - prevent infinite loops caused by the next valid being the same - restore NULL folio initialization in ntfs_writepages() - slab-out-of-bounds read in DeleteIndexEntryRoot Updates: - allow readdir() to finish after directory mutations without rewinddir() - handle attr_set_size() errors when truncating files - make ntfs_writeback_ops static - refactor duplicate kmemdup pattern in do_action() - avoid calling run_get_entry() when run == NULL in ntfs_read_run_nb_ra() Replaced: - use wait_on_buffer() directly - rename ni_readpage_cmpr into ni_read_folio_cmpr" * tag 'ntfs3_for_7.0' of https://github.com/Paragon-Software-Group/linux-ntfs3: (26 commits) fs/ntfs3: add delayed-allocation (delalloc) support fs/ntfs3: avoid calling run_get_entry() when run == NULL in ntfs_read_run_nb_ra() fs/ntfs3: add fall-through between switch labels fs/ntfs3: allow explicit boolean acl/prealloc mount options fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot ntfs3: Restore NULL folio initialization in ntfs_writepages() ntfs3: Refactor duplicate kmemdup pattern in do_action() fs/ntfs3: prevent infinite loops caused by the next valid being the same fs/ntfs3: make ntfs_writeback_ops static ntfs3: fix circular locking dependency in run_unpack_ex fs/ntfs3: implement iomap-based file operations fs/ntfs3: fix deadlock in ni_read_folio_cmpr fs/ntfs3: implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs fs/ntfs3: zero-fill folios beyond i_valid in ntfs_read_folio() fs/ntfs3: handle attr_set_size() errors when truncating files fs/ntfs3: drop preallocated clusters for sparse and compressed files fs/ntfs3: fsync files by syncing parent inodes fs/ntfs3: fix ntfs_mount_options leak in ntfs_fill_super() fs/ntfs3: allow readdir() to finish after directory mutations without rewinddir() fs/ntfs3: improve readahead for bitmap initialization and large directory scans ...
This commit is contained in:
+282
-130
File diff suppressed because it is too large
Load Diff
+13
-4
@@ -52,6 +52,11 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
|
||||
|
||||
if (!attr->non_res) {
|
||||
lsize = le32_to_cpu(attr->res.data_size);
|
||||
if (!lsize) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* attr is resident: lsize < record_size (1K or 4K) */
|
||||
le = kvmalloc(al_aligned(lsize), GFP_KERNEL);
|
||||
if (!le) {
|
||||
@@ -66,6 +71,10 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
|
||||
u16 run_off = le16_to_cpu(attr->nres.run_off);
|
||||
|
||||
lsize = le64_to_cpu(attr->nres.data_size);
|
||||
if (!lsize) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
run_init(&ni->attr_list.run);
|
||||
|
||||
@@ -336,8 +345,8 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
|
||||
le->id = id;
|
||||
memcpy(le->name, name, sizeof(short) * name_len);
|
||||
|
||||
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
|
||||
&new_size, true, &attr);
|
||||
err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
|
||||
&new_size, true, &attr, false);
|
||||
if (err) {
|
||||
/* Undo memmove above. */
|
||||
memmove(le, Add2Ptr(le, sz), old_size - off);
|
||||
@@ -395,8 +404,8 @@ int al_update(struct ntfs_inode *ni, int sync)
|
||||
* Attribute list increased on demand in al_add_le.
|
||||
* Attribute list decreased here.
|
||||
*/
|
||||
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
|
||||
false, &attr);
|
||||
err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
|
||||
false, &attr, false);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -508,6 +508,8 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
|
||||
size_t wpos, wbit, iw, vbo;
|
||||
struct buffer_head *bh = NULL;
|
||||
CLST lcn, clen;
|
||||
struct file_ra_state *ra;
|
||||
struct address_space *mapping = sb->s_bdev->bd_mapping;
|
||||
|
||||
wnd->uptodated = 0;
|
||||
wnd->extent_max = 0;
|
||||
@@ -516,6 +518,13 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
|
||||
|
||||
vbo = 0;
|
||||
|
||||
/* Allocate in memory instead of stack. Not critical if failed. */
|
||||
ra = kzalloc(sizeof(*ra), GFP_NOFS);
|
||||
if (ra) {
|
||||
file_ra_state_init(ra, mapping);
|
||||
ra->ra_pages = (wnd->nbits / 8 + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
for (iw = 0; iw < wnd->nwnd; iw++) {
|
||||
if (iw + 1 == wnd->nwnd)
|
||||
wbits = wnd->bits_last;
|
||||
@@ -552,6 +561,13 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
|
||||
len = ((u64)clen << cluster_bits) - off;
|
||||
}
|
||||
|
||||
if (ra) {
|
||||
pgoff_t idx = lbo >> PAGE_SHIFT;
|
||||
if (!ra_has_index(ra, idx))
|
||||
page_cache_sync_readahead(mapping, ra, NULL,
|
||||
idx, 1);
|
||||
}
|
||||
|
||||
bh = ntfs_bread(sb, lbo >> sb->s_blocksize_bits);
|
||||
if (!bh) {
|
||||
err = -EIO;
|
||||
@@ -638,6 +654,7 @@ next_wnd:
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(ra);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+76
-32
@@ -393,33 +393,77 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
|
||||
* ntfs_readdir - file_operations::iterate_shared
|
||||
*
|
||||
* Use non sorted enumeration.
|
||||
* We have an example of broken volume where sorted enumeration
|
||||
* counts each name twice.
|
||||
* Sorted enumeration may result infinite loop if names tree contains loop.
|
||||
*/
|
||||
static int ntfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
const struct INDEX_ROOT *root;
|
||||
u64 vbo;
|
||||
size_t bit;
|
||||
loff_t eod;
|
||||
int err = 0;
|
||||
struct inode *dir = file_inode(file);
|
||||
struct ntfs_inode *ni = ntfs_i(dir);
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
loff_t i_size = i_size_read(dir);
|
||||
u32 pos = ctx->pos;
|
||||
u64 pos = ctx->pos;
|
||||
u8 *name = NULL;
|
||||
struct indx_node *node = NULL;
|
||||
u8 index_bits = ni->dir.index_bits;
|
||||
size_t max_bit = i_size >> ni->dir.index_bits;
|
||||
loff_t eod = i_size + sbi->record_size;
|
||||
|
||||
/* Name is a buffer of PATH_MAX length. */
|
||||
static_assert(NTFS_NAME_LEN * 4 < PATH_MAX);
|
||||
|
||||
eod = i_size + sbi->record_size;
|
||||
if (!pos) {
|
||||
/*
|
||||
* ni->dir.version increments each directory change.
|
||||
* Save the initial value of ni->dir.version.
|
||||
*/
|
||||
file->private_data = (void *)ni->dir.version;
|
||||
}
|
||||
|
||||
if (pos >= eod)
|
||||
return 0;
|
||||
if (pos >= eod) {
|
||||
if (file->private_data == (void *)ni->dir.version) {
|
||||
/* No changes since first readdir. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle directories that changed after the initial readdir().
|
||||
*
|
||||
* Some user space code implements recursive removal like this instead
|
||||
* of calling rmdir(2) directly:
|
||||
*
|
||||
* fd = opendir(path);
|
||||
* while ((dent = readdir(fd)))
|
||||
* unlinkat(dirfd(fd), dent->d_name, 0);
|
||||
* closedir(fd);
|
||||
*
|
||||
* POSIX leaves unspecified what readdir() should return once the
|
||||
* directory has been modified after opendir()/rewinddir(), so this
|
||||
* pattern is not guaranteed to work on all filesystems or platforms.
|
||||
*
|
||||
* In ntfs3 the internal name tree may be reshaped while entries are
|
||||
* being removed, so there is no stable anchor for continuing a
|
||||
* single-pass walk based on the original readdir() order.
|
||||
*
|
||||
* In practice some widely used tools (for example certain rm(1)
|
||||
* implementations) have used this readdir()/unlink() loop, and some
|
||||
* filesystems behave in a way that effectively makes it work in the
|
||||
* common case.
|
||||
*
|
||||
* The code below follows that practice and tries to provide
|
||||
* "rmdir-like" behaviour for such callers on ntfs3, even though the
|
||||
* situation is not strictly defined by the APIs.
|
||||
*
|
||||
* Apple documents the same readdir()/unlink() issue and a workaround
|
||||
* for HFS file systems in:
|
||||
* https://web.archive.org/web/20220122122948/https:/support.apple.com/kb/TA21420?locale=en_US
|
||||
*/
|
||||
ctx->pos = pos = 3;
|
||||
file->private_data = (void *)ni->dir.version;
|
||||
}
|
||||
|
||||
if (!dir_emit_dots(file, ctx))
|
||||
return 0;
|
||||
@@ -454,58 +498,58 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
if (pos >= sbi->record_size) {
|
||||
bit = (pos - sbi->record_size) >> index_bits;
|
||||
} else {
|
||||
/*
|
||||
* Add each name from root in 'ctx'.
|
||||
*/
|
||||
err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
|
||||
if (err)
|
||||
goto out;
|
||||
bit = 0;
|
||||
}
|
||||
|
||||
if (!i_size) {
|
||||
ctx->pos = eod;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
vbo = (u64)bit << index_bits;
|
||||
if (vbo >= i_size) {
|
||||
ctx->pos = eod;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumerate indexes until the end of dir.
|
||||
*/
|
||||
for (; bit < max_bit; bit += 1) {
|
||||
/* Get the next used index. */
|
||||
err = indx_used_bit(&ni->dir, ni, &bit);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (bit == MINUS_ONE_T) {
|
||||
ctx->pos = eod;
|
||||
goto out;
|
||||
/* no more used indexes. end of dir. */
|
||||
break;
|
||||
}
|
||||
|
||||
vbo = (u64)bit << index_bits;
|
||||
if (vbo >= i_size) {
|
||||
if (bit >= max_bit) {
|
||||
/* Corrupted directory. */
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
|
||||
&node);
|
||||
err = indx_read_ra(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
|
||||
&node, &file->f_ra);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Add each name from index in 'ctx'.
|
||||
*/
|
||||
err = ntfs_read_hdr(sbi, ni, &node->index->ihdr,
|
||||
vbo + sbi->record_size, pos, name, ctx);
|
||||
((u64)bit << index_bits) + sbi->record_size,
|
||||
pos, name, ctx);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
bit += 1;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
kfree(name);
|
||||
put_indx_node(node);
|
||||
|
||||
if (err == 1) {
|
||||
if (!err) {
|
||||
/* End of directory. */
|
||||
ctx->pos = eod;
|
||||
} else if (err == 1) {
|
||||
/* 'ctx' is full. */
|
||||
err = 0;
|
||||
} else if (err == -ENOENT) {
|
||||
@@ -624,7 +668,7 @@ const struct file_operations ntfs_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate_shared = ntfs_readdir,
|
||||
.fsync = generic_file_fsync,
|
||||
.fsync = ntfs_file_fsync,
|
||||
.open = ntfs_file_open,
|
||||
.unlocked_ioctl = ntfs_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
+344
-255
File diff suppressed because it is too large
Load Diff
+201
-181
@@ -123,6 +123,8 @@ void ni_clear(struct ntfs_inode *ni)
|
||||
indx_clear(&ni->dir);
|
||||
else {
|
||||
run_close(&ni->file.run);
|
||||
ntfs_sub_da(ni->mi.sbi, run_len(&ni->file.run_da));
|
||||
run_close(&ni->file.run_da);
|
||||
#ifdef CONFIG_NTFS3_LZX_XPRESS
|
||||
if (ni->file.offs_folio) {
|
||||
/* On-demand allocated page for offsets. */
|
||||
@@ -1850,183 +1852,11 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
|
||||
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,
|
||||
pgoff_t index, gfp_t gfp)
|
||||
pgoff_t index, gfp_t gfp)
|
||||
{
|
||||
struct folio *folio = __filemap_get_folio(mapping, index,
|
||||
FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
|
||||
struct folio *folio = __filemap_get_folio(
|
||||
mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
|
||||
struct page *page;
|
||||
|
||||
if (IS_ERR(folio))
|
||||
@@ -2046,18 +1876,18 @@ static struct page *ntfs_lock_new_page(struct address_space *mapping,
|
||||
}
|
||||
|
||||
/*
|
||||
* ni_readpage_cmpr
|
||||
* ni_read_folio_cmpr
|
||||
*
|
||||
* When decompressing, we typically obtain more than one page per reference.
|
||||
* We inject the additional pages into the page cache.
|
||||
*/
|
||||
int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
|
||||
int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
|
||||
{
|
||||
int err;
|
||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||
struct address_space *mapping = folio->mapping;
|
||||
pgoff_t index = folio->index;
|
||||
u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT;
|
||||
pgoff_t index;
|
||||
u64 frame_vbo, vbo = folio_pos(folio);
|
||||
struct page **pages = NULL; /* Array of at most 16 pages. stack? */
|
||||
u8 frame_bits;
|
||||
CLST frame;
|
||||
@@ -2107,7 +1937,9 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
|
||||
pages[i] = pg;
|
||||
}
|
||||
|
||||
ni_lock(ni);
|
||||
err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);
|
||||
ni_unlock(ni);
|
||||
|
||||
out1:
|
||||
for (i = 0; i < pages_per_frame; i++) {
|
||||
@@ -2184,7 +2016,8 @@ int ni_decompress_file(struct ntfs_inode *ni)
|
||||
|
||||
for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) {
|
||||
err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
|
||||
&clen, &new, false);
|
||||
&clen, &new, false, NULL,
|
||||
false);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
@@ -2405,7 +2238,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
|
||||
struct runs_tree *run = &ni->file.run;
|
||||
u64 valid_size = ni->i_valid;
|
||||
u64 vbo_disk;
|
||||
size_t unc_size;
|
||||
size_t unc_size = 0;
|
||||
u32 frame_size, i, ondisk_size;
|
||||
struct page *pg;
|
||||
struct ATTRIB *attr;
|
||||
@@ -3001,6 +2834,134 @@ bool ni_is_dirty(struct inode *inode)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* ni_seek_data_or_hole
|
||||
*
|
||||
* Helper function for ntfs_llseek( SEEK_DATA/SEEK_HOLE )
|
||||
*/
|
||||
loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data)
|
||||
{
|
||||
int err;
|
||||
u8 cluster_bits = ni->mi.sbi->cluster_bits;
|
||||
CLST vcn, lcn, clen;
|
||||
loff_t vbo;
|
||||
|
||||
/* Enumerate all fragments. */
|
||||
for (vcn = offset >> cluster_bits;; vcn += clen) {
|
||||
err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL, false,
|
||||
NULL, false);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (lcn == RESIDENT_LCN) {
|
||||
/* clen - resident size in bytes. clen == ni->vfs_inode.i_size */
|
||||
if (offset >= clen) {
|
||||
/* check eof. */
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
return clen;
|
||||
}
|
||||
|
||||
if (lcn == EOF_LCN) {
|
||||
if (data) {
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* implicit hole at the end of file. */
|
||||
return ni->vfs_inode.i_size;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
/*
|
||||
* Adjust the file offset to the next location in the file greater than
|
||||
* or equal to offset containing data. If offset points to data, then
|
||||
* the file offset is set to offset.
|
||||
*/
|
||||
if (lcn != SPARSE_LCN) {
|
||||
vbo = (u64)vcn << cluster_bits;
|
||||
return max(vbo, offset);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Adjust the file offset to the next hole in the file greater than or
|
||||
* equal to offset. If offset points into the middle of a hole, then the
|
||||
* file offset is set to offset. If there is no hole past offset, then the
|
||||
* file offset is adjusted to the end of the file
|
||||
* (i.e., there is an implicit hole at the end of any file).
|
||||
*/
|
||||
if (lcn == SPARSE_LCN &&
|
||||
/* native compression hole begins at aligned vcn. */
|
||||
(!(ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) ||
|
||||
!(vcn & (NTFS_LZNT_CLUSTERS - 1)))) {
|
||||
vbo = (u64)vcn << cluster_bits;
|
||||
return max(vbo, offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (!clen) {
|
||||
/* Corrupted file. */
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ni_write_parents
|
||||
*
|
||||
* Helper function for ntfs_file_fsync.
|
||||
*/
|
||||
int ni_write_parents(struct ntfs_inode *ni, int sync)
|
||||
{
|
||||
int err = 0;
|
||||
struct ATTRIB *attr = NULL;
|
||||
struct ATTR_LIST_ENTRY *le = NULL;
|
||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||
struct super_block *sb = sbi->sb;
|
||||
|
||||
while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
|
||||
NULL))) {
|
||||
struct inode *dir;
|
||||
struct ATTR_FILE_NAME *fname;
|
||||
|
||||
fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
|
||||
if (!fname)
|
||||
continue;
|
||||
|
||||
/* Check simple case when parent inode equals current inode. */
|
||||
if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
|
||||
if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
|
||||
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
|
||||
err = -EINVAL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
dir = ntfs_iget5(sb, &fname->home, NULL);
|
||||
if (IS_ERR(dir)) {
|
||||
ntfs_inode_warn(
|
||||
&ni->vfs_inode,
|
||||
"failed to open parent directory r=%lx to write",
|
||||
(long)ino_get(&fname->home));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_bad_inode(dir)) {
|
||||
int err2 = write_inode_now(dir, sync);
|
||||
if (!err)
|
||||
err = err2;
|
||||
}
|
||||
iput(dir);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ni_update_parent
|
||||
*
|
||||
@@ -3277,3 +3238,62 @@ out:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force to allocate all delay allocated clusters.
|
||||
*/
|
||||
int ni_allocate_da_blocks(struct ntfs_inode *ni)
|
||||
{
|
||||
int err;
|
||||
|
||||
ni_lock(ni);
|
||||
down_write(&ni->file.run_lock);
|
||||
|
||||
err = ni_allocate_da_blocks_locked(ni);
|
||||
|
||||
up_write(&ni->file.run_lock);
|
||||
ni_unlock(ni);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force to allocate all delay allocated clusters.
|
||||
*/
|
||||
int ni_allocate_da_blocks_locked(struct ntfs_inode *ni)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!ni->file.run_da.count)
|
||||
return 0;
|
||||
|
||||
if (is_sparsed(ni)) {
|
||||
CLST vcn, lcn, clen, alen;
|
||||
bool new;
|
||||
|
||||
/*
|
||||
* Sparse file allocates clusters in 'attr_data_get_block_locked'
|
||||
*/
|
||||
while (run_get_entry(&ni->file.run_da, 0, &vcn, &lcn, &clen)) {
|
||||
/* TODO: zero=true? */
|
||||
err = attr_data_get_block_locked(ni, vcn, clen, &lcn,
|
||||
&alen, &new, true,
|
||||
NULL, true);
|
||||
if (err)
|
||||
break;
|
||||
if (!new) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Normal file allocates clusters in 'attr_set_size'
|
||||
*/
|
||||
err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
|
||||
ni->vfs_inode.i_size, &ni->i_valid,
|
||||
false, NULL, true);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
+35
-30
@@ -1074,6 +1074,8 @@ struct ntfs_log {
|
||||
u32 client_undo_commit;
|
||||
|
||||
struct restart_info rst_info, rst_info2;
|
||||
|
||||
struct file_ra_state read_ahead;
|
||||
};
|
||||
|
||||
static inline u32 lsn_to_vbo(struct ntfs_log *log, const u64 lsn)
|
||||
@@ -1164,8 +1166,8 @@ static int read_log_page(struct ntfs_log *log, u32 vbo,
|
||||
|
||||
page_buf = page_off ? log->one_page_buf : *buffer;
|
||||
|
||||
err = ntfs_read_run_nb(ni->mi.sbi, &ni->file.run, page_vbo, page_buf,
|
||||
log->page_size, NULL);
|
||||
err = ntfs_read_run_nb_ra(ni->mi.sbi, &ni->file.run, page_vbo, page_buf,
|
||||
log->page_size, NULL, &log->read_ahead);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -3028,6 +3030,26 @@ static struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi,
|
||||
return attr;
|
||||
}
|
||||
|
||||
/*
|
||||
* update_oa_attr - Synchronize OpenAttr's attribute pointer with modified attribute
|
||||
* @oa2: OpenAttr structure in memory that needs to be updated
|
||||
* @attr: Modified attribute from MFT record to duplicate
|
||||
*
|
||||
* Returns true on success, false on allocation failure.
|
||||
*/
|
||||
static bool update_oa_attr(struct OpenAttr *oa2, struct ATTRIB *attr)
|
||||
{
|
||||
void *p2;
|
||||
|
||||
p2 = kmemdup(attr, le32_to_cpu(attr->size), GFP_NOFS);
|
||||
if (p2) {
|
||||
kfree(oa2->attr);
|
||||
oa2->attr = p2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_action - Common routine for the Redo and Undo Passes.
|
||||
* @rlsn: If it is NULL then undo.
|
||||
@@ -3251,15 +3273,8 @@ skip_load_parent:
|
||||
le16_add_cpu(&rec->hard_links, 1);
|
||||
|
||||
oa2 = find_loaded_attr(log, attr, rno_base);
|
||||
if (oa2) {
|
||||
void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
|
||||
GFP_NOFS);
|
||||
if (p2) {
|
||||
// run_close(oa2->run1);
|
||||
kfree(oa2->attr);
|
||||
oa2->attr = p2;
|
||||
}
|
||||
}
|
||||
if (oa2)
|
||||
update_oa_attr(oa2, attr);
|
||||
|
||||
mi->dirty = true;
|
||||
break;
|
||||
@@ -3318,16 +3333,8 @@ move_data:
|
||||
memmove(Add2Ptr(attr, aoff), data, dlen);
|
||||
|
||||
oa2 = find_loaded_attr(log, attr, rno_base);
|
||||
if (oa2) {
|
||||
void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
|
||||
GFP_NOFS);
|
||||
if (p2) {
|
||||
// run_close(&oa2->run0);
|
||||
oa2->run1 = &oa2->run0;
|
||||
kfree(oa2->attr);
|
||||
oa2->attr = p2;
|
||||
}
|
||||
}
|
||||
if (oa2 && update_oa_attr(oa2, attr))
|
||||
oa2->run1 = &oa2->run0;
|
||||
|
||||
mi->dirty = true;
|
||||
break;
|
||||
@@ -3377,14 +3384,9 @@ move_data:
|
||||
attr->nres.total_size = new_sz->total_size;
|
||||
|
||||
oa2 = find_loaded_attr(log, attr, rno_base);
|
||||
if (oa2) {
|
||||
void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
|
||||
GFP_NOFS);
|
||||
if (p2) {
|
||||
kfree(oa2->attr);
|
||||
oa2->attr = p2;
|
||||
}
|
||||
}
|
||||
if (oa2)
|
||||
update_oa_attr(oa2, attr);
|
||||
|
||||
mi->dirty = true;
|
||||
break;
|
||||
|
||||
@@ -3429,6 +3431,9 @@ move_data:
|
||||
|
||||
e1 = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
|
||||
esize = le16_to_cpu(e1->size);
|
||||
if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize)
|
||||
goto dirty_vol;
|
||||
|
||||
e2 = Add2Ptr(e1, esize);
|
||||
|
||||
memmove(e1, e2, PtrOffset(e2, Add2Ptr(hdr, used)));
|
||||
@@ -5128,7 +5133,7 @@ commit_undo:
|
||||
|
||||
undo_action_done:
|
||||
|
||||
ntfs_update_mftmirr(sbi, 0);
|
||||
ntfs_update_mftmirr(sbi);
|
||||
|
||||
sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY;
|
||||
|
||||
|
||||
+70
-42
@@ -445,36 +445,59 @@ up_write:
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_check_for_free_space
|
||||
* ntfs_check_free_space
|
||||
*
|
||||
* Check if it is possible to allocate 'clen' clusters and 'mlen' Mft records
|
||||
*/
|
||||
bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen)
|
||||
bool ntfs_check_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen,
|
||||
bool da)
|
||||
{
|
||||
size_t free, zlen, avail;
|
||||
struct wnd_bitmap *wnd;
|
||||
CLST da_clusters = ntfs_get_da(sbi);
|
||||
|
||||
wnd = &sbi->used.bitmap;
|
||||
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
|
||||
free = wnd_zeroes(wnd);
|
||||
|
||||
if (free >= da_clusters) {
|
||||
free -= da_clusters;
|
||||
} else {
|
||||
free = 0;
|
||||
}
|
||||
|
||||
zlen = min_t(size_t, NTFS_MIN_MFT_ZONE, wnd_zone_len(wnd));
|
||||
up_read(&wnd->rw_lock);
|
||||
|
||||
if (free < zlen + clen)
|
||||
if (free < zlen + clen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
avail = free - (zlen + clen);
|
||||
|
||||
wnd = &sbi->mft.bitmap;
|
||||
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT);
|
||||
free = wnd_zeroes(wnd);
|
||||
zlen = wnd_zone_len(wnd);
|
||||
up_read(&wnd->rw_lock);
|
||||
/*
|
||||
* When delalloc is active then keep in mind some reserved space.
|
||||
* The worst case: 1 mft record per each ~500 clusters.
|
||||
*/
|
||||
if (da) {
|
||||
/* 1 mft record per each 1024 clusters. */
|
||||
mlen += da_clusters >> 10;
|
||||
}
|
||||
|
||||
if (free >= zlen + mlen)
|
||||
return true;
|
||||
if (mlen || !avail) {
|
||||
wnd = &sbi->mft.bitmap;
|
||||
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT);
|
||||
free = wnd_zeroes(wnd);
|
||||
zlen = wnd_zone_len(wnd);
|
||||
up_read(&wnd->rw_lock);
|
||||
|
||||
return avail >= bytes_to_cluster(sbi, mlen << sbi->record_bits);
|
||||
if (free < zlen + mlen &&
|
||||
avail < bytes_to_cluster(sbi, mlen << sbi->record_bits)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -509,8 +532,8 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi)
|
||||
|
||||
/* Step 1: Resize $MFT::DATA. */
|
||||
down_write(&ni->file.run_lock);
|
||||
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run,
|
||||
new_mft_bytes, NULL, false, &attr);
|
||||
err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
|
||||
new_mft_bytes, NULL, false, &attr, false);
|
||||
|
||||
if (err) {
|
||||
up_write(&ni->file.run_lock);
|
||||
@@ -525,7 +548,7 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi)
|
||||
new_bitmap_bytes = ntfs3_bitmap_size(new_mft_total);
|
||||
|
||||
err = attr_set_size(ni, ATTR_BITMAP, NULL, 0, &sbi->mft.bitmap.run,
|
||||
new_bitmap_bytes, &new_bitmap_bytes, true, NULL);
|
||||
new_bitmap_bytes, &new_bitmap_bytes, true);
|
||||
|
||||
/* Refresh MFT Zone if necessary. */
|
||||
down_write_nested(&sbi->used.bitmap.rw_lock, BITMAP_MUTEX_CLUSTERS);
|
||||
@@ -843,9 +866,8 @@ int ntfs_refresh_zone(struct ntfs_sb_info *sbi)
|
||||
/*
|
||||
* 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;
|
||||
u32 blocksize, bytes;
|
||||
sector_t block1, block2;
|
||||
@@ -875,9 +897,7 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer_locked(bh2))
|
||||
__wait_on_buffer(bh2);
|
||||
|
||||
wait_on_buffer(bh2);
|
||||
lock_buffer(bh2);
|
||||
memcpy(bh2->b_data, bh1->b_data, blocksize);
|
||||
set_buffer_uptodate(bh2);
|
||||
@@ -886,12 +906,7 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
|
||||
|
||||
put_bh(bh1);
|
||||
bh1 = NULL;
|
||||
|
||||
err = wait ? sync_dirty_buffer(bh2) : 0;
|
||||
|
||||
put_bh(bh2);
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
|
||||
sbi->flags &= ~NTFS_FLAGS_MFTMIRR;
|
||||
@@ -1069,9 +1084,7 @@ int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (buffer_locked(bh))
|
||||
__wait_on_buffer(bh);
|
||||
|
||||
wait_on_buffer(bh);
|
||||
lock_buffer(bh);
|
||||
if (buf) {
|
||||
memcpy(bh->b_data + off, buf, op);
|
||||
@@ -1168,11 +1181,13 @@ struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi,
|
||||
return ntfs_bread(sb, lbo >> sb->s_blocksize_bits);
|
||||
}
|
||||
|
||||
int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb)
|
||||
int ntfs_read_run_nb_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb,
|
||||
struct file_ra_state *ra)
|
||||
{
|
||||
int err;
|
||||
struct super_block *sb = sbi->sb;
|
||||
struct address_space *mapping = sb->s_bdev->bd_mapping;
|
||||
u32 blocksize = sb->s_blocksize;
|
||||
u8 cluster_bits = sbi->cluster_bits;
|
||||
u32 off = vbo & sbi->cluster_mask;
|
||||
@@ -1212,10 +1227,22 @@ int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
nb->bytes = bytes;
|
||||
}
|
||||
|
||||
if (ra && !ra->ra_pages)
|
||||
file_ra_state_init(ra, mapping);
|
||||
|
||||
for (;;) {
|
||||
u32 len32 = len >= bytes ? bytes : len;
|
||||
sector_t block = lbo >> sb->s_blocksize_bits;
|
||||
|
||||
if (ra) {
|
||||
pgoff_t index = lbo >> PAGE_SHIFT;
|
||||
if (!ra_has_index(ra, index)) {
|
||||
page_cache_sync_readahead(mapping, ra, NULL,
|
||||
index, 1);
|
||||
ra->prev_pos = (loff_t)index << PAGE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
u32 op = blocksize - off;
|
||||
|
||||
@@ -1252,6 +1279,12 @@ int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
|
||||
} while (len32);
|
||||
|
||||
if (!run) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get next fragment to read. */
|
||||
vcn_next = vcn + clen;
|
||||
if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) ||
|
||||
vcn != vcn_next) {
|
||||
@@ -1286,11 +1319,11 @@ out:
|
||||
*
|
||||
* Return: < 0 if error, 0 if ok, -E_NTFS_FIXUP if need to update fixups.
|
||||
*/
|
||||
int ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
|
||||
struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
|
||||
struct ntfs_buffers *nb)
|
||||
int ntfs_read_bh_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
|
||||
struct ntfs_buffers *nb, struct file_ra_state *ra)
|
||||
{
|
||||
int err = ntfs_read_run_nb(sbi, run, vbo, rhdr, bytes, nb);
|
||||
int err = ntfs_read_run_nb_ra(sbi, run, vbo, rhdr, bytes, nb, ra);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
@@ -1347,12 +1380,9 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (buffer_locked(bh))
|
||||
__wait_on_buffer(bh);
|
||||
|
||||
wait_on_buffer(bh);
|
||||
lock_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
{
|
||||
if (!buffer_uptodate(bh)) {
|
||||
memset(bh->b_data, 0, blocksize);
|
||||
set_buffer_uptodate(bh);
|
||||
}
|
||||
@@ -1427,9 +1457,7 @@ int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
|
||||
if (op > bytes)
|
||||
op = bytes;
|
||||
|
||||
if (buffer_locked(bh))
|
||||
__wait_on_buffer(bh);
|
||||
|
||||
wait_on_buffer(bh);
|
||||
lock_buffer(bh);
|
||||
|
||||
bh_data = bh->b_data + off;
|
||||
@@ -2186,7 +2214,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
|
||||
if (new_sds_size > ni->vfs_inode.i_size) {
|
||||
err = attr_set_size(ni, ATTR_DATA, SDS_NAME,
|
||||
ARRAY_SIZE(SDS_NAME), &ni->file.run,
|
||||
new_sds_size, &new_sds_size, false, NULL);
|
||||
new_sds_size, &new_sds_size, false);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
+27
-22
@@ -252,9 +252,7 @@ static int bmp_buf_get(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
|
||||
bbuf->bh = bh;
|
||||
|
||||
if (buffer_locked(bh))
|
||||
__wait_on_buffer(bh);
|
||||
|
||||
wait_on_buffer(bh);
|
||||
lock_buffer(bh);
|
||||
|
||||
sb = sbi->sb;
|
||||
@@ -1028,17 +1026,18 @@ static int indx_write(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
}
|
||||
|
||||
/*
|
||||
* indx_read
|
||||
* indx_read_ra
|
||||
*
|
||||
* If ntfs_readdir calls this function
|
||||
* inode is shared locked and no ni_lock.
|
||||
* Use rw_semaphore for read/write access to alloc_run.
|
||||
*/
|
||||
int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
|
||||
struct indx_node **node)
|
||||
int indx_read_ra(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
|
||||
struct indx_node **node, struct file_ra_state *ra)
|
||||
{
|
||||
int err;
|
||||
struct INDEX_BUFFER *ib;
|
||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||
struct runs_tree *run = &indx->alloc_run;
|
||||
struct rw_semaphore *lock = &indx->run_lock;
|
||||
u64 vbo = (u64)vbn << indx->vbn2vbo_bits;
|
||||
@@ -1064,7 +1063,7 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
|
||||
}
|
||||
|
||||
down_read(lock);
|
||||
err = ntfs_read_bh(ni->mi.sbi, run, vbo, &ib->rhdr, bytes, &in->nb);
|
||||
err = ntfs_read_bh_ra(sbi, run, vbo, &ib->rhdr, bytes, &in->nb, ra);
|
||||
up_read(lock);
|
||||
if (!err)
|
||||
goto ok;
|
||||
@@ -1084,7 +1083,7 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
|
||||
goto out;
|
||||
|
||||
down_read(lock);
|
||||
err = ntfs_read_bh(ni->mi.sbi, run, vbo, &ib->rhdr, bytes, &in->nb);
|
||||
err = ntfs_read_bh_ra(sbi, run, vbo, &ib->rhdr, bytes, &in->nb, ra);
|
||||
up_read(lock);
|
||||
if (err == -E_NTFS_FIXUP)
|
||||
goto ok;
|
||||
@@ -1100,7 +1099,7 @@ ok:
|
||||
}
|
||||
|
||||
if (err == -E_NTFS_FIXUP) {
|
||||
ntfs_write_bh(ni->mi.sbi, &ib->rhdr, &in->nb, 0);
|
||||
ntfs_write_bh(sbi, &ib->rhdr, &in->nb, 0);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
@@ -1190,7 +1189,12 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fnd_push(fnd, node, e);
|
||||
err = fnd_push(fnd, node, e);
|
||||
|
||||
if (err) {
|
||||
put_indx_node(node);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
*entry = e;
|
||||
@@ -1442,8 +1446,8 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
|
||||
run_init(&run);
|
||||
|
||||
err = attr_allocate_clusters(sbi, &run, 0, 0, len, NULL, ALLOCATE_DEF,
|
||||
&alen, 0, NULL, NULL);
|
||||
err = attr_allocate_clusters(sbi, &run, NULL, 0, 0, len, NULL,
|
||||
ALLOCATE_DEF, &alen, 0, NULL, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -1527,8 +1531,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
/* Increase bitmap. */
|
||||
err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
|
||||
&indx->bitmap_run,
|
||||
ntfs3_bitmap_size(bit + 1), NULL, true,
|
||||
NULL);
|
||||
ntfs3_bitmap_size(bit + 1), NULL, true);
|
||||
if (err)
|
||||
goto out1;
|
||||
}
|
||||
@@ -1549,8 +1552,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
|
||||
/* Increase allocation. */
|
||||
err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len,
|
||||
&indx->alloc_run, data_size, &data_size, true,
|
||||
NULL);
|
||||
&indx->alloc_run, data_size, &data_size, true);
|
||||
if (err) {
|
||||
if (bmp)
|
||||
goto out2;
|
||||
@@ -1568,7 +1570,7 @@ out:
|
||||
out2:
|
||||
/* Ops. No space? */
|
||||
attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
|
||||
&indx->bitmap_run, bmp_size, &bmp_size_v, false, NULL);
|
||||
&indx->bitmap_run, bmp_size, &bmp_size_v, false);
|
||||
|
||||
out1:
|
||||
return err;
|
||||
@@ -1998,6 +2000,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
fnd->level - 1, fnd);
|
||||
}
|
||||
|
||||
indx->version += 1;
|
||||
out:
|
||||
fnd_put(fnd_a);
|
||||
out1:
|
||||
@@ -2101,7 +2104,7 @@ static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
new_data = (u64)bit << indx->index_bits;
|
||||
|
||||
err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len,
|
||||
&indx->alloc_run, new_data, &new_data, false, NULL);
|
||||
&indx->alloc_run, new_data, &new_data, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -2113,7 +2116,7 @@ static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
return 0;
|
||||
|
||||
err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
|
||||
&indx->bitmap_run, bpb, &bpb, false, NULL);
|
||||
&indx->bitmap_run, bpb, &bpb, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -2328,6 +2331,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
hdr = &root->ihdr;
|
||||
e = fnd->root_de;
|
||||
n = NULL;
|
||||
ib = NULL;
|
||||
}
|
||||
|
||||
e_size = le16_to_cpu(e->size);
|
||||
@@ -2350,7 +2354,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
* Check to see if removing that entry made
|
||||
* the leaf empty.
|
||||
*/
|
||||
if (ib_is_leaf(ib) && ib_is_empty(ib)) {
|
||||
if (ib && ib_is_leaf(ib) && ib_is_empty(ib)) {
|
||||
fnd_pop(fnd);
|
||||
fnd_push(fnd2, n, e);
|
||||
}
|
||||
@@ -2598,7 +2602,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
in = &s_index_names[indx->type];
|
||||
|
||||
err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len,
|
||||
&indx->alloc_run, 0, NULL, false, NULL);
|
||||
&indx->alloc_run, 0, NULL, false);
|
||||
if (in->name == I30_NAME)
|
||||
i_size_write(&ni->vfs_inode, 0);
|
||||
|
||||
@@ -2607,7 +2611,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
run_close(&indx->alloc_run);
|
||||
|
||||
err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
|
||||
&indx->bitmap_run, 0, NULL, false, NULL);
|
||||
&indx->bitmap_run, 0, NULL, false);
|
||||
err = ni_remove_attr(ni, ATTR_BITMAP, in->name, in->name_len,
|
||||
false, NULL);
|
||||
run_close(&indx->bitmap_run);
|
||||
@@ -2645,6 +2649,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
mi->dirty = true;
|
||||
}
|
||||
|
||||
indx->version += 1;
|
||||
out:
|
||||
fnd_put(fnd2);
|
||||
out1:
|
||||
|
||||
+454
-390
File diff suppressed because it is too large
Load Diff
@@ -77,10 +77,14 @@ static_assert(sizeof(size_t) == 8);
|
||||
typedef u32 CLST;
|
||||
#endif
|
||||
|
||||
/* On-disk sparsed cluster is marked as -1. */
|
||||
#define SPARSE_LCN64 ((u64)-1)
|
||||
#define SPARSE_LCN ((CLST)-1)
|
||||
/* Below is virtual (not on-disk) values. */
|
||||
#define RESIDENT_LCN ((CLST)-2)
|
||||
#define COMPRESSED_LCN ((CLST)-3)
|
||||
#define EOF_LCN ((CLST)-4)
|
||||
#define DELALLOC_LCN ((CLST)-5)
|
||||
|
||||
enum RECORD_NUM {
|
||||
MFT_REC_MFT = 0,
|
||||
|
||||
+121
-32
@@ -109,6 +109,7 @@ struct ntfs_mount_options {
|
||||
unsigned force : 1; /* RW mount dirty volume. */
|
||||
unsigned prealloc : 1; /* Preallocate space when file is growing. */
|
||||
unsigned nocase : 1; /* case insensitive. */
|
||||
unsigned delalloc : 1; /* delay allocation. */
|
||||
};
|
||||
|
||||
/* Special value to unpack and deallocate. */
|
||||
@@ -133,7 +134,8 @@ struct ntfs_buffers {
|
||||
enum ALLOCATE_OPT {
|
||||
ALLOCATE_DEF = 0, // Allocate all clusters.
|
||||
ALLOCATE_MFT = 1, // Allocate for MFT.
|
||||
ALLOCATE_ZERO = 2, // Zeroout new allocated clusters
|
||||
ALLOCATE_ZERO = 2, // Zeroout new allocated clusters.
|
||||
ALLOCATE_ONE_FR = 4, // Allocate one fragment only.
|
||||
};
|
||||
|
||||
enum bitmap_mutex_classes {
|
||||
@@ -192,6 +194,7 @@ struct ntfs_index {
|
||||
struct runs_tree alloc_run;
|
||||
/* read/write access to 'bitmap_run'/'alloc_run' while ntfs_readdir */
|
||||
struct rw_semaphore run_lock;
|
||||
size_t version; /* increment each change */
|
||||
|
||||
/*TODO: Remove 'cmp'. */
|
||||
NTFS_CMP_FUNC cmp;
|
||||
@@ -213,7 +216,7 @@ struct ntfs_sb_info {
|
||||
|
||||
u32 discard_granularity;
|
||||
u64 discard_granularity_mask_inv; // ~(discard_granularity_mask_inv-1)
|
||||
u32 bdev_blocksize_mask; // bdev_logical_block_size(bdev) - 1;
|
||||
u32 bdev_blocksize; // bdev_logical_block_size(bdev)
|
||||
|
||||
u32 cluster_size; // bytes per cluster
|
||||
u32 cluster_mask; // == cluster_size - 1
|
||||
@@ -272,6 +275,12 @@ struct ntfs_sb_info {
|
||||
struct {
|
||||
struct wnd_bitmap bitmap; // $Bitmap::Data
|
||||
CLST next_free_lcn;
|
||||
/* Total sum of delay allocated clusters in all files. */
|
||||
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
|
||||
atomic64_t da;
|
||||
#else
|
||||
atomic_t da;
|
||||
#endif
|
||||
} used;
|
||||
|
||||
struct {
|
||||
@@ -379,7 +388,7 @@ struct ntfs_inode {
|
||||
*/
|
||||
u8 mi_loaded;
|
||||
|
||||
/*
|
||||
/*
|
||||
* Use this field to avoid any write(s).
|
||||
* If inode is bad during initialization - use make_bad_inode
|
||||
* If inode is bad during operations - use this field
|
||||
@@ -390,7 +399,14 @@ struct ntfs_inode {
|
||||
struct ntfs_index dir;
|
||||
struct {
|
||||
struct rw_semaphore run_lock;
|
||||
/* Unpacked runs from just one record. */
|
||||
struct runs_tree run;
|
||||
/*
|
||||
* Pairs [vcn, len] for all delay allocated clusters.
|
||||
* Normal file always contains delayed clusters in one fragment.
|
||||
* TODO: use 2 CLST per pair instead of 3.
|
||||
*/
|
||||
struct runs_tree run_da;
|
||||
#ifdef CONFIG_NTFS3_LZX_XPRESS
|
||||
struct folio *offs_folio;
|
||||
#endif
|
||||
@@ -430,20 +446,32 @@ enum REPARSE_SIGN {
|
||||
|
||||
/* Functions from attrib.c */
|
||||
int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
|
||||
CLST vcn, CLST lcn, CLST len, CLST *pre_alloc,
|
||||
enum ALLOCATE_OPT opt, CLST *alen, const size_t fr,
|
||||
CLST *new_lcn, CLST *new_len);
|
||||
struct runs_tree *run_da, CLST vcn, CLST lcn,
|
||||
CLST len, CLST *pre_alloc, enum ALLOCATE_OPT opt,
|
||||
CLST *alen, const size_t fr, CLST *new_lcn,
|
||||
CLST *new_len);
|
||||
int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
|
||||
struct ATTR_LIST_ENTRY *le, struct mft_inode *mi,
|
||||
u64 new_size, struct runs_tree *run,
|
||||
struct ATTRIB **ins_attr, struct page *page);
|
||||
int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||
const __le16 *name, u8 name_len, struct runs_tree *run,
|
||||
u64 new_size, const u64 *new_valid, bool keep_prealloc,
|
||||
struct ATTRIB **ret);
|
||||
int attr_set_size_ex(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||
const __le16 *name, u8 name_len, struct runs_tree *run,
|
||||
u64 new_size, const u64 *new_valid, bool keep_prealloc,
|
||||
struct ATTRIB **ret, bool no_da);
|
||||
static inline int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||
const __le16 *name, u8 name_len,
|
||||
struct runs_tree *run, u64 new_size,
|
||||
const u64 *new_valid, bool keep_prealloc)
|
||||
{
|
||||
return attr_set_size_ex(ni, type, name, name_len, run, new_size,
|
||||
new_valid, keep_prealloc, NULL, false);
|
||||
}
|
||||
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
|
||||
CLST *len, bool *new, bool zero);
|
||||
int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio);
|
||||
CLST *len, bool *new, bool zero, void **res,
|
||||
bool no_da);
|
||||
int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
|
||||
CLST *lcn, CLST *len, bool *new, bool zero,
|
||||
void **res, bool no_da);
|
||||
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,
|
||||
const __le16 *name, u8 name_len, struct runs_tree *run,
|
||||
@@ -512,6 +540,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
int ntfs_file_open(struct inode *inode, struct file *file);
|
||||
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
__u64 start, __u64 len);
|
||||
int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg);
|
||||
long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg);
|
||||
extern const struct inode_operations ntfs_special_inode_operations;
|
||||
@@ -567,9 +596,7 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
|
||||
struct REPARSE_DATA_BUFFER *buffer);
|
||||
int ni_write_inode(struct inode *inode, int sync, const char *hint);
|
||||
#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_readpage_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_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
|
||||
u32 pages_per_frame, int copy);
|
||||
@@ -590,6 +617,10 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
|
||||
struct NTFS_DE *new_de);
|
||||
|
||||
bool ni_is_dirty(struct inode *inode);
|
||||
loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data);
|
||||
int ni_write_parents(struct ntfs_inode *ni, int sync);
|
||||
int ni_allocate_da_blocks(struct ntfs_inode *ni);
|
||||
int ni_allocate_da_blocks_locked(struct ntfs_inode *ni);
|
||||
|
||||
/* Globals from fslog.c */
|
||||
bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
|
||||
@@ -605,13 +636,14 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi);
|
||||
int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
|
||||
CLST *new_lcn, CLST *new_len,
|
||||
enum ALLOCATE_OPT opt);
|
||||
bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen);
|
||||
bool ntfs_check_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen,
|
||||
bool da);
|
||||
int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft,
|
||||
struct ntfs_inode *ni, struct mft_inode **mi);
|
||||
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_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);
|
||||
#define _ntfs_bad_inode(i) ntfs_bad_inode(i, __func__)
|
||||
enum NTFS_DIRTY_FLAGS {
|
||||
@@ -626,11 +658,27 @@ int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, const void *buf, size_t bytes, int sync);
|
||||
struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi,
|
||||
const struct runs_tree *run, u64 vbo);
|
||||
int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb);
|
||||
int ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
|
||||
struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
|
||||
struct ntfs_buffers *nb);
|
||||
int ntfs_read_run_nb_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb,
|
||||
struct file_ra_state *ra);
|
||||
static inline int ntfs_read_run_nb(struct ntfs_sb_info *sbi,
|
||||
const struct runs_tree *run, u64 vbo,
|
||||
void *buf, u32 bytes,
|
||||
struct ntfs_buffers *nb)
|
||||
{
|
||||
return ntfs_read_run_nb_ra(sbi, run, vbo, buf, bytes, nb, NULL);
|
||||
}
|
||||
int ntfs_read_bh_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
|
||||
struct ntfs_buffers *nb, struct file_ra_state *ra);
|
||||
static inline int ntfs_read_bh(struct ntfs_sb_info *sbi,
|
||||
const struct runs_tree *run, u64 vbo,
|
||||
struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
|
||||
struct ntfs_buffers *nb)
|
||||
{
|
||||
return ntfs_read_bh_ra(sbi, run, vbo, rhdr, bytes, nb, NULL);
|
||||
}
|
||||
|
||||
int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
|
||||
u32 bytes, struct ntfs_buffers *nb);
|
||||
int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
|
||||
@@ -696,8 +744,13 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi,
|
||||
const struct ATTRIB *attr, enum index_mutex_classed type);
|
||||
struct INDEX_ROOT *indx_get_root(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
struct ATTRIB **attr, struct mft_inode **mi);
|
||||
int indx_read(struct ntfs_index *idx, struct ntfs_inode *ni, CLST vbn,
|
||||
struct indx_node **node);
|
||||
int indx_read_ra(struct ntfs_index *idx, struct ntfs_inode *ni, CLST vbn,
|
||||
struct indx_node **node, struct file_ra_state *ra);
|
||||
static inline int indx_read(struct ntfs_index *idx, struct ntfs_inode *ni,
|
||||
CLST vbn, struct indx_node **node)
|
||||
{
|
||||
return indx_read_ra(idx, ni, vbn, node, NULL);
|
||||
}
|
||||
int indx_find(struct ntfs_index *indx, struct ntfs_inode *dir,
|
||||
const struct INDEX_ROOT *root, const void *Key, size_t KeyLen,
|
||||
const void *param, int *diff, struct NTFS_DE **entry,
|
||||
@@ -721,13 +774,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,
|
||||
const struct cpu_str *name);
|
||||
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 ntfs_sync_inode(struct inode *inode);
|
||||
int inode_read_data(struct inode *inode, void *data, size_t bytes);
|
||||
@@ -738,6 +784,8 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
|
||||
int ntfs_link_inode(struct inode *inode, struct dentry *dentry);
|
||||
int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry);
|
||||
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 address_space_operations ntfs_aops;
|
||||
extern const struct address_space_operations ntfs_aops_cmpr;
|
||||
@@ -815,7 +863,8 @@ void run_truncate_around(struct runs_tree *run, CLST vcn);
|
||||
bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len,
|
||||
bool is_mft);
|
||||
bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub);
|
||||
bool run_insert_range(struct runs_tree *run, CLST vcn, CLST len);
|
||||
int run_insert_range(struct runs_tree *run, CLST vcn, CLST len);
|
||||
int run_insert_range_da(struct runs_tree *run, CLST vcn, CLST len);
|
||||
bool run_get_entry(const struct runs_tree *run, size_t index, CLST *vcn,
|
||||
CLST *lcn, CLST *len);
|
||||
bool run_is_mapped_full(const struct runs_tree *run, CLST svcn, CLST evcn);
|
||||
@@ -835,6 +884,9 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
|
||||
#endif
|
||||
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn);
|
||||
int run_clone(const struct runs_tree *run, struct runs_tree *new_run);
|
||||
bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done);
|
||||
CLST run_len(const struct runs_tree *run);
|
||||
CLST run_get_max_vcn(const struct runs_tree *run);
|
||||
|
||||
/* Globals from super.c */
|
||||
void *ntfs_set_shared(void *ptr, u32 bytes);
|
||||
@@ -1011,6 +1063,36 @@ static inline int ntfs3_forced_shutdown(struct super_block *sb)
|
||||
return test_bit(NTFS_FLAGS_SHUTDOWN_BIT, &ntfs_sb(sb)->flags);
|
||||
}
|
||||
|
||||
/* Returns total sum of delay allocated clusters in all files. */
|
||||
static inline CLST ntfs_get_da(struct ntfs_sb_info *sbi)
|
||||
{
|
||||
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
|
||||
return atomic64_read(&sbi->used.da);
|
||||
#else
|
||||
return atomic_read(&sbi->used.da);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Update total count of delay allocated clusters. */
|
||||
static inline void ntfs_add_da(struct ntfs_sb_info *sbi, CLST da)
|
||||
{
|
||||
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
|
||||
atomic64_add(da, &sbi->used.da);
|
||||
#else
|
||||
atomic_add(da, &sbi->used.da);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Update total count of delay allocated clusters. */
|
||||
static inline void ntfs_sub_da(struct ntfs_sb_info *sbi, CLST da)
|
||||
{
|
||||
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
|
||||
atomic64_sub(da, &sbi->used.da);
|
||||
#else
|
||||
atomic_sub(da, &sbi->used.da);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_up_cluster - Align up on cluster boundary.
|
||||
*/
|
||||
@@ -1084,6 +1166,13 @@ static inline int is_resident(struct ntfs_inode *ni)
|
||||
return ni->ni_flags & NI_FLAG_RESIDENT;
|
||||
}
|
||||
|
||||
static inline loff_t ntfs_get_maxbytes(struct ntfs_inode *ni)
|
||||
{
|
||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||
return is_sparsed(ni) || is_compressed(ni) ? sbi->maxbytes_sparse :
|
||||
sbi->maxbytes;
|
||||
}
|
||||
|
||||
static inline void le16_sub_cpu(__le16 *var, u16 val)
|
||||
{
|
||||
*var = cpu_to_le16(le16_to_cpu(*var) - val);
|
||||
|
||||
+149
-14
@@ -454,7 +454,7 @@ requires_new_range:
|
||||
|
||||
/*
|
||||
* If existing range fits then were done.
|
||||
* Otherwise extend found one and fall back to range jocode.
|
||||
* Otherwise extend found one and fall back to range join code.
|
||||
*/
|
||||
if (r->vcn + r->len < vcn + len)
|
||||
r->len += len - ((r->vcn + r->len) - vcn);
|
||||
@@ -482,7 +482,8 @@ requires_new_range:
|
||||
return true;
|
||||
}
|
||||
|
||||
/* run_collapse_range
|
||||
/*
|
||||
* run_collapse_range
|
||||
*
|
||||
* Helper for attr_collapse_range(),
|
||||
* which is helper for fallocate(collapse_range).
|
||||
@@ -493,8 +494,9 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub)
|
||||
struct ntfs_run *r, *e, *eat_start, *eat_end;
|
||||
CLST end;
|
||||
|
||||
if (WARN_ON(!run_lookup(run, vcn, &index)))
|
||||
return true; /* Should never be here. */
|
||||
if (!run_lookup(run, vcn, &index) && index >= run->count) {
|
||||
return true;
|
||||
}
|
||||
|
||||
e = run->runs + run->count;
|
||||
r = run->runs + index;
|
||||
@@ -560,13 +562,13 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub)
|
||||
* Helper for attr_insert_range(),
|
||||
* which is helper for fallocate(insert_range).
|
||||
*/
|
||||
bool run_insert_range(struct runs_tree *run, CLST vcn, CLST len)
|
||||
int run_insert_range(struct runs_tree *run, CLST vcn, CLST len)
|
||||
{
|
||||
size_t index;
|
||||
struct ntfs_run *r, *e;
|
||||
|
||||
if (WARN_ON(!run_lookup(run, vcn, &index)))
|
||||
return false; /* Should never be here. */
|
||||
return -EINVAL; /* Should never be here. */
|
||||
|
||||
e = run->runs + run->count;
|
||||
r = run->runs + index;
|
||||
@@ -588,13 +590,49 @@ bool run_insert_range(struct runs_tree *run, CLST vcn, CLST len)
|
||||
r->len = len1;
|
||||
|
||||
if (!run_add_entry(run, vcn + len, lcn2, len2, false))
|
||||
return false;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!run_add_entry(run, vcn, SPARSE_LCN, len, false))
|
||||
return false;
|
||||
return -ENOMEM;
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* run_insert_range_da
|
||||
*
|
||||
* Helper for attr_insert_range(),
|
||||
* which is helper for fallocate(insert_range).
|
||||
*/
|
||||
int run_insert_range_da(struct runs_tree *run, CLST vcn, CLST len)
|
||||
{
|
||||
struct ntfs_run *r, *r0 = NULL, *e = run->runs + run->count;
|
||||
;
|
||||
|
||||
for (r = run->runs; r < e; r++) {
|
||||
CLST end = r->vcn + r->len;
|
||||
|
||||
if (vcn >= end)
|
||||
continue;
|
||||
|
||||
if (!r0 && r->vcn < vcn) {
|
||||
r0 = r;
|
||||
} else {
|
||||
r->vcn += len;
|
||||
}
|
||||
}
|
||||
|
||||
if (r0) {
|
||||
/* split fragment. */
|
||||
CLST len1 = vcn - r0->vcn;
|
||||
CLST len2 = r0->len - len1;
|
||||
|
||||
r0->len = len1;
|
||||
if (!run_add_entry(run, vcn + len, SPARSE_LCN, len2, false))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1131,11 +1169,14 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
|
||||
struct rw_semaphore *lock =
|
||||
is_mounted(sbi) ? &sbi->mft.ni->file.run_lock :
|
||||
NULL;
|
||||
if (lock)
|
||||
down_read(lock);
|
||||
ntfs_refresh_zone(sbi);
|
||||
if (lock)
|
||||
up_read(lock);
|
||||
if (lock) {
|
||||
if (down_read_trylock(lock)) {
|
||||
ntfs_refresh_zone(sbi);
|
||||
up_read(lock);
|
||||
}
|
||||
} else {
|
||||
ntfs_refresh_zone(sbi);
|
||||
}
|
||||
}
|
||||
up_write(&wnd->rw_lock);
|
||||
if (err)
|
||||
@@ -1206,3 +1247,97 @@ int run_clone(const struct runs_tree *run, struct runs_tree *new_run)
|
||||
new_run->count = run->count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* run_remove_range
|
||||
*
|
||||
*/
|
||||
bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done)
|
||||
{
|
||||
size_t index, eat;
|
||||
struct ntfs_run *r, *e, *eat_start, *eat_end;
|
||||
CLST end, d;
|
||||
|
||||
*done = 0;
|
||||
|
||||
/* Fast check. */
|
||||
if (!run->count)
|
||||
return true;
|
||||
|
||||
if (!run_lookup(run, vcn, &index) && index >= run->count) {
|
||||
/* No entries in this run. */
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
e = run->runs + run->count;
|
||||
r = run->runs + index;
|
||||
end = vcn + len;
|
||||
|
||||
if (vcn > r->vcn) {
|
||||
CLST r_end = r->vcn + r->len;
|
||||
d = vcn - r->vcn;
|
||||
|
||||
if (r_end > end) {
|
||||
/* Remove a middle part, split. */
|
||||
*done += len;
|
||||
r->len = d;
|
||||
return run_add_entry(run, end, r->lcn, r_end - end,
|
||||
false);
|
||||
}
|
||||
/* Remove tail of run .*/
|
||||
*done += r->len - d;
|
||||
r->len = d;
|
||||
r += 1;
|
||||
}
|
||||
|
||||
eat_start = r;
|
||||
eat_end = r;
|
||||
|
||||
for (; r < e; r++) {
|
||||
if (r->vcn >= end)
|
||||
continue;
|
||||
|
||||
if (r->vcn + r->len <= end) {
|
||||
/* Eat this run. */
|
||||
*done += r->len;
|
||||
eat_end = r + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
d = end - r->vcn;
|
||||
*done += d;
|
||||
if (r->lcn != SPARSE_LCN)
|
||||
r->lcn += d;
|
||||
r->len -= d;
|
||||
r->vcn = end;
|
||||
}
|
||||
|
||||
eat = eat_end - eat_start;
|
||||
memmove(eat_start, eat_end, (e - eat_end) * sizeof(*r));
|
||||
run->count -= eat;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CLST run_len(const struct runs_tree *run)
|
||||
{
|
||||
const struct ntfs_run *r, *e;
|
||||
CLST len = 0;
|
||||
|
||||
for (r = run->runs, e = r + run->count; r < e; r++) {
|
||||
len += r->len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
CLST run_get_max_vcn(const struct runs_tree *run)
|
||||
{
|
||||
const struct ntfs_run *r;
|
||||
if (!run->count)
|
||||
return 0;
|
||||
|
||||
r = run->runs + run->count - 1;
|
||||
return r->vcn + r->len;
|
||||
}
|
||||
|
||||
+50
-23
@@ -58,9 +58,9 @@
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/exportfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/fs_parser.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
@@ -264,9 +264,13 @@ enum Opt {
|
||||
Opt_windows_names,
|
||||
Opt_showmeta,
|
||||
Opt_acl,
|
||||
Opt_acl_bool,
|
||||
Opt_iocharset,
|
||||
Opt_prealloc,
|
||||
Opt_prealloc_bool,
|
||||
Opt_nocase,
|
||||
Opt_delalloc,
|
||||
Opt_delalloc_bool,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
@@ -285,10 +289,14 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
|
||||
fsparam_flag("hide_dot_files", Opt_hide_dot_files),
|
||||
fsparam_flag("windows_names", Opt_windows_names),
|
||||
fsparam_flag("showmeta", Opt_showmeta),
|
||||
fsparam_flag_no("acl", Opt_acl),
|
||||
fsparam_flag("acl", Opt_acl),
|
||||
fsparam_bool("acl", Opt_acl_bool),
|
||||
fsparam_string("iocharset", Opt_iocharset),
|
||||
fsparam_flag_no("prealloc", Opt_prealloc),
|
||||
fsparam_flag("prealloc", Opt_prealloc),
|
||||
fsparam_bool("prealloc", Opt_prealloc_bool),
|
||||
fsparam_flag("nocase", Opt_nocase),
|
||||
fsparam_flag("delalloc", Opt_delalloc),
|
||||
fsparam_bool("delalloc", Opt_delalloc_bool),
|
||||
{}
|
||||
};
|
||||
// clang-format on
|
||||
@@ -379,15 +387,17 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
|
||||
case Opt_showmeta:
|
||||
opts->showmeta = 1;
|
||||
break;
|
||||
case Opt_acl:
|
||||
if (!result.negated)
|
||||
case Opt_acl_bool:
|
||||
if (result.boolean) {
|
||||
fallthrough;
|
||||
case Opt_acl:
|
||||
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
|
||||
fc->sb_flags |= SB_POSIXACL;
|
||||
#else
|
||||
return invalf(
|
||||
fc, "ntfs3: Support for ACL not compiled in!");
|
||||
#endif
|
||||
else
|
||||
} else
|
||||
fc->sb_flags &= ~SB_POSIXACL;
|
||||
break;
|
||||
case Opt_iocharset:
|
||||
@@ -396,11 +406,20 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
|
||||
param->string = NULL;
|
||||
break;
|
||||
case Opt_prealloc:
|
||||
opts->prealloc = !result.negated;
|
||||
opts->prealloc = 1;
|
||||
break;
|
||||
case Opt_prealloc_bool:
|
||||
opts->prealloc = result.boolean;
|
||||
break;
|
||||
case Opt_nocase:
|
||||
opts->nocase = 1;
|
||||
break;
|
||||
case Opt_delalloc:
|
||||
opts->delalloc = 1;
|
||||
break;
|
||||
case Opt_delalloc_bool:
|
||||
opts->delalloc = result.boolean;
|
||||
break;
|
||||
default:
|
||||
/* Should not be here unless we forget add case. */
|
||||
return -EINVAL;
|
||||
@@ -674,7 +693,7 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)
|
||||
sbi->volume.ni = NULL;
|
||||
}
|
||||
|
||||
ntfs_update_mftmirr(sbi, 0);
|
||||
ntfs_update_mftmirr(sbi);
|
||||
|
||||
indx_clear(&sbi->security.index_sii);
|
||||
indx_clear(&sbi->security.index_sdh);
|
||||
@@ -705,9 +724,7 @@ static void ntfs_put_super(struct super_block *sb)
|
||||
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
|
||||
|
||||
if (sbi->options) {
|
||||
unload_nls(sbi->options->nls);
|
||||
kfree(sbi->options->nls_name);
|
||||
kfree(sbi->options);
|
||||
put_mount_options(sbi->options);
|
||||
sbi->options = NULL;
|
||||
}
|
||||
|
||||
@@ -719,14 +736,22 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
struct wnd_bitmap *wnd = &sbi->used.bitmap;
|
||||
CLST da_clusters = ntfs_get_da(sbi);
|
||||
|
||||
buf->f_type = sb->s_magic;
|
||||
buf->f_bsize = sbi->cluster_size;
|
||||
buf->f_bsize = buf->f_frsize = sbi->cluster_size;
|
||||
buf->f_blocks = wnd->nbits;
|
||||
|
||||
buf->f_bfree = buf->f_bavail = wnd_zeroes(wnd);
|
||||
buf->f_bfree = wnd_zeroes(wnd);
|
||||
if (buf->f_bfree > da_clusters) {
|
||||
buf->f_bfree -= da_clusters;
|
||||
} else {
|
||||
buf->f_bfree = 0;
|
||||
}
|
||||
buf->f_bavail = buf->f_bfree;
|
||||
|
||||
buf->f_fsid.val[0] = sbi->volume.ser_num;
|
||||
buf->f_fsid.val[1] = (sbi->volume.ser_num >> 32);
|
||||
buf->f_fsid.val[1] = sbi->volume.ser_num >> 32;
|
||||
buf->f_namelen = NTFS_NAME_LEN;
|
||||
|
||||
return 0;
|
||||
@@ -771,6 +796,8 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root)
|
||||
seq_puts(m, ",prealloc");
|
||||
if (opts->nocase)
|
||||
seq_puts(m, ",nocase");
|
||||
if (opts->delalloc)
|
||||
seq_puts(m, ",delalloc");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -823,7 +850,12 @@ static int ntfs_sync_fs(struct super_block *sb, int wait)
|
||||
if (!err)
|
||||
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;
|
||||
}
|
||||
@@ -1076,7 +1108,7 @@ read_boot:
|
||||
dev_size += sector_size - 1;
|
||||
}
|
||||
|
||||
sbi->bdev_blocksize_mask = max(boot_sector_size, sector_size) - 1;
|
||||
sbi->bdev_blocksize = max(boot_sector_size, sector_size);
|
||||
sbi->mft.lbo = mlcn << cluster_bits;
|
||||
sbi->mft.lbo2 = mlcn2 << cluster_bits;
|
||||
|
||||
@@ -1253,7 +1285,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
}
|
||||
}
|
||||
sbi->options = options;
|
||||
fc->fs_private = NULL;
|
||||
sb->s_flags |= SB_NODIRATIME;
|
||||
sb->s_magic = 0x7366746e; // "ntfs"
|
||||
sb->s_op = &ntfs_sops;
|
||||
@@ -1652,9 +1683,7 @@ load_root:
|
||||
*/
|
||||
struct buffer_head *bh0 = sb_getblk(sb, 0);
|
||||
if (bh0) {
|
||||
if (buffer_locked(bh0))
|
||||
__wait_on_buffer(bh0);
|
||||
|
||||
wait_on_buffer(bh0);
|
||||
lock_buffer(bh0);
|
||||
memcpy(bh0->b_data, boot2, sizeof(*boot2));
|
||||
set_buffer_uptodate(bh0);
|
||||
@@ -1679,9 +1708,7 @@ put_inode_out:
|
||||
out:
|
||||
/* sbi->options == options */
|
||||
if (options) {
|
||||
unload_nls(options->nls);
|
||||
kfree(options->nls_name);
|
||||
kfree(options);
|
||||
put_mount_options(sbi->options);
|
||||
sbi->options = NULL;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -460,7 +460,7 @@ update_ea:
|
||||
|
||||
new_sz = size;
|
||||
err = attr_set_size(ni, ATTR_EA, NULL, 0, &ea_run, new_sz, &new_sz,
|
||||
false, NULL);
|
||||
false);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user