Commit 3d1a3745 authored by Steve French's avatar Steve French

Add sparse file support to SMB2/SMB3 mounts

Many Linux filesystes 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: default avatarSteve French <smfrench@gmail.com>
Reviewed-by: default avatarShirish Pargaonkar <spargaonkar@suse.com>
parent 8ae31240
...@@ -883,6 +883,7 @@ struct cifs_tcon { ...@@ -883,6 +883,7 @@ struct cifs_tcon {
for this mount even if server would support */ for this mount even if server would support */
bool local_lease:1; /* check leases (only) on local system not remote */ 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_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 */ bool need_reconnect:1; /* connection reset, tid now invalid */
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
bool print:1; /* set if connection to printer share */ bool print:1; /* set if connection to printer share */
......
...@@ -736,6 +736,49 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -736,6 +736,49 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile, __u64 size, bool set_alloc) struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
{ {
__le64 eof = cpu_to_le64(size); __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;
/*
* 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, return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, &eof, false); cfile->fid.volatile_fid, cfile->pid, &eof, false);
} }
......
...@@ -1224,7 +1224,9 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, ...@@ -1224,7 +1224,9 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
cifs_dbg(FYI, "SMB2 IOCTL\n"); 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 */ /* zero out returned data len, in case of error */
if (plen) if (plen)
*plen = 0; *plen = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment