Commit a2343df3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ntfs3_for_6.8' of https://github.com/Paragon-Software-Group/linux-ntfs3

Pull ntfs3 fixes from Konstantin Komarov:
 "Fixed:
   - size update for compressed file
   - some logic errors, overflows
   - memory leak
   - some code was refactored

  Added:
   - implement super_operations::shutdown

  Improved:
   - alternative boot processing
   - reduced stack usage"

* tag 'ntfs3_for_6.8' of https://github.com/Paragon-Software-Group/linux-ntfs3: (28 commits)
  fs/ntfs3: Slightly simplify ntfs_inode_printk()
  fs/ntfs3: Add ioctl operation for directories (FITRIM)
  fs/ntfs3: Fix oob in ntfs_listxattr
  fs/ntfs3: Fix an NULL dereference bug
  fs/ntfs3: Update inode->i_size after success write into compressed file
  fs/ntfs3: Fixed overflow check in mi_enum_attr()
  fs/ntfs3: Correct function is_rst_area_valid
  fs/ntfs3: Use i_size_read and i_size_write
  fs/ntfs3: Prevent generic message "attempt to access beyond end of device"
  fs/ntfs3: use non-movable memory for ntfs3 MFT buffer cache
  fs/ntfs3: Use kvfree to free memory allocated by kvmalloc
  fs/ntfs3: Disable ATTR_LIST_ENTRY size check
  fs/ntfs3: Fix c/mtime typo
  fs/ntfs3: Add NULL ptr dereference checking at the end of attr_allocate_frame()
  fs/ntfs3: Add and fix comments
  fs/ntfs3: ntfs3_forced_shutdown use int instead of bool
  fs/ntfs3: Implement super_operations::shutdown
  fs/ntfs3: Drop suid and sgid bits as a part of fpunch
  fs/ntfs3: Add file_modified
  fs/ntfs3: Correct use bh_read
  ...
