Commit 5b7c893e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ntfs3 updates from Konstantin Komarov:
"New:
   - implement fallocate for compressed files
   - add support for the compression attribute
   - optimize large writes to sparse files

 Fixes:
   - fix several potential deadlock scenarios
   - fix various internal bugs detected by syzbot
   - add checks before accessing NTFS structures during parsing
   - correct the format of output messages

  Refactoring:
   - replace fsparam_flag_no with fsparam_flag in options parser
   - remove unused functions and macros"

* tag 'ntfs3_for_6.12' of https://github.com/Paragon-Software-Group/linux-ntfs3: (25 commits)
  fs/ntfs3: Format output messages like others fs in kernel
  fs/ntfs3: Additional check in ntfs_file_release
  fs/ntfs3: Fix general protection fault in run_is_mapped_full
  fs/ntfs3: Sequential field availability check in mi_enum_attr()
  fs/ntfs3: Additional check in ni_clear()
  fs/ntfs3: Fix possible deadlock in mi_read
  ntfs3: Change to non-blocking allocation in ntfs_d_hash
  fs/ntfs3: Remove unused al_delete_le
  fs/ntfs3: Rename ntfs3_setattr into ntfs_setattr
  fs/ntfs3: Replace fsparam_flag_no -> fsparam_flag
  fs/ntfs3: Add support for the compression attribute
  fs/ntfs3: Implement fallocate for compressed files
  fs/ntfs3: Make checks in run_unpack more clear
  fs/ntfs3: Add rough attr alloc_size check
  fs/ntfs3: Stale inode instead of bad
  fs/ntfs3: Refactor enum_rstbl to suppress static checker
  fs/ntfs3: Fix sparse warning in ni_fiemap
  fs/ntfs3: Fix warning possible deadlock in ntfs_set_state
  fs/ntfs3: Fix sparse warning for bigendian
  fs/ntfs3: Separete common code for file_read/write iter/splice
  ...
parents b2760b83 48dbc127
Loading
Loading
Loading
Loading
+86 −10
Original line number Diff line number Diff line
@@ -976,15 +976,17 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
		goto out;

	/* Check for compressed frame. */
	err = attr_is_frame_compressed(ni, attr, vcn >> NTFS_LZNT_CUNIT, &hint);
	err = attr_is_frame_compressed(ni, attr_b, vcn >> NTFS_LZNT_CUNIT,
				       &hint);
	if (err)
		goto out;

	if (hint) {
		/* if frame is compressed - don't touch it. */
		*lcn = COMPRESSED_LCN;
		*len = hint;
		err = -EOPNOTSUPP;
		/* length to the end of frame. */
		*len = NTFS_LZNT_CLUSTERS - (vcn & (NTFS_LZNT_CLUSTERS - 1));
		err = 0;
		goto out;
	}

@@ -1027,16 +1029,16 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,

		/* Check if 'vcn' and 'vcn0' in different attribute segments. */
		if (vcn < svcn || evcn1 <= vcn) {
			/* Load attribute for truncated vcn. */
			attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0,
					    &vcn, &mi);
			if (!attr) {
			struct ATTRIB *attr2;
			/* Load runs for truncated vcn. */
			attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL,
					     0, &vcn, &mi);
			if (!attr2) {
				err = -EINVAL;
				goto out;
			}
			svcn = le64_to_cpu(attr->nres.svcn);
			evcn1 = le64_to_cpu(attr->nres.evcn) + 1;
			err = attr_load_runs(attr, ni, run, NULL);
			evcn1 = le64_to_cpu(attr2->nres.evcn) + 1;
			err = attr_load_runs(attr2, ni, run, NULL);
			if (err)
				goto out;
		}
@@ -1517,6 +1519,9 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,

/*
 * attr_is_frame_compressed - Used to detect compressed frame.
 *
 * attr - base (primary) attribute segment.
 * Only base segments contains valid 'attr->nres.c_unit'
 */
int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr,
			     CLST frame, CLST *clst_data)
@@ -2600,3 +2605,74 @@ int attr_force_nonresident(struct ntfs_inode *ni)

	return err;
}

/*
 * Change the compression of data attribute
 */
