[PATCH] [CIFS] Add sparse file support to SMB2/SMB3 mounts
Shirish Pargaonkar
shirishpargaonkar at gmail.com
Tue Aug 12 22:34:35 MDT 2014
On Tue, Aug 12, 2014 at 9:13 AM, Steve French <smfrench at gmail.com> wrote:
> Many Linux filesystems make a file "sparse" when extending
> a file with ftruncate. This does work for CIFS to Samba
> (only) but not for SMB2/SMB3 (to Samba or Windows) since
> there is a "set sparse" fsctl which is supposed to be
> sent to mark a file as sparse.
>
> This patch marks a file as sparse by sending this simple
> set sparse fsctl if it is extended more than 2 pages.
> It has been tested to Windows 8.1, Samba and various
> SMB2/SMB3 servers which do support setting sparse (and
> MacOS which does not appear to support the fsctl yet).
> If a server share does not support setting a file
> as sparse, then we do not retry setting sparse on that
> share.
>
> The disk space savings for sparse files can be quite
> large (even more significant on Windows servers than Samba).
>
> Signed-off-by: Steve French <smfrench at gmail.com>
> ---
> fs/cifs/cifsglob.h | 1 +
> fs/cifs/smb2ops.c | 43 +++++++++++++++++++++++++++++++++++++++++++
> fs/cifs/smb2pdu.c | 4 +++-
> 3 files changed, 47 insertions(+), 1 deletion(-)
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 0012e1e..bc20a6e 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -883,6 +883,7 @@ struct cifs_tcon {
> for this mount even if server would support */
> bool local_lease:1; /* check leases (only) on local system not remote */
> bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */
> + bool broken_sparse_sup; /* if server or share does not support sparse */
> bool need_reconnect:1; /* connection reset, tid now invalid */
> #ifdef CONFIG_CIFS_SMB2
> bool print:1; /* set if connection to printer share */
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index 77f8aeb..7463436 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -736,6 +736,49 @@ smb2_set_file_size(const unsigned int xid, struct
> cifs_tcon *tcon,
> struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
> {
> __le64 eof = cpu_to_le64(size);
> + struct inode *inode;
> +
> + /*
> + * If extending file more than one page make sparse. Many Linux fs
> + * make files sparse by default when extending via ftruncate
> + */
> + inode = cfile->dentry->d_inode;
> +
> + if (!set_alloc && (size > inode->i_size + 8192)) {
> + struct cifsInodeInfo *cifsi;
> + __u8 set_sparse = 1;
> + int rc;
> +
> + cifsi = CIFS_I(inode);
> +
> + /* if file already sparse or no server support don't bother */
> + if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
> + goto smb2_set_eof;
Is this check always valid? Is it possible that the file is not
sparse anymore at the server?
> +
> + /*
> + * Can't check for sparse support on share the usual way via the
> + * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share
> + * since Samba server doesn't set the flag on the share, yet
> + * supports the set sparse FSCTL and returns sparse correctly
> + * in the file attributes. If we fail setting sparse though we
> + * mark that server does not support sparse files for this share
> + * to avoid repeatedly sending the unsupported fsctl to server
> + * if the file is repeatedly extended.
> + */
> + if (tcon->broken_sparse_sup)
> + goto smb2_set_eof;
> +
> + rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
> + cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
> + true /* is_fctl */, &set_sparse, 1, NULL, NULL);
> + if (rc) {
> + tcon->broken_sparse_sup = true;
> + cifs_dbg(FYI, "set sparse rc = %d\n", rc);
> + } else
> + cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE;
> + }
> +
> +smb2_set_eof:
> return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
> cfile->fid.volatile_fid, cfile->pid, &eof, false);
> }
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 42ebc1a..74440af 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -1224,7 +1224,9 @@ SMB2_ioctl(const unsigned int xid, struct
> cifs_tcon *tcon, u64 persistent_fid,
>
> cifs_dbg(FYI, "SMB2 IOCTL\n");
>
> - *out_data = NULL;
> + if (out_data != NULL)
> + *out_data = NULL;
> +
> /* zero out returned data len, in case of error */
> if (plen)
> *plen = 0;
>
>
> --
> Thanks,
>
> Steve
More information about the samba-technical
mailing list