|
@@ -30,6 +30,7 @@
|
|
|
#include <linux/writeback.h>
|
|
|
#include <linux/task_io_accounting_ops.h>
|
|
|
#include <linux/delay.h>
|
|
|
+#include <linux/mount.h>
|
|
|
#include <asm/div64.h>
|
|
|
#include "cifsfs.h"
|
|
|
#include "cifspdu.h"
|
|
@@ -39,27 +40,6 @@
|
|
|
#include "cifs_debug.h"
|
|
|
#include "cifs_fs_sb.h"
|
|
|
|
|
|
-static inline struct cifsFileInfo *cifs_init_private(
|
|
|
- struct cifsFileInfo *private_data, struct inode *inode,
|
|
|
- struct file *file, __u16 netfid)
|
|
|
-{
|
|
|
- memset(private_data, 0, sizeof(struct cifsFileInfo));
|
|
|
- private_data->netfid = netfid;
|
|
|
- private_data->pid = current->tgid;
|
|
|
- mutex_init(&private_data->fh_mutex);
|
|
|
- mutex_init(&private_data->lock_mutex);
|
|
|
- INIT_LIST_HEAD(&private_data->llist);
|
|
|
- private_data->pfile = file; /* needed for writepage */
|
|
|
- private_data->pInode = inode;
|
|
|
- private_data->invalidHandle = false;
|
|
|
- private_data->closePend = false;
|
|
|
- /* Initialize reference count to one. The private data is
|
|
|
- freed on the release of the last reference */
|
|
|
- atomic_set(&private_data->count, 1);
|
|
|
-
|
|
|
- return private_data;
|
|
|
-}
|
|
|
-
|
|
|
static inline int cifs_convert_flags(unsigned int flags)
|
|
|
{
|
|
|
if ((flags & O_ACCMODE) == O_RDONLY)
|
|
@@ -123,9 +103,11 @@ static inline int cifs_get_disposition(unsigned int flags)
|
|
|
}
|
|
|
|
|
|
/* all arguments to this function must be checked for validity in caller */
|
|
|
-static inline int cifs_posix_open_inode_helper(struct inode *inode,
|
|
|
- struct file *file, struct cifsInodeInfo *pCifsInode,
|
|
|
- struct cifsFileInfo *pCifsFile, int oplock, u16 netfid)
|
|
|
+static inline int
|
|
|
+cifs_posix_open_inode_helper(struct inode *inode, struct file *file,
|
|
|
+ struct cifsInodeInfo *pCifsInode,
|
|
|
+ struct cifsFileInfo *pCifsFile, __u32 oplock,
|
|
|
+ u16 netfid)
|
|
|
{
|
|
|
|
|
|
write_lock(&GlobalSMBSeslock);
|
|
@@ -219,17 +201,6 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
|
|
|
struct timespec temp;
|
|
|
int rc;
|
|
|
|
|
|
- /* want handles we can use to read with first
|
|
|
- in the list so we do not have to walk the
|
|
|
- list to search for one in write_begin */
|
|
|
- if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
|
|
|
- list_add_tail(&pCifsFile->flist,
|
|
|
- &pCifsInode->openFileList);
|
|
|
- } else {
|
|
|
- list_add(&pCifsFile->flist,
|
|
|
- &pCifsInode->openFileList);
|
|
|
- }
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
if (pCifsInode->clientCanCacheRead) {
|
|
|
/* we have the inode open somewhere else
|
|
|
no need to discard cache data */
|
|
@@ -279,7 +250,8 @@ client_can_cache:
|
|
|
int cifs_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
int rc = -EACCES;
|
|
|
- int xid, oplock;
|
|
|
+ int xid;
|
|
|
+ __u32 oplock;
|
|
|
struct cifs_sb_info *cifs_sb;
|
|
|
struct cifsTconInfo *tcon;
|
|
|
struct cifsFileInfo *pCifsFile;
|
|
@@ -324,7 +296,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|
|
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
|
|
int oflags = (int) cifs_posix_convert_flags(file->f_flags);
|
|
|
/* can not refresh inode info since size could be stale */
|
|
|
- rc = cifs_posix_open(full_path, &inode, inode->i_sb,
|
|
|
+ rc = cifs_posix_open(full_path, &inode, file->f_path.mnt,
|
|
|
cifs_sb->mnt_file_mode /* ignored */,
|
|
|
oflags, &oplock, &netfid, xid);
|
|
|
if (rc == 0) {
|
|
@@ -414,24 +386,17 @@ int cifs_open(struct inode *inode, struct file *file)
|
|
|
cFYI(1, ("cifs_open returned 0x%x", rc));
|
|
|
goto out;
|
|
|
}
|
|
|
- file->private_data =
|
|
|
- kmalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
|
|
+
|
|
|
+ pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt,
|
|
|
+ file->f_flags);
|
|
|
+ file->private_data = pCifsFile;
|
|
|
if (file->private_data == NULL) {
|
|
|
rc = -ENOMEM;
|
|
|
goto out;
|
|
|
}
|
|
|
- pCifsFile = cifs_init_private(file->private_data, inode, file, netfid);
|
|
|
- write_lock(&GlobalSMBSeslock);
|
|
|
- list_add(&pCifsFile->tlist, &tcon->openFileList);
|
|
|
|
|
|
- pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
|
|
|
- if (pCifsInode) {
|
|
|
- rc = cifs_open_inode_helper(inode, file, pCifsInode,
|
|
|
- pCifsFile, tcon,
|
|
|
- &oplock, buf, full_path, xid);
|
|
|
- } else {
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
- }
|
|
|
+ rc = cifs_open_inode_helper(inode, file, pCifsInode, pCifsFile, tcon,
|
|
|
+ &oplock, buf, full_path, xid);
|
|
|
|
|
|
if (oplock & CIFS_CREATE_ACTION) {
|
|
|
/* time to set mode which we can not set earlier due to
|
|
@@ -474,7 +439,8 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile)
|
|
|
static int cifs_reopen_file(struct file *file, bool can_flush)
|
|
|
{
|
|
|
int rc = -EACCES;
|
|
|
- int xid, oplock;
|
|
|
+ int xid;
|
|
|
+ __u32 oplock;
|
|
|
struct cifs_sb_info *cifs_sb;
|
|
|
struct cifsTconInfo *tcon;
|
|
|
struct cifsFileInfo *pCifsFile;
|
|
@@ -543,7 +509,7 @@ reopen_error_exit:
|
|
|
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
|
|
int oflags = (int) cifs_posix_convert_flags(file->f_flags);
|
|
|
/* can not refresh inode info since size could be stale */
|
|
|
- rc = cifs_posix_open(full_path, NULL, inode->i_sb,
|
|
|
+ rc = cifs_posix_open(full_path, NULL, file->f_path.mnt,
|
|
|
cifs_sb->mnt_file_mode /* ignored */,
|
|
|
oflags, &oplock, &netfid, xid);
|
|
|
if (rc == 0) {
|
|
@@ -2308,6 +2274,73 @@ out:
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+cifs_oplock_break(struct slow_work *work)
|
|
|
+{
|
|
|
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
|
|
|
+ oplock_break);
|
|
|
+ struct inode *inode = cfile->pInode;
|
|
|
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
|
|
|
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->mnt->mnt_sb);
|
|
|
+ int rc, waitrc = 0;
|
|
|
+
|
|
|
+ if (inode && S_ISREG(inode->i_mode)) {
|
|
|
+#ifdef CONFIG_CIFS_EXPERIMENTAL
|
|
|
+ if (cinode->clientCanCacheAll == 0)
|
|
|
+ break_lease(inode, FMODE_READ);
|
|
|
+ else if (cinode->clientCanCacheRead == 0)
|
|
|
+ break_lease(inode, FMODE_WRITE);
|
|
|
+#endif
|
|
|
+ rc = filemap_fdatawrite(inode->i_mapping);
|
|
|
+ if (cinode->clientCanCacheRead == 0) {
|
|
|
+ waitrc = filemap_fdatawait(inode->i_mapping);
|
|
|
+ invalidate_remote_inode(inode);
|
|
|
+ }
|
|
|
+ if (!rc)
|
|
|
+ rc = waitrc;
|
|
|
+ if (rc)
|
|
|
+ cinode->write_behind_rc = rc;
|
|
|
+ cFYI(1, ("Oplock flush inode %p rc %d", inode, rc));
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * releasing stale oplock after recent reconnect of smb session using
|
|
|
+ * a now incorrect file handle is not a data integrity issue but do
|
|
|
+ * not bother sending an oplock release if session to server still is
|
|
|
+ * disconnected since oplock already released by the server
|
|
|
+ */
|
|
|
+ if (!cfile->closePend && !cfile->oplock_break_cancelled) {
|
|
|
+ rc = CIFSSMBLock(0, cifs_sb->tcon, cfile->netfid, 0, 0, 0, 0,
|
|
|
+ LOCKING_ANDX_OPLOCK_RELEASE, false);
|
|
|
+ cFYI(1, ("Oplock release rc = %d", rc));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+cifs_oplock_break_get(struct slow_work *work)
|
|
|
+{
|
|
|
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
|
|
|
+ oplock_break);
|
|
|
+ mntget(cfile->mnt);
|
|
|
+ cifsFileInfo_get(cfile);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+cifs_oplock_break_put(struct slow_work *work)
|
|
|
+{
|
|
|
+ struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
|
|
|
+ oplock_break);
|
|
|
+ mntput(cfile->mnt);
|
|
|
+ cifsFileInfo_put(cfile);
|
|
|
+}
|
|
|
+
|
|
|
+const struct slow_work_ops cifs_oplock_break_ops = {
|
|
|
+ .get_ref = cifs_oplock_break_get,
|
|
|
+ .put_ref = cifs_oplock_break_put,
|
|
|
+ .execute = cifs_oplock_break,
|
|
|
+};
|
|
|
+
|
|
|
const struct address_space_operations cifs_addr_ops = {
|
|
|
.readpage = cifs_readpage,
|
|
|
.readpages = cifs_readpages,
|