int attr_set_compress(struct ntfs_inode *ni, bool compr)
{
	struct ATTRIB *attr;
	struct mft_inode *mi;

	attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi);
	if (!attr)
		return -ENOENT;

	if (is_attr_compressed(attr) == !!compr) {
		/* Already required compressed state. */
		return 0;
	}

	if (attr->non_res) {
		u16 run_off;
		u32 run_size;
		char *run;

		if (attr->nres.data_size) {
			/*
			 * There are rare cases when it possible to change
			 * compress state without big changes.
			 * TODO: Process these cases.
			 */
			return -EOPNOTSUPP;
		}

		run_off = le16_to_cpu(attr->nres.run_off);
		run_size = le32_to_cpu(attr->size) - run_off;
		run = Add2Ptr(attr, run_off);

		if (!compr) {
			/* remove field 'attr->nres.total_size'. */
			memmove(run - 8, run, run_size);
			run_off -= 8;
		}

		if (!mi_resize_attr(mi, attr, compr ? +8 : -8)) {
			/*
			 * Ignore rare case when there are no 8 bytes in record with attr.
			 * TODO: split attribute.
			 */
			return -EOPNOTSUPP;
		}

		if (compr) {
			/* Make a gap for 'attr->nres.total_size'. */
			memmove(run + 8, run, run_size);
			run_off += 8;
			attr->nres.total_size = attr->nres.alloc_size;
		}
		attr->nres.run_off = cpu_to_le16(run_off);
	}

	/* Update data attribute flags. */
	if (compr) {
		attr->flags |= ATTR_FLAG_COMPRESSED;
		attr->nres.c_unit = NTFS_LZNT_CUNIT;
	} else {
		attr->flags &= ~ATTR_FLAG_COMPRESSED;
		attr->nres.c_unit = 0;
	}
	mi->dirty = true;

	return 0;
}
+0 −53
Original line number Diff line number Diff line
@@ -382,59 +382,6 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
	return true;
}

/*
 * 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, u8 name_len, const struct MFT_REF *ref)
{
	u16 size;
	struct ATTR_LIST_ENTRY *le;
	size_t off;
	typeof(ni->attr_list) *al = &ni->attr_list;

	/* Scan forward to the first le that matches the input. */
	le = al_find_ex(ni, NULL, type, name, name_len, &vcn);
	if (!le)
		return false;

	off = PtrOffset(al->le, le);

next:
	if (off >= al->size)
		return false;
	if (le->type != type)
		return false;
	if (le->name_len != name_len)
		return false;
	if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len,
				       ni->mi.sbi->upcase, true))
		return false;
	if (le64_to_cpu(le->vcn) != vcn)
		return false;

	/*
	 * The caller specified a segment reference, so we have to
	 * scan through the matching entries until we find that segment
	 * reference or we run of matching entries.
	 */
	if (ref && memcmp(ref, &le->ref, sizeof(*ref))) {
		off += le16_to_cpu(le->size);
		le = Add2Ptr(al->le, off);
		goto next;
	}

	/* Save on stack the size of 'le'. */
	size = le16_to_cpu(le->size);
	/* Delete the le. */
	memmove(le, Add2Ptr(le, size), al->size - (off + size));

	al->size -= size;
	al->dirty = true;

	return true;
}

int al_update(struct ntfs_inode *ni, int sync)
{
	int err;
+138 −47
Original line number Diff line number Diff line
@@ -82,13 +82,14 @@ int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
		      struct fileattr *fa)
{
	struct inode *inode = d_inode(dentry);
	struct ntfs_inode *ni = ntfs_i(inode);
	u32 flags = fa->flags;
	unsigned int new_fl = 0;

	if (fileattr_has_fsx(fa))
		return -EOPNOTSUPP;

	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL))
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_COMPR_FL))
		return -EOPNOTSUPP;

	if (flags & FS_IMMUTABLE_FL)
