Commit 2f3ebaba authored by Ronnie Sahlberg's avatar Ronnie Sahlberg Committed by Steve French
Browse files

cifs: add fiemap support



Useful for improved copy performance as well as for
applications which query allocated ranges of sparse
files.

Signed-off-by: default avatarRonnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent d7bef4c4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -986,6 +986,7 @@ const struct inode_operations cifs_file_inode_ops = {
	.getattr = cifs_getattr,
	.permission = cifs_permission,
	.listxattr = cifs_listxattr,
	.fiemap = cifs_fiemap,
};

const struct inode_operations cifs_symlink_inode_ops = {
+2 −0
Original line number Diff line number Diff line
@@ -84,6 +84,8 @@ extern int cifs_revalidate_mapping(struct inode *inode);
extern int cifs_zap_mapping(struct inode *inode);
extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int);
extern int cifs_setattr(struct dentry *, struct iattr *);
extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start,
		       u64 len);

extern const struct inode_operations cifs_file_inode_ops;
extern const struct inode_operations cifs_symlink_inode_ops;
+3 −0
Original line number Diff line number Diff line
@@ -493,6 +493,9 @@ struct smb_version_operations {
			 char *full_path,
			 umode_t mode,
			 dev_t device_number);
	/* version specific fiemap implementation */
	int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *,
		      struct fiemap_extent_info *, u64, u64);
};

struct smb_version_values {
+37 −0
Original line number Diff line number Diff line
@@ -2116,6 +2116,43 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
	return rc;
}

int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
		u64 len)
{
	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
	struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->vfs_inode.i_sb);
	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
	struct TCP_Server_Info *server = tcon->ses->server;
	struct cifsFileInfo *cfile;
	int rc;

	/*
	 * We need to be sure that all dirty pages are written as they
	 * might fill holes on the server.
	 */
	if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
	    inode->i_mapping->nrpages != 0) {
		rc = filemap_fdatawait(inode->i_mapping);
		if (rc) {
			mapping_set_error(inode->i_mapping, rc);
			return rc;
		}
	}

	cfile = find_readable_file(cifs_i, false);
	if (cfile == NULL)
		return -EINVAL;

	if (server->ops->fiemap) {
		rc = server->ops->fiemap(tcon, cfile, fei, start, len);
		cifsFileInfo_put(cfile);
		return rc;
	}

	cifsFileInfo_put(cfile);
	return -ENOTSUPP;
}

static int cifs_truncate_page(struct address_space *mapping, loff_t from)
{
	pgoff_t index = from >> PAGE_SHIFT;
+77 −0
Original line number Diff line number Diff line
@@ -2886,6 +2886,79 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
	return rc;
}

static int smb3_fiemap(struct cifs_tcon *tcon,
		       struct cifsFileInfo *cfile,
		       struct fiemap_extent_info *fei, u64 start, u64 len)
{
	unsigned int xid;
	struct file_allocated_range_buffer in_data, *out_data;
	u32 out_data_len;
	int i, num, rc, flags, last_blob;
	u64 next;

	if (fiemap_check_flags(fei, FIEMAP_FLAG_SYNC))
		return -EBADR;

	xid = get_xid();
 again:
	in_data.file_offset = cpu_to_le64(start);
	in_data.length = cpu_to_le64(len);

	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
			cfile->fid.volatile_fid,
			FSCTL_QUERY_ALLOCATED_RANGES, true,
			(char *)&in_data, sizeof(in_data),
			1024 * sizeof(struct file_allocated_range_buffer),
			(char **)&out_data, &out_data_len);
	if (rc == -E2BIG) {
		last_blob = 0;
		rc = 0;
	} else
		last_blob = 1;
	if (rc)
		goto out;

	if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
		rc = -EINVAL;
		goto out;
	}
	if (out_data_len % sizeof(struct file_allocated_range_buffer)) {
		rc = -EINVAL;
		goto out;
	}

	num = out_data_len / sizeof(struct file_allocated_range_buffer);
	for (i = 0; i < num; i++) {
		flags = 0;
		if (i == num - 1 && last_blob)
			flags |= FIEMAP_EXTENT_LAST;

		rc = fiemap_fill_next_extent(fei,
				le64_to_cpu(out_data[i].file_offset),
				le64_to_cpu(out_data[i].file_offset),
				le64_to_cpu(out_data[i].length),
				flags);
		if (rc < 0)
			goto out;
		if (rc == 1) {
			rc = 0;
			goto out;
		}
	}

	if (!last_blob) {
		next = le64_to_cpu(out_data[num - 1].file_offset) +
		  le64_to_cpu(out_data[num - 1].length);
		len = len - (next - start);
		start = next;
		goto again;
	}

 out:
	free_xid(xid);
	kfree(out_data);
	return rc;
}

static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
			   loff_t off, loff_t len)
@@ -4054,6 +4127,7 @@ struct smb_version_operations smb20_operations = {
	.next_header = smb2_next_header,
	.ioctl_query_info = smb2_ioctl_query_info,
	.make_node = smb2_make_node,
	.fiemap = smb3_fiemap,
};

struct smb_version_operations smb21_operations = {
@@ -4153,6 +4227,7 @@ struct smb_version_operations smb21_operations = {
	.next_header = smb2_next_header,
	.ioctl_query_info = smb2_ioctl_query_info,
	.make_node = smb2_make_node,
	.fiemap = smb3_fiemap,
};

struct smb_version_operations smb30_operations = {
@@ -4261,6 +4336,7 @@ struct smb_version_operations smb30_operations = {
	.next_header = smb2_next_header,
	.ioctl_query_info = smb2_ioctl_query_info,
	.make_node = smb2_make_node,
	.fiemap = smb3_fiemap,
};

struct smb_version_operations smb311_operations = {
@@ -4370,6 +4446,7 @@ struct smb_version_operations smb311_operations = {
	.next_header = smb2_next_header,
	.ioctl_query_info = smb2_ioctl_query_info,
	.make_node = smb2_make_node,
	.fiemap = smb3_fiemap,
};

struct smb_version_values smb20_values = {
Loading