Commit eb8d7fc8 authored by Pavel Shilovsky's avatar Pavel Shilovsky

Add strict cache mode for 2.6.37

parent f87f60b3
...@@ -8,7 +8,7 @@ etercifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ ...@@ -8,7 +8,7 @@ etercifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o \ md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o \
readdir.o ioctl.o sess.o export.o readdir.o ioctl.o sess.o export.o
cifs-$(CONFIG_CIFS_ACL) += cifsacl.o etercifs-$(CONFIG_CIFS_ACL) += cifsacl.o
etercifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o etercifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
......
...@@ -452,6 +452,11 @@ A partial list of the supported mount options follows: ...@@ -452,6 +452,11 @@ A partial list of the supported mount options follows:
if oplock (caching token) is granted and held. Note that if oplock (caching token) is granted and held. Note that
direct allows write operations larger than page size direct allows write operations larger than page size
to be sent to the server. to be sent to the server.
strictcache Use for switching on strict cache mode. In this mode the
client reads from the cache all the time it has Oplock Level II,
otherwise - reads from the server. All written data are stored
in the cache, but if the client doesn't have Exclusive Oplock,
it writes the data to the server.
acl Allow setfacl and getfacl to manage posix ACLs if server acl Allow setfacl and getfacl to manage posix ACLs if server
supports them. (default) supports them. (default)
noacl Do not allow setfacl and getfacl calls on this mount noacl Do not allow setfacl and getfacl calls on this mount
......
...@@ -40,7 +40,8 @@ ...@@ -40,7 +40,8 @@
#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ #define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */
#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ #define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */
#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ #define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */
#define CIFS_MOUNT_WINE_MODE 0x40000 /* use pid forwarding for wine apps */ #define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */
#define CIFS_MOUNT_WINE_MODE 0x80000 /* use pid forwarding for wine apps */
struct cifs_sb_info { struct cifs_sb_info {
struct rb_root tlink_tree; struct rb_root tlink_tree;
......
...@@ -585,10 +585,17 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -585,10 +585,17 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
{ {
struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
ssize_t written; ssize_t written;
int rc;
written = generic_file_aio_write(iocb, iov, nr_segs, pos); written = generic_file_aio_write(iocb, iov, nr_segs, pos);
if (!CIFS_I(inode)->clientCanCacheAll)
filemap_fdatawrite(inode->i_mapping); if (CIFS_I(inode)->clientCanCacheAll)
return written;
rc = filemap_fdatawrite(inode->i_mapping);
if (rc)
cFYI(1, "cifs_file_aio_write: %d rc on %p inode", rc, inode);
return written; return written;
} }
...@@ -718,6 +725,25 @@ const struct file_operations cifs_file_ops = { ...@@ -718,6 +725,25 @@ const struct file_operations cifs_file_ops = {
.setlease = cifs_setlease, .setlease = cifs_setlease,
}; };
const struct file_operations cifs_file_strict_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = cifs_strict_writev,
.open = cifs_open,
.release = cifs_close,
.lock = cifs_lock,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
.mmap = cifs_file_strict_mmap,
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_ops = { const struct file_operations cifs_file_direct_ops = {
/* no aio, no readv - /* no aio, no readv -
BB reevaluate whether they can be done with directio, no cache */ BB reevaluate whether they can be done with directio, no cache */
...@@ -736,6 +762,7 @@ const struct file_operations cifs_file_direct_ops = { ...@@ -736,6 +762,7 @@ const struct file_operations cifs_file_direct_ops = {
.llseek = cifs_llseek, .llseek = cifs_llseek,
.setlease = cifs_setlease, .setlease = cifs_setlease,
}; };
const struct file_operations cifs_file_nobrl_ops = { const struct file_operations cifs_file_nobrl_ops = {
.read = do_sync_read, .read = do_sync_read,
.write = do_sync_write, .write = do_sync_write,
...@@ -754,6 +781,24 @@ const struct file_operations cifs_file_nobrl_ops = { ...@@ -754,6 +781,24 @@ const struct file_operations cifs_file_nobrl_ops = {
.setlease = cifs_setlease, .setlease = cifs_setlease,
}; };
const struct file_operations cifs_file_strict_nobrl_ops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = cifs_strict_readv,
.aio_write = cifs_strict_writev,
.open = cifs_open,
.release = cifs_close,
.fsync = cifs_strict_fsync,
.flush = cifs_flush,
.mmap = cifs_file_strict_mmap,
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
#ifdef CONFIG_CIFS_POSIX
.unlocked_ioctl = cifs_ioctl,
#endif /* CONFIG_CIFS_POSIX */
.setlease = cifs_setlease,
};
const struct file_operations cifs_file_direct_nobrl_ops = { const struct file_operations cifs_file_direct_nobrl_ops = {
/* no mmap, no aio, no readv - /* no mmap, no aio, no readv -
BB reevaluate whether they can be done with directio, no cache */ BB reevaluate whether they can be done with directio, no cache */
......
...@@ -61,6 +61,7 @@ extern int cifs_rename(struct inode *, struct dentry *, struct inode *, ...@@ -61,6 +61,7 @@ extern int cifs_rename(struct inode *, struct dentry *, struct inode *,
struct dentry *); struct dentry *);
extern int cifs_revalidate_file(struct file *filp); extern int cifs_revalidate_file(struct file *filp);
extern int cifs_revalidate_dentry(struct dentry *); extern int cifs_revalidate_dentry(struct dentry *);
extern void cifs_invalidate_mapping(struct inode *inode);
extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int cifs_setattr(struct dentry *, struct iattr *); extern int cifs_setattr(struct dentry *, struct iattr *);
...@@ -72,19 +73,27 @@ extern const struct inode_operations cifs_dfs_referral_inode_operations; ...@@ -72,19 +73,27 @@ extern const struct inode_operations cifs_dfs_referral_inode_operations;
/* Functions related to files and directories */ /* Functions related to files and directories */
extern const struct file_operations cifs_file_ops; extern const struct file_operations cifs_file_ops;
extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */
extern const struct file_operations cifs_file_nobrl_ops; extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */
extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */ extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */
extern const struct file_operations cifs_file_direct_nobrl_ops;
extern const struct file_operations cifs_file_strict_nobrl_ops;
extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_open(struct inode *inode, struct file *file);
extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file);
extern int cifs_closedir(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file);
extern ssize_t cifs_user_read(struct file *file, char __user *read_data, extern ssize_t cifs_user_read(struct file *file, char __user *read_data,
size_t read_size, loff_t *poffset); size_t read_size, loff_t *poffset);
extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, extern ssize_t cifs_user_write(struct file *file, const char __user *write_data,
size_t write_size, loff_t *poffset); size_t write_size, loff_t *poffset);
extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_lock(struct file *, int, struct file_lock *);
extern int cifs_fsync(struct file *, int); extern int cifs_fsync(struct file *, int);
extern int cifs_strict_fsync(struct file *, int);
extern int cifs_flush(struct file *, fl_owner_t id); extern int cifs_flush(struct file *, fl_owner_t id);
extern int cifs_file_mmap(struct file * , struct vm_area_struct *); extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *);
extern const struct file_operations cifs_dir_ops; extern const struct file_operations cifs_dir_ops;
extern int cifs_dir_open(struct inode *inode, struct file *file); extern int cifs_dir_open(struct inode *inode, struct file *file);
extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir); extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir);
......
...@@ -79,6 +79,8 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); ...@@ -79,6 +79,8 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
extern bool is_valid_oplock_break(struct smb_hdr *smb, extern bool is_valid_oplock_break(struct smb_hdr *smb,
struct TCP_Server_Info *); struct TCP_Server_Info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written);
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool); extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize(struct smb_hdr *ptr);
......
...@@ -84,6 +84,7 @@ struct smb_vol { ...@@ -84,6 +84,7 @@ struct smb_vol {
bool no_xattr:1; /* set if xattr (EA) support should be disabled*/ bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
bool server_ino:1; /* use inode numbers from server ie UniqueId */ bool server_ino:1; /* use inode numbers from server ie UniqueId */
bool direct_io:1; bool direct_io:1;
bool strict_io:1; /* strict cache behavior */
bool remap:1; /* set to remap seven reserved chars in filenames */ bool remap:1; /* set to remap seven reserved chars in filenames */
bool posix_paths:1; /* unset to not ask for posix pathnames. */ bool posix_paths:1; /* unset to not ask for posix pathnames. */
bool no_linux_ext:1; bool no_linux_ext:1;
...@@ -1337,7 +1338,7 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1337,7 +1338,7 @@ cifs_parse_mount_options(char *options, const char *devname,
} else if (strnicmp(data, "wine", 4) == 0) { } else if (strnicmp(data, "wine", 4) == 0) {
vol->wine_mode = 1; vol->wine_mode = 1;
vol->mand_lock = 1; vol->mand_lock = 1;
vol->direct_io = 1; vol->strict_io = 1;
} else if (strnicmp(data, "cifsacl", 7) == 0) { } else if (strnicmp(data, "cifsacl", 7) == 0) {
vol->cifs_acl = 1; vol->cifs_acl = 1;
} else if (strnicmp(data, "nocifsacl", 9) == 0) { } else if (strnicmp(data, "nocifsacl", 9) == 0) {
...@@ -1362,6 +1363,8 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1362,6 +1363,8 @@ cifs_parse_mount_options(char *options, const char *devname,
vol->direct_io = 1; vol->direct_io = 1;
} else if (strnicmp(data, "forcedirectio", 13) == 0) { } else if (strnicmp(data, "forcedirectio", 13) == 0) {
vol->direct_io = 1; vol->direct_io = 1;
} else if (strnicmp(data, "strictcache", 11) == 0) {
vol->strict_io = 1;
} else if (strnicmp(data, "noac", 4) == 0) { } else if (strnicmp(data, "noac", 4) == 0) {
printk(KERN_WARNING "CIFS: Mount option noac not " printk(KERN_WARNING "CIFS: Mount option noac not "
"supported. Instead set " "supported. Instead set "
...@@ -2652,6 +2655,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, ...@@ -2652,6 +2655,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,
if (pvolume_info->multiuser) if (pvolume_info->multiuser)
cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER |
CIFS_MOUNT_NO_PERM); CIFS_MOUNT_NO_PERM);
if (pvolume_info->strict_io)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO;
if (pvolume_info->direct_io) { if (pvolume_info->direct_io) {
cFYI(1, "mounting share using direct i/o"); cFYI(1, "mounting share using direct i/o");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
......
...@@ -269,6 +269,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) ...@@ -269,6 +269,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
struct inode *inode = cifs_file->dentry->d_inode; struct inode *inode = cifs_file->dentry->d_inode;
struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink); struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink);
struct cifsInodeInfo *cifsi = CIFS_I(inode); struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsLockInfo *li, *tmp; struct cifsLockInfo *li, *tmp;
spin_lock(&cifs_file_list_lock); spin_lock(&cifs_file_list_lock);
...@@ -284,6 +285,13 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) ...@@ -284,6 +285,13 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
if (list_empty(&cifsi->openFileList)) { if (list_empty(&cifsi->openFileList)) {
cFYI(1, "closing last open instance for inode %p", cFYI(1, "closing last open instance for inode %p",
cifs_file->dentry->d_inode); cifs_file->dentry->d_inode);
/* in strict cache mode we need invalidate mapping on the last
close because it may cause a error when we open this file
again and get at least level II oplock */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
CIFS_I(inode)->invalid_mapping = true;
cifs_set_oplock_level(cifsi, 0); cifs_set_oplock_level(cifsi, 0);
} }
spin_unlock(&cifs_file_list_lock); spin_unlock(&cifs_file_list_lock);
...@@ -927,7 +935,7 @@ cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset) ...@@ -927,7 +935,7 @@ cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset)
} }
/* update the file size (if needed) after a write */ /* update the file size (if needed) after a write */
static void void
cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written) unsigned int bytes_written)
{ {
...@@ -1455,6 +1463,7 @@ retry: ...@@ -1455,6 +1463,7 @@ retry:
break; break;
} }
if (n_iov) { if (n_iov) {
retry_write:
open_file = find_writable_file(CIFS_I(mapping->host), open_file = find_writable_file(CIFS_I(mapping->host),
false); false);
if (!open_file) { if (!open_file) {
...@@ -1472,31 +1481,55 @@ retry: ...@@ -1472,31 +1481,55 @@ retry:
offset, &bytes_written, offset, &bytes_written,
iov, n_iov, long_op); iov, n_iov, long_op);
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
cifs_update_eof(cifsi, offset, bytes_written);
} }
if (rc || bytes_written < bytes_to_write) { cFYI(1, "Write2 rc=%d, wrote=%u", rc, bytes_written);
cERROR(1, "Write2 ret %d, wrote %d",
rc, bytes_written); /*
mapping_set_error(mapping, rc); * For now, treat a short write as if nothing got
} else { * written. A zero length write however indicates
* ENOSPC or EFBIG. We have no way to know which
* though, so call it ENOSPC for now. EFBIG would
* get translated to AS_EIO anyway.
*
* FIXME: make it take into account the data that did
* get written
*/
if (rc == 0) {
if (bytes_written == 0)
rc = -ENOSPC;
else if (bytes_written < bytes_to_write)
rc = -EAGAIN;
}
/* retry on data-integrity flush */
if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN)
goto retry_write;
/* fix the stats and EOF */
if (bytes_written > 0) {
cifs_stats_bytes_written(tcon, bytes_written); cifs_stats_bytes_written(tcon, bytes_written);
cifs_update_eof(cifsi, offset, bytes_written);
} }
for (i = 0; i < n_iov; i++) { for (i = 0; i < n_iov; i++) {
page = pvec.pages[first + i]; page = pvec.pages[first + i];
/* Should we also set page error on /* on retryable write error, redirty page */
success rc but too little data written? */ if (rc == -EAGAIN)
/* BB investigate retry logic on temporary redirty_page_for_writepage(wbc, page);
server crash cases and how recovery works else if (rc != 0)
when page marked as error */
if (rc)
SetPageError(page); SetPageError(page);
kunmap(page); kunmap(page);
unlock_page(page); unlock_page(page);
end_page_writeback(page); end_page_writeback(page);
page_cache_release(page); page_cache_release(page);
} }
if (rc != -EAGAIN)
mapping_set_error(mapping, rc);
else
rc = 0;
if ((wbc->nr_to_write -= n_iov) <= 0) if ((wbc->nr_to_write -= n_iov) <= 0)
done = 1; done = 1;
index = next; index = next;
...@@ -1608,27 +1641,47 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, ...@@ -1608,27 +1641,47 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
return rc; return rc;
} }
int cifs_fsync(struct file *file, int datasync) int cifs_strict_fsync(struct file *file, int datasync)
{ {
int xid; int xid;
int rc = 0; int rc = 0;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct cifsFileInfo *smbfile = file->private_data; struct cifsFileInfo *smbfile = file->private_data;
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
xid = GetXid(); xid = GetXid();
cFYI(1, "Sync file - name: %s datasync: 0x%x", cFYI(1, "Sync file - name: %s datasync: 0x%x",
file->f_path.dentry->d_name.name, datasync); file->f_path.dentry->d_name.name, datasync);
rc = filemap_write_and_wait(inode->i_mapping); if (!CIFS_I(inode)->clientCanCacheRead)
if (rc == 0) { cifs_invalidate_mapping(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
tcon = tlink_tcon(smbfile->tlink); tcon = tlink_tcon(smbfile->tlink);
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
}
FreeXid(xid);
return rc;
}
int cifs_fsync(struct file *file, int datasync)
{
int xid;
int rc = 0;
struct cifsTconInfo *tcon;
struct cifsFileInfo *smbfile = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
xid = GetXid();
cFYI(1, "Sync file - name: %s datasync: 0x%x",
file->f_path.dentry->d_name.name, datasync);
tcon = tlink_tcon(smbfile->tlink);
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
...@@ -1679,30 +1732,240 @@ int cifs_flush(struct file *file, fl_owner_t id) ...@@ -1679,30 +1732,240 @@ int cifs_flush(struct file *file, fl_owner_t id)
return rc; return rc;
} }
ssize_t cifs_user_read(struct file *file, char __user *read_data, static int
size_t read_size, loff_t *poffset) cifs_write_allocate_pages(struct page **pages, unsigned long num_pages)
{ {
int rc = -EACCES; int rc = 0;
unsigned long i;
for (i = 0; i < num_pages; i++) {
pages[i] = alloc_page(__GFP_HIGHMEM);
if (!pages[i]) {
/*
* save number of pages we have already allocated and
* return with ENOMEM error
*/
num_pages = i;
rc = -ENOMEM;
goto error;
}
}
return rc;
error:
for (i = 0; i < num_pages; i++)
put_page(pages[i]);
return rc;
}
static inline
size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
{
size_t num_pages;
size_t clen;
clen = min_t(const size_t, len, wsize);
num_pages = clen / PAGE_CACHE_SIZE;
if (clen % PAGE_CACHE_SIZE)
num_pages++;
if (cur_len)
*cur_len = clen;
return num_pages;
}
static ssize_t
cifs_iovec_write(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
{
unsigned int written;
ssize_t total_written = 0;
unsigned long num_pages, npages, i;
size_t copied, len, cur_len;
struct kvec *to_send;
struct page **pages;
struct iov_iter it;
struct inode *inode;
struct cifsFileInfo *open_file;
struct cifsTconInfo *pTcon;
struct cifs_sb_info *cifs_sb;
int xid, rc;
__u32 netpid;
len = iov_length(iov, nr_segs);
if (!len)
return 0;
rc = generic_write_checks(file, poffset, &len, 0);
if (rc)
return rc;
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
num_pages = get_numpages(cifs_sb->wsize, len, &cur_len);
pages = kmalloc(sizeof(struct pages *)*num_pages, GFP_KERNEL);
if (!pages)
return -ENOMEM;
to_send = kmalloc(sizeof(struct kvec)*(num_pages + 1), GFP_KERNEL);
if (!to_send) {
kfree(pages);
return -ENOMEM;
}
rc = cifs_write_allocate_pages(pages, num_pages);
if (rc) {
kfree(pages);
kfree(to_send);
return rc;
}
xid = GetXid();
open_file = file->private_data;
pTcon = tlink_tcon(open_file->tlink);
inode = file->f_path.dentry->d_inode;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_WINE_MODE)
netpid = open_file->pid;
else
netpid = current->tgid;
iov_iter_init(&it, iov, nr_segs, len, 0);
npages = num_pages;
do {
size_t save_len = cur_len;
for (i = 0; i < npages; i++) {
copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
copied = iov_iter_copy_from_user(pages[i], &it, 0,
copied);
cur_len -= copied;
iov_iter_advance(&it, copied);
to_send[i+1].iov_base = kmap(pages[i]);
to_send[i+1].iov_len = copied;
}
cur_len = save_len - cur_len;
do {
if (open_file->invalidHandle) {
rc = cifs_reopen_file(open_file, false);
if (rc != 0)
break;
}
rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid,
netpid, cur_len, *poffset, &written,
to_send, npages, 0);
} while (rc == -EAGAIN);
for (i = 0; i < npages; i++)
kunmap(pages[i]);
if (written) {
len -= written;
total_written += written;
cifs_update_eof(CIFS_I(inode), *poffset, written);
*poffset += written;
} else if (rc < 0) {
if (!total_written)
total_written = rc;
break;
}
/* get length and number of kvecs of the next write */
npages = get_numpages(cifs_sb->wsize, len, &cur_len);
} while (len > 0);
if (total_written > 0) {
spin_lock(&inode->i_lock);
if (*poffset > inode->i_size)
i_size_write(inode, *poffset);
spin_unlock(&inode->i_lock);
}
cifs_stats_bytes_written(pTcon, total_written);
mark_inode_dirty_sync(inode);
for (i = 0; i < num_pages; i++)
put_page(pages[i]);
kfree(to_send);
kfree(pages);
FreeXid(xid);
return total_written;
}
static ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t written;
struct inode *inode;
inode = iocb->ki_filp->f_path.dentry->d_inode;
/*
* BB - optimize the way when signing is disabled. We can drop this
* extra memory-to-memory copying and use iovec buffers for constructing
* write request.
*/
written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos);
if (written > 0) {
CIFS_I(inode)->invalid_mapping = true;
iocb->ki_pos = pos;
}
return written;
}
ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct inode *inode;
inode = iocb->ki_filp->f_path.dentry->d_inode;
if (CIFS_I(inode)->clientCanCacheAll)
return generic_file_aio_write(iocb, iov, nr_segs, pos);
/*
* In strict cache mode we need to write the data to the server exactly
* from the pos to pos+len-1 rather than flush all affected pages
* because it may cause a error with mandatory locks on these pages but
* not on the region from pos to ppos+len-1.
*/
return cifs_user_writev(iocb, iov, nr_segs, pos);
}
static ssize_t
cifs_iovec_read(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *poffset)
{
int rc;
int xid;
ssize_t total_read;
unsigned int bytes_read = 0; unsigned int bytes_read = 0;
unsigned int total_read = 0; size_t len, cur_len;
unsigned int current_read_size; int iov_offset = 0;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
int xid;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
char *smb_read_data;
char __user *current_offset;
struct smb_com_read_rsp *pSMBr; struct smb_com_read_rsp *pSMBr;
__u32 netpid; __u32 netpid;
char *read_data;
if (!nr_segs)
return 0;
len = iov_length(iov, nr_segs);
if (!len)
return 0;
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
if (file->private_data == NULL) {
rc = -EBADF;
FreeXid(xid);
return rc;
}
open_file = file->private_data; open_file = file->private_data;
pTcon = tlink_tcon(open_file->tlink); pTcon = tlink_tcon(open_file->tlink);
...@@ -1714,13 +1977,11 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1714,13 +1977,11 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
if ((file->f_flags & O_ACCMODE) == O_WRONLY) if ((file->f_flags & O_ACCMODE) == O_WRONLY)
cFYI(1, "attempting read on write only file instance"); cFYI(1, "attempting read on write only file instance");
for (total_read = 0, current_offset = read_data; for (total_read = 0; total_read < len; total_read += bytes_read) {
read_size > total_read; cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize);
total_read += bytes_read, current_offset += bytes_read) {
current_read_size = min_t(const int, read_size - total_read,
cifs_sb->rsize);
rc = -EAGAIN; rc = -EAGAIN;
smb_read_data = NULL; read_data = NULL;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
int buf_type = CIFS_NO_BUFFER; int buf_type = CIFS_NO_BUFFER;
if (open_file->invalidHandle) { if (open_file->invalidHandle) {
...@@ -1728,27 +1989,25 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1728,27 +1989,25 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
if (rc != 0) if (rc != 0)
break; break;
} }
rc = CIFSSMBRead(xid, pTcon, rc = CIFSSMBRead(xid, pTcon, open_file->netfid,
open_file->netfid, netpid, netpid, cur_len, *poffset, &bytes_read,
current_read_size, *poffset, &read_data, &buf_type);
&bytes_read, &smb_read_data, pSMBr = (struct smb_com_read_rsp *)read_data;
&buf_type); if (read_data) {
pSMBr = (struct smb_com_read_rsp *)smb_read_data; char *data_offset = read_data + 4 +
if (smb_read_data) { le16_to_cpu(pSMBr->DataOffset);
if (copy_to_user(current_offset, if (memcpy_toiovecend(iov, data_offset,
smb_read_data + iov_offset, bytes_read))
4 /* RFC1001 length field */ +
le16_to_cpu(pSMBr->DataOffset),
bytes_read))
rc = -EFAULT; rc = -EFAULT;
if (buf_type == CIFS_SMALL_BUFFER) if (buf_type == CIFS_SMALL_BUFFER)
cifs_small_buf_release(smb_read_data); cifs_small_buf_release(read_data);
else if (buf_type == CIFS_LARGE_BUFFER) else if (buf_type == CIFS_LARGE_BUFFER)
cifs_buf_release(smb_read_data); cifs_buf_release(read_data);
smb_read_data = NULL; read_data = NULL;
iov_offset += bytes_read;
} }
} }
if (rc || (bytes_read == 0)) { if (rc || (bytes_read == 0)) {
if (total_read) { if (total_read) {
break; break;
...@@ -1761,13 +2020,57 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1761,13 +2020,57 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
*poffset += bytes_read; *poffset += bytes_read;
} }
} }
FreeXid(xid); FreeXid(xid);
return total_read; return total_read;
} }
ssize_t cifs_user_read(struct file *file, char __user *read_data,
size_t read_size, loff_t *poffset)
{
struct iovec iov;
iov.iov_base = read_data;
iov.iov_len = read_size;
return cifs_iovec_read(file, &iov, 1, poffset);
}
static ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
ssize_t read;
read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos);
if (read > 0)
iocb->ki_pos = pos;
return read;
}
ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct inode *inode;
inode = iocb->ki_filp->f_path.dentry->d_inode;
if (CIFS_I(inode)->clientCanCacheRead)
return generic_file_aio_read(iocb, iov, nr_segs, pos);
/*
* In strict cache mode we need to read from the server all the time
* if we don't have level II oplock because the server can delay mtime
* change - so we can't make a decision about inode invalidating.
* And we can also fail with pagereading if there are mandatory locks
* on pages affected by this read but not on the region from pos to
* pos+len-1.
*/
return cifs_user_readv(iocb, iov, nr_segs, pos);
}
static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
loff_t *poffset) loff_t *poffset)
{ {
int rc = -EACCES; int rc = -EACCES;
unsigned int bytes_read = 0; unsigned int bytes_read = 0;
...@@ -1841,6 +2144,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, ...@@ -1841,6 +2144,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
return total_read; return total_read;
} }
int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)
{
int rc, xid;
struct inode *inode = file->f_path.dentry->d_inode;
xid = GetXid();
if (!CIFS_I(inode)->clientCanCacheRead)
cifs_invalidate_mapping(inode);
rc = generic_file_mmap(file, vma);
FreeXid(xid);
return rc;
}
int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
{ {
int rc, xid; int rc, xid;
......
...@@ -44,13 +44,17 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) ...@@ -44,13 +44,17 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
inode->i_fop = &cifs_file_direct_nobrl_ops; inode->i_fop = &cifs_file_direct_nobrl_ops;
else else
inode->i_fop = &cifs_file_direct_ops; inode->i_fop = &cifs_file_direct_ops;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
inode->i_fop = &cifs_file_strict_nobrl_ops;
else
inode->i_fop = &cifs_file_strict_ops;
} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
inode->i_fop = &cifs_file_nobrl_ops; inode->i_fop = &cifs_file_nobrl_ops;
else { /* not direct, send byte range locks */ else { /* not direct, send byte range locks */
inode->i_fop = &cifs_file_ops; inode->i_fop = &cifs_file_ops;
} }
/* check if server can support readpages */ /* check if server can support readpages */
if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf < if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf <
PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
...@@ -1679,7 +1683,7 @@ cifs_inode_needs_reval(struct inode *inode) ...@@ -1679,7 +1683,7 @@ cifs_inode_needs_reval(struct inode *inode)
/* /*
* Zap the cache. Called when invalid_mapping flag is set. * Zap the cache. Called when invalid_mapping flag is set.
*/ */
static void void
cifs_invalidate_mapping(struct inode *inode) cifs_invalidate_mapping(struct inode *inode)
{ {
int rc; int rc;
...@@ -1687,12 +1691,18 @@ cifs_invalidate_mapping(struct inode *inode) ...@@ -1687,12 +1691,18 @@ cifs_invalidate_mapping(struct inode *inode)
cifs_i->invalid_mapping = false; cifs_i->invalid_mapping = false;
/* write back any cached data */
if (inode->i_mapping && inode->i_mapping->nrpages != 0) { if (inode->i_mapping && inode->i_mapping->nrpages != 0) {
/* write back any cached data */
rc = filemap_write_and_wait(inode->i_mapping); rc = filemap_write_and_wait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc); mapping_set_error(inode->i_mapping, rc);
rc = invalidate_inode_pages2(inode->i_mapping);
if (rc) {
cERROR(1, "%s: could not invalidate inode %p", __func__,
inode);
cifs_i->invalid_mapping = true;
}
} }
invalidate_remote_inode(inode);
cifs_fscache_reset_inode_cookie(inode); cifs_fscache_reset_inode_cookie(inode);
} }
......
...@@ -170,7 +170,7 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len) ...@@ -170,7 +170,7 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len)
{ {
int rc, alen, slen; int rc, alen, slen;
const char *pct; const char *pct;
char *endp, scope_id[13]; char scope_id[13];
struct sockaddr_in *s4 = (struct sockaddr_in *) dst; struct sockaddr_in *s4 = (struct sockaddr_in *) dst;
struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst;
...@@ -197,9 +197,9 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len) ...@@ -197,9 +197,9 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len)
memcpy(scope_id, pct + 1, slen); memcpy(scope_id, pct + 1, slen);
scope_id[slen] = '\0'; scope_id[slen] = '\0';
s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0); rc = strict_strtoul(scope_id, 0,
if (endp != scope_id + slen) (unsigned long *)&s6->sin6_scope_id);
return 0; rc = (rc == 0) ? 1 : 0;
} }
return rc; return rc;
......
...@@ -667,13 +667,13 @@ ssetup_ntlmssp_authenticate: ...@@ -667,13 +667,13 @@ ssetup_ntlmssp_authenticate:
if (type == LANMAN) { if (type == LANMAN) {
#ifdef CONFIG_CIFS_WEAK_PW_HASH #ifdef CONFIG_CIFS_WEAK_PW_HASH
char lnm_session_key[CIFS_SESS_KEY_SIZE]; char lnm_session_key[CIFS_AUTH_RESP_SIZE];
pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
/* no capabilities flags in old lanman negotiation */ /* no capabilities flags in old lanman negotiation */
pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
/* Calculate hash with password and copy into bcc_ptr. /* Calculate hash with password and copy into bcc_ptr.
* Encryption Key (stored as in cryptkey) gets used if the * Encryption Key (stored as in cryptkey) gets used if the
...@@ -686,8 +686,8 @@ ssetup_ntlmssp_authenticate: ...@@ -686,8 +686,8 @@ ssetup_ntlmssp_authenticate:
true : false, lnm_session_key); true : false, lnm_session_key);
ses->flags |= CIFS_SES_LANMAN; ses->flags |= CIFS_SES_LANMAN;
memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE); memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE; bcc_ptr += CIFS_AUTH_RESP_SIZE;
/* can not sign if LANMAN negotiated so no need /* can not sign if LANMAN negotiated so no need
to calculate signing key? but what if server to calculate signing key? but what if server
......
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