@@ -97,6 +98,15 @@ int ntfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
	if (flags & FS_APPEND_FL)
		new_fl |= S_APPEND;

	/* Allowed to change compression for empty files and for directories only. */
	if (!is_dedup(ni) && !is_encrypted(ni) &&
	    (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
		/* Change compress state. */
		int err = ni_set_compress(inode, flags & FS_COMPR_FL);
		if (err)
			return err;
	}

	inode_set_flags(inode, new_fl, S_IMMUTABLE | S_APPEND);

	inode_set_ctime_current(inode);
@@ -407,6 +417,42 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
		err = 0;
	}

	if (file && is_sparsed(ni)) {
		/*
		 * This code optimizes large writes to sparse file.
		 * TODO: merge this fragment with fallocate fragment.
		 */
		struct ntfs_sb_info *sbi = ni->mi.sbi;
		CLST vcn = pos >> sbi->cluster_bits;
		CLST cend = bytes_to_cluster(sbi, end);
		CLST cend_v = bytes_to_cluster(sbi, ni->i_valid);
		CLST lcn, clen;
		bool new;

		if (cend_v > cend)
			cend_v = cend;

		/*
		 * Allocate and zero new clusters.
		 * Zeroing these clusters may be too long.
		 */
		for (; vcn < cend_v; vcn += clen) {
			err = attr_data_get_block(ni, vcn, cend_v - vcn, &lcn,
						  &clen, &new, true);
			if (err)
				goto out;
		}
		/*
		 * Allocate but not zero new clusters.
		 */
		for (; vcn < cend; vcn += clen) {
			err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
						  &clen, &new, false);
			if (err)
				goto out;
		}
	}

	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
	mark_inode_dirty(inode);

@@ -483,7 +529,7 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
}

/*
 * ntfs_fallocate
 * ntfs_fallocate - file_operations::ntfs_fallocate
 *
 * Preallocate space for a file. This implements ntfs's fallocate file
 * operation, which gets called from sys_fallocate system call. User
@@ -618,6 +664,8 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
		ni_lock(ni);
		err = attr_collapse_range(ni, vbo, len);
		ni_unlock(ni);
		if (err)
			goto out;
	} else if (mode & FALLOC_FL_INSERT_RANGE) {
		/* Check new size. */
		err = inode_newsize_ok(inode, new_size);
@@ -740,9 +788,9 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
}

/*
 * ntfs3_setattr - inode_operations::setattr
 * ntfs_setattr - inode_operations::setattr
 */
int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
		 struct iattr *attr)
{
	struct inode *inode = d_inode(dentry);
@@ -803,10 +851,12 @@ int ntfs3_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
	return err;
}

static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
/*
 * check_read_restriction:
 * common code for ntfs_file_read_iter and ntfs_file_splice_read
 */
static int check_read_restriction(struct inode *inode)
{
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
@@ -817,11 +867,6 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
		return -EOPNOTSUPP;
	}

	if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) {
		ntfs_inode_warn(inode, "direct i/o + compressed not supported");
		return -EOPNOTSUPP;
	}

#ifndef CONFIG_NTFS3_LZX_XPRESS
	if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) {
		ntfs_inode_warn(
@@ -836,37 +881,44 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
		return -EOPNOTSUPP;
	}

	return generic_file_read_iter(iocb, iter);
	return 0;
}

static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
				     struct pipe_inode_info *pipe, size_t len,
				     unsigned int flags)
/*
 * ntfs_file_read_iter - file_operations::read_iter
 */
static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct inode *inode = file_inode(in);
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);
	ssize_t err;

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

	if (is_encrypted(ni)) {
		ntfs_inode_warn(inode, "encrypted i/o not supported");
	if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) {
		ntfs_inode_warn(inode, "direct i/o + compressed not supported");
		return -EOPNOTSUPP;
	}

#ifndef CONFIG_NTFS3_LZX_XPRESS
	if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) {
		ntfs_inode_warn(
			inode,
			"activate CONFIG_NTFS3_LZX_XPRESS to read external compressed files");
		return -EOPNOTSUPP;
	return generic_file_read_iter(iocb, iter);
}
#endif

	if (is_dedup(ni)) {
		ntfs_inode_warn(inode, "read deduplicated not supported");
		return -EOPNOTSUPP;
	}
/*
 * ntfs_file_splice_read - file_operations::splice_read
 */
static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,
				     struct pipe_inode_info *pipe, size_t len,
				     unsigned int flags)
{
	struct inode *inode = file_inode(in);
	ssize_t err;

	err = check_read_restriction(inode);
	if (err)
		return err;

	return filemap_splice_read(in, ppos, pipe, len, flags);
}
@@ -1134,14 +1186,11 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
}

/*
 * ntfs_file_write_iter - file_operations::write_iter
 * check_write_restriction:
 * common code for ntfs_file_write_iter and ntfs_file_splice_write
 */
