| From: Ronnie Sahlberg <lsahlber@redhat.com> |
| Date: Wed, 5 Jun 2019 10:38:38 +1000 |
| Subject: 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> |
| --- |
| fs/cifs/cifsfs.c | 1 + |
| fs/cifs/cifsglob.h | 5 +++++ |
| fs/cifs/file.c | 8 ++++++-- |
| 3 files changed, 12 insertions(+), 2 deletions(-) |
| |
| --- 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 |
| --- 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 |
| --- a/fs/cifs/file.c |
| +++ b/fs/cifs/file.c |
| @@ -337,10 +337,12 @@ cifs_new_fileinfo(struct cifs_fid *fid, |
| 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 cifsFileIn |
| 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; |