diff options
author | Dave Kleikamp <shaggy@linux.vnet.ibm.com> | 2009-08-31 11:07:12 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2009-09-01 18:35:01 -0400 |
commit | 6ab409b53dcaf28f83d518a6702f904b7cee3f41 (patch) | |
tree | 050bfb690ac9df049343034681478a5bb174a823 /fs/cifs/file.c | |
parent | 1b49c5566136455764a8d17ead25784f534c202d (diff) |
cifs: Replace wrtPending with a real reference count
Currently, cifs_close() tries to wait until all I/O is complete and then
frees the file private data. If I/O does not completely in a reasonable
amount of time it frees the structure anyway, leaving a potential use-
after-free situation.
This patch changes the wrtPending counter to a complete reference count and
lets the last user free the structure.
Signed-off-by: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Tested-by: Shirish Pargaonkar <shirishp@us.ibm.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r-- | fs/cifs/file.c | 43 |
1 files changed, 11 insertions, 32 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index c34b7f8a217b..fa7beac8b80e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -53,11 +53,9 @@ static inline struct cifsFileInfo *cifs_init_private( | |||
53 | private_data->pInode = inode; | 53 | private_data->pInode = inode; |
54 | private_data->invalidHandle = false; | 54 | private_data->invalidHandle = false; |
55 | private_data->closePend = false; | 55 | private_data->closePend = false; |
56 | /* we have to track num writers to the inode, since writepages | 56 | /* Initialize reference count to one. The private data is |
57 | does not tell us which handle the write is for so there can | 57 | freed on the release of the last reference */ |
58 | be a close (overlapping with write) of the filehandle that | 58 | atomic_set(&private_data->count, 1); |
59 | cifs_writepages chose to use */ | ||
60 | atomic_set(&private_data->wrtPending, 0); | ||
61 | 59 | ||
62 | return private_data; | 60 | return private_data; |
63 | } | 61 | } |
@@ -643,7 +641,7 @@ int cifs_close(struct inode *inode, struct file *file) | |||
643 | if (!pTcon->need_reconnect) { | 641 | if (!pTcon->need_reconnect) { |
644 | write_unlock(&GlobalSMBSeslock); | 642 | write_unlock(&GlobalSMBSeslock); |
645 | timeout = 2; | 643 | timeout = 2; |
646 | while ((atomic_read(&pSMBFile->wrtPending) != 0) | 644 | while ((atomic_read(&pSMBFile->count) != 1) |
647 | && (timeout <= 2048)) { | 645 | && (timeout <= 2048)) { |
648 | /* Give write a better chance to get to | 646 | /* Give write a better chance to get to |
649 | server ahead of the close. We do not | 647 | server ahead of the close. We do not |
@@ -657,8 +655,6 @@ int cifs_close(struct inode *inode, struct file *file) | |||
657 | msleep(timeout); | 655 | msleep(timeout); |
658 | timeout *= 4; | 656 | timeout *= 4; |
659 | } | 657 | } |
660 | if (atomic_read(&pSMBFile->wrtPending)) | ||
661 | cERROR(1, ("close with pending write")); | ||
662 | if (!pTcon->need_reconnect && | 658 | if (!pTcon->need_reconnect && |
663 | !pSMBFile->invalidHandle) | 659 | !pSMBFile->invalidHandle) |
664 | rc = CIFSSMBClose(xid, pTcon, | 660 | rc = CIFSSMBClose(xid, pTcon, |
@@ -681,24 +677,7 @@ int cifs_close(struct inode *inode, struct file *file) | |||
681 | list_del(&pSMBFile->flist); | 677 | list_del(&pSMBFile->flist); |
682 | list_del(&pSMBFile->tlist); | 678 | list_del(&pSMBFile->tlist); |
683 | write_unlock(&GlobalSMBSeslock); | 679 | write_unlock(&GlobalSMBSeslock); |
684 | timeout = 10; | 680 | cifsFileInfo_put(file->private_data); |
685 | /* We waited above to give the SMBWrite a chance to issue | ||
686 | on the wire (so we do not get SMBWrite returning EBADF | ||
687 | if writepages is racing with close. Note that writepages | ||
688 | does not specify a file handle, so it is possible for a file | ||
689 | to be opened twice, and the application close the "wrong" | ||
690 | file handle - in these cases we delay long enough to allow | ||
691 | the SMBWrite to get on the wire before the SMB Close. | ||
692 | We allow total wait here over 45 seconds, more than | ||
693 | oplock break time, and more than enough to allow any write | ||
694 | to complete on the server, or to time out on the client */ | ||
695 | while ((atomic_read(&pSMBFile->wrtPending) != 0) | ||
696 | && (timeout <= 50000)) { | ||
697 | cERROR(1, ("writes pending, delay free of handle")); | ||
698 | msleep(timeout); | ||
699 | timeout *= 8; | ||
700 | } | ||
701 | kfree(file->private_data); | ||
702 | file->private_data = NULL; | 681 | file->private_data = NULL; |
703 | } else | 682 | } else |
704 | rc = -EBADF; | 683 | rc = -EBADF; |
@@ -1236,7 +1215,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) | |||
1236 | if (!open_file->invalidHandle) { | 1215 | if (!open_file->invalidHandle) { |
1237 | /* found a good file */ | 1216 | /* found a good file */ |
1238 | /* lock it so it will not be closed on us */ | 1217 | /* lock it so it will not be closed on us */ |
1239 | atomic_inc(&open_file->wrtPending); | 1218 | cifsFileInfo_get(open_file); |
1240 | read_unlock(&GlobalSMBSeslock); | 1219 | read_unlock(&GlobalSMBSeslock); |
1241 | return open_file; | 1220 | return open_file; |
1242 | } /* else might as well continue, and look for | 1221 | } /* else might as well continue, and look for |
@@ -1276,7 +1255,7 @@ refind_writable: | |||
1276 | if (open_file->pfile && | 1255 | if (open_file->pfile && |
1277 | ((open_file->pfile->f_flags & O_RDWR) || | 1256 | ((open_file->pfile->f_flags & O_RDWR) || |
1278 | (open_file->pfile->f_flags & O_WRONLY))) { | 1257 | (open_file->pfile->f_flags & O_WRONLY))) { |
1279 | atomic_inc(&open_file->wrtPending); | 1258 | cifsFileInfo_get(open_file); |
1280 | 1259 | ||
1281 | if (!open_file->invalidHandle) { | 1260 | if (!open_file->invalidHandle) { |
1282 | /* found a good writable file */ | 1261 | /* found a good writable file */ |
@@ -1293,7 +1272,7 @@ refind_writable: | |||
1293 | else { /* start over in case this was deleted */ | 1272 | else { /* start over in case this was deleted */ |
1294 | /* since the list could be modified */ | 1273 | /* since the list could be modified */ |
1295 | read_lock(&GlobalSMBSeslock); | 1274 | read_lock(&GlobalSMBSeslock); |
1296 | atomic_dec(&open_file->wrtPending); | 1275 | cifsFileInfo_put(open_file); |
1297 | goto refind_writable; | 1276 | goto refind_writable; |
1298 | } | 1277 | } |
1299 | } | 1278 | } |
@@ -1309,7 +1288,7 @@ refind_writable: | |||
1309 | read_lock(&GlobalSMBSeslock); | 1288 | read_lock(&GlobalSMBSeslock); |
1310 | /* can not use this handle, no write | 1289 | /* can not use this handle, no write |
1311 | pending on this one after all */ | 1290 | pending on this one after all */ |
1312 | atomic_dec(&open_file->wrtPending); | 1291 | cifsFileInfo_put(open_file); |
1313 | 1292 | ||
1314 | if (open_file->closePend) /* list could have changed */ | 1293 | if (open_file->closePend) /* list could have changed */ |
1315 | goto refind_writable; | 1294 | goto refind_writable; |
@@ -1373,7 +1352,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) | |||
1373 | if (open_file) { | 1352 | if (open_file) { |
1374 | bytes_written = cifs_write(open_file->pfile, write_data, | 1353 | bytes_written = cifs_write(open_file->pfile, write_data, |
1375 | to-from, &offset); | 1354 | to-from, &offset); |
1376 | atomic_dec(&open_file->wrtPending); | 1355 | cifsFileInfo_put(open_file); |
1377 | /* Does mm or vfs already set times? */ | 1356 | /* Does mm or vfs already set times? */ |
1378 | inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); | 1357 | inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); |
1379 | if ((bytes_written > 0) && (offset)) | 1358 | if ((bytes_written > 0) && (offset)) |
@@ -1562,7 +1541,7 @@ retry: | |||
1562 | bytes_to_write, offset, | 1541 | bytes_to_write, offset, |
1563 | &bytes_written, iov, n_iov, | 1542 | &bytes_written, iov, n_iov, |
1564 | long_op); | 1543 | long_op); |
1565 | atomic_dec(&open_file->wrtPending); | 1544 | cifsFileInfo_put(open_file); |
1566 | cifs_update_eof(cifsi, offset, bytes_written); | 1545 | cifs_update_eof(cifsi, offset, bytes_written); |
1567 | 1546 | ||
1568 | if (rc || bytes_written < bytes_to_write) { | 1547 | if (rc || bytes_written < bytes_to_write) { |