On Christian Perrier's request, here is a patch that should fix both the 
segfault (and visibility issue) and the write error.

Cheers,
Kai
-- 
Kai Blin
WorldForge developer  http://www.worldforge.org/
Wine developer        http://wiki.winehq.org/KaiBlin
Samba team member     http://www.samba.org/samba/team/
--
Will code for cotton.
diff --git a/source/lib/system.c b/source/lib/system.c
index eabb6d6..5b06d3c 100644
--- a/source/lib/system.c
+++ b/source/lib/system.c
@@ -142,6 +142,20 @@ ssize_t sys_write(int fd, const void *buf, size_t count)
 }
 
 /*******************************************************************
+A writev wrapper that will deal with EINTR.
+********************************************************************/
+
+ssize_t sys_writev(int fd, const struct iovec *iov, int iovcnt)
+{
+	ssize_t ret;
+
+	do {
+		ret = writev(fd, iov, iovcnt);
+	} while (ret == -1 && errno == EINTR);
+	return ret;
+}
+
+/*******************************************************************
 A pread wrapper that will deal with EINTR and 64-bit file offsets.
 ********************************************************************/
 
diff --git a/source/lib/util_sock.c b/source/lib/util_sock.c
index e64b003..9c37e0c 100644
--- a/source/lib/util_sock.c
+++ b/source/lib/util_sock.c
@@ -1037,40 +1037,109 @@ NTSTATUS read_data(int fd, char *buffer, size_t N)
 }
 
 /****************************************************************************
- Write data to a fd.
+ Write all data from an iov array
 ****************************************************************************/
 
