Commit 36b93aed authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ntfs3 updates from Konstantin Komarov:
 "Updates:
   - support /proc/fs/ntfs3/<dev>/volinfo and label
   - alternative boot if primary boot is corrupted
   - small optimizations

  Fixes:
   - fix endian problems
   - fix logic errors
   - code refactoring and reformatting"

* tag 'ntfs3_for_6.5' of https://github.com/Paragon-Software-Group/linux-ntfs3:
  fs/ntfs3: Correct mode for label entry inside /proc/fs/ntfs3/
  fs/ntfs3: Add support /proc/fs/ntfs3/<dev>/volinfo and /proc/fs/ntfs3/<dev>/label
  fs/ntfs3: Fix endian problem
  fs/ntfs3: Add ability to format new mft records with bigger/smaller header
  fs/ntfs3: Code refactoring
  fs/ntfs3: Code formatting
  fs/ntfs3: Do not update primary boot in ntfs_init_from_boot()
  fs/ntfs3: Alternative boot if primary boot is corrupted
  fs/ntfs3: Mark ntfs dirty when on-disk struct is corrupted
  fs/ntfs3: Fix ntfs_atomic_open
  fs/ntfs3: Correct checking while generating attr_list
  fs/ntfs3: Use __GFP_NOWARN allocation at ntfs_load_attr_list()
  fs: ntfs3: Fix possible null-pointer dereferences in mi_read()
  fs/ntfs3: Return error for inconsistent extended attributes
  fs/ntfs3: Enhance sanity check while generating attr_list
  fs/ntfs3: Use wrapper i_blocksize() in ntfs_zero_range()
  ntfs: Fix panic about slab-out-of-bounds caused by ntfs_listxattr()
parents 986ffe60 44b4494d
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)

	if (!attr->non_res) {
		lsize = le32_to_cpu(attr->res.data_size);
		le = kmalloc(al_aligned(lsize), GFP_NOFS);
		le = kmalloc(al_aligned(lsize), GFP_NOFS | __GFP_NOWARN);
		if (!le) {
			err = -ENOMEM;
			goto out;
@@ -80,7 +80,7 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
		if (err < 0)
			goto out;

		le = kmalloc(al_aligned(lsize), GFP_NOFS);
		le = kmalloc(al_aligned(lsize), GFP_NOFS | __GFP_NOWARN);
		if (!le) {
			err = -ENOMEM;
			goto out;
@@ -375,8 +375,7 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
 * al_delete_le - Delete first le from the list which matches its parameters.
 */
bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
		  const __le16 *name, size_t name_len,
		  const struct MFT_REF *ref)
		  const __le16 *name, u8 name_len, const struct MFT_REF *ref)
{
	u16 size;
	struct ATTR_LIST_ENTRY *le;
+3 −3
Original line number Diff line number Diff line
@@ -179,7 +179,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
{
	int err = 0;
	struct address_space *mapping = inode->i_mapping;
	u32 blocksize = 1 << inode->i_blkbits;
	u32 blocksize = i_blocksize(inode);
	pgoff_t idx = vbo >> PAGE_SHIFT;
	u32 from = vbo & (PAGE_SIZE - 1);
	pgoff_t idx_end = (vbo_to + PAGE_SIZE - 1) >> PAGE_SHIFT;
+31 −27
Original line number Diff line number Diff line
@@ -236,6 +236,7 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr,
	return attr;

out:
	ntfs_inode_err(&ni->vfs_inode, "failed to parse mft record");
	ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR);
	return NULL;
}
@@ -384,7 +385,7 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi)
 * ni_remove_attr - Remove all attributes for the given type/name/id.
 */
int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
		   const __le16 *name, size_t name_len, bool base_only,
		   const __le16 *name, u8 name_len, bool base_only,
		   const __le16 *id)
{
	int err;
@@ -517,6 +518,9 @@ ni_ins_new_attr(struct ntfs_inode *ni, struct mft_inode *mi,
 */
static int ni_repack(struct ntfs_inode *ni)
{
#if 1
	return 0;
#else
	int err = 0;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	struct mft_inode *mi, *mi_p = NULL;
@@ -639,6 +643,7 @@ static int ni_repack(struct ntfs_inode *ni)

	run_close(&run);
	return err;
#endif
}

/*
@@ -813,10 +818,8 @@ int ni_create_attr_list(struct ntfs_inode *ni)
	 * Looks like one record_size is always enough.
	 */
	le = kmalloc(al_aligned(rs), GFP_NOFS);
	if (!le) {
		err = -ENOMEM;
		goto out;
	}
	if (!le)
		return -ENOMEM;

	mi_get_ref(&ni->mi, &le->ref);
	ni->attr_list.le = le;
@@ -865,15 +868,16 @@ int ni_create_attr_list(struct ntfs_inode *ni)

		if (to_free > free_b) {
			err = -EINVAL;
			goto out1;
			goto out;
		}
	}

	/* Allocate child MFT. */
	err = ntfs_look_free_mft(sbi, &rno, is_mft, ni, &mi);
	if (err)
		goto out1;
		goto out;

	err = -EINVAL;
	/* Call mi_remove_attr() in reverse order to keep pointers 'arr_move' valid. */
	while (to_free > 0) {
		struct ATTRIB *b = arr_move[--nb];
@@ -882,7 +886,8 @@ int ni_create_attr_list(struct ntfs_inode *ni)

		attr = mi_insert_attr(mi, b->type, Add2Ptr(b, name_off),
				      b->name_len, asize, name_off);
		WARN_ON(!attr);
		if (!attr)
			goto out;

		mi_get_ref(mi, &le_b[nb]->ref);
		le_b[nb]->id = attr->id;
@@ -892,17 +897,20 @@ int ni_create_attr_list(struct ntfs_inode *ni)
		attr->id = le_b[nb]->id;

		/* Remove from primary record. */
		WARN_ON(!mi_remove_attr(NULL, &ni->mi, b));
		if (!mi_remove_attr(NULL, &ni->mi, b))
			goto out;

		if (to_free <= asize)
			break;
		to_free -= asize;
		WARN_ON(!nb);
		if (!nb)
			goto out;
	}

	attr = mi_insert_attr(&ni->mi, ATTR_LIST, NULL, 0,
			      lsize + SIZEOF_RESIDENT, SIZEOF_RESIDENT);
	WARN_ON(!attr);
	if (!attr)
		goto out;

	attr->non_res = 0;
	attr->flags = 0;
@@ -916,14 +924,12 @@ int ni_create_attr_list(struct ntfs_inode *ni)
	ni->attr_list.dirty = false;

	mark_inode_dirty(&ni->vfs_inode);
	goto out;
	return 0;

out1:
out:
	kfree(ni->attr_list.le);
	ni->attr_list.le = NULL;
	ni->attr_list.size = 0;

out:
	return err;
}

@@ -1638,14 +1644,13 @@ int ni_delete_all(struct ntfs_inode *ni)
 * Return: File name attribute by its value.
 */
struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
				     const struct cpu_str *uni,
				     const struct le_str *uni,
				     const struct MFT_REF *home_dir,
				     struct mft_inode **mi,
				     struct ATTR_LIST_ENTRY **le)
{
	struct ATTRIB *attr = NULL;
	struct ATTR_FILE_NAME *fname;
	struct le_str *fns;

	if (le)
		*le = NULL;
@@ -1669,10 +1674,9 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
	if (uni->len != fname->name_len)
		goto next;

	fns = (struct le_str *)&fname->name_len;
	if (ntfs_cmp_names_cpu(uni, fns, NULL, false))
	if (ntfs_cmp_names(uni->name, uni->len, fname->name, uni->len, NULL,
			   false))
		goto next;

	return fname;
}

@@ -2910,7 +2914,7 @@ int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
	/* Find name in record. */
	mi_get_ref(&dir_ni->mi, &de_name->home);

	fname = ni_fname_name(ni, (struct cpu_str *)&de_name->name_len,
	fname = ni_fname_name(ni, (struct le_str *)&de_name->name_len,
			      &de_name->home, &mi, &le);
	if (!fname)
		return -ENOENT;
+79 −20
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/nls.h>

#include "debug.h"
#include "ntfs.h"
@@ -178,7 +179,7 @@ int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes,
	/* Check errors. */
	if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- ||
	    fn * SECTOR_SIZE > bytes) {
		return -EINVAL; /* Native chkntfs returns ok! */
		return -E_NTFS_CORRUPT;
	}

	/* Get fixup pointer. */
