cifs: add support for fallocate mode 0 for non-sparse files

RHBZ 1336264

When we extend a file we must also force the size to be updated.

This fixes an issue with holetest in xfs-tests which performs the following
sequence :
1, create a new file
2, use fallocate mode==0 to populate the file
3, mmap the file
4, touch each page by reading the mmapped region.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Ronnie Sahlberg 2020-01-17 11:45:02 +10:00 committed by Steve French
parent fe12926863
commit 8bd0d70144
3 changed files with 34 additions and 37 deletions

View File

@ -149,6 +149,9 @@ extern ssize_t cifs_file_copychunk_range(unsigned int xid,
size_t len, unsigned int flags); size_t len, unsigned int flags);
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern void cifs_setsize(struct inode *inode, loff_t offset);
extern int cifs_truncate_page(struct address_space *mapping, loff_t from);
#ifdef CONFIG_CIFS_NFSD_EXPORT #ifdef CONFIG_CIFS_NFSD_EXPORT
extern const struct export_operations cifs_export_ops; extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */ #endif /* CONFIG_CIFS_NFSD_EXPORT */

View File

@ -2228,7 +2228,7 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
return -ENOTSUPP; return -ENOTSUPP;
} }
static int cifs_truncate_page(struct address_space *mapping, loff_t from) int cifs_truncate_page(struct address_space *mapping, loff_t from)
{ {
pgoff_t index = from >> PAGE_SHIFT; pgoff_t index = from >> PAGE_SHIFT;
unsigned offset = from & (PAGE_SIZE - 1); unsigned offset = from & (PAGE_SIZE - 1);
@ -2245,7 +2245,7 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from)
return rc; return rc;
} }
static void cifs_setsize(struct inode *inode, loff_t offset) void cifs_setsize(struct inode *inode, loff_t offset)
{ {
struct cifsInodeInfo *cifs_i = CIFS_I(inode); struct cifsInodeInfo *cifs_i = CIFS_I(inode);

View File

@ -12,6 +12,7 @@
#include <linux/uuid.h> #include <linux/uuid.h>
#include <linux/sort.h> #include <linux/sort.h>
#include <crypto/aead.h> #include <crypto/aead.h>
#include "cifsfs.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "smb2pdu.h" #include "smb2pdu.h"
#include "smb2proto.h" #include "smb2proto.h"
@ -3171,29 +3172,33 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
return rc; return rc;
} }
/*
* Extending the file
*/
if ((keep_size == false) && i_size_read(inode) < off + len) {
if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0)
smb2_set_sparse(xid, tcon, cfile, inode, false);
eof = cpu_to_le64(off + len);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, &eof);
if (rc == 0) {
cifsi->server_eof = off + len;
cifs_setsize(inode, off + len);
cifs_truncate_page(inode->i_mapping, inode->i_size);
truncate_setsize(inode, off + len);
}
goto out;
}
/* /*
* Files are non-sparse by default so falloc may be a no-op * Files are non-sparse by default so falloc may be a no-op
* Must check if file sparse. If not sparse, and not extending * Must check if file sparse. If not sparse, and since we are not
* then no need to do anything since file already allocated * extending then no need to do anything since file already allocated
*/ */
if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
if (keep_size == true) rc = 0;
rc = 0; goto out;
/* check if extending file */
else if (i_size_read(inode) >= off + len)
/* not extending file and already not sparse */
rc = 0;
/* BB: in future add else clause to extend file */
else
rc = -EOPNOTSUPP;
if (rc)
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len, rc);
else
trace_smb3_falloc_done(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len);
free_xid(xid);
return rc;
} }
if ((keep_size == true) || (i_size_read(inode) >= off + len)) { if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
@ -3207,25 +3212,14 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
*/ */
if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) {
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, goto out;
tcon->tid, tcon->ses->Suid, off, len, rc);
free_xid(xid);
return rc;
}
smb2_set_sparse(xid, tcon, cfile, inode, false);
rc = 0;
} else {
smb2_set_sparse(xid, tcon, cfile, inode, false);
rc = 0;
if (i_size_read(inode) < off + len) {
eof = cpu_to_le64(off + len);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid,
&eof);
} }
} }
smb2_set_sparse(xid, tcon, cfile, inode, false);
rc = 0;
out:
if (rc) if (rc)
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid,
tcon->ses->Suid, off, len, rc); tcon->ses->Suid, off, len, rc);