parents 4356e9f8 622cd3da
...@@ -886,7 +886,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, ...@@ -886,7 +886,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
struct runs_tree *run = &ni->file.run; struct runs_tree *run = &ni->file.run;
struct ntfs_sb_info *sbi; struct ntfs_sb_info *sbi;
u8 cluster_bits; u8 cluster_bits;
struct ATTRIB *attr = NULL, *attr_b; struct ATTRIB *attr, *attr_b;
struct ATTR_LIST_ENTRY *le, *le_b; struct ATTR_LIST_ENTRY *le, *le_b;
struct mft_inode *mi, *mi_b; struct mft_inode *mi, *mi_b;
CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0, alen; CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0, alen;
...@@ -904,12 +904,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, ...@@ -904,12 +904,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
*len = 0; *len = 0;
up_read(&ni->file.run_lock); up_read(&ni->file.run_lock);
if (*len) { if (*len && (*lcn != SPARSE_LCN || !new))
if (*lcn != SPARSE_LCN || !new) return 0; /* Fast normal way without allocation. */
return 0; /* Fast normal way without allocation. */
else if (clen > *len)
clen = *len;
}
/* No cluster in cache or we need to allocate cluster in hole. */ /* No cluster in cache or we need to allocate cluster in hole. */
sbi = ni->mi.sbi; sbi = ni->mi.sbi;
...@@ -918,6 +914,17 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, ...@@ -918,6 +914,17 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
ni_lock(ni); ni_lock(ni);
down_write(&ni->file.run_lock); down_write(&ni->file.run_lock);
/* Repeat the code above (under write lock). */
if (!run_lookup_entry(run, vcn, lcn, len, NULL))
*len = 0;
if (*len) {
if (*lcn != SPARSE_LCN || !new)
goto out; /* normal way without allocation. */
if (clen > *len)
clen = *len;
}
le_b = NULL; le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b);
if (!attr_b) { if (!attr_b) {
...@@ -1736,8 +1743,10 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, ...@@ -1736,8 +1743,10 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
le_b = NULL; le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL,
0, NULL, &mi_b); 0, NULL, &mi_b);
if (!attr_b) if (!attr_b) {
return -ENOENT; err = -ENOENT;
goto out;
}
attr = attr_b; attr = attr_b;
le = le_b; le = le_b;
...@@ -1818,13 +1827,15 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, ...@@ -1818,13 +1827,15 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
ok: ok:
run_truncate_around(run, vcn); run_truncate_around(run, vcn);
out: out:
if (new_valid > data_size) if (attr_b) {
new_valid = data_size; if (new_valid > data_size)
new_valid = data_size;
valid_size = le64_to_cpu(attr_b->nres.valid_size); valid_size = le64_to_cpu(attr_b->nres.valid_size);
if (new_valid != valid_size) { if (new_valid != valid_size) {
attr_b->nres.valid_size = cpu_to_le64(valid_size); attr_b->nres.valid_size = cpu_to_le64(valid_size);
mi_b->dirty = true; mi_b->dirty = true;
}
} }
return err; return err;
...@@ -2073,7 +2084,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) ...@@ -2073,7 +2084,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
/* Update inode size. */ /* Update inode size. */
ni->i_valid = valid_size; ni->i_valid = valid_size;
ni->vfs_inode.i_size = data_size; i_size_write(&ni->vfs_inode, data_size);
inode_set_bytes(&ni->vfs_inode, total_size); inode_set_bytes(&ni->vfs_inode, total_size);
ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
mark_inode_dirty(&ni->vfs_inode); mark_inode_dirty(&ni->vfs_inode);
...@@ -2488,7 +2499,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) ...@@ -2488,7 +2499,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
mi_b->dirty = true; mi_b->dirty = true;
done: done:
ni->vfs_inode.i_size += bytes; i_size_write(&ni->vfs_inode, ni->vfs_inode.i_size + bytes);
ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
mark_inode_dirty(&ni->vfs_inode); mark_inode_dirty(&ni->vfs_inode);
......
...@@ -29,7 +29,7 @@ static inline bool al_is_valid_le(const struct ntfs_inode *ni, ...@@ -29,7 +29,7 @@ static inline bool al_is_valid_le(const struct ntfs_inode *ni,
void al_destroy(struct ntfs_inode *ni) void al_destroy(struct ntfs_inode *ni)
{ {
run_close(&ni->attr_list.run); run_close(&ni->attr_list.run);
kfree(ni->attr_list.le); kvfree(ni->attr_list.le);
ni->attr_list.le = NULL; ni->attr_list.le = NULL;
ni->attr_list.size = 0; ni->attr_list.size = 0;
ni->attr_list.dirty = false; ni->attr_list.dirty = false;
...@@ -127,12 +127,13 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, ...@@ -127,12 +127,13 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
{ {
size_t off; size_t off;
u16 sz; u16 sz;
const unsigned le_min_size = le_size(0);
if (!le) { if (!le) {
le = ni->attr_list.le; le = ni->attr_list.le;
} else { } else {
sz = le16_to_cpu(le->size); sz = le16_to_cpu(le->size);
if (sz < sizeof(struct ATTR_LIST_ENTRY)) { if (sz < le_min_size) {
/* Impossible 'cause we should not return such le. */ /* Impossible 'cause we should not return such le. */
return NULL; return NULL;
} }
...@@ -141,7 +142,7 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, ...@@ -141,7 +142,7 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
/* Check boundary. */ /* Check boundary. */
off = PtrOffset(ni->attr_list.le, le); off = PtrOffset(ni->attr_list.le, le);
if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) { if (off + le_min_size > ni->attr_list.size) {
/* The regular end of list. */ /* The regular end of list. */
return NULL; return NULL;
} }
...@@ -149,8 +150,7 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, ...@@ -149,8 +150,7 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
sz = le16_to_cpu(le->size); sz = le16_to_cpu(le->size);
/* Check le for errors. */ /* Check le for errors. */
if (sz < sizeof(struct ATTR_LIST_ENTRY) || if (sz < le_min_size || off + sz > ni->attr_list.size ||
off + sz > ni->attr_list.size ||
sz < le->name_off + le->name_len * sizeof(short)) { sz < le->name_off + le->name_len * sizeof(short)) {
return NULL; return NULL;
} }
...@@ -318,7 +318,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, ...@@ -318,7 +318,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
memcpy(ptr, al->le, off); memcpy(ptr, al->le, off);
memcpy(Add2Ptr(ptr, off + sz), le, old_size - off); memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
le = Add2Ptr(ptr, off); le = Add2Ptr(ptr, off);
kfree(al->le); kvfree(al->le);
al->le = ptr; al->le = ptr;
} else { } else {
memmove(Add2Ptr(le, sz), le, old_size - off); memmove(Add2Ptr(le, sz), le, old_size - off);
......
...@@ -124,7 +124,7 @@ void wnd_close(struct wnd_bitmap *wnd) ...@@ -124,7 +124,7 @@ void wnd_close(struct wnd_bitmap *wnd)
{ {
struct rb_node *node, *next; struct rb_node *node, *next;
kfree(wnd->free_bits); kvfree(wnd->free_bits);
wnd->free_bits = NULL; wnd->free_bits = NULL;
run_close(&wnd->run); run_close(&wnd->run);
...@@ -1360,7 +1360,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) ...@@ -1360,7 +1360,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits)
memcpy(new_free, wnd->free_bits, wnd->nwnd * sizeof(short)); memcpy(new_free, wnd->free_bits, wnd->nwnd * sizeof(short));
memset(new_free + wnd->nwnd, 0, memset(new_free + wnd->nwnd, 0,
(new_wnd - wnd->nwnd) * sizeof(short)); (new_wnd - wnd->nwnd) * sizeof(short));
kfree(wnd->free_bits); kvfree(wnd->free_bits);
wnd->free_bits = new_free; wnd->free_bits = new_free;
} }
......
...@@ -309,11 +309,31 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, ...@@ -309,11 +309,31 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
return 0; return 0;
} }
/* NTFS: symlinks are "dir + reparse" or "file + reparse" */ /*
if (fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT) * NTFS: symlinks are "dir + reparse" or "file + reparse"
dt_type = DT_LNK; * Unfortunately reparse attribute is used for many purposes (several dozens).
else * It is not possible here to know is this name symlink or not.
dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG; * To get exactly the type of name we should to open inode (read mft).
* getattr for opened file (fstat) correctly returns symlink.
*/
dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
/*
* It is not reliable to detect the type of name using duplicated information
* stored in parent directory.
* The only correct way to get the type of name - read MFT record and find ATTR_STD.
* The code below is not good idea.
* It does additional locks/reads just to get the type of name.
* Should we use additional mount option to enable branch below?
*/
if ((fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT) &&
ino != ni->mi.rno) {
struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL);
if (!IS_ERR_OR_NULL(inode)) {
dt_type = fs_umode_to_dtype(inode->i_mode);
iput(inode);
}
}
return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type); return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
} }
...@@ -495,11 +515,9 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, ...@@ -495,11 +515,9 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
struct INDEX_HDR *hdr; struct INDEX_HDR *hdr;
const struct ATTR_FILE_NAME *fname; const struct ATTR_FILE_NAME *fname;
u32 e_size, off, end; u32 e_size, off, end;
u64 vbo = 0;
size_t drs = 0, fles = 0, bit = 0; size_t drs = 0, fles = 0, bit = 0;
loff_t i_size = ni->vfs_inode.i_size;
struct indx_node *node = NULL; struct indx_node *node = NULL;
u8 index_bits = ni->dir.index_bits; size_t max_indx = i_size_read(&ni->vfs_inode) >> ni->dir.index_bits;
if (is_empty) if (is_empty)
*is_empty = true; *is_empty = true;
...@@ -518,8 +536,10 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, ...@@ -518,8 +536,10 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
e = Add2Ptr(hdr, off); e = Add2Ptr(hdr, off);
e_size = le16_to_cpu(e->size); e_size = le16_to_cpu(e->size);
if (e_size < sizeof(struct NTFS_DE) || if (e_size < sizeof(struct NTFS_DE) ||
off + e_size > end) off + e_size > end) {
/* Looks like corruption. */
break; break;
}
if (de_is_last(e)) if (de_is_last(e))
break; break;
...@@ -543,7 +563,7 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, ...@@ -543,7 +563,7 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
fles += 1; fles += 1;
} }
if (vbo >= i_size) if (bit >= max_indx)
goto out; goto out;
err = indx_used_bit(&ni->dir, ni, &bit); err = indx_used_bit(&ni->dir, ni, &bit);
...@@ -553,8 +573,7 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, ...@@ -553,8 +573,7 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
if (bit == MINUS_ONE_T) if (bit == MINUS_ONE_T)
goto out; goto out;
vbo = (u64)bit << index_bits; if (bit >= max_indx)
if (vbo >= i_size)
goto out; goto out;
err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits, err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
...@@ -564,7 +583,6 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, ...@@ -564,7 +583,6 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
hdr = &node->index->ihdr; hdr = &node->index->ihdr;
bit += 1; bit += 1;
vbo = (u64)bit << ni->dir.idx2vbn_bits;
} }
out: out:
...@@ -593,5 +611,9 @@ const struct file_operations ntfs_dir_operations = { ...@@ -593,5 +611,9 @@ const struct file_operations ntfs_dir_operations = {
.iterate_shared = ntfs_readdir, .iterate_shared = ntfs_readdir,
.fsync = generic_file_fsync, .fsync = generic_file_fsync,
.open = ntfs_file_open, .open = ntfs_file_open,
.unlocked_ioctl = ntfs_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ntfs_compat_ioctl,
#endif
}; };
// clang-format on // clang-format on
...@@ -48,7 +48,7 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg) ...@@ -48,7 +48,7 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
return 0; return 0;
} }
static long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{ {
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
...@@ -61,7 +61,7 @@ static long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) ...@@ -61,7 +61,7 @@ static long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg)
} }
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
static long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg) long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg)
{ {
return ntfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); return ntfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
...@@ -188,6 +188,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) ...@@ -188,6 +188,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
u32 bh_next, bh_off, to; u32 bh_next, bh_off, to;
sector_t iblock; sector_t iblock;
struct folio *folio; struct folio *folio;
bool dirty = false;
for (; idx < idx_end; idx += 1, from = 0) { for (; idx < idx_end; idx += 1, from = 0) {
page_off = (loff_t)idx << PAGE_SHIFT; page_off = (loff_t)idx << PAGE_SHIFT;
...@@ -223,29 +224,27 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) ...@@ -223,29 +224,27 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
/* Ok, it's mapped. Make sure it's up-to-date. */ /* Ok, it's mapped. Make sure it's up-to-date. */
if (folio_test_uptodate(folio)) if (folio_test_uptodate(folio))
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
else if (bh_read(bh, 0) < 0) {
if (!buffer_uptodate(bh)) { err = -EIO;
err = bh_read(bh, 0); folio_unlock(folio);
if (err < 0) { folio_put(folio);
folio_unlock(folio); goto out;
folio_put(folio);
goto out;
}
} }
mark_buffer_dirty(bh); mark_buffer_dirty(bh);
} while (bh_off = bh_next, iblock += 1, } while (bh_off = bh_next, iblock += 1,
head != (bh = bh->b_this_page)); head != (bh = bh->b_this_page));
folio_zero_segment(folio, from, to); folio_zero_segment(folio, from, to);
dirty = true;
folio_unlock(folio); folio_unlock(folio);
folio_put(folio); folio_put(folio);
cond_resched(); cond_resched();
} }
out: out:
mark_inode_dirty(inode); if (dirty)
mark_inode_dirty(inode);
return err; return err;
} }
...@@ -261,6 +260,9 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -261,6 +260,9 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
bool rw = vma->vm_flags & VM_WRITE; bool rw = vma->vm_flags & VM_WRITE;
int err; int err;
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
if (is_encrypted(ni)) { if (is_encrypted(ni)) {
ntfs_inode_warn(inode, "mmap encrypted not supported"); ntfs_inode_warn(inode, "mmap encrypted not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -499,10 +501,14 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) ...@@ -499,10 +501,14 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
ni_lock(ni); ni_lock(ni);
err = attr_punch_hole(ni, vbo, len, &frame_size); err = attr_punch_hole(ni, vbo, len, &frame_size);
ni_unlock(ni); ni_unlock(ni);
if (!err)
goto ok;
if (err != E_NTFS_NOTALIGNED) if (err != E_NTFS_NOTALIGNED)
goto out; goto out;
/* Process not aligned punch. */ /* Process not aligned punch. */
err = 0;
mask = frame_size - 1; mask = frame_size - 1;
vbo_a = (vbo + mask) & ~mask; vbo_a = (vbo + mask) & ~mask;
end_a = end & ~mask; end_a = end & ~mask;
...@@ -525,6 +531,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) ...@@ -525,6 +531,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
ni_lock(ni); ni_lock(ni);
err = attr_punch_hole(ni, vbo_a, end_a - vbo_a, NULL); err = attr_punch_hole(ni, vbo_a, end_a - vbo_a, NULL);
ni_unlock(ni); ni_unlock(ni);
if (err)
goto out;
} }
} else if (mode & FALLOC_FL_COLLAPSE_RANGE) { } else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
/* /*
...@@ -564,6 +572,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) ...@@ -564,6 +572,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
ni_lock(ni); ni_lock(ni);
err = attr_insert_range(ni, vbo, len); err = attr_insert_range(ni, vbo, len);
ni_unlock(ni); ni_unlock(ni);
if (err)
goto out;
} else { } else {
/* Check new size. */ /* Check new size. */
u8 cluster_bits = sbi->cluster_bits; u8 cluster_bits = sbi->cluster_bits;
...@@ -633,11 +643,18 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) ...@@ -633,11 +643,18 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
&ni->file.run, i_size, &ni->i_valid, &ni->file.run, i_size, &ni->i_valid,
true, NULL); true, NULL);
ni_unlock(ni); ni_unlock(ni);
if (err)
goto out;
} else if (new_size > i_size) { } else if (new_size > i_size) {
inode->i_size = new_size; i_size_write(inode, new_size);
} }
} }
ok:
err = file_modified(file);
if (err)
goto out;
out: out:
if (map_locked) if (map_locked)
filemap_invalidate_unlock(mapping); filemap_invalidate_unlock(mapping);
...@@ -663,6 +680,9 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -663,6 +680,9 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
umode_t mode = inode->i_mode; umode_t mode = inode->i_mode;
int err; int err;
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
err = setattr_prepare(idmap, dentry, attr); err = setattr_prepare(idmap, dentry, attr);
if (err) if (err)
goto out; goto out;
...@@ -676,7 +696,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -676,7 +696,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
goto out; goto out;
} }
inode_dio_wait(inode); inode_dio_wait(inode);
oldsize = inode->i_size; oldsize = i_size_read(inode);
newsize = attr->ia_size; newsize = attr->ia_size;
if (newsize <= oldsize) if (newsize <= oldsize)
...@@ -688,7 +708,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -688,7 +708,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
goto out; goto out;
ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
inode->i_size = newsize; i_size_write(inode, newsize);
} }
setattr_copy(idmap, inode, attr); setattr_copy(idmap, inode, attr);
...@@ -718,6 +738,9 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -718,6 +738,9 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
struct ntfs_inode *ni = ntfs_i(inode); struct ntfs_inode *ni = ntfs_i(inode);
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
if (is_encrypted(ni)) { if (is_encrypted(ni)) {
ntfs_inode_warn(inode, "encrypted i/o not supported"); ntfs_inode_warn(inode, "encrypted i/o not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -752,6 +775,9 @@ static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos, ...@@ -752,6 +775,9 @@ static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
struct inode *inode = in->f_mapping->host; struct inode *inode = in->f_mapping->host;
struct ntfs_inode *ni = ntfs_i(inode); struct ntfs_inode *ni = ntfs_i(inode);
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
if (is_encrypted(ni)) { if (is_encrypted(ni)) {
ntfs_inode_warn(inode, "encrypted i/o not supported"); ntfs_inode_warn(inode, "encrypted i/o not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -821,7 +847,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) ...@@ -821,7 +847,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
size_t count = iov_iter_count(from); size_t count = iov_iter_count(from);
loff_t pos = iocb->ki_pos; loff_t pos = iocb->ki_pos;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
loff_t i_size = inode->i_size; loff_t i_size = i_size_read(inode);
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
struct ntfs_inode *ni = ntfs_i(inode); struct ntfs_inode *ni = ntfs_i(inode);
u64 valid = ni->i_valid; u64 valid = ni->i_valid;
...@@ -1028,6 +1054,8 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) ...@@ -1028,6 +1054,8 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
iocb->ki_pos += written; iocb->ki_pos += written;
if (iocb->ki_pos > ni->i_valid) if (iocb->ki_pos > ni->i_valid)
ni->i_valid = iocb->ki_pos; ni->i_valid = iocb->ki_pos;
if (iocb->ki_pos > i_size)
i_size_write(inode, iocb->ki_pos);
return written; return written;
} }
...@@ -1041,8 +1069,12 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -1041,8 +1069,12 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
ssize_t ret; ssize_t ret;
int err;
struct ntfs_inode *ni = ntfs_i(inode); struct ntfs_inode *ni = ntfs_i(inode);
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
if (is_encrypted(ni)) { if (is_encrypted(ni)) {
ntfs_inode_warn(inode, "encrypted i/o not supported"); ntfs_inode_warn(inode, "encrypted i/o not supported");
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1068,6 +1100,12 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -1068,6 +1100,12 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (ret <= 0) if (ret <= 0)
goto out; goto out;
err = file_modified(iocb->ki_filp);
if (err) {
ret = err;
goto out;
}
if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) {
/* Should never be here, see ntfs_file_open(). */ /* Should never be here, see ntfs_file_open(). */
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
...@@ -1097,6 +1135,9 @@ int ntfs_file_open(struct inode *inode, struct file *file) ...@@ -1097,6 +1135,9 @@ int ntfs_file_open(struct inode *inode, struct file *file)
{ {
struct ntfs_inode *ni = ntfs_i(inode); struct ntfs_inode *ni = ntfs_i(inode);
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
if (unlikely((is_compressed(ni) || is_encrypted(ni)) && if (unlikely((is_compressed(ni) || is_encrypted(ni)) &&
(file->f_flags & O_DIRECT))) { (file->f_flags & O_DIRECT))) {
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1138,7 +1179,8 @@ static int ntfs_file_release(struct inode *inode, struct file *file) ...@@ -1138,7 +1179,8 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
down_write(&ni->file.run_lock); down_write(&ni->file.run_lock);
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run,
inode->i_size, &ni->i_valid, false, NULL); i_size_read(inode), &ni->i_valid, false,
NULL);
up_write(&ni->file.run_lock); up_write(&ni->file.run_lock);
ni_unlock(ni); ni_unlock(ni);
......
...@@ -778,7 +778,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) ...@@ -778,7 +778,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
run_deallocate(sbi, &ni->attr_list.run, true); run_deallocate(sbi, &ni->attr_list.run, true);
run_close(&ni->attr_list.run); run_close(&ni->attr_list.run);
ni->attr_list.size = 0; ni->attr_list.size = 0;
kfree(ni->attr_list.le); kvfree(ni->attr_list.le);
ni->attr_list.le = NULL; ni->attr_list.le = NULL;
ni->attr_list.dirty = false; ni->attr_list.dirty = false;
...@@ -927,7 +927,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) ...@@ -927,7 +927,7 @@ int ni_create_attr_list(struct ntfs_inode *ni)
return 0; return 0;
out: out:
kfree(ni->attr_list.le); kvfree(ni->attr_list.le);
ni->attr_list.le = NULL; ni->attr_list.le = NULL;
ni->attr_list.size = 0; ni->attr_list.size = 0;
return err; return err;
...@@ -2099,7 +2099,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) ...@@ -2099,7 +2099,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page)
gfp_t gfp_mask; gfp_t gfp_mask;
struct page *pg; struct page *pg;
if (vbo >= ni->vfs_inode.i_size) { if (vbo >= i_size_read(&ni->vfs_inode)) {
SetPageUptodate(page); SetPageUptodate(page);
err = 0; err = 0;
goto out; goto out;
...@@ -2173,7 +2173,7 @@ int ni_decompress_file(struct ntfs_inode *ni) ...@@ -2173,7 +2173,7 @@ int ni_decompress_file(struct ntfs_inode *ni)
{ {
struct ntfs_sb_info *sbi = ni->mi.sbi; struct ntfs_sb_info *sbi = ni->mi.sbi;
struct inode *inode = &ni->vfs_inode; struct inode *inode = &ni->vfs_inode;
loff_t i_size = inode->i_size; loff_t i_size = i_size_read(inode);
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
gfp_t gfp_mask = mapping_gfp_mask(mapping); gfp_t gfp_mask = mapping_gfp_mask(mapping);
struct page **pages = NULL; struct page **pages = NULL;
...@@ -2457,6 +2457,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, ...@@ -2457,6 +2457,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
struct ATTR_LIST_ENTRY *le = NULL; struct ATTR_LIST_ENTRY *le = NULL;
struct runs_tree *run = &ni->file.run; struct runs_tree *run = &ni->file.run;
u64 valid_size = ni->i_valid; u64 valid_size = ni->i_valid;
loff_t i_size = i_size_read(&ni->vfs_inode);
u64 vbo_disk; u64 vbo_disk;
size_t unc_size; size_t unc_size;
u32 frame_size, i, npages_disk, ondisk_size; u32 frame_size, i, npages_disk, ondisk_size;
...@@ -2548,7 +2549,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, ...@@ -2548,7 +2549,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
} }
} }
frames = (ni->vfs_inode.i_size - 1) >> frame_bits; frames = (i_size - 1) >> frame_bits;
err = attr_wof_frame_info(ni, attr, run, frame64, frames, err = attr_wof_frame_info(ni, attr, run, frame64, frames,
frame_bits, &ondisk_size, &vbo_data); frame_bits, &ondisk_size, &vbo_data);
...@@ -2556,8 +2557,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, ...@@ -2556,8 +2557,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
goto out2; goto out2;
if (frame64 == frames) { if (frame64 == frames) {
unc_size = 1 + ((ni->vfs_inode.i_size - 1) & unc_size = 1 + ((i_size - 1) & (frame_size - 1));
(frame_size - 1));
ondisk_size = attr_size(attr) - vbo_data; ondisk_size = attr_size(attr) - vbo_data;
} else { } else {
unc_size = frame_size; unc_size = frame_size;
...@@ -3259,6 +3259,9 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) ...@@ -3259,6 +3259,9 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)
if (is_bad_inode(inode) || sb_rdonly(sb)) if (is_bad_inode(inode) || sb_rdonly(sb))
return 0; return 0;
if (unlikely(ntfs3_forced_shutdown(sb)))
return -EIO;
if (!ni_trylock(ni)) { if (!ni_trylock(ni)) {
/* 'ni' is under modification, skip for now. */ /* 'ni' is under modification, skip for now. */
mark_inode_dirty_sync(inode); mark_inode_dirty_sync(inode);
...@@ -3288,7 +3291,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) ...@@ -3288,7 +3291,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)
modified = true; modified = true;
} }
ts = inode_get_mtime(inode); ts = inode_get_ctime(inode);
dup.c_time = kernel2nt(&ts); dup.c_time = kernel2nt(&ts);
if (std->c_time != dup.c_time) { if (std->c_time != dup.c_time) {
std->c_time = dup.c_time; std->c_time = dup.c_time;
......
This diff is collapsed.
...@@ -853,7 +853,8 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) ...@@ -853,7 +853,8 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
/* /*
* sb can be NULL here. In this case sbi->flags should be 0 too. * sb can be NULL here. In this case sbi->flags should be 0 too.
*/ */
if (!sb || !(sbi->flags & NTFS_FLAGS_MFTMIRR)) if (!sb || !(sbi->flags & NTFS_FLAGS_MFTMIRR) ||
unlikely(ntfs3_forced_shutdown(sb)))
return; return;
blocksize = sb->s_blocksize; blocksize = sb->s_blocksize;
...@@ -1006,6 +1007,30 @@ static inline __le32 security_hash(const void *sd, size_t bytes) ...@@ -1006,6 +1007,30 @@ static inline __le32 security_hash(const void *sd, size_t bytes)
return cpu_to_le32(hash); return cpu_to_le32(hash);
} }
/*
* simple wrapper for sb_bread_unmovable.
*/
struct buffer_head *ntfs_bread(struct super_block *sb, sector_t block)
{
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct buffer_head *bh;
if (unlikely(block >= sbi->volume.blocks)) {
/* prevent generic message "attempt to access beyond end of device" */
ntfs_err(sb, "try to read out of volume at offset 0x%llx",
(u64)block << sb->s_blocksize_bits);
return NULL;
}
bh = sb_bread_unmovable(sb, block);
if (bh)
return bh;
ntfs_err(sb, "failed to read volume at offset 0x%llx",
(u64)block << sb->s_blocksize_bits);
return NULL;
}
int ntfs_sb_read(struct super_block *sb, u64 lbo, size_t bytes, void *buffer) int ntfs_sb_read(struct super_block *sb, u64 lbo, size_t bytes, void *buffer)
{ {
struct block_device *bdev = sb->s_bdev; struct block_device *bdev = sb->s_bdev;
...@@ -2128,8 +2153,8 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, ...@@ -2128,8 +2153,8 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
if (le32_to_cpu(d_security->size) == new_sec_size && if (le32_to_cpu(d_security->size) == new_sec_size &&
d_security->key.hash == hash_key.hash && d_security->key.hash == hash_key.hash &&
!memcmp(d_security + 1, sd, size_sd)) { !memcmp(d_security + 1, sd, size_sd)) {
*security_id = d_security->key.sec_id;
/* Such security already exists. */ /* Such security already exists. */
*security_id = d_security->key.sec_id;
err = 0; err = 0;
goto out; goto out;
} }
......
...@@ -1462,7 +1462,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, ...@@ -1462,7 +1462,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
goto out2; goto out2;
if (in->name == I30_NAME) { if (in->name == I30_NAME) {
ni->vfs_inode.i_size = data_size; i_size_write(&ni->vfs_inode, data_size);
inode_set_bytes(&ni->vfs_inode, alloc_size); inode_set_bytes(&ni->vfs_inode, alloc_size);
} }
...@@ -1544,7 +1544,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, ...@@ -1544,7 +1544,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
} }
if (in->name == I30_NAME) if (in->name == I30_NAME)
ni->vfs_inode.i_size = data_size; i_size_write(&ni->vfs_inode, data_size);
*vbn = bit << indx->idx2vbn_bits; *vbn = bit << indx->idx2vbn_bits;
...@@ -2090,7 +2090,7 @@ static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni, ...@@ -2090,7 +2090,7 @@ static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni,
return err; return err;
if (in->name == I30_NAME) if (in->name == I30_NAME)
ni->vfs_inode.i_size = new_data; i_size_write(&ni->vfs_inode, new_data);
bpb = bitmap_size(bit); bpb = bitmap_size(bit);
if (bpb * 8 == nbits) if (bpb * 8 == nbits)
...@@ -2576,7 +2576,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, ...@@ -2576,7 +2576,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len, 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, NULL);
if (in->name == I30_NAME) if (in->name == I30_NAME)
ni->vfs_inode.i_size = 0; i_size_write(&ni->vfs_inode, 0);
err = ni_remove_attr(ni, ATTR_ALLOC, in->name, in->name_len, err = ni_remove_attr(ni, ATTR_ALLOC, in->name, in->name_len,
false, NULL); false, NULL);
......
...@@ -345,9 +345,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, ...@@ -345,9 +345,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
inode->i_size = le16_to_cpu(rp.SymbolicLinkReparseBuffer inode->i_size = le16_to_cpu(rp.SymbolicLinkReparseBuffer
.PrintNameLength) / .PrintNameLength) /
sizeof(u16); sizeof(u16);
ni->i_valid = inode->i_size; ni->i_valid = inode->i_size;
/* Clear directory bit. */ /* Clear directory bit. */
if (ni->ni_flags & NI_FLAG_DIR) { if (ni->ni_flags & NI_FLAG_DIR) {
indx_clear(&ni->dir); indx_clear(&ni->dir);
...@@ -412,7 +410,6 @@ static struct inode *ntfs_read_mft(struct inode *inode, ...@@ -412,7 +410,6 @@ static struct inode *ntfs_read_mft(struct inode *inode,
goto out; goto out;
if (!is_match && name) { if (!is_match && name) {
/* Reuse rec as buffer for ascii name. */
err = -ENOENT; err = -ENOENT;
goto out; goto out;
} }
...@@ -427,6 +424,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, ...@@ -427,6 +424,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
if (names != le16_to_cpu(rec->hard_links)) { if (names != le16_to_cpu(rec->hard_links)) {
/* Correct minor error on the fly. Do not mark inode as dirty. */ /* Correct minor error on the fly. Do not mark inode as dirty. */
ntfs_inode_warn(inode, "Correct links count -> %u.", names);
rec->hard_links = cpu_to_le16(names); rec->hard_links = cpu_to_le16(names);
ni->mi.dirty = true; ni->mi.dirty = true;
} }
...@@ -653,9 +651,10 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, ...@@ -653,9 +651,10 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo,
off = vbo & (PAGE_SIZE - 1); off = vbo & (PAGE_SIZE - 1);
folio_set_bh(bh, folio, off); folio_set_bh(bh, folio, off);
err = bh_read(bh, 0); if (bh_read(bh, 0) < 0) {
if (err < 0) err = -EIO;
goto out; goto out;
}
folio_zero_segment(folio, off + voff, off + block_size); folio_zero_segment(folio, off + voff, off + block_size);
} }
} }
...@@ -853,9 +852,13 @@ static int ntfs_resident_writepage(struct folio *folio, ...@@ -853,9 +852,13 @@ static int ntfs_resident_writepage(struct folio *folio,
struct writeback_control *wbc, void *data) struct writeback_control *wbc, void *data)
{ {
struct address_space *mapping = data; struct address_space *mapping = data;
struct ntfs_inode *ni = ntfs_i(mapping->host); struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
int ret; int ret;
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
ni_lock(ni); ni_lock(ni);
ret = attr_data_write_resident(ni, &folio->page); ret = attr_data_write_resident(ni, &folio->page);
ni_unlock(ni); ni_unlock(ni);
...@@ -869,7 +872,12 @@ static int ntfs_resident_writepage(struct folio *folio, ...@@ -869,7 +872,12 @@ 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)
{ {
if (is_resident(ntfs_i(mapping->host))) struct inode *inode = mapping->host;
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
if (is_resident(ntfs_i(inode)))
return write_cache_pages(mapping, wbc, ntfs_resident_writepage, return write_cache_pages(mapping, wbc, ntfs_resident_writepage,
mapping); mapping);
return mpage_writepages(mapping, wbc, ntfs_get_block); return mpage_writepages(mapping, wbc, ntfs_get_block);
...@@ -889,6 +897,9 @@ int ntfs_write_begin(struct file *file, struct address_space *mapping, ...@@ -889,6 +897,9 @@ int ntfs_write_begin(struct file *file, struct address_space *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);
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
*pagep = NULL; *pagep = NULL;
if (is_resident(ni)) { if (is_resident(ni)) {
struct page *page = struct page *page =
...@@ -974,7 +985,7 @@ int ntfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, ...@@ -974,7 +985,7 @@ int ntfs_write_end(struct file *file, struct address_space *mapping, loff_t pos,
} }
if (pos + err > inode->i_size) { if (pos + err > inode->i_size) {
inode->i_size = pos + err; i_size_write(inode, pos + err);
dirty = true; dirty = true;
} }
...@@ -1306,6 +1317,11 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, ...@@ -1306,6 +1317,11 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
goto out1; goto out1;
} }
if (unlikely(ntfs3_forced_shutdown(sb))) {
err = -EIO;
goto out2;
}
/* Mark rw ntfs as dirty. it will be cleared at umount. */ /* Mark rw ntfs as dirty. it will be cleared at umount. */
ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
......
...@@ -181,6 +181,9 @@ static int ntfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -181,6 +181,9 @@ static int ntfs_unlink(struct inode *dir, struct dentry *dentry)
struct ntfs_inode *ni = ntfs_i(dir); struct ntfs_inode *ni = ntfs_i(dir);
int err; int err;
if (unlikely(ntfs3_forced_shutdown(dir->i_sb)))
return -EIO;
ni_lock_dir(ni); ni_lock_dir(ni);
err = ntfs_unlink_inode(dir, dentry); err = ntfs_unlink_inode(dir, dentry);
...@@ -199,6 +202,9 @@ static int ntfs_symlink(struct mnt_idmap *idmap, struct inode *dir, ...@@ -199,6 +202,9 @@ static int ntfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
u32 size = strlen(symname); u32 size = strlen(symname);
struct inode *inode; struct inode *inode;
if (unlikely(ntfs3_forced_shutdown(dir->i_sb)))
return -EIO;
inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFLNK | 0777, 0, inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFLNK | 0777, 0,
symname, size, NULL); symname, size, NULL);
...@@ -227,6 +233,9 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -227,6 +233,9 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry)
struct ntfs_inode *ni = ntfs_i(dir); struct ntfs_inode *ni = ntfs_i(dir);
int err; int err;
if (unlikely(ntfs3_forced_shutdown(dir->i_sb)))
return -EIO;
ni_lock_dir(ni); ni_lock_dir(ni);
err = ntfs_unlink_inode(dir, dentry); err = ntfs_unlink_inode(dir, dentry);
...@@ -264,6 +273,9 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir, ...@@ -264,6 +273,9 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir,
1024); 1024);
static_assert(PATH_MAX >= 4 * 1024); static_assert(PATH_MAX >= 4 * 1024);
if (unlikely(ntfs3_forced_shutdown(sb)))
return -EIO;
if (flags & ~RENAME_NOREPLACE) if (flags & ~RENAME_NOREPLACE)
return -EINVAL; return -EINVAL;
......
...@@ -523,12 +523,10 @@ struct ATTR_LIST_ENTRY { ...@@ -523,12 +523,10 @@ struct ATTR_LIST_ENTRY {
__le64 vcn; // 0x08: Starting VCN of this attribute. __le64 vcn; // 0x08: Starting VCN of this attribute.
struct MFT_REF ref; // 0x10: MFT record number with attribute. struct MFT_REF ref; // 0x10: MFT record number with attribute.
__le16 id; // 0x18: struct ATTRIB ID. __le16 id; // 0x18: struct ATTRIB ID.
__le16 name[3]; // 0x1A: Just to align. To get real name can use bNameOffset. __le16 name[]; // 0x1A: To get real name use name_off.
}; // sizeof(0x20) }; // sizeof(0x20)
static_assert(sizeof(struct ATTR_LIST_ENTRY) == 0x20);
static inline u32 le_size(u8 name_len) static inline u32 le_size(u8 name_len)
{ {
return ALIGN(offsetof(struct ATTR_LIST_ENTRY, name) + return ALIGN(offsetof(struct ATTR_LIST_ENTRY, name) +
......
...@@ -61,6 +61,8 @@ enum utf16_endian; ...@@ -61,6 +61,8 @@ enum utf16_endian;
/* sbi->flags */ /* sbi->flags */
#define NTFS_FLAGS_NODISCARD 0x00000001 #define NTFS_FLAGS_NODISCARD 0x00000001
/* ntfs in shutdown state. */
#define NTFS_FLAGS_SHUTDOWN_BIT 0x00000002 /* == 4*/
/* Set when LogFile is replaying. */ /* Set when LogFile is replaying. */
#define NTFS_FLAGS_LOG_REPLAYING 0x00000008 #define NTFS_FLAGS_LOG_REPLAYING 0x00000008
/* Set when we changed first MFT's which copy must be updated in $MftMirr. */ /* Set when we changed first MFT's which copy must be updated in $MftMirr. */
...@@ -226,7 +228,7 @@ struct ntfs_sb_info { ...@@ -226,7 +228,7 @@ struct ntfs_sb_info {
u64 maxbytes; // Maximum size for normal files. u64 maxbytes; // Maximum size for normal files.
u64 maxbytes_sparse; // Maximum size for sparse file. u64 maxbytes_sparse; // Maximum size for sparse file.
u32 flags; // See NTFS_FLAGS_XXX. unsigned long flags; // See NTFS_FLAGS_
CLST zone_max; // Maximum MFT zone length in clusters CLST zone_max; // Maximum MFT zone length in clusters
CLST bad_clusters; // The count of marked bad clusters. CLST bad_clusters; // The count of marked bad clusters.
...@@ -473,7 +475,7 @@ bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, ...@@ -473,7 +475,7 @@ bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
int al_update(struct ntfs_inode *ni, int sync); int al_update(struct ntfs_inode *ni, int sync);
static inline size_t al_aligned(size_t size) static inline size_t al_aligned(size_t size)
{ {
return (size + 1023) & ~(size_t)1023; return size_add(size, 1023) & ~(size_t)1023;
} }
/* Globals from bitfunc.c */ /* Globals from bitfunc.c */
...@@ -500,6 +502,8 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -500,6 +502,8 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
int ntfs_file_open(struct inode *inode, struct file *file); int ntfs_file_open(struct inode *inode, struct file *file);
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len); __u64 start, __u64 len);
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; extern const struct inode_operations ntfs_special_inode_operations;
extern const struct inode_operations ntfs_file_inode_operations; extern const struct inode_operations ntfs_file_inode_operations;
extern const struct file_operations ntfs_file_operations; extern const struct file_operations ntfs_file_operations;
...@@ -584,6 +588,7 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes); ...@@ -584,6 +588,7 @@ bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
int log_replay(struct ntfs_inode *ni, bool *initialized); int log_replay(struct ntfs_inode *ni, bool *initialized);
/* Globals from fsntfs.c */ /* Globals from fsntfs.c */
struct buffer_head *ntfs_bread(struct super_block *sb, sector_t block);
bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes); bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes);
int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes,
bool simple); bool simple);
...@@ -872,7 +877,7 @@ int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode, ...@@ -872,7 +877,7 @@ int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode,
int ntfs_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry); int ntfs_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry);
ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size); ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
extern const struct xattr_handler * const ntfs_xattr_handlers[]; extern const struct xattr_handler *const ntfs_xattr_handlers[];
int ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size); int ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size);
void ntfs_get_wsl_perm(struct inode *inode); void ntfs_get_wsl_perm(struct inode *inode);
...@@ -999,6 +1004,11 @@ static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb) ...@@ -999,6 +1004,11 @@ static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb)
return sb->s_fs_info; return sb->s_fs_info;
} }
static inline int ntfs3_forced_shutdown(struct super_block *sb)
{
return test_bit(NTFS_FLAGS_SHUTDOWN_BIT, &ntfs_sb(sb)->flags);
}
/* /*
* ntfs_up_cluster - Align up on cluster boundary. * ntfs_up_cluster - Align up on cluster boundary.
*/ */
...@@ -1025,19 +1035,6 @@ static inline u64 bytes_to_block(const struct super_block *sb, u64 size) ...@@ -1025,19 +1035,6 @@ static inline u64 bytes_to_block(const struct super_block *sb, u64 size)
return (size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; return (size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
} }
static inline struct buffer_head *ntfs_bread(struct super_block *sb,
sector_t block)
{
struct buffer_head *bh = sb_bread(sb, block);
if (bh)
return bh;
ntfs_err(sb, "failed to read volume at offset 0x%llx",
(u64)block << sb->s_blocksize_bits);
return NULL;
}
static inline struct ntfs_inode *ntfs_i(struct inode *inode) static inline struct ntfs_inode *ntfs_i(struct inode *inode)
{ {
return container_of(inode, struct ntfs_inode, vfs_inode); return container_of(inode, struct ntfs_inode, vfs_inode);
......
...@@ -279,7 +279,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) ...@@ -279,7 +279,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
if (t16 > asize) if (t16 > asize)
return NULL; return NULL;
if (t16 + le32_to_cpu(attr->res.data_size) > asize) if (le32_to_cpu(attr->res.data_size) > asize - t16)
return NULL; return NULL;
t32 = sizeof(short) * attr->name_len; t32 = sizeof(short) * attr->name_len;
...@@ -535,8 +535,20 @@ bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi, ...@@ -535,8 +535,20 @@ bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
return false; return false;
if (ni && is_attr_indexed(attr)) { if (ni && is_attr_indexed(attr)) {
le16_add_cpu(&ni->mi.mrec->hard_links, -1); u16 links = le16_to_cpu(ni->mi.mrec->hard_links);
ni->mi.dirty = true; struct ATTR_FILE_NAME *fname =
attr->type != ATTR_NAME ?
NULL :
resident_data_ex(attr,
SIZEOF_ATTRIBUTE_FILENAME);
if (fname && fname->type == FILE_NAME_DOS) {
/* Do not decrease links count deleting DOS name. */
} else if (!links) {
/* minor error. Not critical. */
} else {
ni->mi.mrec->hard_links = cpu_to_le16(links - 1);
ni->mi.dirty = true;
}
} }
used -= asize; used -= asize;
......
...@@ -122,13 +122,12 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) ...@@ -122,13 +122,12 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...)
if (name) { if (name) {
struct dentry *de = d_find_alias(inode); struct dentry *de = d_find_alias(inode);
const u32 name_len = ARRAY_SIZE(s_name_buf) - 1;
if (de) { if (de) {
spin_lock(&de->d_lock); spin_lock(&de->d_lock);
snprintf(name, name_len, " \"%s\"", de->d_name.name); snprintf(name, sizeof(s_name_buf), " \"%s\"",
de->d_name.name);
spin_unlock(&de->d_lock); spin_unlock(&de->d_lock);
name[name_len] = 0; /* To be sure. */
} else { } else {
name[0] = 0; name[0] = 0;
} }
...@@ -625,7 +624,7 @@ static void ntfs3_free_sbi(struct ntfs_sb_info *sbi) ...@@ -625,7 +624,7 @@ static void ntfs3_free_sbi(struct ntfs_sb_info *sbi)
{ {
kfree(sbi->new_rec); kfree(sbi->new_rec);
kvfree(ntfs_put_shared(sbi->upcase)); kvfree(ntfs_put_shared(sbi->upcase));
kfree(sbi->def_table); kvfree(sbi->def_table);
kfree(sbi->compress.lznt); kfree(sbi->compress.lznt);
#ifdef CONFIG_NTFS3_LZX_XPRESS #ifdef CONFIG_NTFS3_LZX_XPRESS
xpress_free_decompressor(sbi->compress.xpress); xpress_free_decompressor(sbi->compress.xpress);
...@@ -714,6 +713,14 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) ...@@ -714,6 +713,14 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root)
return 0; return 0;
} }
/*
* ntfs_shutdown - super_operations::shutdown
*/
static void ntfs_shutdown(struct super_block *sb)
{
set_bit(NTFS_FLAGS_SHUTDOWN_BIT, &ntfs_sb(sb)->flags);
}
/* /*
* ntfs_sync_fs - super_operations::sync_fs * ntfs_sync_fs - super_operations::sync_fs
*/ */
...@@ -724,6 +731,9 @@ static int ntfs_sync_fs(struct super_block *sb, int wait) ...@@ -724,6 +731,9 @@ static int ntfs_sync_fs(struct super_block *sb, int wait)
struct ntfs_inode *ni; struct ntfs_inode *ni;
struct inode *inode; struct inode *inode;
if (unlikely(ntfs3_forced_shutdown(sb)))
return -EIO;
ni = sbi->security.ni; ni = sbi->security.ni;
if (ni) { if (ni) {
inode = &ni->vfs_inode; inode = &ni->vfs_inode;
...@@ -763,6 +773,7 @@ static const struct super_operations ntfs_sops = { ...@@ -763,6 +773,7 @@ static const struct super_operations ntfs_sops = {
.put_super = ntfs_put_super, .put_super = ntfs_put_super,
.statfs = ntfs_statfs, .statfs = ntfs_statfs,
.show_options = ntfs_show_options, .show_options = ntfs_show_options,
.shutdown = ntfs_shutdown,
.sync_fs = ntfs_sync_fs, .sync_fs = ntfs_sync_fs,
.write_inode = ntfs3_write_inode, .write_inode = ntfs3_write_inode,
}; };
...@@ -866,6 +877,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -866,6 +877,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
u16 fn, ao; u16 fn, ao;
u8 cluster_bits; u8 cluster_bits;
u32 boot_off = 0; u32 boot_off = 0;
sector_t boot_block = 0;
const char *hint = "Primary boot"; const char *hint = "Primary boot";
/* Save original dev_size. Used with alternative boot. */ /* Save original dev_size. Used with alternative boot. */
...@@ -873,11 +885,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -873,11 +885,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
sbi->volume.blocks = dev_size >> PAGE_SHIFT; sbi->volume.blocks = dev_size >> PAGE_SHIFT;
bh = ntfs_bread(sb, 0); read_boot:
bh = ntfs_bread(sb, boot_block);
if (!bh) if (!bh)
return -EIO; return boot_block ? -EINVAL : -EIO;
check_boot:
err = -EINVAL; err = -EINVAL;
/* Corrupted image; do not read OOB */ /* Corrupted image; do not read OOB */
...@@ -1108,26 +1120,24 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ...@@ -1108,26 +1120,24 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
} }
out: out:
if (err == -EINVAL && !bh->b_blocknr && dev_size0 > PAGE_SHIFT) { brelse(bh);
if (err == -EINVAL && !boot_block && dev_size0 > PAGE_SHIFT) {
u32 block_size = min_t(u32, sector_size, PAGE_SIZE); u32 block_size = min_t(u32, sector_size, PAGE_SIZE);
u64 lbo = dev_size0 - sizeof(*boot); u64 lbo = dev_size0 - sizeof(*boot);
/* boot_block = lbo >> blksize_bits(block_size);
* Try alternative boot (last sector)
*/
brelse(bh);
sb_set_blocksize(sb, block_size);
bh = ntfs_bread(sb, lbo >> blksize_bits(block_size));
if (!bh)
return -EINVAL;
boot_off = lbo & (block_size - 1); boot_off = lbo & (block_size - 1);
hint = "Alternative boot"; if (boot_block && block_size >= boot_off + sizeof(*boot)) {
dev_size = dev_size0; /* restore original size. */ /*
goto check_boot; * Try alternative boot (last sector)
*/
sb_set_blocksize(sb, block_size);
hint = "Alternative boot";
dev_size = dev_size0; /* restore original size. */
goto read_boot;
}
} }
brelse(bh);
return err; return err;
} }
......
...@@ -219,6 +219,9 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, ...@@ -219,6 +219,9 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
if (!ea->name_len) if (!ea->name_len)
break; break;
if (ea->name_len > ea_size)
break;
if (buffer) { if (buffer) {
/* Check if we can use field ea->name */ /* Check if we can use field ea->name */
if (off + ea_size > size) if (off + ea_size > size)
...@@ -744,6 +747,9 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, ...@@ -744,6 +747,9 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de,
int err; int err;
struct ntfs_inode *ni = ntfs_i(inode); struct ntfs_inode *ni = ntfs_i(inode);
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
/* Dispatch request. */ /* Dispatch request. */
if (!strcmp(name, SYSTEM_DOS_ATTRIB)) { if (!strcmp(name, SYSTEM_DOS_ATTRIB)) {
/* system.dos_attrib */ /* system.dos_attrib */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment