aboutsummaryrefslogtreecommitdiff
path: root/fs/cifs/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r--fs/cifs/file.c1055
1 files changed, 615 insertions, 440 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 71e9ad9f596..7d7bbdc4c8e 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -169,16 +169,20 @@ posix_open_ret:
static int
cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
- struct cifs_tcon *tcon, unsigned int f_flags, __u32 *poplock,
- __u16 *pnetfid, unsigned int xid)
+ struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
+ struct cifs_fid *fid, unsigned int xid)
{
int rc;
- int desiredAccess;
+ int desired_access;
int disposition;
int create_options = CREATE_NOT_DIR;
FILE_ALL_INFO *buf;
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ if (!server->ops->open)
+ return -ENOSYS;
- desiredAccess = cifs_convert_flags(f_flags);
+ desired_access = cifs_convert_flags(f_flags);
/*********************************************************************
* open flag mapping table:
@@ -215,16 +219,9 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
- if (tcon->ses->capabilities & CAP_NT_SMBS)
- rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
- desiredAccess, create_options, pnetfid, poplock, buf,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
- & CIFS_MOUNT_MAP_SPECIAL_CHR);
- else
- rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
- desiredAccess, CREATE_NOT_DIR, pnetfid, poplock, buf,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
- & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ rc = server->ops->open(xid, tcon, full_path, disposition,
+ desired_access, create_options, fid, oplock, buf,
+ cifs_sb);
if (rc)
goto out;
@@ -234,7 +231,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
xid);
else
rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb,
- xid, pnetfid);
+ xid, &fid->netfid);
out:
kfree(buf);
@@ -242,48 +239,62 @@ out:
}
struct cifsFileInfo *
-cifs_new_fileinfo(__u16 fileHandle, struct file *file,
+cifs_new_fileinfo(struct cifs_fid *fid, 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->count = 1;
- 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);
- INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
- INIT_LIST_HEAD(&pCifsFile->llist);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifsFileInfo *cfile;
+ struct cifs_fid_locks *fdlocks;
+ struct cifs_tcon *tcon = tlink_tcon(tlink);
+
+ cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+ if (cfile == NULL)
+ return cfile;
+
+ fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL);
+ if (!fdlocks) {
+ kfree(cfile);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&fdlocks->locks);
+ fdlocks->cfile = cfile;
+ cfile->llist = fdlocks;
+ down_write(&cinode->lock_sem);
+ list_add(&fdlocks->llist, &cinode->llist);
+ up_write(&cinode->lock_sem);
+
+ cfile->count = 1;
+ cfile->pid = current->tgid;
+ cfile->uid = current_fsuid();
+ cfile->dentry = dget(dentry);
+ cfile->f_flags = file->f_flags;
+ cfile->invalidHandle = false;
+ cfile->tlink = cifs_get_tlink(tlink);
+ INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
+ mutex_init(&cfile->fh_mutex);
spin_lock(&cifs_file_list_lock);
- list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
+ if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE)
+ oplock = fid->pending_open->oplock;
+ list_del(&fid->pending_open->olist);
+
+ tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);
+
+ list_add(&cfile->tlist, &tcon->openFileList);
/* if readable file instance put first in list*/
if (file->f_mode & FMODE_READ)
- list_add(&pCifsFile->flist, &pCifsInode->openFileList);
+ list_add(&cfile->flist, &cinode->openFileList);
else
- list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList);
+ list_add_tail(&cfile->flist, &cinode->openFileList);
spin_unlock(&cifs_file_list_lock);
- cifs_set_oplock_level(pCifsInode, oplock);
- pCifsInode->can_cache_brlcks = pCifsInode->clientCanCacheAll;
-
- file->private_data = pCifsFile;
- return pCifsFile;
+ file->private_data = cfile;
+ return cfile;
}
-static void cifs_del_lock_waiters(struct cifsLockInfo *lock);
-
struct cifsFileInfo *
cifsFileInfo_get(struct cifsFileInfo *cifs_file)
{
@@ -302,9 +313,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
struct inode *inode = cifs_file->dentry->d_inode;
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsLockInfo *li, *tmp;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
spin_lock(&cifs_file_list_lock);
if (--cifs_file->count > 0) {
@@ -312,6 +326,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
return;
}
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
+ /* store open in pending opens to make sure we don't miss lease break */
+ cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);
+
/* remove it from the lists */
list_del(&cifs_file->flist);
list_del(&cifs_file->tlist);
@@ -319,13 +339,13 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
if (list_empty(&cifsi->openFileList)) {
cFYI(1, "closing last open instance for inode %p",
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 */
+ /*
+ * 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);
}
spin_unlock(&cifs_file_list_lock);
@@ -333,23 +353,30 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
cancel_work_sync(&cifs_file->oplock_break);
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
+ struct TCP_Server_Info *server = tcon->ses->server;
unsigned int xid;
- int rc;
+
xid = get_xid();
- rc = CIFSSMBClose(xid, tcon, cifs_file->netfid);
- free_xid(xid);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &cifs_file->fid);
+ _free_xid(xid);
}
- /* Delete any outstanding lock records. We'll lose them when the file
+ cifs_del_pending_open(&open);
+
+ /*
+ * Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
*/
- mutex_lock(&cifsi->lock_mutex);
- list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) {
+ down_write(&cifsi->lock_sem);
+ list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
list_del(&li->llist);
cifs_del_lock_waiters(li);
kfree(li);
}
- mutex_unlock(&cifsi->lock_mutex);
+ list_del(&cifs_file->llist->llist);
+ kfree(cifs_file->llist);
+ up_write(&cifsi->lock_sem);
cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry);
@@ -357,17 +384,20 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
}
int cifs_open(struct inode *inode, struct file *file)
+
{
int rc = -EACCES;
unsigned int xid;
__u32 oplock;
struct cifs_sb_info *cifs_sb;
+ struct TCP_Server_Info *server;
struct cifs_tcon *tcon;
struct tcon_link *tlink;
- struct cifsFileInfo *pCifsFile = NULL;
+ struct cifsFileInfo *cfile = NULL;
char *full_path = NULL;
bool posix_open_ok = false;
- __u16 netfid;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
xid = get_xid();
@@ -378,6 +408,7 @@ int cifs_open(struct inode *inode, struct file *file)
return PTR_ERR(tlink);
}
tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
full_path = build_path_from_dentry(file->f_path.dentry);
if (full_path == NULL) {
@@ -388,7 +419,7 @@ int cifs_open(struct inode *inode, struct file *file)
cFYI(1, "inode = 0x%p file flags are 0x%x for %s",
inode, file->f_flags, full_path);
- if (tcon->ses->server->oplocks)
+ if (server->oplocks)
oplock = REQ_OPLOCK;
else
oplock = 0;
@@ -399,7 +430,7 @@ int cifs_open(struct inode *inode, struct file *file)
/* 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 */,
- file->f_flags, &oplock, &netfid, xid);
+ file->f_flags, &oplock, &fid.netfid, xid);
if (rc == 0) {
cFYI(1, "posix open succeeded");
posix_open_ok = true;
@@ -415,20 +446,34 @@ int cifs_open(struct inode *inode, struct file *file)
} else if ((rc != -EIO) && (rc != -EREMOTE) &&
(rc != -EOPNOTSUPP)) /* path not found or net err */
goto out;
- /* else fallthrough to retry open the old way on network i/o
- or DFS errors */
+ /*
+ * Else fallthrough to retry open the old way on network i/o
+ * or DFS errors.
+ */
}
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
+ cifs_add_pending_open(&fid, tlink, &open);
+
if (!posix_open_ok) {
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
- file->f_flags, &oplock, &netfid, xid);
- if (rc)
+ file->f_flags, &oplock, &fid, xid);
+ if (rc) {
+ cifs_del_pending_open(&open);
goto out;
+ }
}
- pCifsFile = cifs_new_fileinfo(netfid, file, tlink, oplock);
- if (pCifsFile == NULL) {
- CIFSSMBClose(xid, tcon, netfid);
+ cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
+ if (cfile == NULL) {
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ cifs_del_pending_open(&open);
rc = -ENOMEM;
goto out;
}
@@ -436,8 +481,10 @@ int cifs_open(struct inode *inode, struct file *file)
cifs_fscache_set_inode_cookie(inode, file);
if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) {
- /* time to set mode which we can not set earlier due to
- problems creating new read-only files */
+ /*
+ * Time to set mode which we can not set earlier due to
+ * problems creating new read-only files.
+ */
struct cifs_unix_set_info_args args = {
.mode = inode->i_mode,
.uid = NO_CHANGE_64,
@@ -447,8 +494,8 @@ int cifs_open(struct inode *inode, struct file *file)
.mtime = NO_CHANGE_64,
.device = 0,
};
- CIFSSMBUnixSetFileInfo(xid, tcon, &args, netfid,
- pCifsFile->pid);
+ CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid,
+ cfile->pid);
}
out:
@@ -458,59 +505,66 @@ out:
return rc;
}
-/* Try to reacquire byte range locks that were released when session */
-/* to server was lost */
+/*
+ * Try to reacquire byte range locks that were released when session
+ * to server was lost
+ */
static int cifs_relock_file(struct cifsFileInfo *cifsFile)
{
int rc = 0;
-/* BB list all locks open on this file and relock */
+ /* BB list all locks open on this file and relock */
return rc;
}
-static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
+static int
+cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
{
int rc = -EACCES;
unsigned int xid;
__u32 oplock;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
- struct cifsInodeInfo *pCifsInode;
+ struct TCP_Server_Info *server;
+ struct cifsInodeInfo *cinode;
struct inode *inode;
char *full_path = NULL;
- int desiredAccess;
+ int desired_access;
int disposition = FILE_OPEN;
int create_options = CREATE_NOT_DIR;
- __u16 netfid;
+ struct cifs_fid fid;
xid = get_xid();
- mutex_lock(&pCifsFile->fh_mutex);
- if (!pCifsFile->invalidHandle) {
- mutex_unlock(&pCifsFile->fh_mutex);
+ mutex_lock(&cfile->fh_mutex);
+ if (!cfile->invalidHandle) {
+ mutex_unlock(&cfile->fh_mutex);
rc = 0;
free_xid(xid);
return rc;
}
- inode = pCifsFile->dentry->d_inode;
+ inode = cfile->dentry->d_inode;
cifs_sb = CIFS_SB(inode->i_sb);
- tcon = tlink_tcon(pCifsFile->tlink);
+ tcon = tlink_tcon(cfile->tlink);
+ server = tcon->ses->server;
-/* 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(pCifsFile->dentry);
+ /*
+ * 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(cfile->dentry);
if (full_path == NULL) {
rc = -ENOMEM;
- mutex_unlock(&pCifsFile->fh_mutex);
+ mutex_unlock(&cfile->fh_mutex);
free_xid(xid);
return rc;
}
- cFYI(1, "inode = 0x%p file flags 0x%x for %s",
- inode, pCifsFile->f_flags, full_path);
+ cFYI(1, "inode = 0x%p file flags 0x%x for %s", inode, cfile->f_flags,
+ full_path);
if (tcon->ses->server->oplocks)
oplock = REQ_OPLOCK;
@@ -524,69 +578,72 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
* 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 &
+ unsigned int oflags = cfile->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);
+ cifs_sb->mnt_file_mode /* ignored */,
+ oflags, &oplock, &fid.netfid, xid);
if (rc == 0) {
cFYI(1, "posix reopen succeeded");
goto reopen_success;
}
- /* fallthrough to retry open the old way on errors, especially
- in the reconnect path it is important to retry hard */
+ /*
+ * fallthrough to retry open the old way on errors, especially
+ * in the reconnect path it is important to retry hard
+ */
}
- desiredAccess = cifs_convert_flags(pCifsFile->f_flags);
+ desired_access = cifs_convert_flags(cfile->f_flags);
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
- /* Can not refresh inode by passing in file_info buf to be returned
- by SMBOpen and then calling get_inode_info with returned buf
- since file might have write behind data that needs to be flushed
- and server version of file size can be stale. If we knew for sure
- that inode was not dirty locally we could do this */
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
- rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess,
- create_options, &netfid, &oplock, NULL,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ /*
+ * Can not refresh inode by passing in file_info buf to be returned by
+ * CIFSSMBOpen and then calling get_inode_info with returned buf since
+ * file might have write behind data that needs to be flushed and server
+ * version of file size can be stale. If we knew for sure that inode was
+ * not dirty locally we could do this.
+ */
+ rc = server->ops->open(xid, tcon, full_path, disposition,
+ desired_access, create_options, &fid, &oplock,
+ NULL, cifs_sb);
if (rc) {
- mutex_unlock(&pCifsFile->fh_mutex);
- cFYI(1, "cifs_open returned 0x%x", rc);
+ mutex_unlock(&cfile->fh_mutex);
+ cFYI(1, "cifs_reopen returned 0x%x", rc);
cFYI(1, "oplock: %d", oplock);
goto reopen_error_exit;
}
reopen_success:
- pCifsFile->netfid = netfid;
- pCifsFile->invalidHandle = false;
- mutex_unlock(&pCifsFile->fh_mutex);
- pCifsInode = CIFS_I(inode);
+ cfile->invalidHandle = false;
+ mutex_unlock(&cfile->fh_mutex);
+ cinode = CIFS_I(inode);
if (can_flush) {
rc = filemap_write_and_wait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc);
if (tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&inode,
- full_path, inode->i_sb, xid);
+ 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 */
-
- cifs_set_oplock_level(pCifsInode, oplock);
+ 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 inode info.
+ */
- cifs_relock_file(pCifsFile);
+ server->ops->set_fid(cfile, &fid, oplock);
+ cifs_relock_file(cfile);
reopen_error_exit:
kfree(full_path);
@@ -609,42 +666,48 @@ int cifs_closedir(struct inode *inode, struct file *file)
{
int rc = 0;
unsigned int xid;
- struct cifsFileInfo *pCFileStruct = file->private_data;
- char *ptmp;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ char *buf;
cFYI(1, "Closedir inode = 0x%p", inode);
+ if (cfile == NULL)
+ return rc;
+
xid = get_xid();
+ tcon = tlink_tcon(cfile->tlink);
+ server = tcon->ses->server;
- if (pCFileStruct) {
- struct cifs_tcon *pTcon = tlink_tcon(pCFileStruct->tlink);
+ cFYI(1, "Freeing private data in close dir");
+ spin_lock(&cifs_file_list_lock);
+ if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) {
+ cfile->invalidHandle = true;
+ spin_unlock(&cifs_file_list_lock);
+ if (server->ops->close_dir)
+ rc = server->ops->close_dir(xid, tcon, &cfile->fid);
+ else
+ rc = -ENOSYS;
+ cFYI(1, "Closing uncompleted readdir with rc %d", rc);
+ /* not much we can do if it fails anyway, ignore rc */
+ rc = 0;
+ } else
+ spin_unlock(&cifs_file_list_lock);
- cFYI(1, "Freeing private data in close dir");
- spin_lock(&cifs_file_list_lock);
- if (!pCFileStruct->srch_inf.endOfSearch &&
- !pCFileStruct->invalidHandle) {
- pCFileStruct->invalidHandle = true;
- 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
- spin_unlock(&cifs_file_list_lock);
- ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
- if (ptmp) {
- cFYI(1, "closedir free smb buf in srch struct");
- pCFileStruct->srch_inf.ntwrk_buf_start = NULL;
- if (pCFileStruct->srch_inf.smallBuf)
- cifs_small_buf_release(ptmp);
- else
- cifs_buf_release(ptmp);
- }
- cifs_put_tlink(pCFileStruct->tlink);
- kfree(file->private_data);
- file->private_data = NULL;
+ buf = cfile->srch_inf.ntwrk_buf_start;
+ if (buf) {
+ cFYI(1, "closedir free smb buf in srch struct");
+ cfile->srch_inf.ntwrk_buf_start = NULL;
+ if (cfile->srch_inf.smallBuf)
+ cifs_small_buf_release(buf);
+ else
+ cifs_buf_release(buf);
}
+
+ cifs_put_tlink(cfile->tlink);
+ kfree(file->private_data);
+ file->private_data = NULL;
/* BB can we lock the filestruct while this is going on? */
free_xid(xid);
return rc;
@@ -666,7 +729,7 @@ cifs_lock_init(__u64 offset, __u64 length, __u8 type)
return lock;
}
-static void
+void
cifs_del_lock_waiters(struct cifsLockInfo *lock)
{
struct cifsLockInfo *li, *tmp;
@@ -677,45 +740,47 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
}
static bool
-cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
- __u64 length, __u8 type, struct cifsFileInfo *cur,
- struct cifsLockInfo **conf_lock)
+cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
+ __u64 length, __u8 type, struct cifsFileInfo *cfile,
+ struct cifsLockInfo **conf_lock, bool rw_check)
{
struct cifsLockInfo *li;
+ struct cifsFileInfo *cur_cfile = fdlocks->cfile;
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
- list_for_each_entry(li, &cfile->llist, llist) {
+ list_for_each_entry(li, &fdlocks->locks, llist) {
if (offset + length <= li->offset ||
offset >= li->offset + li->length)
continue;
- else if ((type & server->vals->shared_lock_type) &&
- ((server->ops->compare_fids(cur, cfile) &&
- current->tgid == li->pid) || type == li->type))
+ if (rw_check && server->ops->compare_fids(cfile, cur_cfile) &&
+ current->tgid == li->pid)
continue;
- else {
+ if ((type & server->vals->shared_lock_type) &&
+ ((server->ops->compare_fids(cfile, cur_cfile) &&
+ current->tgid == li->pid) || type == li->type))
+ continue;
+ if (conf_lock)
*conf_lock = li;
- return true;
- }
+ return true;
}
return false;
}
-static bool
+bool
cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
- __u8 type, struct cifsLockInfo **conf_lock)
+ __u8 type, struct cifsLockInfo **conf_lock,
+ bool rw_check)
{
bool rc = false;
- struct cifsFileInfo *fid, *tmp;
+ struct cifs_fid_locks *cur;
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
- spin_lock(&cifs_file_list_lock);
- list_for_each_entry_safe(fid, tmp, &cinode->openFileList, flist) {
- rc = cifs_find_fid_lock_conflict(fid, offset, length, type,
- cfile, conf_lock);
+ list_for_each_entry(cur, &cinode->llist, llist) {
+ rc = cifs_find_fid_lock_conflict(cur, offset, length, type,
+ cfile, conf_lock, rw_check);
if (rc)
break;
}
- spin_unlock(&cifs_file_list_lock);
return rc;
}
@@ -737,10 +802,10 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
bool exist;
- mutex_lock(&cinode->lock_mutex);
+ down_read(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, offset, length, type,
- &conf_lock);
+ &conf_lock, false);
if (exist) {
flock->fl_start = conf_lock->offset;
flock->fl_end = conf_lock->offset + conf_lock->length - 1;
@@ -754,7 +819,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
else
flock->fl_type = F_UNLCK;
- mutex_unlock(&cinode->lock_mutex);
+ up_read(&cinode->lock_sem);
return rc;
}
@@ -762,9 +827,9 @@ static void
cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
{
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
- mutex_lock(&cinode->lock_mutex);
- list_add_tail(&lock->llist, &cfile->llist);
- mutex_unlock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
+ list_add_tail(&lock->llist, &cfile->llist->locks);
+ up_write(&cinode->lock_sem);
}
/*
@@ -784,13 +849,13 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
try_again:
exist = false;
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
- lock->type, &conf_lock);
+ lock->type, &conf_lock, false);
if (!exist && cinode->can_cache_brlcks) {
- list_add_tail(&lock->llist, &cfile->llist);
- mutex_unlock(&cinode->lock_mutex);
+ list_add_tail(&lock->llist, &cfile->llist->locks);
+ up_write(&cinode->lock_sem);
return rc;
}
@@ -800,17 +865,17 @@ try_again:
rc = -EACCES;
else {
list_add_tail(&lock->blist, &conf_lock->blist);
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
rc = wait_event_interruptible(lock->block_q,
(lock->blist.prev == &lock->blist) &&
(lock->blist.next == &lock->blist));
if (!rc)
goto try_again;
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
list_del_init(&lock->blist);
}
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
return rc;
}
@@ -831,7 +896,7 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock)
if ((flock->fl_flags & FL_POSIX) == 0)
return 1;
- mutex_lock(&cinode->lock_mutex);
+ down_read(&cinode->lock_sem);
posix_test_lock(file, flock);
if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) {
@@ -839,7 +904,7 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock)
rc = 1;
}
- mutex_unlock(&cinode->lock_mutex);
+ up_read(&cinode->lock_sem);
return rc;
}
@@ -859,14 +924,14 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
return rc;
try_again:
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
return rc;
}
rc = posix_lock_file(file, flock, NULL);
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
if (rc == FILE_LOCK_DEFERRED) {
rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next);
if (!rc)
@@ -876,7 +941,7 @@ try_again:
return rc;
}
-static int
+int
cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
{
unsigned int xid;
@@ -893,9 +958,10 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
xid = get_xid();
tcon = tlink_tcon(cfile->tlink);
- mutex_lock(&cinode->lock_mutex);
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return rc;
}
@@ -906,7 +972,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return -EINVAL;
}
@@ -915,7 +981,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
sizeof(LOCKING_ANDX_RANGE);
buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
if (!buf) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return -ENOMEM;
}
@@ -923,7 +989,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
for (i = 0; i < 2; i++) {
cur = buf;
num = 0;
- list_for_each_entry_safe(li, tmp, &cfile->llist, llist) {
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
if (li->type != types[i])
continue;
cur->Pid = cpu_to_le16(li->pid);
@@ -932,7 +998,8 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
cur->OffsetLow = cpu_to_le32((u32)li->offset);
cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32));
if (++num == max_num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ stored_rc = cifs_lockv(xid, tcon,
+ cfile->fid.netfid,
(__u8)li->type, 0, num,
buf);
if (stored_rc)
@@ -944,7 +1011,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
}
if (num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid,
(__u8)types[i], 0, num, buf);
if (stored_rc)
rc = stored_rc;
@@ -952,7 +1019,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
}
cinode->can_cache_brlcks = false;
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
kfree(buf);
free_xid(xid);
@@ -987,9 +1054,10 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
xid = get_xid();
- mutex_lock(&cinode->lock_mutex);
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return rc;
}
@@ -1005,7 +1073,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
/*
* Allocating count locks is enough because no FL_POSIX locks can be
- * added to the list while we are holding cinode->lock_mutex that
+ * added to the list while we are holding cinode->lock_sem that
* protects locking operations of this inode.
*/
for (; i < count; i++) {
@@ -1038,7 +1106,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
type = CIFS_WRLCK;
lck = list_entry(el, struct lock_to_push, llist);
lck->pid = flock->fl_pid;
- lck->netfid = cfile->netfid;
+ lck->netfid = cfile->fid.netfid;
lck->length = length;
lck->type = type;
lck->offset = flock->fl_start;
@@ -1060,7 +1128,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
out:
cinode->can_cache_brlcks = false;
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return rc;
@@ -1083,7 +1151,7 @@ cifs_push_locks(struct cifsFileInfo *cfile)
((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
return cifs_push_posix_locks(cfile);
- return cifs_push_mandatory_locks(cfile);
+ return tcon->ses->server->ops->push_mand_locks(cfile);
}
static void
@@ -1104,7 +1172,8 @@ cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock,
if (flock->fl_flags & FL_LEASE)
cFYI(1, "Lease on file - not implemented yet");
if (flock->fl_flags &
- (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE)))
+ (~(FL_POSIX | FL_FLOCK | FL_SLEEP |
+ FL_ACCESS | FL_LEASE | FL_CLOSE)))
cFYI(1, "Unknown lock flags 0x%x", flock->fl_flags);
*type = server->vals->large_lock_type;
@@ -1134,15 +1203,6 @@ cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock,
}
static int
-cifs_mandatory_lock(unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
- __u64 length, __u32 type, int lock, int unlock, bool wait)
-{
- return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->netfid,
- current->tgid, length, offset, unlock, lock,
- (__u8)type, wait, 0);
-}
-
-static int
cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
bool wait_flag, bool posix_lck, unsigned int xid)
{
@@ -1151,7 +1211,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
- __u16 netfid = cfile->netfid;
+ __u16 netfid = cfile->fid.netfid;
if (posix_lck) {
int posix_lock_type;
@@ -1175,11 +1235,11 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
return rc;
/* BB we could chain these into one lock request BB */
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, type,
- 1, 0, false);
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type,
+ 1, 0, false);
if (rc == 0) {
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
- type, 0, 1, false);
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type, 0, 1, false);
flock->fl_type = F_UNLCK;
if (rc != 0)
cERROR(1, "Error unlocking previously locked "
@@ -1192,13 +1252,14 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
return 0;
}
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
- type | server->vals->shared_lock_type, 1, 0,
- false);
+ type &= ~server->vals->exclusive_lock_type;
+
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type | server->vals->shared_lock_type,
+ 1, 0, false);
if (rc == 0) {
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
- type | server->vals->shared_lock_type,
- 0, 1, false);
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type | server->vals->shared_lock_type, 0, 1, false);
flock->fl_type = F_RDLCK;
if (rc != 0)
cERROR(1, "Error unlocking previously locked "
@@ -1209,7 +1270,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
return 0;
}
-static void
+void
cifs_move_llist(struct list_head *source, struct list_head *dest)
{
struct list_head *li, *tmp;
@@ -1217,7 +1278,7 @@ cifs_move_llist(struct list_head *source, struct list_head *dest)
list_move(li, dest);
}
-static void
+void
cifs_free_llist(struct list_head *llist)
{
struct cifsLockInfo *li, *tmp;
@@ -1228,7 +1289,7 @@ cifs_free_llist(struct list_head *llist)
}
}
-static int
+int
cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
unsigned int xid)
{
@@ -1260,11 +1321,11 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
if (!buf)
return -ENOMEM;
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
for (i = 0; i < 2; i++) {
cur = buf;
num = 0;
- list_for_each_entry_safe(li, tmp, &cfile->llist, llist) {
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
if (flock->fl_start > li->offset ||
(flock->fl_start + length) <
(li->offset + li->length))
@@ -1295,7 +1356,8 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
*/
list_move(&li->llist, &tmp_llist);
if (++num == max_num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ stored_rc = cifs_lockv(xid, tcon,
+ cfile->fid.netfid,
li->type, num, 0, buf);
if (stored_rc) {
/*
@@ -1304,7 +1366,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
* list to the head of the file's list.
*/
cifs_move_llist(&tmp_llist,
- &cfile->llist);
+ &cfile->llist->locks);
rc = stored_rc;
} else
/*
@@ -1318,23 +1380,24 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
cur++;
}
if (num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ s