|
@@ -60,34 +60,32 @@ static inline int cifs_convert_flags(unsigned int flags)
|
|
|
FILE_READ_DATA);
|
|
|
}
|
|
|
|
|
|
-static inline fmode_t cifs_posix_convert_flags(unsigned int flags)
|
|
|
+static u32 cifs_posix_convert_flags(unsigned int flags)
|
|
|
{
|
|
|
- fmode_t posix_flags = 0;
|
|
|
+ u32 posix_flags = 0;
|
|
|
|
|
|
if ((flags & O_ACCMODE) == O_RDONLY)
|
|
|
- posix_flags = FMODE_READ;
|
|
|
+ posix_flags = SMB_O_RDONLY;
|
|
|
else if ((flags & O_ACCMODE) == O_WRONLY)
|
|
|
- posix_flags = FMODE_WRITE;
|
|
|
- else if ((flags & O_ACCMODE) == O_RDWR) {
|
|
|
- /* GENERIC_ALL is too much permission to request
|
|
|
- can cause unnecessary access denied on create */
|
|
|
- /* return GENERIC_ALL; */
|
|
|
- posix_flags = FMODE_READ | FMODE_WRITE;
|
|
|
- }
|
|
|
- /* can not map O_CREAT or O_EXCL or O_TRUNC flags when
|
|
|
- reopening a file. They had their effect on the original open */
|
|
|
- if (flags & O_APPEND)
|
|
|
- posix_flags |= (fmode_t)O_APPEND;
|
|
|
+ posix_flags = SMB_O_WRONLY;
|
|
|
+ else if ((flags & O_ACCMODE) == O_RDWR)
|
|
|
+ posix_flags = SMB_O_RDWR;
|
|
|
+
|
|
|
+ if (flags & O_CREAT)
|
|
|
+ posix_flags |= SMB_O_CREAT;
|
|
|
+ if (flags & O_EXCL)
|
|
|
+ posix_flags |= SMB_O_EXCL;
|
|
|
+ if (flags & O_TRUNC)
|
|
|
+ posix_flags |= SMB_O_TRUNC;
|
|
|
+ /* be safe and imply O_SYNC for O_DSYNC */
|
|
|
if (flags & O_DSYNC)
|
|
|
- posix_flags |= (fmode_t)O_DSYNC;
|
|
|
- if (flags & __O_SYNC)
|
|
|
- posix_flags |= (fmode_t)__O_SYNC;
|
|
|
+ posix_flags |= SMB_O_SYNC;
|
|
|
if (flags & O_DIRECTORY)
|
|
|
- posix_flags |= (fmode_t)O_DIRECTORY;
|
|
|
+ posix_flags |= SMB_O_DIRECTORY;
|
|
|
if (flags & O_NOFOLLOW)
|
|
|
- posix_flags |= (fmode_t)O_NOFOLLOW;
|
|
|
+ posix_flags |= SMB_O_NOFOLLOW;
|
|
|
if (flags & O_DIRECT)
|
|
|
- posix_flags |= (fmode_t)O_DIRECT;
|
|
|
+ posix_flags |= SMB_O_DIRECT;
|
|
|
|
|
|
return posix_flags;
|
|
|
}
|
|
@@ -106,66 +104,8 @@ static inline int cifs_get_disposition(unsigned int flags)
|
|
|
return FILE_OPEN;
|
|
|
}
|
|
|
|
|
|
-/* 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, __u32 oplock,
|
|
|
- u16 netfid)
|
|
|
-{
|
|
|
-
|
|
|
- write_lock(&GlobalSMBSeslock);
|
|
|
-
|
|
|
- pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
|
|
|
- if (pCifsInode == NULL) {
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- if (pCifsInode->clientCanCacheRead) {
|
|
|
- /* we have the inode open somewhere else
|
|
|
- no need to discard cache data */
|
|
|
- goto psx_client_can_cache;
|
|
|
- }
|
|
|
-
|
|
|
- /* BB FIXME need to fix this check to move it earlier into posix_open
|
|
|
- BB fIX following section BB FIXME */
|
|
|
-
|
|
|
- /* if not oplocked, invalidate inode pages if mtime or file
|
|
|
- size changed */
|
|
|
-/* temp = cifs_NTtimeToUnix(le64_to_cpu(buf->LastWriteTime));
|
|
|
- if (timespec_equal(&file->f_path.dentry->d_inode->i_mtime, &temp) &&
|
|
|
- (file->f_path.dentry->d_inode->i_size ==
|
|
|
- (loff_t)le64_to_cpu(buf->EndOfFile))) {
|
|
|
- cFYI(1, "inode unchanged on server");
|
|
|
- } else {
|
|
|
- if (file->f_path.dentry->d_inode->i_mapping) {
|
|
|
- rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping);
|
|
|
- if (rc != 0)
|
|
|
- CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc;
|
|
|
- }
|
|
|
- cFYI(1, "invalidating remote inode since open detected it "
|
|
|
- "changed");
|
|
|
- invalidate_remote_inode(file->f_path.dentry->d_inode);
|
|
|
- } */
|
|
|
-
|
|
|
-psx_client_can_cache:
|
|
|
- if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
- pCifsInode->clientCanCacheAll = true;
|
|
|
- pCifsInode->clientCanCacheRead = true;
|
|
|
- cFYI(1, "Exclusive Oplock granted on inode %p",
|
|
|
- file->f_path.dentry->d_inode);
|
|
|
- } else if ((oplock & 0xF) == OPLOCK_READ)
|
|
|
- pCifsInode->clientCanCacheRead = true;
|
|
|
-
|
|
|
- /* will have to change the unlock if we reenable the
|
|
|
- filemap_fdatawrite (which does not seem necessary */
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* all arguments to this function must be checked for validity in caller */
|
|
|
static inline int cifs_open_inode_helper(struct inode *inode,
|
|
|
- struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf,
|
|
|
+ struct cifsTconInfo *pTcon, __u32 oplock, FILE_ALL_INFO *buf,
|
|
|
char *full_path, int xid)
|
|
|
{
|
|
|
struct cifsInodeInfo *pCifsInode = CIFS_I(inode);
|
|
@@ -207,16 +147,175 @@ client_can_cache:
|
|
|
rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb,
|
|
|
xid, NULL);
|
|
|
|
|
|
- if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
+ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
pCifsInode->clientCanCacheAll = true;
|
|
|
pCifsInode->clientCanCacheRead = true;
|
|
|
cFYI(1, "Exclusive Oplock granted on inode %p", inode);
|
|
|
- } else if ((*oplock & 0xF) == OPLOCK_READ)
|
|
|
+ } else if ((oplock & 0xF) == OPLOCK_READ)
|
|
|
pCifsInode->clientCanCacheRead = true;
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+int cifs_posix_open(char *full_path, struct inode **pinode,
|
|
|
+ struct super_block *sb, int mode, unsigned int f_flags,
|
|
|
+ __u32 *poplock, __u16 *pnetfid, int xid)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ FILE_UNIX_BASIC_INFO *presp_data;
|
|
|
+ __u32 posix_flags = 0;
|
|
|
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
|
|
+ struct cifs_fattr fattr;
|
|
|
+ struct tcon_link *tlink;
|
|
|
+ struct cifsTconInfo *tcon;
|
|
|
+
|
|
|
+ cFYI(1, "posix open %s", full_path);
|
|
|
+
|
|
|
+ presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
|
|
|
+ if (presp_data == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ tlink = cifs_sb_tlink(cifs_sb);
|
|
|
+ if (IS_ERR(tlink)) {
|
|
|
+ rc = PTR_ERR(tlink);
|
|
|
+ goto posix_open_ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ tcon = tlink_tcon(tlink);
|
|
|
+ mode &= ~current_umask();
|
|
|
+
|
|
|
+ posix_flags = cifs_posix_convert_flags(f_flags);
|
|
|
+ rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data,
|
|
|
+ poplock, full_path, cifs_sb->local_nls,
|
|
|
+ cifs_sb->mnt_cifs_flags &
|
|
|
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
+ cifs_put_tlink(tlink);
|
|
|
+
|
|
|
+ if (rc)
|
|
|
+ goto posix_open_ret;
|
|
|
+
|
|
|
+ if (presp_data->Type == cpu_to_le32(-1))
|
|
|
+ goto posix_open_ret; /* open ok, caller does qpathinfo */
|
|
|
+
|
|
|
+ if (!pinode)
|
|
|
+ goto posix_open_ret; /* caller does not need info */
|
|
|
+
|
|
|
+ cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb);
|
|
|
+
|
|
|
+ /* get new inode and set it up */
|
|
|
+ if (*pinode == NULL) {
|
|
|
+ cifs_fill_uniqueid(sb, &fattr);
|
|
|
+ *pinode = cifs_iget(sb, &fattr);
|
|
|
+ if (!*pinode) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto posix_open_ret;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ cifs_fattr_to_inode(*pinode, &fattr);
|
|
|
+ }
|
|
|
+
|
|
|
+posix_open_ret:
|
|
|
+ kfree(presp_data);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+struct cifsFileInfo *
|
|
|
+cifs_new_fileinfo(__u16 fileHandle, struct file *file,
|
|
|
+ struct tcon_link *tlink, __u32 oplock)
|
|
|
+{
|
|
|
+ struct dentry *dentry = file->f_path.dentry;
|
|
|
+ struct inode *inode = dentry->d_inode;
|
|
|
+ struct cifsInodeInfo *pCifsInode = CIFS_I(inode);
|
|
|
+ struct cifsFileInfo *pCifsFile;
|
|
|
+
|
|
|
+ pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
|
|
+ if (pCifsFile == NULL)
|
|
|
+ return pCifsFile;
|
|
|
+
|
|
|
+ pCifsFile->netfid = fileHandle;
|
|
|
+ pCifsFile->pid = current->tgid;
|
|
|
+ pCifsFile->uid = current_fsuid();
|
|
|
+ pCifsFile->dentry = dget(dentry);
|
|
|
+ pCifsFile->f_flags = file->f_flags;
|
|
|
+ pCifsFile->invalidHandle = false;
|
|
|
+ pCifsFile->tlink = cifs_get_tlink(tlink);
|
|
|
+ mutex_init(&pCifsFile->fh_mutex);
|
|
|
+ mutex_init(&pCifsFile->lock_mutex);
|
|
|
+ INIT_LIST_HEAD(&pCifsFile->llist);
|
|
|
+ atomic_set(&pCifsFile->count, 1);
|
|
|
+ INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
|
|
|
+
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
+ list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
|
|
|
+ /* if readable file instance put first in list*/
|
|
|
+ if (file->f_mode & FMODE_READ)
|
|
|
+ list_add(&pCifsFile->flist, &pCifsInode->openFileList);
|
|
|
+ else
|
|
|
+ list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
+
|
|
|
+ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
+ pCifsInode->clientCanCacheAll = true;
|
|
|
+ pCifsInode->clientCanCacheRead = true;
|
|
|
+ cFYI(1, "Exclusive Oplock inode %p", inode);
|
|
|
+ } else if ((oplock & 0xF) == OPLOCK_READ)
|
|
|
+ pCifsInode->clientCanCacheRead = true;
|
|
|
+
|
|
|
+ file->private_data = pCifsFile;
|
|
|
+ return pCifsFile;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Release a reference on the file private data. This may involve closing
|
|
|
+ * the filehandle out on the server.
|
|
|
+ */
|
|
|
+void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
|
|
+{
|
|
|
+ struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink);
|
|
|
+ struct cifsInodeInfo *cifsi = CIFS_I(cifs_file->dentry->d_inode);
|
|
|
+ struct cifsLockInfo *li, *tmp;
|
|
|
+
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
+ if (!atomic_dec_and_test(&cifs_file->count)) {
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* remove it from the lists */
|
|
|
+ list_del(&cifs_file->flist);
|
|
|
+ list_del(&cifs_file->tlist);
|
|
|
+
|
|
|
+ if (list_empty(&cifsi->openFileList)) {
|
|
|
+ cFYI(1, "closing last open instance for inode %p",
|
|
|
+ cifs_file->dentry->d_inode);
|
|
|
+ cifsi->clientCanCacheRead = false;
|
|
|
+ cifsi->clientCanCacheAll = false;
|
|
|
+ }
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
+
|
|
|
+ if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
|
|
|
+ int xid, rc;
|
|
|
+
|
|
|
+ xid = GetXid();
|
|
|
+ rc = CIFSSMBClose(xid, tcon, cifs_file->netfid);
|
|
|
+ FreeXid(xid);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Delete any outstanding lock records. We'll lose them when the file
|
|
|
+ * is closed anyway.
|
|
|
+ */
|
|
|
+ mutex_lock(&cifs_file->lock_mutex);
|
|
|
+ list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) {
|
|
|
+ list_del(&li->llist);
|
|
|
+ kfree(li);
|
|
|
+ }
|
|
|
+ mutex_unlock(&cifs_file->lock_mutex);
|
|
|
+
|
|
|
+ cifs_put_tlink(cifs_file->tlink);
|
|
|
+ dput(cifs_file->dentry);
|
|
|
+ kfree(cifs_file);
|
|
|
+}
|
|
|
+
|
|
|
int cifs_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
int rc = -EACCES;
|
|
@@ -224,6 +323,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|
|
__u32 oplock;
|
|
|
struct cifs_sb_info *cifs_sb;
|
|
|
struct cifsTconInfo *tcon;
|
|
|
+ struct tcon_link *tlink;
|
|
|
struct cifsFileInfo *pCifsFile = NULL;
|
|
|
struct cifsInodeInfo *pCifsInode;
|
|
|
char *full_path = NULL;
|
|
@@ -235,7 +335,12 @@ int cifs_open(struct inode *inode, struct file *file)
|
|
|
xid = GetXid();
|
|
|
|
|
|
cifs_sb = CIFS_SB(inode->i_sb);
|
|
|
- tcon = cifs_sb->tcon;
|
|
|
+ tlink = cifs_sb_tlink(cifs_sb);
|
|
|
+ if (IS_ERR(tlink)) {
|
|
|
+ FreeXid(xid);
|
|
|
+ return PTR_ERR(tlink);
|
|
|
+ }
|
|
|
+ tcon = tlink_tcon(tlink);
|
|
|
|
|
|
pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
|
|
|
|
|
@@ -257,27 +362,15 @@ int cifs_open(struct inode *inode, struct file *file)
|
|
|
(tcon->ses->capabilities & CAP_UNIX) &&
|
|
|
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
|
|
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
|
|
- int oflags = (int) cifs_posix_convert_flags(file->f_flags);
|
|
|
- oflags |= SMB_O_CREAT;
|
|
|
/* can not refresh inode info since size could be stale */
|
|
|
rc = cifs_posix_open(full_path, &inode, inode->i_sb,
|
|
|
cifs_sb->mnt_file_mode /* ignored */,
|
|
|
- oflags, &oplock, &netfid, xid);
|
|
|
+ file->f_flags, &oplock, &netfid, xid);
|
|
|
if (rc == 0) {
|
|
|
cFYI(1, "posix open succeeded");
|
|
|
- /* no need for special case handling of setting mode
|
|
|
- on read only files needed here */
|
|
|
|
|
|
- rc = cifs_posix_open_inode_helper(inode, file,
|
|
|
- pCifsInode, oplock, netfid);
|
|
|
- if (rc != 0) {
|
|
|
- CIFSSMBClose(xid, tcon, netfid);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- pCifsFile = cifs_new_fileinfo(inode, netfid, file,
|
|
|
- file->f_path.mnt,
|
|
|
- oflags);
|
|
|
+ pCifsFile = cifs_new_fileinfo(netfid, file, tlink,
|
|
|
+ oplock);
|
|
|
if (pCifsFile == NULL) {
|
|
|
CIFSSMBClose(xid, tcon, netfid);
|
|
|
rc = -ENOMEM;
|
|
@@ -345,7 +438,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
|
|
|
+ if (tcon->ses->capabilities & CAP_NT_SMBS)
|
|
|
rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
|
|
|
desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
|
|
|
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
|
|
@@ -365,12 +458,11 @@ int cifs_open(struct inode *inode, struct file *file)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- rc = cifs_open_inode_helper(inode, tcon, &oplock, buf, full_path, xid);
|
|
|
+ rc = cifs_open_inode_helper(inode, tcon, oplock, buf, full_path, xid);
|
|
|
if (rc != 0)
|
|
|
goto out;
|
|
|
|
|
|
- pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt,
|
|
|
- file->f_flags);
|
|
|
+ pCifsFile = cifs_new_fileinfo(netfid, file, tlink, oplock);
|
|
|
if (pCifsFile == NULL) {
|
|
|
rc = -ENOMEM;
|
|
|
goto out;
|
|
@@ -402,6 +494,7 @@ out:
|
|
|
kfree(buf);
|
|
|
kfree(full_path);
|
|
|
FreeXid(xid);
|
|
|
+ cifs_put_tlink(tlink);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
@@ -416,14 +509,13 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile)
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
-static int cifs_reopen_file(struct file *file, bool can_flush)
|
|
|
+static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
|
|
|
{
|
|
|
int rc = -EACCES;
|
|
|
int xid;
|
|
|
__u32 oplock;
|
|
|
struct cifs_sb_info *cifs_sb;
|
|
|
struct cifsTconInfo *tcon;
|
|
|
- struct cifsFileInfo *pCifsFile;
|
|
|
struct cifsInodeInfo *pCifsInode;
|
|
|
struct inode *inode;
|
|
|
char *full_path = NULL;
|
|
@@ -431,11 +523,6 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
|
|
|
int disposition = FILE_OPEN;
|
|
|
__u16 netfid;
|
|
|
|
|
|
- if (file->private_data)
|
|
|
- pCifsFile = file->private_data;
|
|
|
- else
|
|
|
- return -EBADF;
|
|
|
-
|
|
|
xid = GetXid();
|
|
|
mutex_lock(&pCifsFile->fh_mutex);
|
|
|
if (!pCifsFile->invalidHandle) {
|
|
@@ -445,39 +532,24 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
- if (file->f_path.dentry == NULL) {
|
|
|
- cERROR(1, "no valid name if dentry freed");
|
|
|
- dump_stack();
|
|
|
- rc = -EBADF;
|
|
|
- goto reopen_error_exit;
|
|
|
- }
|
|
|
-
|
|
|
- inode = file->f_path.dentry->d_inode;
|
|
|
- if (inode == NULL) {
|
|
|
- cERROR(1, "inode not valid");
|
|
|
- dump_stack();
|
|
|
- rc = -EBADF;
|
|
|
- goto reopen_error_exit;
|
|
|
- }
|
|
|
-
|
|
|
+ inode = pCifsFile->dentry->d_inode;
|
|
|
cifs_sb = CIFS_SB(inode->i_sb);
|
|
|
- tcon = cifs_sb->tcon;
|
|
|
+ tcon = tlink_tcon(pCifsFile->tlink);
|
|
|
|
|
|
/* can not grab rename sem here because various ops, including
|
|
|
those that already have the rename sem can end up causing writepage
|
|
|
to get called and if the server was down that means we end up here,
|
|
|
and we can never tell if the caller already has the rename_sem */
|
|
|
- full_path = build_path_from_dentry(file->f_path.dentry);
|
|
|
+ full_path = build_path_from_dentry(pCifsFile->dentry);
|
|
|
if (full_path == NULL) {
|
|
|
rc = -ENOMEM;
|
|
|
-reopen_error_exit:
|
|
|
mutex_unlock(&pCifsFile->fh_mutex);
|
|
|
FreeXid(xid);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
cFYI(1, "inode = 0x%p file flags 0x%x for %s",
|
|
|
- inode, file->f_flags, full_path);
|
|
|
+ inode, pCifsFile->f_flags, full_path);
|
|
|
|
|
|
if (oplockEnabled)
|
|
|
oplock = REQ_OPLOCK;
|
|
@@ -487,8 +559,14 @@ reopen_error_exit:
|
|
|
if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
|
|
|
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
|
|
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 */
|
|
|
+
|
|
|
+ /*
|
|
|
+ * O_CREAT, O_EXCL and O_TRUNC already had their effect on the
|
|
|
+ * original open. Must mask them off for a reopen.
|
|
|
+ */
|
|
|
+ unsigned int oflags = pCifsFile->f_flags &
|
|
|
+ ~(O_CREAT | O_EXCL | O_TRUNC);
|
|
|
+
|
|
|
rc = cifs_posix_open(full_path, NULL, inode->i_sb,
|
|
|
cifs_sb->mnt_file_mode /* ignored */,
|
|
|
oflags, &oplock, &netfid, xid);
|
|
@@ -500,7 +578,7 @@ reopen_error_exit:
|
|
|
in the reconnect path it is important to retry hard */
|
|
|
}
|
|
|
|
|
|
- desiredAccess = cifs_convert_flags(file->f_flags);
|
|
|
+ desiredAccess = cifs_convert_flags(pCifsFile->f_flags);
|
|
|
|
|
|
/* Can not refresh inode by passing in file_info buf to be returned
|
|
|
by SMBOpen and then calling get_inode_info with returned buf
|
|
@@ -516,49 +594,50 @@ reopen_error_exit:
|
|
|
mutex_unlock(&pCifsFile->fh_mutex);
|
|
|
cFYI(1, "cifs_open returned 0x%x", rc);
|
|
|
cFYI(1, "oplock: %d", oplock);
|
|
|
- } else {
|
|
|
+ goto reopen_error_exit;
|
|
|
+ }
|
|
|
+
|
|
|
reopen_success:
|
|
|
- pCifsFile->netfid = netfid;
|
|
|
- pCifsFile->invalidHandle = false;
|
|
|
- mutex_unlock(&pCifsFile->fh_mutex);
|
|
|
- pCifsInode = CIFS_I(inode);
|
|
|
- if (pCifsInode) {
|
|
|
- if (can_flush) {
|
|
|
- rc = filemap_write_and_wait(inode->i_mapping);
|
|
|
- if (rc != 0)
|
|
|
- CIFS_I(inode)->write_behind_rc = rc;
|
|
|
- /* temporarily disable caching while we
|
|
|
- go to server to get inode info */
|
|
|
- pCifsInode->clientCanCacheAll = false;
|
|
|
- pCifsInode->clientCanCacheRead = false;
|
|
|
- if (tcon->unix_ext)
|
|
|
- rc = cifs_get_inode_info_unix(&inode,
|
|
|
- full_path, inode->i_sb, xid);
|
|
|
- else
|
|
|
- rc = cifs_get_inode_info(&inode,
|
|
|
- full_path, NULL, inode->i_sb,
|
|
|
- xid, NULL);
|
|
|
- } /* else we are writing out data to server already
|
|
|
- and could deadlock if we tried to flush data, and
|
|
|
- since we do not know if we have data that would
|
|
|
- invalidate the current end of file on the server
|
|
|
- we can not go to the server to get the new inod
|
|
|
- info */
|
|
|
- if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
- pCifsInode->clientCanCacheAll = true;
|
|
|
- pCifsInode->clientCanCacheRead = true;
|
|
|
- cFYI(1, "Exclusive Oplock granted on inode %p",
|
|
|
- file->f_path.dentry->d_inode);
|
|
|
- } else if ((oplock & 0xF) == OPLOCK_READ) {
|
|
|
- pCifsInode->clientCanCacheRead = true;
|
|
|
- pCifsInode->clientCanCacheAll = false;
|
|
|
- } else {
|
|
|
- pCifsInode->clientCanCacheRead = false;
|
|
|
- pCifsInode->clientCanCacheAll = false;
|
|
|
- }
|
|
|
- cifs_relock_file(pCifsFile);
|
|
|
- }
|
|
|
+ pCifsFile->netfid = netfid;
|
|
|
+ pCifsFile->invalidHandle = false;
|
|
|
+ mutex_unlock(&pCifsFile->fh_mutex);
|
|
|
+ pCifsInode = CIFS_I(inode);
|
|
|
+
|
|
|
+ if (can_flush) {
|
|
|
+ rc = filemap_write_and_wait(inode->i_mapping);
|
|
|
+ if (rc != 0)
|
|
|
+ CIFS_I(inode)->write_behind_rc = rc;
|
|
|
+
|
|
|
+ pCifsInode->clientCanCacheAll = false;
|
|
|
+ pCifsInode->clientCanCacheRead = false;
|
|
|
+ if (tcon->unix_ext)
|
|
|
+ rc = cifs_get_inode_info_unix(&inode,
|
|
|
+ full_path, inode->i_sb, xid);
|
|
|
+ else
|
|
|
+ rc = cifs_get_inode_info(&inode,
|
|
|
+ full_path, NULL, inode->i_sb,
|
|
|
+ xid, NULL);
|
|
|
+ } /* else we are writing out data to server already
|
|
|
+ and could deadlock if we tried to flush data, and
|
|
|
+ since we do not know if we have data that would
|
|
|
+ invalidate the current end of file on the server
|
|
|
+ we can not go to the server to get the new inod
|
|
|
+ info */
|
|
|
+ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
+ pCifsInode->clientCanCacheAll = true;
|
|
|
+ pCifsInode->clientCanCacheRead = true;
|
|
|
+ cFYI(1, "Exclusive Oplock granted on inode %p",
|
|
|
+ pCifsFile->dentry->d_inode);
|
|
|
+ } else if ((oplock & 0xF) == OPLOCK_READ) {
|
|
|
+ pCifsInode->clientCanCacheRead = true;
|
|
|
+ pCifsInode->clientCanCacheAll = false;
|
|
|
+ } else {
|
|
|
+ pCifsInode->clientCanCacheRead = false;
|
|
|
+ pCifsInode->clientCanCacheAll = false;
|
|
|
}
|
|
|
+ cifs_relock_file(pCifsFile);
|
|
|
+
|
|
|
+reopen_error_exit:
|
|
|
kfree(full_path);
|
|
|
FreeXid(xid);
|
|
|
return rc;
|
|
@@ -566,79 +645,11 @@ reopen_success:
|
|
|
|
|
|
int cifs_close(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
- int rc = 0;
|
|
|
- int xid, timeout;
|
|
|
- struct cifs_sb_info *cifs_sb;
|
|
|
- struct cifsTconInfo *pTcon;
|
|
|
- struct cifsFileInfo *pSMBFile = file->private_data;
|
|
|
+ cifsFileInfo_put(file->private_data);
|
|
|
+ file->private_data = NULL;
|
|
|
|
|
|
- xid = GetXid();
|
|
|
-
|
|
|
- cifs_sb = CIFS_SB(inode->i_sb);
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
- if (pSMBFile) {
|
|
|
- struct cifsLockInfo *li, *tmp;
|
|
|
- write_lock(&GlobalSMBSeslock);
|
|
|
- pSMBFile->closePend = true;
|
|
|
- if (pTcon) {
|
|
|
- /* no sense reconnecting to close a file that is
|
|
|
- already closed */
|
|
|
- if (!pTcon->need_reconnect) {
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
- timeout = 2;
|
|
|
- while ((atomic_read(&pSMBFile->count) != 1)
|
|
|
- && (timeout <= 2048)) {
|
|
|
- /* Give write a better chance to get to
|
|
|
- server ahead of the close. We do not
|
|
|
- want to add a wait_q here as it would
|
|
|
- increase the memory utilization as
|
|
|
- the struct would be in each open file,
|
|
|
- but this should give enough time to
|
|
|
- clear the socket */
|
|
|
- cFYI(DBG2, "close delay, write pending");
|
|
|
- msleep(timeout);
|
|
|
- timeout *= 4;
|
|
|
- }
|
|
|
- if (!pTcon->need_reconnect &&
|
|
|
- !pSMBFile->invalidHandle)
|
|
|
- rc = CIFSSMBClose(xid, pTcon,
|
|
|
- pSMBFile->netfid);
|
|
|
- } else
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
- } else
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
-
|
|
|
- /* Delete any outstanding lock records.
|
|
|
- We'll lose them when the file is closed anyway. */
|
|
|
- mutex_lock(&pSMBFile->lock_mutex);
|
|
|
- list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) {
|
|
|
- list_del(&li->llist);
|
|
|
- kfree(li);
|
|
|
- }
|
|
|
- mutex_unlock(&pSMBFile->lock_mutex);
|
|
|
-
|
|
|
- write_lock(&GlobalSMBSeslock);
|
|
|
- list_del(&pSMBFile->flist);
|
|
|
- list_del(&pSMBFile->tlist);
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
- cifsFileInfo_put(file->private_data);
|
|
|
- file->private_data = NULL;
|
|
|
- } else
|
|
|
- rc = -EBADF;
|
|
|
-
|
|
|
- read_lock(&GlobalSMBSeslock);
|
|
|
- if (list_empty(&(CIFS_I(inode)->openFileList))) {
|
|
|
- cFYI(1, "closing last open instance for inode %p", inode);
|
|
|
- /* if the file is not open we do not know if we can cache info
|
|
|
- on this inode, much less write behind and read ahead */
|
|
|
- CIFS_I(inode)->clientCanCacheRead = false;
|
|
|
- CIFS_I(inode)->clientCanCacheAll = false;
|
|
|
- }
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
- if ((rc == 0) && CIFS_I(inode)->write_behind_rc)
|
|
|
- rc = CIFS_I(inode)->write_behind_rc;
|
|
|
- FreeXid(xid);
|
|
|
- return rc;
|
|
|
+ /* return code from the ->release op is always ignored */
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
int cifs_closedir(struct inode *inode, struct file *file)
|
|
@@ -653,25 +664,21 @@ int cifs_closedir(struct inode *inode, struct file *file)
|
|
|
xid = GetXid();
|
|
|
|
|
|
if (pCFileStruct) {
|
|
|
- struct cifsTconInfo *pTcon;
|
|
|
- struct cifs_sb_info *cifs_sb =
|
|
|
- CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
-
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
+ struct cifsTconInfo *pTcon = tlink_tcon(pCFileStruct->tlink);
|
|
|
|
|
|
cFYI(1, "Freeing private data in close dir");
|
|
|
- write_lock(&GlobalSMBSeslock);
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
if (!pCFileStruct->srch_inf.endOfSearch &&
|
|
|
!pCFileStruct->invalidHandle) {
|
|
|
pCFileStruct->invalidHandle = true;
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid);
|
|
|
cFYI(1, "Closing uncompleted readdir with rc %d",
|
|
|
rc);
|
|
|
/* not much we can do if it fails anyway, ignore rc */
|
|
|
rc = 0;
|
|
|
} else
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
|
|
|
if (ptmp) {
|
|
|
cFYI(1, "closedir free smb buf in srch struct");
|
|
@@ -681,6 +688,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
|
|
|
else
|
|
|
cifs_buf_release(ptmp);
|
|
|
}
|
|
|
+ cifs_put_tlink(pCFileStruct->tlink);
|
|
|
kfree(file->private_data);
|
|
|
file->private_data = NULL;
|
|
|
}
|
|
@@ -767,7 +775,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
|
|
|
cFYI(1, "Unknown type of lock");
|
|
|
|
|
|
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
- tcon = cifs_sb->tcon;
|
|
|
+ tcon = tlink_tcon(((struct cifsFileInfo *)file->private_data)->tlink);
|
|
|
|
|
|
if (file->private_data == NULL) {
|
|
|
rc = -EBADF;
|
|
@@ -960,14 +968,14 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
|
|
|
|
|
|
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
-
|
|
|
/* cFYI(1, " write %d bytes to offset %lld of %s", write_size,
|
|
|
*poffset, file->f_path.dentry->d_name.name); */
|
|
|
|
|
|
if (file->private_data == NULL)
|
|
|
return -EBADF;
|
|
|
+
|
|
|
open_file = file->private_data;
|
|
|
+ pTcon = tlink_tcon(open_file->tlink);
|
|
|
|
|
|
rc = generic_write_checks(file, poffset, &write_size, 0);
|
|
|
if (rc)
|
|
@@ -988,19 +996,12 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
|
|
|
we blocked so return what we managed to write */
|
|
|
return total_written;
|
|
|
}
|
|
|
- if (open_file->closePend) {
|
|
|
- FreeXid(xid);
|
|
|
- if (total_written)
|
|
|
- return total_written;
|
|
|
- else
|
|
|
- return -EBADF;
|
|
|
- }
|
|
|
if (open_file->invalidHandle) {
|
|
|
/* we could deadlock if we called
|
|
|
filemap_fdatawait from here so tell
|
|
|
reopen_file not to flush data to server
|
|
|
now */
|
|
|
- rc = cifs_reopen_file(file, false);
|
|
|
+ rc = cifs_reopen_file(open_file, false);
|
|
|
if (rc != 0)
|
|
|
break;
|
|
|
}
|
|
@@ -1048,8 +1049,9 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
|
|
|
return total_written;
|
|
|
}
|
|
|
|
|
|
-static ssize_t cifs_write(struct file *file, const char *write_data,
|
|
|
- size_t write_size, loff_t *poffset)
|
|
|
+static ssize_t cifs_write(struct cifsFileInfo *open_file,
|
|
|
+ const char *write_data, size_t write_size,
|
|
|
+ loff_t *poffset)
|
|
|
{
|
|
|
int rc = 0;
|
|
|
unsigned int bytes_written = 0;
|
|
@@ -1057,19 +1059,15 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
|
|
struct cifs_sb_info *cifs_sb;
|
|
|
struct cifsTconInfo *pTcon;
|
|
|
int xid, long_op;
|
|
|
- struct cifsFileInfo *open_file;
|
|
|
- struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode);
|
|
|
+ struct dentry *dentry = open_file->dentry;
|
|
|
+ struct cifsInodeInfo *cifsi = CIFS_I(dentry->d_inode);
|
|
|
|
|
|
- cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
-
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
+ cifs_sb = CIFS_SB(dentry->d_sb);
|
|
|
|
|
|
cFYI(1, "write %zd bytes to offset %lld of %s", write_size,
|
|
|
- *poffset, file->f_path.dentry->d_name.name);
|
|
|
+ *poffset, dentry->d_name.name);
|
|
|
|
|
|
- if (file->private_data == NULL)
|
|
|
- return -EBADF;
|
|
|
- open_file = file->private_data;
|
|
|
+ pTcon = tlink_tcon(open_file->tlink);
|
|
|
|
|
|
xid = GetXid();
|
|
|
|
|
@@ -1078,28 +1076,12 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
|
|
total_written += bytes_written) {
|
|
|
rc = -EAGAIN;
|
|
|
while (rc == -EAGAIN) {
|
|
|
- if (file->private_data == NULL) {
|
|
|
- /* file has been closed on us */
|
|
|
- FreeXid(xid);
|
|
|
- /* if we have gotten here we have written some data
|
|
|
- and blocked, and the file has been freed on us
|
|
|
- while we blocked so return what we managed to
|
|
|
- write */
|
|
|
- return total_written;
|
|
|
- }
|
|
|
- if (open_file->closePend) {
|
|
|
- FreeXid(xid);
|
|
|
- if (total_written)
|
|
|
- return total_written;
|
|
|
- else
|
|
|
- return -EBADF;
|
|
|
- }
|
|
|
if (open_file->invalidHandle) {
|
|
|
/* we could deadlock if we called
|
|
|
filemap_fdatawait from here so tell
|
|
|
reopen_file not to flush data to
|
|
|
server now */
|
|
|
- rc = cifs_reopen_file(file, false);
|
|
|
+ rc = cifs_reopen_file(open_file, false);
|
|
|
if (rc != 0)
|
|
|
break;
|
|
|
}
|
|
@@ -1146,43 +1128,41 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
|
|
|
|
|
cifs_stats_bytes_written(pTcon, total_written);
|
|
|
|
|
|
- /* since the write may have blocked check these pointers again */
|
|
|
- if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) {
|
|
|
-/*BB We could make this contingent on superblock ATIME flag too */
|
|
|
-/* file->f_path.dentry->d_inode->i_ctime =
|
|
|
- file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/
|
|
|
- if (total_written > 0) {
|
|
|
- spin_lock(&file->f_path.dentry->d_inode->i_lock);
|
|
|
- if (*poffset > file->f_path.dentry->d_inode->i_size)
|
|
|
- i_size_write(file->f_path.dentry->d_inode,
|
|
|
- *poffset);
|
|
|
- spin_unlock(&file->f_path.dentry->d_inode->i_lock);
|
|
|
- }
|
|
|
- mark_inode_dirty_sync(file->f_path.dentry->d_inode);
|
|
|
+ if (total_written > 0) {
|
|
|
+ spin_lock(&dentry->d_inode->i_lock);
|
|
|
+ if (*poffset > dentry->d_inode->i_size)
|
|
|
+ i_size_write(dentry->d_inode, *poffset);
|
|
|
+ spin_unlock(&dentry->d_inode->i_lock);
|
|
|
}
|
|
|
+ mark_inode_dirty_sync(dentry->d_inode);
|
|
|
FreeXid(xid);
|
|
|
return total_written;
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
|
|
-struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
|
|
|
+struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
|
|
|
+ bool fsuid_only)
|
|
|
{
|
|
|
struct cifsFileInfo *open_file = NULL;
|
|
|
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
|
|
|
+
|
|
|
+ /* only filter by fsuid on multiuser mounts */
|
|
|
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
|
|
|
+ fsuid_only = false;
|
|
|
|
|
|
- read_lock(&GlobalSMBSeslock);
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
/* we could simply get the first_list_entry since write-only entries
|
|
|
are always at the end of the list but since the first entry might
|
|
|
have a close pending, we go through the whole list */
|
|
|
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
|
|
- if (open_file->closePend)
|
|
|
+ if (fsuid_only && open_file->uid != current_fsuid())
|
|
|
continue;
|
|
|
- if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) ||
|
|
|
- (open_file->pfile->f_flags & O_RDONLY))) {
|
|
|
+ if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
|
|
|
if (!open_file->invalidHandle) {
|
|
|
/* found a good file */
|
|
|
/* lock it so it will not be closed on us */
|
|
|
cifsFileInfo_get(open_file);
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
return open_file;
|
|
|
} /* else might as well continue, and look for
|
|
|
another, or simply have the caller reopen it
|
|
@@ -1190,14 +1170,16 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
|
|
|
} else /* write only file */
|
|
|
break; /* write only files are last so must be done */
|
|
|
}
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
return NULL;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
|
|
+struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
|
|
|
+ bool fsuid_only)
|
|
|
{
|
|
|
struct cifsFileInfo *open_file;
|
|
|
+ struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
|
|
|
bool any_available = false;
|
|
|
int rc;
|
|
|
|
|
@@ -1211,53 +1193,39 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- read_lock(&GlobalSMBSeslock);
|
|
|
+ /* only filter by fsuid on multiuser mounts */
|
|
|
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
|
|
|
+ fsuid_only = false;
|
|
|
+
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
refind_writable:
|
|
|
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
|
|
- if (open_file->closePend ||
|
|
|
- (!any_available && open_file->pid != current->tgid))
|
|
|
+ if (!any_available && open_file->pid != current->tgid)
|
|
|
continue;
|
|
|
-
|
|
|
- if (open_file->pfile &&
|
|
|
- ((open_file->pfile->f_flags & O_RDWR) ||
|
|
|
- (open_file->pfile->f_flags & O_WRONLY))) {
|
|
|
+ if (fsuid_only && open_file->uid != current_fsuid())
|
|
|
+ continue;
|
|
|
+ if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
|
|
|
cifsFileInfo_get(open_file);
|
|
|
|
|
|
if (!open_file->invalidHandle) {
|
|
|
/* found a good writable file */
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
return open_file;
|
|
|
}
|
|
|
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
+
|
|
|
/* Had to unlock since following call can block */
|
|
|
- rc = cifs_reopen_file(open_file->pfile, false);
|
|
|
- if (!rc) {
|
|
|
- if (!open_file->closePend)
|
|
|
- return open_file;
|
|
|
- else { /* start over in case this was deleted */
|
|
|
- /* since the list could be modified */
|
|
|
- read_lock(&GlobalSMBSeslock);
|
|
|
- cifsFileInfo_put(open_file);
|
|
|
- goto refind_writable;
|
|
|
- }
|
|
|
- }
|
|
|
+ rc = cifs_reopen_file(open_file, false);
|
|
|
+ if (!rc)
|
|
|
+ return open_file;
|
|
|
|
|
|
- /* if it fails, try another handle if possible -
|
|
|
- (we can not do this if closePending since
|
|
|
- loop could be modified - in which case we
|
|
|
- have to start at the beginning of the list
|
|
|
- again. Note that it would be bad
|
|
|
- to hold up writepages here (rather than
|
|
|
- in caller) with continuous retries */
|
|
|
+ /* if it fails, try another handle if possible */
|
|
|
cFYI(1, "wp failed on reopen file");
|
|
|
- read_lock(&GlobalSMBSeslock);
|
|
|
- /* can not use this handle, no write
|
|
|
- pending on this one after all */
|
|
|
cifsFileInfo_put(open_file);
|
|
|
|
|
|
- if (open_file->closePend) /* list could have changed */
|
|
|
- goto refind_writable;
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
+
|
|
|
/* else we simply continue to the next entry. Thus
|
|
|
we do not loop on reopen errors. If we
|
|
|
can not reopen the file, for example if we
|
|
@@ -1272,7 +1240,7 @@ refind_writable:
|
|
|
any_available = true;
|
|
|
goto refind_writable;
|
|
|
}
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
@@ -1284,7 +1252,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|
|
int rc = -EFAULT;
|
|
|
int bytes_written = 0;
|
|
|
struct cifs_sb_info *cifs_sb;
|
|
|
- struct cifsTconInfo *pTcon;
|
|
|
struct inode *inode;
|
|
|
struct cifsFileInfo *open_file;
|
|
|
|
|
@@ -1293,7 +1260,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|
|
|
|
|
inode = page->mapping->host;
|
|
|
cifs_sb = CIFS_SB(inode->i_sb);
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
|
|
|
offset += (loff_t)from;
|
|
|
write_data = kmap(page);
|
|
@@ -1314,10 +1280,10 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|
|
if (mapping->host->i_size - offset < (loff_t)to)
|
|
|
to = (unsigned)(mapping->host->i_size - offset);
|
|
|
|
|
|
- open_file = find_writable_file(CIFS_I(mapping->host));
|
|
|
+ open_file = find_writable_file(CIFS_I(mapping->host), false);
|
|
|
if (open_file) {
|
|
|
- bytes_written = cifs_write(open_file->pfile, write_data,
|
|
|
- to-from, &offset);
|
|
|
+ bytes_written = cifs_write(open_file, write_data,
|
|
|
+ to - from, &offset);
|
|
|
cifsFileInfo_put(open_file);
|
|
|
/* Does mm or vfs already set times? */
|
|
|
inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
|
|
@@ -1352,6 +1318,7 @@ static int cifs_writepages(struct address_space *mapping,
|
|
|
int nr_pages;
|
|
|
__u64 offset = 0;
|
|
|
struct cifsFileInfo *open_file;
|
|
|
+ struct cifsTconInfo *tcon;
|
|
|
struct cifsInodeInfo *cifsi = CIFS_I(mapping->host);
|
|
|
struct page *page;
|
|
|
struct pagevec pvec;
|
|
@@ -1359,6 +1326,15 @@ static int cifs_writepages(struct address_space *mapping,
|
|
|
int scanned = 0;
|
|
|
int xid, long_op;
|
|
|
|
|
|
+ /*
|
|
|
+ * BB: Is this meaningful for a non-block-device file system?
|
|
|
+ * If it is, we should test it again after we do I/O
|
|
|
+ */
|
|
|
+ if (wbc->nonblocking && bdi_write_congested(bdi)) {
|
|
|
+ wbc->encountered_congestion = 1;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
cifs_sb = CIFS_SB(mapping->host->i_sb);
|
|
|
|
|
|
/*
|
|
@@ -1368,27 +1344,29 @@ static int cifs_writepages(struct address_space *mapping,
|
|
|
if (cifs_sb->wsize < PAGE_CACHE_SIZE)
|
|
|
return generic_writepages(mapping, wbc);
|
|
|
|
|
|
- if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
|
|
|
- if (cifs_sb->tcon->ses->server->secMode &
|
|
|
- (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
|
|
|
- if (!experimEnabled)
|
|
|
- return generic_writepages(mapping, wbc);
|
|
|
-
|
|
|
iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL);
|
|
|
if (iov == NULL)
|
|
|
return generic_writepages(mapping, wbc);
|
|
|
|
|
|
-
|
|
|
/*
|
|
|
- * BB: Is this meaningful for a non-block-device file system?
|
|
|
- * If it is, we should test it again after we do I/O
|
|
|
+ * if there's no open file, then this is likely to fail too,
|
|
|
+ * but it'll at least handle the return. Maybe it should be
|
|
|
+ * a BUG() instead?
|
|
|
*/
|
|
|
- if (wbc->nonblocking && bdi_write_congested(bdi)) {
|
|
|
- wbc->encountered_congestion = 1;
|
|
|
+ open_file = find_writable_file(CIFS_I(mapping->host), false);
|
|
|
+ if (!open_file) {
|
|
|
kfree(iov);
|
|
|
- return 0;
|
|
|
+ return generic_writepages(mapping, wbc);
|
|
|
}
|
|
|
|
|
|
+ tcon = tlink_tcon(open_file->tlink);
|
|
|
+ if (!experimEnabled && tcon->ses->server->secMode &
|
|
|
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
|
|
|
+ cifsFileInfo_put(open_file);
|
|
|
+ return generic_writepages(mapping, wbc);
|
|
|
+ }
|
|
|
+ cifsFileInfo_put(open_file);
|
|
|
+
|
|
|
xid = GetXid();
|
|
|
|
|
|
pagevec_init(&pvec, 0);
|
|
@@ -1492,38 +1470,34 @@ retry:
|
|
|
break;
|
|
|
}
|
|
|
if (n_iov) {
|
|
|
- /* Search for a writable handle every time we call
|
|
|
- * CIFSSMBWrite2. We can't rely on the last handle
|
|
|
- * we used to still be valid
|
|
|
- */
|
|
|
- open_file = find_writable_file(CIFS_I(mapping->host));
|
|
|
+ open_file = find_writable_file(CIFS_I(mapping->host),
|
|
|
+ false);
|
|
|
if (!open_file) {
|
|
|
cERROR(1, "No writable handles for inode");
|
|
|
rc = -EBADF;
|
|
|
} else {
|
|
|
long_op = cifs_write_timeout(cifsi, offset);
|
|
|
- rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
|
|
|
- open_file->netfid,
|
|
|
+ rc = CIFSSMBWrite2(xid, tcon, open_file->netfid,
|
|
|
bytes_to_write, offset,
|
|
|
&bytes_written, iov, n_iov,
|
|
|
long_op);
|
|
|
cifsFileInfo_put(open_file);
|
|
|
cifs_update_eof(cifsi, offset, bytes_written);
|
|
|
+ }
|
|
|
|
|
|
- if (rc || bytes_written < bytes_to_write) {
|
|
|
- cERROR(1, "Write2 ret %d, wrote %d",
|
|
|
- rc, bytes_written);
|
|
|
- /* BB what if continued retry is
|
|
|
- requested via mount flags? */
|
|
|
- if (rc == -ENOSPC)
|
|
|
- set_bit(AS_ENOSPC, &mapping->flags);
|
|
|
- else
|
|
|
- set_bit(AS_EIO, &mapping->flags);
|
|
|
- } else {
|
|
|
- cifs_stats_bytes_written(cifs_sb->tcon,
|
|
|
- bytes_written);
|
|
|
- }
|
|
|
+ if (rc || bytes_written < bytes_to_write) {
|
|
|
+ cERROR(1, "Write2 ret %d, wrote %d",
|
|
|
+ rc, bytes_written);
|
|
|
+ /* BB what if continued retry is
|
|
|
+ requested via mount flags? */
|
|
|
+ if (rc == -ENOSPC)
|
|
|
+ set_bit(AS_ENOSPC, &mapping->flags);
|
|
|
+ else
|
|
|
+ set_bit(AS_EIO, &mapping->flags);
|
|
|
+ } else {
|
|
|
+ cifs_stats_bytes_written(tcon, bytes_written);
|
|
|
}
|
|
|
+
|
|
|
for (i = 0; i < n_iov; i++) {
|
|
|
page = pvec.pages[first + i];
|
|
|
/* Should we also set page error on
|
|
@@ -1624,7 +1598,8 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
|
|
|
/* BB check if anything else missing out of ppw
|
|
|
such as updating last write time */
|
|
|
page_data = kmap(page);
|
|
|
- rc = cifs_write(file, page_data + offset, copied, &pos);
|
|
|
+ rc = cifs_write(file->private_data, page_data + offset,
|
|
|
+ copied, &pos);
|
|
|
/* if (rc < 0) should we set writebehind rc? */
|
|
|
kunmap(page);
|
|
|
|
|
@@ -1665,7 +1640,7 @@ int cifs_fsync(struct file *file, int datasync)
|
|
|
if (rc == 0) {
|
|
|
rc = CIFS_I(inode)->write_behind_rc;
|
|
|
CIFS_I(inode)->write_behind_rc = 0;
|
|
|
- tcon = CIFS_SB(inode->i_sb)->tcon;
|
|
|
+ tcon = tlink_tcon(smbfile->tlink);
|
|
|
if (!rc && tcon && smbfile &&
|
|
|
!(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
|
|
|
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
|
|
@@ -1750,7 +1725,6 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
|
|
|
|
|
|
xid = GetXid();
|
|
|
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
|
|
|
if (file->private_data == NULL) {
|
|
|
rc = -EBADF;
|
|
@@ -1758,6 +1732,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
|
|
|
return rc;
|
|
|
}
|
|
|
open_file = file->private_data;
|
|
|
+ pTcon = tlink_tcon(open_file->tlink);
|
|
|
|
|
|
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
|
|
cFYI(1, "attempting read on write only file instance");
|
|
@@ -1771,9 +1746,8 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
|
|
|
smb_read_data = NULL;
|
|
|
while (rc == -EAGAIN) {
|
|
|
int buf_type = CIFS_NO_BUFFER;
|
|
|
- if ((open_file->invalidHandle) &&
|
|
|
- (!open_file->closePend)) {
|
|
|
- rc = cifs_reopen_file(file, true);
|
|
|
+ if (open_file->invalidHandle) {
|
|
|
+ rc = cifs_reopen_file(open_file, true);
|
|
|
if (rc != 0)
|
|
|
break;
|
|
|
}
|
|
@@ -1831,7 +1805,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
|
|
|
|
|
|
xid = GetXid();
|
|
|
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
|
|
|
if (file->private_data == NULL) {
|
|
|
rc = -EBADF;
|
|
@@ -1839,6 +1812,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
|
|
|
return rc;
|
|
|
}
|
|
|
open_file = file->private_data;
|
|
|
+ pTcon = tlink_tcon(open_file->tlink);
|
|
|
|
|
|
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
|
|
cFYI(1, "attempting read on write only file instance");
|
|
@@ -1857,9 +1831,8 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
|
|
|
}
|
|
|
rc = -EAGAIN;
|
|
|
while (rc == -EAGAIN) {
|
|
|
- if ((open_file->invalidHandle) &&
|
|
|
- (!open_file->closePend)) {
|
|
|
- rc = cifs_reopen_file(file, true);
|
|
|
+ if (open_file->invalidHandle) {
|
|
|
+ rc = cifs_reopen_file(open_file, true);
|
|
|
if (rc != 0)
|
|
|
break;
|
|
|
}
|
|
@@ -1974,7 +1947,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
|
}
|
|
|
open_file = file->private_data;
|
|
|
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
|
|
- pTcon = cifs_sb->tcon;
|
|
|
+ pTcon = tlink_tcon(open_file->tlink);
|
|
|
|
|
|
/*
|
|
|
* Reads as many pages as possible from fscache. Returns -ENOBUFS
|
|
@@ -2022,9 +1995,8 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|
|
read_size, contig_pages);
|
|
|
rc = -EAGAIN;
|
|
|
while (rc == -EAGAIN) {
|
|
|
- if ((open_file->invalidHandle) &&
|
|
|
- (!open_file->closePend)) {
|
|
|
- rc = cifs_reopen_file(file, true);
|
|
|
+ if (open_file->invalidHandle) {
|
|
|
+ rc = cifs_reopen_file(open_file, true);
|
|
|
if (rc != 0)
|
|
|
break;
|
|
|
}
|
|
@@ -2173,18 +2145,14 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
|
|
|
{
|
|
|
struct cifsFileInfo *open_file;
|
|
|
|
|
|
- read_lock(&GlobalSMBSeslock);
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
|
|
- if (open_file->closePend)
|
|
|
- continue;
|
|
|
- if (open_file->pfile &&
|
|
|
- ((open_file->pfile->f_flags & O_RDWR) ||
|
|
|
- (open_file->pfile->f_flags & O_WRONLY))) {
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
+ if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
return 1;
|
|
|
}
|
|
|
}
|
|
|
- read_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -2310,9 +2278,8 @@ void cifs_oplock_break(struct work_struct *work)
|
|
|
{
|
|
|
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
|
|
|
oplock_break);
|
|
|
- struct inode *inode = cfile->pInode;
|
|
|
+ struct inode *inode = cfile->dentry->d_inode;
|
|
|
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)) {
|
|
@@ -2338,9 +2305,9 @@ void cifs_oplock_break(struct work_struct *work)
|
|
|
* 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);
|
|
|
+ if (!cfile->oplock_break_cancelled) {
|
|
|
+ rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0,
|
|
|
+ 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false);
|
|
|
cFYI(1, "Oplock release rc = %d", rc);
|
|
|
}
|
|
|
|
|
@@ -2349,22 +2316,22 @@ void cifs_oplock_break(struct work_struct *work)
|
|
|
* finished grabbing reference for us. Make sure it's done by
|
|
|
* waiting for GlobalSMSSeslock.
|
|
|
*/
|
|
|
- write_lock(&GlobalSMBSeslock);
|
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
+ spin_lock(&cifs_file_list_lock);
|
|
|
+ spin_unlock(&cifs_file_list_lock);
|
|
|
|
|
|
cifs_oplock_break_put(cfile);
|
|
|
}
|
|
|
|
|
|
void cifs_oplock_break_get(struct cifsFileInfo *cfile)
|
|
|
{
|
|
|
- mntget(cfile->mnt);
|
|
|
+ cifs_sb_active(cfile->dentry->d_sb);
|
|
|
cifsFileInfo_get(cfile);
|
|
|
}
|
|
|
|
|
|
void cifs_oplock_break_put(struct cifsFileInfo *cfile)
|
|
|
{
|
|
|
- mntput(cfile->mnt);
|
|
|
cifsFileInfo_put(cfile);
|
|
|
+ cifs_sb_deactive(cfile->dentry->d_sb);
|
|
|
}
|
|
|
|
|
|
const struct address_space_operations cifs_addr_ops = {
|