static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
static int check_write_restriction(struct inode *inode)
{
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	ssize_t ret;
	int err;
	struct ntfs_inode *ni = ntfs_i(inode);

	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
@@ -1152,13 +1201,31 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
		return -EOPNOTSUPP;
	}

	if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) {
		ntfs_inode_warn(inode, "direct i/o + compressed not supported");
	if (is_dedup(ni)) {
		ntfs_inode_warn(inode, "write into deduplicated not supported");
		return -EOPNOTSUPP;
	}

	if (is_dedup(ni)) {
		ntfs_inode_warn(inode, "write into deduplicated not supported");
	return 0;
}

/*
 * ntfs_file_write_iter - file_operations::write_iter
 */
static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
	struct file *file = iocb->ki_filp;
	struct inode *inode = file_inode(file);
	struct ntfs_inode *ni = ntfs_i(inode);
	ssize_t ret;
	int err;

	err = check_write_restriction(inode);
	if (err)
		return err;

	if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) {
		ntfs_inode_warn(inode, "direct i/o + compressed not supported");
		return -EOPNOTSUPP;
	}

@@ -1246,7 +1313,14 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
	/* If we are last writer on the inode, drop the block reservation. */
	if (sbi->options->prealloc &&
	    ((file->f_mode & FMODE_WRITE) &&
	     atomic_read(&inode->i_writecount) == 1)) {
	     atomic_read(&inode->i_writecount) == 1)
	   /*
	    * The only file when inode->i_fop = &ntfs_file_operations and
	    * init_rwsem(&ni->file.run_lock) is not called explicitly is MFT.
	    *
	    * Add additional check here.
	    */
	    && inode->i_ino != MFT_REC_MFT) {
		ni_lock(ni);
		down_write(&ni->file.run_lock);

@@ -1282,10 +1356,27 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
	return err;
}

/*
 * ntfs_file_splice_write - file_operations::splice_write
 */
static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
				      struct file *file, loff_t *ppos,
				      size_t len, unsigned int flags)
{
	ssize_t err;
	struct inode *inode = file_inode(file);

	err = check_write_restriction(inode);
	if (err)
		return err;

	return iter_file_splice_write(pipe, file, ppos, len, flags);
}

// clang-format off
const struct inode_operations ntfs_file_inode_operations = {
	.getattr	= ntfs_getattr,
	.setattr	= ntfs3_setattr,
	.setattr	= ntfs_setattr,
	.listxattr	= ntfs_listxattr,
	.get_acl	= ntfs_get_acl,
	.set_acl	= ntfs_set_acl,
@@ -1303,10 +1394,10 @@ const struct file_operations ntfs_file_operations = {
	.compat_ioctl	= ntfs_compat_ioctl,
#endif
	.splice_read	= ntfs_file_splice_read,
	.splice_write	= ntfs_file_splice_write,
	.mmap		= ntfs_file_mmap,
	.open		= ntfs_file_open,
	.fsync		= generic_file_fsync,
	.splice_write	= iter_file_splice_write,
	.fallocate	= ntfs_fallocate,
	.release	= ntfs_file_release,
};
+83 −14
Original line number Diff line number Diff line
@@ -102,7 +102,9 @@ void ni_clear(struct ntfs_inode *ni)
{
	struct rb_node *node;

	if (!ni->vfs_inode.i_nlink && ni->mi.mrec && is_rec_inuse(ni->mi.mrec))
	if (!ni->vfs_inode.i_nlink && ni->mi.mrec &&
	    is_rec_inuse(ni->mi.mrec) &&
	    !(ni->mi.sbi->flags & NTFS_FLAGS_LOG_REPLAYING))
		ni_delete_all(ni);

	al_destroy(ni);
@@ -1900,13 +1902,13 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,

/*
 * fiemap_fill_next_extent_k - a copy of fiemap_fill_next_extent
 * but it accepts kernel address for fi_extents_start
 * but it uses 'fe_k' instead of fieinfo->fi_extents_start
 */
static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo,
				     u64 logical, u64 phys, u64 len, u32 flags)
				     struct fiemap_extent *fe_k, u64 logical,
				     u64 phys, u64 len, u32 flags)
{
	struct fiemap_extent extent;
	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;

	/* only count the extents */
	if (fieinfo->fi_extents_max == 0) {
@@ -1930,8 +1932,7 @@ static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo,
	extent.fe_length = len;
	extent.fe_flags = flags;

	dest += fieinfo->fi_extents_mapped;
	memcpy(dest, &extent, sizeof(extent));
	memcpy(fe_k + fieinfo->fi_extents_mapped, &extent, sizeof(extent));

	fieinfo->fi_extents_mapped++;
	if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
@@ -1949,7 +1950,6 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
	      __u64 vbo, __u64 len)
{
	int err = 0;
	struct fiemap_extent __user *fe_u = fieinfo->fi_extents_start;
	struct fiemap_extent *fe_k = NULL;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	u8 cluster_bits = sbi->cluster_bits;
@@ -2008,7 +2008,6 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
		err = -ENOMEM;
		goto out;
	}
	fieinfo->fi_extents_start = fe_k;

	end = vbo + len;
	alloc_size = le64_to_cpu(attr->nres.alloc_size);
@@ -2098,8 +2097,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
			if (vbo + dlen >= end)
				flags |= FIEMAP_EXTENT_LAST;

			err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, dlen,
							flags);
			err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo,
							dlen, flags);

			if (err < 0)
				break;