-ssize_t write_data(int fd, const char *buffer, size_t N)
+ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt)
 {
-	size_t total=0;
-	ssize_t ret;
-	char addr[INET6_ADDRSTRLEN];
+	int i;
+	size_t to_send;
+	ssize_t thistime;
+	size_t sent;
+	struct iovec *iov_copy, *iov;
 
-	while (total < N) {
-		ret = sys_write(fd,buffer + total,N - total);
+	to_send = 0;
+	for (i=0; i<iovcnt; i++) {
+		to_send += orig_iov[i].iov_len;
+	}
 
-		if (ret == -1) {
-			if (fd == get_client_fd()) {
-				/* Try and give an error message saying
-				 * what client failed. */
-				DEBUG(0,("write_data: write failure in "
-					"writing to client %s. Error %s\n",
-					get_peer_addr(fd,addr,sizeof(addr)),
-					strerror(errno) ));
-			} else {
-				DEBUG(0,("write_data: write failure. "
-					"Error = %s\n", strerror(errno) ));
+	thistime = sys_writev(fd, orig_iov, iovcnt);
+	if ((thistime <= 0) || (thistime == to_send)) {
+		return thistime;
+	}
+	sent = thistime;
+
+	/*
+	 * We could not send everything in one call. Make a copy of iov that
+	 * we can mess with. We keep a copy of the array start in iov_copy for
+	 * the TALLOC_FREE, because we're going to modify iov later on,
+	 * discarding elements.
+	 */
+
+	iov_copy = (struct iovec *)TALLOC_MEMDUP(
+		talloc_tos(), orig_iov, sizeof(struct iovec) * iovcnt);
+
+	if (iov_copy == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+	iov = iov_copy;
+
+	while (sent < to_send) {
+		/*
+		 * We have to discard "thistime" bytes from the beginning
+		 * iov array, "thistime" contains the number of bytes sent
+		 * via writev last.
+		 */
+		while (thistime > 0) {
+			if (thistime < iov[0].iov_len) {
+				char *new_base =
+					(char *)iov[0].iov_base + thistime;
+				iov[0].iov_base = new_base;
+				iov[0].iov_len -= thistime;
+				break;
 			}
-			return -1;
+			thistime -= iov[0].iov_len;
+			iov += 1;
+			iovcnt -= 1;
 		}
 
-		if (ret == 0) {
-			return total;
+		thistime = sys_writev(fd, iov, iovcnt);
+		if (thistime <= 0) {
+			break;
 		}
+		sent += thistime;
+	}
+
+	TALLOC_FREE(iov_copy);
+	return sent;
+}
+
+/****************************************************************************
+ Write data to a fd.
+****************************************************************************/
+
+/****************************************************************************
+ Write data to a fd.
+****************************************************************************/
+
+ssize_t write_data(int fd, const char *buffer, size_t N)
+{
+	ssize_t ret;
+	struct iovec iov;
+
+	iov.iov_base = CONST_DISCARD(char *, buffer);
+	iov.iov_len = N;
+
+	ret = write_data_iov(fd, &iov, 1);
+	if (ret >= 0) {
+		return ret;
+	}
 
-		total += ret;
+	if (fd == get_client_fd()) {
+		char addr[INET6_ADDRSTRLEN];
+		/*
+		 * Try and give an error message saying what client failed.
+		 */
+		DEBUG(0, ("write_data: write failure in writing to client %s. "
+			  "Error %s\n", get_peer_addr(fd,addr,sizeof(addr)),
+			  strerror(errno)));
+	} else {
+		DEBUG(0,("write_data: write failure. Error = %s\n",
+			 strerror(errno) ));
 	}
-	return (ssize_t)total;
+
+	return -1;
 }
 
 /****************************************************************************
diff --git a/source/libsmb/clientgen.c b/source/libsmb/clientgen.c
index d6d26e4..aeb1c28 100644
--- a/source/libsmb/clientgen.c
+++ b/source/libsmb/clientgen.c
@@ -315,7 +315,7 @@ bool cli_send_smb_direct_writeX(struct cli_state *cli,
 	/* First length to send is the offset to the data. */
 	size_t len = SVAL(cli->outbuf,smb_vwv11) + 4;
 	size_t nwritten=0;
-	ssize_t ret;
+	struct iovec iov[2];
 
 	/* fd == -1 causes segfaults -- Tom (t...@ninja.nl) */
 	if (cli->fd == -1) {
@@ -327,33 +327,19 @@ bool cli_send_smb_direct_writeX(struct cli_state *cli,
 		return false;
 	}
 
-	while (nwritten < len) {
-		ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten);
-		if (ret <= 0) {
-			close(cli->fd);
-			cli->fd = -1;
-			cli->smb_rw_error = SMB_WRITE_ERROR;
-			DEBUG(0,("Error writing %d bytes to client. %d (%s)\n",
-				(int)len,(int)ret, strerror(errno) ));
-			return false;
-		}
-		nwritten += ret;
-	}
+	iov[0].iov_base = cli->outbuf;
+	iov[0].iov_len = len;
+	iov[1].iov_base = CONST_DISCARD(char *, p);
+	iov[1].iov_len = extradata;
 
-	/* Now write the extra data. */
-	nwritten=0;
-	while (nwritten < extradata) {
-		ret = write_socket(cli->fd,p+nwritten,extradata - nwritten);
-		if (ret <= 0) {
-			close(cli->fd);
-			cli->fd = -1;
-			cli->smb_rw_error = SMB_WRITE_ERROR;
-			DEBUG(0,("Error writing %d extradata "
-				"bytes to client. %d (%s)\n",
-				(int)extradata,(int)ret, strerror(errno) ));
-			return false;
-		}
-		nwritten += ret;
+	nwritten = write_data_iov(cli->fd, iov, 2);
+	if (nwritten < (len + extradata)) {
+		close(cli->fd);
+		cli->fd = -1;
+		cli->smb_rw_error = SMB_WRITE_ERROR;
+		DEBUG(0,("Error writing %d bytes to client. (%s)\n",
+			 (int)(len+extradata), strerror(errno)));
+		return false;
 	}
 
 	/* Increment the mid so we can tell between responses. */
diff --git a/source/libsmb/clilist.c b/source/libsmb/clilist.c
index cebafc6..1431b80 100644
--- a/source/libsmb/clilist.c
+++ b/source/libsmb/clilist.c
@@ -79,16 +79,17 @@ static size_t interpret_long_filename(TALLOC_CTX *ctx,
 			p += 27;
 			p += clistr_align_in(cli, p, 0);
 
-			/* We can safely use +1 here (which is required by OS/2)
-			 * instead of +2 as the STR_TERMINATE flag below is
+			/* We can safely use len here (which is required by OS/2)
+			 * and the NAS-BASIC server instead of +2 or +1 as the
+			 * STR_TERMINATE flag below is
 			 * actually used as the length calculation.
-			 * The len+2 is merely an upper bound.
+			 * The len is merely an upper bound.
 			 * Due to the explicit 2 byte null termination
 			 * in cli_receive_trans/cli_receive_nt_trans
 			 * we know this is safe. JRA + kukks
 			 */
 
-			if (p + len + 1 > pdata_end) {
+			if (p + len > pdata_end) {
 				return pdata_end - base;
 			}
 

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to