@@ -1661,7 +1662,8 @@ int ntfs_vbo_to_lbo(struct ntfs_sb_info *sbi, const struct runs_tree *run,
	return 0;
}

struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, bool dir)
struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno,
				  enum RECORD_FLAG flag)
{
	int err = 0;
	struct super_block *sb = sbi->sb;
@@ -1673,8 +1675,7 @@ struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, bool dir)

	ni = ntfs_i(inode);

	err = mi_format_new(&ni->mi, sbi, rno, dir ? RECORD_FLAG_DIR : 0,
			    false);
	err = mi_format_new(&ni->mi, sbi, rno, flag, false);
	if (err)
		goto out;

@@ -1937,7 +1938,7 @@ int ntfs_security_init(struct ntfs_sb_info *sbi)
			break;

		sii_e = (struct NTFS_DE_SII *)ne;
		if (le16_to_cpu(ne->view.data_size) < SIZEOF_SECURITY_HDR)
		if (le16_to_cpu(ne->view.data_size) < sizeof(sii_e->sec_hdr))
			continue;

		next_id = le32_to_cpu(sii_e->sec_id) + 1;
@@ -1998,18 +1999,18 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id,
		goto out;

	t32 = le32_to_cpu(sii_e->sec_hdr.size);
	if (t32 < SIZEOF_SECURITY_HDR) {
	if (t32 < sizeof(struct SECURITY_HDR)) {
		err = -EINVAL;
		goto out;
	}

	if (t32 > SIZEOF_SECURITY_HDR + 0x10000) {
	if (t32 > sizeof(struct SECURITY_HDR) + 0x10000) {
		/* Looks like too big security. 0x10000 - is arbitrary big number. */
		err = -EFBIG;
		goto out;
	}

	*size = t32 - SIZEOF_SECURITY_HDR;
	*size = t32 - sizeof(struct SECURITY_HDR);

	p = kmalloc(*size, GFP_NOFS);
	if (!p) {
@@ -2023,14 +2024,14 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id,
	if (err)
		goto out;

	if (memcmp(&d_security, &sii_e->sec_hdr, SIZEOF_SECURITY_HDR)) {
	if (memcmp(&d_security, &sii_e->sec_hdr, sizeof(d_security))) {
		err = -EINVAL;
		goto out;
	}

	err = ntfs_read_run_nb(sbi, &ni->file.run,
			       le64_to_cpu(sii_e->sec_hdr.off) +
				       SIZEOF_SECURITY_HDR,
				       sizeof(struct SECURITY_HDR),
			       p, *size, NULL);
	if (err)
		goto out;
@@ -2069,7 +2070,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
	struct NTFS_DE_SDH sdh_e;
	struct NTFS_DE_SII sii_e;
	struct SECURITY_HDR *d_security;
	u32 new_sec_size = size_sd + SIZEOF_SECURITY_HDR;
	u32 new_sec_size = size_sd + sizeof(struct SECURITY_HDR);
	u32 aligned_sec_size = ALIGN(new_sec_size, 16);
	struct SECURITY_KEY hash_key;
	struct ntfs_fnd *fnd_sdh = NULL;
@@ -2207,14 +2208,14 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
	/* Fill SII entry. */
	sii_e.de.view.data_off =
		cpu_to_le16(offsetof(struct NTFS_DE_SII, sec_hdr));
	sii_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR);
	sii_e.de.view.data_size = cpu_to_le16(sizeof(struct SECURITY_HDR));
	sii_e.de.view.res = 0;
	sii_e.de.size = cpu_to_le16(SIZEOF_SII_DIRENTRY);
	sii_e.de.size = cpu_to_le16(sizeof(struct NTFS_DE_SII));
	sii_e.de.key_size = cpu_to_le16(sizeof(d_security->key.sec_id));
	sii_e.de.flags = 0;
	sii_e.de.res = 0;
	sii_e.sec_id = d_security->key.sec_id;
	memcpy(&sii_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR);
	memcpy(&sii_e.sec_hdr, d_security, sizeof(struct SECURITY_HDR));

	err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL, 0);
	if (err)
@@ -2223,7 +2224,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
	/* Fill SDH entry. */
	sdh_e.de.view.data_off =
		cpu_to_le16(offsetof(struct NTFS_DE_SDH, sec_hdr));
	sdh_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR);
	sdh_e.de.view.data_size = cpu_to_le16(sizeof(struct SECURITY_HDR));
	sdh_e.de.view.res = 0;
	sdh_e.de.size = cpu_to_le16(SIZEOF_SDH_DIRENTRY);
	sdh_e.de.key_size = cpu_to_le16(sizeof(sdh_e.key));
@@ -2231,7 +2232,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
	sdh_e.de.res = 0;
	sdh_e.key.hash = d_security->key.hash;
	sdh_e.key.sec_id = d_security->key.sec_id;
	memcpy(&sdh_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR);
	memcpy(&sdh_e.sec_hdr, d_security, sizeof(struct SECURITY_HDR));
	sdh_e.magic[0] = cpu_to_le16('I');
	sdh_e.magic[1] = cpu_to_le16('I');

@@ -2522,7 +2523,8 @@ void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim)
/*
 * run_deallocate - Deallocate clusters.
 */
