diff -ur linux-2.6.16.20.orig/fs/smbfs/file.c linux-2.6.16.20/fs/smbfs/file.c
--- linux-2.6.16.20.orig/fs/smbfs/file.c	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/file.c	2006-06-07 11:35:02.000000000 -0400
@@ -233,7 +233,7 @@
 
 	VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n",
 		(long)dentry->d_inode->i_size,
-		dentry->d_inode->i_flags, dentry->d_inode->i_atime);
+		dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec);
 
 	status = generic_file_read(file, buf, count, ppos);
 out:
@@ -343,7 +343,7 @@
 		result = generic_file_write(file, buf, count, ppos);
 		VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
 			(long) file->f_pos, (long) dentry->d_inode->i_size,
-			dentry->d_inode->i_mtime, dentry->d_inode->i_atime);
+			dentry->d_inode->i_mtime.tv_sec, dentry->d_inode->i_atime.tv_sec);
 	}
 out:
 	return result;
diff -ur linux-2.6.16.20.orig/fs/smbfs/inode.c linux-2.6.16.20/fs/smbfs/inode.c
--- linux-2.6.16.20.orig/fs/smbfs/inode.c	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/inode.c	2006-06-07 11:35:02.000000000 -0400
@@ -216,7 +216,7 @@
 	if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) {
 		VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n",
 			inode->i_ino,
-			(long) last_time, (long) inode->i_mtime,
+			(long) last_time, (long) inode->i_mtime.tv_sec,
 			(long) last_sz, (long) inode->i_size);
 
 		if (!S_ISDIR(inode->i_mode))
diff -ur linux-2.6.16.20.orig/fs/smbfs/proc.c linux-2.6.16.20/fs/smbfs/proc.c
--- linux-2.6.16.20.orig/fs/smbfs/proc.c	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/proc.c	2006-06-07 11:35:02.000000000 -0400
@@ -2594,7 +2594,7 @@
 	fattr->f_mtime.tv_sec = date_dos2unix(server, date, time);
 	fattr->f_mtime.tv_nsec = 0;
 	VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n",
-		mask, date, time, fattr->f_mtime);
+		mask, date, time, fattr->f_mtime.tv_sec);
 	fattr->f_size = DVAL(req->rq_data, 12);
 	/* ULONG allocation size */
 	fattr->attr = WVAL(req->rq_data, 20);
diff -ur linux-2.6.16.20.orig/fs/smbfs/proto.h linux-2.6.16.20/fs/smbfs/proto.h
--- linux-2.6.16.20.orig/fs/smbfs/proto.h	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/proto.h	2006-06-07 11:35:02.000000000 -0400
@@ -1,5 +1,5 @@
 /*
- *  Autogenerated with cproto on:  Sat Sep 13 17:18:51 CEST 2003
+ *  Autogenerated with cproto on:  Mon Jun 5 10:10:55 EDT 2006
  */
 
 struct smb_request;
@@ -23,8 +23,7 @@
 extern int smb_proc_rmdir(struct dentry *dentry);
 extern int smb_proc_unlink(struct dentry *dentry);
 extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid);
-extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr,
-				 struct super_block *sb);
+extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, struct super_block *sb);
 extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr);
 extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr);
 extern int smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, unsigned int major, unsigned int minor);
@@ -78,7 +77,7 @@
 extern int smb_init_request_cache(void);
 extern void smb_destroy_request_cache(void);
 extern struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize);
-extern void smb_rput(struct smb_request *req);
+extern int smb_rput(struct smb_request *req);
 extern int smb_add_request(struct smb_request *req);
 extern int smb_request_send_server(struct smb_sb_info *server);
 extern int smb_request_recv(struct smb_sb_info *server);
diff -ur linux-2.6.16.20.orig/fs/smbfs/request.c linux-2.6.16.20/fs/smbfs/request.c
--- linux-2.6.16.20.orig/fs/smbfs/request.c	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/request.c	2006-06-07 11:35:02.000000000 -0400
@@ -28,6 +28,7 @@
 static kmem_cache_t *req_cachep;
 
 static int smb_request_send_req(struct smb_request *req);
+static int smb_request_undo(struct smb_sb_info *server, struct smb_request *req);
 
 /*
   /proc/slabinfo:
@@ -92,13 +93,15 @@
 	struct smb_request *req = NULL;
 
 	for (;;) {
-		atomic_inc(&server->nr_requests);
-		if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) {
+		if (atomic_inc_and_test(&server->nr_requests) <= MAX_REQUEST_HARD) {
 			req = smb_do_alloc_request(server, bufsize);
 			if (req != NULL)
 				break;
 		}
 
+		/* allocation failed (or there are too many requests active) */
+		atomic_dec(&server->nr_requests);
+
 #if 0
 		/*
 		 * Try to free up at least one request in order to stay
@@ -137,12 +140,14 @@
 {
 	atomic_inc(&req->rq_count);
 }
-void smb_rput(struct smb_request *req)
+int smb_rput(struct smb_request *req)
 {
 	if (atomic_dec_and_test(&req->rq_count)) {
 		list_del_init(&req->rq_queue);
 		smb_free_request(req);
+		return 1;
 	}
+	return 0;
 }
 
 /* setup to receive the data part of the SMB */
