Commit a2343df3 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+28 −17
Original line number Diff line number Diff line
@@ -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 ntfs_sb_info *sbi;
	u8 cluster_bits;
	struct ATTRIB *attr = NULL, *attr_b;
	struct ATTRIB *attr, *attr_b;
	struct ATTR_LIST_ENTRY *le, *le_b;
	struct mft_inode *mi, *mi_b;
	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,
		*len = 0;
	up_read(&ni->file.run_lock);

	if (*len) {
		if (*lcn != SPARSE_LCN || !new)
	if (*len && (*lcn != SPARSE_LCN || !new))
		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. */
	sbi = ni->mi.sbi;
@@ -918,6 +914,17 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
	ni_lock(ni);
	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;
	attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b);
	if (!attr_b) {
@@ -1736,8 +1743,10 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
			le_b = NULL;
			attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL,
					      0, NULL, &mi_b);
			if (!attr_b)
				return -ENOENT;
			if (!attr_b) {
				err = -ENOENT;
				goto out;
			}

			attr = attr_b;
			le = le_b;
@@ -1818,6 +1827,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
ok:
	run_truncate_around(run, vcn);
out:
	if (attr_b) {
		if (new_valid > data_size)
			new_valid = data_size;

@@ -1826,6 +1836,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
			attr_b->nres.valid_size = cpu_to_le64(valid_size);
			mi_b->dirty = true;
		}
	}

	return err;
}
@@ -2073,7 +2084,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)

	/* Update inode 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);
	ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
	mark_inode_dirty(&ni->vfs_inode);
@@ -2488,7 +2499,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
	mi_b->dirty = true;

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;
	mark_inode_dirty(&ni->vfs_inode);

+6 −6
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ static inline bool al_is_valid_le(const struct ntfs_inode *ni,
void al_destroy(struct ntfs_inode *ni)
{
	run_close(&ni->attr_list.run);
	kfree(ni->attr_list.le);
	kvfree(ni->attr_list.le);
	ni->attr_list.le = NULL;
	ni->attr_list.size = 0;
	ni->attr_list.dirty = false;
@@ -127,12 +127,13 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
{
	size_t off;
	u16 sz;
	const unsigned le_min_size = le_size(0);

	if (!le) {
		le = ni->attr_list.le;
	} else {
		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. */
			return NULL;
		}
@@ -141,7 +142,7 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,

	/* Check boundary. */
	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. */
		return NULL;
	}
@@ -149,8 +150,7 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
	sz = le16_to_cpu(le->size);

	/* Check le for errors. */
	if (sz < sizeof(struct ATTR_LIST_ENTRY) ||
	    off + sz > ni->attr_list.size ||
	if (sz < le_min_size || off + sz > ni->attr_list.size ||
	    sz < le->name_off + le->name_len * sizeof(short)) {
		return NULL;
	}
@@ -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(Add2Ptr(ptr, off + sz), le, old_size - off);
		le = Add2Ptr(ptr, off);
		kfree(al->le);
		kvfree(al->le);
		al->le = ptr;
	} else {
		memmove(Add2Ptr(le, sz), le, old_size - off);
+2 −2
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ void wnd_close(struct wnd_bitmap *wnd)
{
	struct rb_node *node, *next;

	kfree(wnd->free_bits);
	kvfree(wnd->free_bits);
	wnd->free_bits = NULL;
	run_close(&wnd->run);

@@ -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));
		memset(new_free + wnd->nwnd, 0,
		       (new_wnd - wnd->nwnd) * sizeof(short));
		kfree(wnd->free_bits);
		kvfree(wnd->free_bits);
		wnd->free_bits = new_free;
	}

+35 −13
Original line number Diff line number Diff line
@@ -309,12 +309,32 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
		return 0;
	}

	/* NTFS: symlinks are "dir + reparse" or "file + reparse" */
	if (fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT)
		dt_type = DT_LNK;
	else
	/*
	 * NTFS: symlinks are "dir + reparse" or "file + reparse"
	 * Unfortunately reparse attribute is used for many purposes (several dozens).
	 * It is not possible here to know is this name symlink or not.
	 * 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);
}

@@ -495,11 +515,9 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
	struct INDEX_HDR *hdr;
	const struct ATTR_FILE_NAME *fname;
	u32 e_size, off, end;
	u64 vbo = 0;
	size_t drs = 0, fles = 0, bit = 0;
	loff_t i_size = ni->vfs_inode.i_size;
	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)
		*is_empty = true;
@@ -518,8 +536,10 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
			e = Add2Ptr(hdr, off);
			e_size = le16_to_cpu(e->size);
			if (e_size < sizeof(struct NTFS_DE) ||
			    off + e_size > end)
			    off + e_size > end) {
				/* Looks like corruption. */
				break;
			}

			if (de_is_last(e))
				break;
@@ -543,7 +563,7 @@ static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
				fles += 1;
		}

		if (vbo >= i_size)
		if (bit >= max_indx)
			goto out;

		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,
		if (bit == MINUS_ONE_T)
			goto out;

		vbo = (u64)bit << index_bits;
		if (vbo >= i_size)
		if (bit >= max_indx)
			goto out;

		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,

		hdr = &node->index->ihdr;
		bit += 1;
		vbo = (u64)bit << ni->dir.idx2vbn_bits;
	}

out:
@@ -593,5 +611,9 @@ const struct file_operations ntfs_dir_operations = {
	.iterate_shared	= ntfs_readdir,
	.fsync		= generic_file_fsync,
	.open		= ntfs_file_open,
	.unlocked_ioctl = ntfs_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl   = ntfs_compat_ioctl,
#endif
};
// clang-format on
+59 −17
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
	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 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)
}

#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));
@@ -188,6 +188,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
	u32 bh_next, bh_off, to;
	sector_t iblock;
	struct folio *folio;
	bool dirty = false;

	for (; idx < idx_end; idx += 1, from = 0) {
		page_off = (loff_t)idx << PAGE_SHIFT;
@@ -223,28 +224,26 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
			/* Ok, it's mapped. Make sure it's up-to-date. */
			if (folio_test_uptodate(folio))
				set_buffer_uptodate(bh);

			if (!buffer_uptodate(bh)) {
				err = bh_read(bh, 0);
				if (err < 0) {
			else if (bh_read(bh, 0) < 0) {
				err = -EIO;
				folio_unlock(folio);
				folio_put(folio);
				goto out;
			}
			}

			mark_buffer_dirty(bh);

		} while (bh_off = bh_next, iblock += 1,
			 head != (bh = bh->b_this_page));

		folio_zero_segment(folio, from, to);
		dirty = true;

		folio_unlock(folio);
		folio_put(folio);
		cond_resched();
	}
out:
	if (dirty)
		mark_inode_dirty(inode);
	return err;
}
@@ -261,6 +260,9 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma)
	bool rw = vma->vm_flags & VM_WRITE;
	int err;

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
		return -EIO;

	if (is_encrypted(ni)) {
		ntfs_inode_warn(inode, "mmap encrypted not supported");
		return -EOPNOTSUPP;
@@ -499,10 +501,14 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
		ni_lock(ni);
		err = attr_punch_hole(ni, vbo, len, &frame_size);
		ni_unlock(ni);
		if (!err)
			goto ok;

		if (err != E_NTFS_NOTALIGNED)
			goto out;

		/* Process not aligned punch. */
		err = 0;
		mask = frame_size - 1;
		vbo_a = (vbo + mask) & ~mask;
		end_a = end & ~mask;
@@ -525,6 +531,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
			ni_lock(ni);
			err = attr_punch_hole(ni, vbo_a, end_a - vbo_a, NULL);
			ni_unlock(ni);
			if (err)
				goto out;
		}
	} 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)
		ni_lock(ni);
		err = attr_insert_range(ni, vbo, len);
		ni_unlock(ni);
		if (err)
			goto out;
	} else {
		/* Check new size. */
		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)
					    &ni->file.run, i_size, &ni->i_valid,
					    true, NULL);
			ni_unlock(ni);
			if (err)
				goto out;
		} 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:
	if (map_locked)
		filemap_invalidate_unlock(mapping);
@@ -663,6 +680,9 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
	umode_t mode = inode->i_mode;
	int err;

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
		return -EIO;

	err = setattr_prepare(idmap, dentry, attr);
	if (err)
		goto out;
@@ -676,7 +696,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
			goto out;
		}
		inode_dio_wait(inode);
		oldsize = inode->i_size;
		oldsize = i_size_read(inode);
		newsize = attr->ia_size;

		if (newsize <= oldsize)
@@ -688,7 +708,7 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
			goto out;

		ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
		inode->i_size = newsize;
		i_size_write(inode, newsize);
	}

	setattr_copy(idmap, inode, attr);
@@ -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 ntfs_inode *ni = ntfs_i(inode);

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
		return -EIO;

	if (is_encrypted(ni)) {
		ntfs_inode_warn(inode, "encrypted i/o not supported");
		return -EOPNOTSUPP;
@@ -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 ntfs_inode *ni = ntfs_i(inode);

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
		return -EIO;

	if (is_encrypted(ni)) {
		ntfs_inode_warn(inode, "encrypted i/o not supported");
		return -EOPNOTSUPP;
@@ -821,7 +847,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
	size_t count = iov_iter_count(from);
	loff_t pos = iocb->ki_pos;
	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 ntfs_inode *ni = ntfs_i(inode);
	u64 valid = ni->i_valid;
@@ -1028,6 +1054,8 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
	iocb->ki_pos += written;
	if (iocb->ki_pos > ni->i_valid)
		ni->i_valid = iocb->ki_pos;
	if (iocb->ki_pos > i_size)
		i_size_write(inode, iocb->ki_pos);

	return written;
}
@@ -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 inode *inode = mapping->host;
	ssize_t ret;
	int err;
	struct ntfs_inode *ni = ntfs_i(inode);

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
		return -EIO;

	if (is_encrypted(ni)) {
		ntfs_inode_warn(inode, "encrypted i/o not supported");
		return -EOPNOTSUPP;
@@ -1068,6 +1100,12 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
	if (ret <= 0)
		goto out;

	err = file_modified(iocb->ki_filp);
	if (err) {
		ret = err;
		goto out;
	}

	if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) {
		/* Should never be here, see ntfs_file_open(). */
		ret = -EOPNOTSUPP;
@@ -1097,6 +1135,9 @@ int ntfs_file_open(struct inode *inode, struct file *file)
{
	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)) &&
		     (file->f_flags & O_DIRECT))) {
		return -EOPNOTSUPP;
@@ -1138,7 +1179,8 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
		down_write(&ni->file.run_lock);

		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);
		ni_unlock(ni);
Loading