diff options
| author | Ronnie Sahlberg <lsahlber@redhat.com> | 2019-06-05 10:38:38 +1000 |
|---|---|---|
| committer | Ben Hutchings <ben@decadent.org.uk> | 2019-10-05 16:19:58 +0100 |
| commit | b252047fb31007179955b812f173feae1ddd6af1 (patch) | |
| tree | b5965ecb1ed622ca1374656c1a5a3ac5c5dfe591 | |
| parent | af782c2fa12478f28e8908dddd9c40135df087f0 (diff) | |
| download | linux-b252047fb31007179955b812f173feae1ddd6af1.tar.gz linux-b252047fb31007179955b812f173feae1ddd6af1.tar.bz2 linux-b252047fb31007179955b812f173feae1ddd6af1.zip | |
cifs: add spinlock for the openFileList to cifsInodeInfo
commit 487317c99477d00f22370625d53be3239febabbe upstream.
We can not depend on the tcon->open_file_lock here since in multiuser mode
we may have the same file/inode open via multiple different tcons.
The current code is race prone and will crash if one user deletes a file
at the same time a different user opens/create the file.
To avoid this we need to have a spinlock attached to the inode and not the tcon.
RHBZ: 1580165
Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
[bwh: Backported to 3.16: adjust context, indentation]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
| -rw-r--r-- | fs/cifs/cifsfs.c | 1 | ||||
| -rw-r--r-- | fs/cifs/cifsglob.h | 5 | ||||
| -rw-r--r-- | fs/cifs/file.c | 8 |
3 files changed, 12 insertions, 2 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 048c0e09f57c..1c43254b980e 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -260,6 +260,7 @@ cifs_alloc_inode(struct super_block *sb) cifs_inode->uniqueid = 0; cifs_inode->createtime = 0; cifs_inode->epoch = 0; + spin_lock_init(&cifs_inode->open_file_lock); #ifdef CONFIG_CIFS_SMB2 generate_random_uuid(cifs_inode->lease_key); #endif diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ff85c3129e9d..a13450937bce 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1116,6 +1116,7 @@ struct cifsInodeInfo { struct rw_semaphore lock_sem; /* protect the fields above */ /* BB add in lists for dirty pages i.e. write caching info for oplock */ struct list_head openFileList; + spinlock_t open_file_lock; /* protects openFileList */ __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ unsigned int oplock; /* oplock/lease level we have */ unsigned int epoch; /* used to track lease state changes */ @@ -1485,10 +1486,14 @@ require use of the stronger protocol */ * tcp_ses_lock protects: * list operations on tcp and SMB session lists * tcon->open_file_lock protects the list of open files hanging off the tcon + * inode->open_file_lock protects the openFileList hanging off the inode * cfile->file_info_lock protects counters and fields in cifs file struct * f_owner.lock protects certain per file struct operations * mapping->page_lock protects certain per page operations * + * Note that the cifs_tcon.open_file_lock should be taken before + * not after the cifsInodeInfo.open_file_lock + * * Semaphores * ---------- * sesSem operations on smb session diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 57aefb3c875e..0ff4c7b49c7b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -337,10 +337,12 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, list_add(&cfile->tlist, &tcon->openFileList); /* if readable file instance put first in list*/ + spin_lock(&cinode->open_file_lock); if (file->f_mode & FMODE_READ) list_add(&cfile->flist, &cinode->openFileList); else list_add_tail(&cfile->flist, &cinode->openFileList); + spin_unlock(&cinode->open_file_lock); spin_unlock(&tcon->open_file_lock); if (fid->purge_cache) @@ -412,7 +414,9 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open); /* remove it from the lists */ + spin_lock(&cifsi->open_file_lock); list_del(&cifs_file->flist); + spin_unlock(&cifsi->open_file_lock); list_del(&cifs_file->tlist); if (list_empty(&cifsi->openFileList)) { @@ -1850,10 +1854,10 @@ refind_writable: if (!rc) return inv_file; else { - spin_lock(&tcon->open_file_lock); + spin_lock(&cifs_inode->open_file_lock); list_move_tail(&inv_file->flist, &cifs_inode->openFileList); - spin_unlock(&tcon->open_file_lock); + spin_unlock(&cifs_inode->open_file_lock); cifsFileInfo_put(inv_file); ++refind; inv_file = NULL; |
