[PATCH] [CIFS] Add sparse file support to SMB2/SMB3 mounts

Shirish Pargaonkar shirishpargaonkar at gmail.com
Wed Aug 13 15:25:13 MDT 2014


Reviewed-by: Shirish Pargaonkar <spargaonkar at suse.com>

On Wed, Aug 13, 2014 at 12:11 AM, Steve French <smfrench at gmail.com> wrote:
> On Tue, Aug 12, 2014 at 11:34 PM, Shirish Pargaonkar
> <shirishpargaonkar at gmail.com> wrote:
>> 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?
>
> If someone unsparsed the file that is fine.  The ftruncate to
> extend the file will still work fine as it did before this patch
> (it just will take up more disk space than it would
> if the file were sparse).  When the actimeo (1 second) expires,
> and we next refresh the inode attributes from the server
> we will notice the file is not sparse and and future
> ftruncate/extends will mark the file sparse.
>
>>
>>> +
>>> +        /*
>>> +         * 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);
>>>  }
>
>
>
>
> --
> Thanks,
>
> Steve


More information about the samba-technical mailing list