@@ -296,8 +301,7 @@
 #endif
 
 	/* add 'req' to the queue of requests */
-	if (smb_lock_server_interruptible(server))
-		return -EINTR;
+	smb_lock_server(server);
 
 	/*
 	 * Try to send the request as the process. If that fails we queue the
@@ -335,19 +339,47 @@
 
 	timeleft = wait_event_interruptible_timeout(req->rq_wait,
 				    req->rq_flags & SMB_REQ_RECEIVED, 30*HZ);
-	if (!timeleft || signal_pending(current)) {
+
+	if (timeleft && signal_pending(current)) {
+		int cmd = *(req->rq_header + smb_com);
+		
+		smb_lock_server(server);
 		/*
-		 * On timeout or on interrupt we want to try and remove the
-		 * request from the recvq/xmitq.
-		 * First check if the request is still part of a queue. (May
-		 * have been removed by some error condition)
+		 * On a signal, we abort any requests that haven't already
+		 * been transmitted, except for SMBclose (fops->release);
+		 * the VFS layer can't cope with release failing.
+		 *
+		 * If we've received the response for a file open or
+		 * creation, but the syscall was interrupted, we undo
+		 * the request (closing the fid so that it's not leaked),
+		 * and return EINTR as expected.
+		 *
+		 * If we've (partially) transmitted a request, we simply
+		 * mark it as interrupted and take care of it in
+		 * smb_request_recv() so that we can return without
+		 * blocking the calling process.
+		 *
+		 * If smb_rput() returns 1, req is no longer valid, so
+		 * we abort as soon as possible.
 		 */
-		smb_lock_server(server);
-		if (!list_empty(&req->rq_queue)) {
+		
+		result = 0;
+
+		if (req->rq_flags & SMB_REQ_RECEIVED) {
+			smb_request_undo(server, req);
+		} else if (!(req->rq_flags & (SMB_REQ_XMIT_STARTED|SMB_REQ_TRANSMITTED)) && cmd != SMBclose) {
 			list_del_init(&req->rq_queue);
-			smb_rput(req);
+			result = smb_rput(req);
+		} else if (req->rq_flags & SMB_REQ_RECEIVED) {
+			smb_request_undo(server, req);
+		} else {
+			req->rq_flags |= SMB_REQ_INTERRUPTED;
 		}
 		smb_unlock_server(server);
+		
+		/* abort now if req has been freed */
+		if (result)
+			return -EINTR;
 	}
 
 	if (!timeleft) {
@@ -365,8 +397,25 @@
 		req->rq_rcls = ERRSRV;
 		req->rq_err  = ERRtimeout;
 
+		/*
+		 * On timeout we want to try and remove the request from
+		 * the recvq/xmitq.
+		 * First check if the request is still part of a queue. (May
+		 * have been removed by some error condition)
+		 */
+		smb_lock_server(server);
+		if (!list_empty(&req->rq_queue)) {
+			list_del_init(&req->rq_queue);
+			result = smb_rput(req);
+		}
+		smb_unlock_server(server);
+
 		/* Just in case it was "stuck" */
 		smbiod_wake_up();
+		
+		/* if req isn't valid anymore, just bail */
+		if (result)
+			return -EIO;
 	}
 	VERBOSE("woke up, rcls=%d\n", req->rq_rcls);
 
@@ -814,8 +863,63 @@
 	if (!result) {
 		list_del_init(&req->rq_queue);
 		req->rq_flags |= SMB_REQ_RECEIVED;
-		smb_rput(req);
+		if (req->rq_flags & SMB_REQ_INTERRUPTED)
+			smb_request_undo(server, req);
 		wake_up_interruptible(&req->rq_wait);
+		smb_rput(req);
 	}
 	return 0;
 }
+
+/*
+ * Undo a request (by e.g. closing a file handle if the request is SMBopen.)
+ * Used to deal with signals received after a request has already been
+ * transmitted.
+ *
+ * The new request is created and queued here, because we expect to be
+ * called with the server lock held and can't rely on smb_add_request().
+ *
+ * Return: <0 on error
+ */
+static
+int smb_request_undo(struct smb_sb_info *server, struct smb_request *req)
+{
+	struct smb_request *creq;
+	int result = 0;
+	int cmd = *(req->rq_header + smb_com);
+	int fid;
+	
+	if (cmd == SMBopen || cmd == SMBcreate) {
+		fid = WVAL(req->rq_header, smb_vwv0);
+
+		if (!(creq = smb_alloc_request(server, 0))) {
+			result = -ENOMEM;
+			goto out;
+		}
+
+		if (server->mid > 0xf000)
+			server->mid = 0;
+		
+		creq->rq_mid = server->mid++;
+
+		smb_setup_header(creq, SMBclose, 3, 0);
+		WSET(creq->rq_header, smb_vwv0, fid);
+		/* use the mtime from the open/create request */
+		DSET(creq->rq_header, smb_vwv1, LVAL(req->rq_header, smb_vwv2));
+		WSET(creq->rq_header, smb_mid, creq->rq_mid);
+		
+		creq->rq_flags |= SMB_REQ_NORETRY;
+		creq->rq_resp_wct = 0;
+		creq->rq_resp_bcc = 0;
+		
+		smb_setup_request(creq);
+
+		/* FIXME: Should we also unlink the file if the request is a SMBcreate? */
+		list_add_tail(&creq->rq_queue, &server->xmitq);
+
+		smbiod_wake_up();
+	}
+
+out:
+	return result;
+}
diff -ur linux-2.6.16.20.orig/fs/smbfs/request.h linux-2.6.16.20/fs/smbfs/request.h
--- linux-2.6.16.20.orig/fs/smbfs/request.h	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/request.h	2006-06-07 11:35:02.000000000 -0400
@@ -63,6 +63,8 @@
 #define SMB_REQ_STATIC		0x0001	/* rq_buffer is static */
 #define SMB_REQ_NORETRY		0x0002	/* request is invalid after retry */
 
+#define SMB_REQ_INTERRUPTED	0x1000	/* request interrupted by signal */
+#define SMB_REQ_XMIT_STARTED	0x2000	/* some data has been sent */
 #define SMB_REQ_TRANSMITTED	0x4000	/* all data has been sent */
 #define SMB_REQ_RECEIVED	0x8000	/* reply received, smbiod is done */
 
diff -ur linux-2.6.16.20.orig/fs/smbfs/smbiod.c linux-2.6.16.20/fs/smbfs/smbiod.c
--- linux-2.6.16.20.orig/fs/smbfs/smbiod.c	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/smbiod.c	2006-06-07 11:35:02.000000000 -0400
@@ -121,15 +121,15 @@
 		req = list_entry(tmp, struct smb_request, rq_queue);
 		req->rq_errno = -EIO;
 		list_del_init(&req->rq_queue);
-		smb_rput(req);
 		wake_up_interruptible(&req->rq_wait);
+		smb_rput(req);
 	}
 	list_for_each_safe(tmp, n, &server->recvq) {
 		req = list_entry(tmp, struct smb_request, rq_queue);
 		req->rq_errno = -EIO;
 		list_del_init(&req->rq_queue);
-		smb_rput(req);
 		wake_up_interruptible(&req->rq_wait);
+		smb_rput(req);
 	}
 }
 
@@ -167,8 +167,8 @@
 			VERBOSE("aborting request %p on xmitq\n", req);
 			req->rq_errno = -EIO;
 			list_del_init(&req->rq_queue);
-			smb_rput(req);
 			wake_up_interruptible(&req->rq_wait);
+			smb_rput(req);
 		}
 	}
 
@@ -193,8 +193,8 @@
 		/* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */
 		req->rq_errno = -EIO;
 		list_del_init(&req->rq_queue);
-		smb_rput(req);
 		wake_up_interruptible(&req->rq_wait);
+		smb_rput(req);
 	}
 
 	smb_close_socket(server);
diff -ur linux-2.6.16.20.orig/fs/smbfs/sock.c linux-2.6.16.20/fs/smbfs/sock.c
--- linux-2.6.16.20.orig/fs/smbfs/sock.c	2006-06-07 11:20:58.000000000 -0400
+++ linux-2.6.16.20/fs/smbfs/sock.c	2006-06-07 11:35:02.000000000 -0400
@@ -331,8 +331,13 @@
 
 	/* Dont repeat bytes and count available bufferspace */
 	rlen = smb_move_iov(&p, &num, iov, req->rq_bytes_recvd);
-	if (req->rq_rlen < rlen)
-		rlen = req->rq_rlen;
+
+	/*
+	 * Don't request more bytes than we have space for, or more than
+	 * we want for this request.
+	 */
+	if (req->rq_rlen - req->rq_bytes_recvd < rlen)
+		rlen = req->rq_rlen - req->rq_bytes_recvd;
 
 	result = kernel_recvmsg(sock, &msg, p, num, rlen, flags);
 
@@ -380,8 +385,16 @@
 
 	if (result >= 0) {
 		req->rq_bytes_sent += result;
-		if (req->rq_bytes_sent >= req->rq_slen)
+		if (req->rq_bytes_sent >= req->rq_slen) {
+			req->rq_flags &= ~SMB_REQ_XMIT_STARTED;
 			req->rq_flags |= SMB_REQ_TRANSMITTED;
+		} else {
+			/*
+			 * If we've sent part of the message, we need to
+			 * send ALL of the message...
+			 */
+			req->rq_flags |= SMB_REQ_XMIT_STARTED;
+		}
 	}
 out:
 	return result;