int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim)
int run_deallocate(struct ntfs_sb_info *sbi, const struct runs_tree *run,
		   bool trim)
{
	CLST lcn, len;
	size_t idx = 0;
@@ -2578,13 +2580,13 @@ static inline bool name_has_forbidden_chars(const struct le_str *fname)
	return false;
}

static inline bool is_reserved_name(struct ntfs_sb_info *sbi,
static inline bool is_reserved_name(const struct ntfs_sb_info *sbi,
				    const struct le_str *fname)
{
	int port_digit;
	const __le16 *name = fname->name;
	int len = fname->len;
	u16 *upcase = sbi->upcase;
	const u16 *upcase = sbi->upcase;

	/* check for 3 chars reserved names (device names) */
	/* name by itself or with any extension is forbidden */
@@ -2618,3 +2620,60 @@ bool valid_windows_name(struct ntfs_sb_info *sbi, const struct le_str *fname)
	return !name_has_forbidden_chars(fname) &&
	       !is_reserved_name(sbi, fname);
}

/*
 * ntfs_set_label - updates current ntfs label.
 */
int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len)
{
	int err;
	struct ATTRIB *attr;
	struct ntfs_inode *ni = sbi->volume.ni;
	const u8 max_ulen = 0x80; /* TODO: use attrdef to get maximum length */
	/* Allocate PATH_MAX bytes. */
	struct cpu_str *uni = __getname();

	if (!uni)
		return -ENOMEM;

	err = ntfs_nls_to_utf16(sbi, label, len, uni, (PATH_MAX - 2) / 2,
				UTF16_LITTLE_ENDIAN);
	if (err < 0)
		goto out;

	if (uni->len > max_ulen) {
		ntfs_warn(sbi->sb, "new label is too long");
		err = -EFBIG;
		goto out;
	}

	ni_lock(ni);

	/* Ignore any errors. */
	ni_remove_attr(ni, ATTR_LABEL, NULL, 0, false, NULL);

	err = ni_insert_resident(ni, uni->len * sizeof(u16), ATTR_LABEL, NULL,
				 0, &attr, NULL, NULL);
	if (err < 0)
		goto unlock_out;

	/* write new label in on-disk struct. */
	memcpy(resident_data(attr), uni->name, uni->len * sizeof(u16));

	/* update cached value of current label. */
	if (len >= ARRAY_SIZE(sbi->volume.label))
		len = ARRAY_SIZE(sbi->volume.label) - 1;
	memcpy(sbi->volume.label, label, len);
	sbi->volume.label[len] = 0;
	mark_inode_dirty_sync(&ni->vfs_inode);

unlock_out:
	ni_unlock(ni);

	if (!err)
		err = _ni_write_inode(&ni->vfs_inode, 0);

out:
	__putname(uni);
	return err;
}
 No newline at end of file
+13 −7
Original line number Diff line number Diff line
@@ -1113,6 +1113,12 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
	*node = in;

out:
	if (err == -E_NTFS_CORRUPT) {
		ntfs_inode_err(&ni->vfs_inode, "directory corrupted");
		ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR);
		err = -EINVAL;
	}

	if (ib != in->index)
		kfree(ib);

Loading