@@ -2120,7 +2119,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
		if (vbo + bytes >= end)
			flags |= FIEMAP_EXTENT_LAST;

		err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, bytes,
		err = fiemap_fill_next_extent_k(fieinfo, fe_k, vbo, lbo, bytes,
						flags);
		if (err < 0)
			break;
@@ -2137,15 +2136,13 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
	/*
	 * Copy to user memory out of lock
	 */
	if (copy_to_user(fe_u, fe_k,
	if (copy_to_user(fieinfo->fi_extents_start, fe_k,
			 fieinfo->fi_extents_max *
				 sizeof(struct fiemap_extent))) {
		err = -EFAULT;
	}

out:
	/* Restore original pointer. */
	fieinfo->fi_extents_start = fe_u;
	kfree(fe_k);
	return err;
}
@@ -3455,3 +3452,75 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)

	return 0;
}

/*
 * ni_set_compress
 *
 * Helper for 'ntfs_fileattr_set'.
 * Changes compression for empty files and directories only.
 */
int ni_set_compress(struct inode *inode, bool compr)
{
	int err;
	struct ntfs_inode *ni = ntfs_i(inode);
	struct ATTR_STD_INFO *std;
	const char *bad_inode;

	if (is_compressed(ni) == !!compr)
		return 0;

	if (is_sparsed(ni)) {
		/* sparse and compress not compatible. */
		return -EOPNOTSUPP;
	}

	if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) {
		/*Skip other inodes. (symlink,fifo,...) */
		return -EOPNOTSUPP;
	}

	bad_inode = NULL;

	ni_lock(ni);

	std = ni_std(ni);
	if (!std) {
		bad_inode = "no std";
		goto out;
	}

	if (S_ISREG(inode->i_mode)) {
		err = attr_set_compress(ni, compr);
		if (err) {
			if (err == -ENOENT) {
				/* Fix on the fly? */
				/* Each file must contain data attribute. */
				bad_inode = "no data attribute";
			}
			goto out;
		}
	}

	ni->std_fa = std->fa;
	if (compr)
		std->fa |= FILE_ATTRIBUTE_COMPRESSED;
	else
		std->fa &= ~FILE_ATTRIBUTE_COMPRESSED;

	if (ni->std_fa != std->fa) {
		ni->std_fa = std->fa;
		ni->mi.dirty = true;
	}
	/* update duplicate information and directory entries in ni_write_inode.*/
	ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
	err = 0;

out:
	ni_unlock(ni);
	if (bad_inode) {
		ntfs_bad_inode(inode, bad_inode);
		err = -EINVAL;
	}

	return err;
}
+17 −2
Original line number Diff line number Diff line
@@ -609,14 +609,29 @@ static inline void add_client(struct CLIENT_REC *ca, u16 index, __le16 *head)
	*head = cpu_to_le16(index);
}

/*
 * Enumerate restart table.
 *
 * @t - table to enumerate.
 * @c - current enumerated element.
 *
 * enumeration starts with @c == NULL
 * returns next element or NULL
 */
static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c)
{
	__le32 *e;
	u32 bprt;
	u16 rsize = t ? le16_to_cpu(t->size) : 0;
	u16 rsize;

	if (!t)
		return NULL;

	rsize = le16_to_cpu(t->size);

	if (!c) {
		if (!t || !t->total)
		/* start enumeration. */
		if (!t->total)
			return NULL;
		e = Add2Ptr(t, sizeof(struct RESTART_TABLE));
	} else {
Loading