aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/file.c
diff options
context:
space:
mode:
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>2012-05-21 10:20:12 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-09 11:32:58 -0400
commit93b71523531161bfdd2ac30dd1ea4bb70d1708bd (patch)
tree867f54842fcd7f4a303318afc6e0c2b2cb8f9ef2 /fs/cifs/file.c
parente48fdd4d095a5340bb88374bbbfa6b72625ab0c1 (diff)
cifs: fix oops while traversing open file list (try #4)
commit 2c0c2a08bed7a3b791f88d09d16ace56acb3dd98 upstream. While traversing the linked list of open file handles, if the identfied file handle is invalid, a reopen is attempted and if it fails, we resume traversing where we stopped and cifs can oops while accessing invalid next element, for list might have changed. So mark the invalid file handle and attempt reopen if no valid file handle is found in rest of the list. If reopen fails, move the invalid file handle to the end of the list and start traversing the list again from the begining. Repeat this four times before giving up and returning an error if file reopen keeps failing. Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r--fs/cifs/file.c57
1 files changed, 33 insertions, 24 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index a9b4a24f2a1..9040cb0695c 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -973,10 +973,11 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
973struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, 973struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
974 bool fsuid_only) 974 bool fsuid_only)
975{ 975{
976 struct cifsFileInfo *open_file; 976 struct cifsFileInfo *open_file, *inv_file = NULL;
977 struct cifs_sb_info *cifs_sb; 977 struct cifs_sb_info *cifs_sb;
978 bool any_available = false; 978 bool any_available = false;
979 int rc; 979 int rc;
980 unsigned int refind = 0;
980 981
981 /* Having a null inode here (because mapping->host was set to zero by 982 /* Having a null inode here (because mapping->host was set to zero by
982 the VFS or MM) should not happen but we had reports of on oops (due to 983 the VFS or MM) should not happen but we had reports of on oops (due to
@@ -996,40 +997,25 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
996 997
997 spin_lock(&cifs_file_list_lock); 998 spin_lock(&cifs_file_list_lock);
998refind_writable: 999refind_writable:
1000 if (refind > MAX_REOPEN_ATT) {
1001 spin_unlock(&cifs_file_list_lock);
1002 return NULL;
1003 }
999 list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { 1004 list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
1000 if (!any_available && open_file->pid != current->tgid) 1005 if (!any_available && open_file->pid != current->tgid)
1001 continue; 1006 continue;
1002 if (fsuid_only && open_file->uid != current_fsuid()) 1007 if (fsuid_only && open_file->uid != current_fsuid())
1003 continue; 1008 continue;
1004 if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { 1009 if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
1005 cifsFileInfo_get(open_file);
1006
1007 if (!open_file->invalidHandle) { 1010 if (!open_file->invalidHandle) {
1008 /* found a good writable file */ 1011 /* found a good writable file */
1012 cifsFileInfo_get(open_file);
1009 spin_unlock(&cifs_file_list_lock); 1013 spin_unlock(&cifs_file_list_lock);
1010 return open_file; 1014 return open_file;
1015 } else {
1016 if (!inv_file)
1017 inv_file = open_file;
1011 } 1018 }
1012
1013 spin_unlock(&cifs_file_list_lock);
1014
1015 /* Had to unlock since following call can block */
1016 rc = cifs_reopen_file(open_file, false);
1017 if (!rc)
1018 return open_file;
1019
1020 /* if it fails, try another handle if possible */
1021 cFYI(1, "wp failed on reopen file");
1022 cifsFileInfo_put(open_file);
1023
1024 spin_lock(&cifs_file_list_lock);
1025
1026 /* else we simply continue to the next entry. Thus
1027 we do not loop on reopen errors. If we
1028 can not reopen the file, for example if we
1029 reconnected to a server with another client
1030 racing to delete or lock the file we would not
1031 make progress if we restarted before the beginning
1032 of the loop here. */
1033 } 1019 }
1034 } 1020 }
1035 /* couldn't find useable FH with same pid, try any available */ 1021 /* couldn't find useable FH with same pid, try any available */
@@ -1037,7 +1023,30 @@ refind_writable:
1037 any_available = true; 1023 any_available = true;
1038 goto refind_writable; 1024 goto refind_writable;
1039 } 1025 }
1026
1027 if (inv_file) {
1028 any_available = false;
1029 cifsFileInfo_get(inv_file);
1030 }
1031
1040 spin_unlock(&cifs_file_list_lock); 1032 spin_unlock(&cifs_file_list_lock);
1033
1034 if (inv_file) {
1035 rc = cifs_reopen_file(inv_file, false);
1036 if (!rc)
1037 return inv_file;
1038 else {
1039 spin_lock(&cifs_file_list_lock);
1040 list_move_tail(&inv_file->flist,
1041 &cifs_inode->openFileList);
1042 spin_unlock(&cifs_file_list_lock);
1043 cifsFileInfo_put(inv_file);
1044 spin_lock(&cifs_file_list_lock);
1045 ++refind;
1046 goto refind_writable;
1047 }
1048 }
1049
1041 return NULL; 1050 return NULL;
1042} 1051}
1043 1052