[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Patch 1/2] tabled: fix bugs in streaming of data



This patch fixes 3 plain coding bugs in the streaming mechanism.

1. Whenever we loop back to "restart" label, we have to clear n_iov.
   Otherwise, the loop that fills iov[] writes beyond the end of the
   array. The result is binary garbage on console and a hang.

   Behold the evils of initialized variables. Fix is obvious.

   BTW, array indexes are signed.

2. When we consume a queue element partially, we ought to advance its
   buffer pointer when we decrement the length. Otherwise, we resend
   the data that was sent already, causing a corruption in transfer.

   The fix is obvious (add tmp->buf += sz).

3. The accounting (through unfortunately named write_cnt) was incorrect
   because wr->len may be decremented by the time the element is freed.

Since we're at it, we implement a couple of safety improvements.
First, we change the API to the callback a little. From now on, it
does not have access to the struct client_write anymore, and cannot
accidentially rely on wr->buf (which can be changed now).
Also, we renamed "len" into "togo" and named the new member "length",
to make sure that all use instances for "len" were reviewed.

Signed-Off-By: Pete Zaitcev <zaitcev@xxxxxxxxxx>

---
 server/object.c |    8 +++-----
 server/server.c |   31 ++++++++++++++++---------------
 server/tabled.h |   11 ++++++-----
 3 files changed, 25 insertions(+), 25 deletions(-)

commit c3671739c217b4b7a63d82543d94318f368a109f
Author: Master <zaitcev@xxxxxxxxxxxxxxxxxx>
Date:   Tue Jan 5 00:04:15 2010 -0700

    Bugfixes for the object streaming to client.

diff --git a/server/object.c b/server/object.c
index 103b36e..d61a446 100644
--- a/server/object.c
+++ b/server/object.c
@@ -983,8 +983,7 @@ void cli_in_end(struct client *cli)
 	stor_close(&cli->in_ce);
 }
 
-static bool object_get_more(struct client *cli, struct client_write *wr,
-			    bool done);
+static bool object_get_more(struct client *cli, void *cb_data, bool done);
 
 /*
  * Return true iff cli_writeq was called. This is compatible with the
@@ -1036,12 +1035,11 @@ err_out:
 }
 
 /* callback from the client side: a queued write is being disposed */
-static bool object_get_more(struct client *cli, struct client_write *wr,
-			    bool done)
+static bool object_get_more(struct client *cli, void *cb_data, bool done)
 {
 
 	/* free now-written buffer */
-	free(wr->cb_data);
+	free(cb_data);
 
 	/* do not queue more, if !completion or fd was closed early */
 	if (!done)	/* FIXME We used to test for input errors here. */
diff --git a/server/server.c b/server/server.c
index 32bb8a4..f16d2ab 100644
--- a/server/server.c
+++ b/server/server.c
@@ -386,10 +386,10 @@ static bool cli_write_free(struct client *cli, struct client_write *tmp,
 {
 	bool rcb = false;
 
-	cli->write_cnt -= tmp->len;
+	cli->write_cnt -= tmp->length;
 	list_del(&tmp->node);
 	if (tmp->cb)
-		rcb = tmp->cb(cli, tmp, done);
+		rcb = tmp->cb(cli, tmp->cb_data, done);
 	free(tmp);
 
 	return rcb;
@@ -487,7 +487,7 @@ static bool cli_evt_recycle(struct client *cli, unsigned int events)
 
 static void cli_writable(struct client *cli)
 {
-	unsigned int n_iov = 0;
+	int n_iov;
 	struct client_write *tmp;
 	ssize_t rc;
 	struct iovec iov[CLI_MAX_WR_IOV];
@@ -497,13 +497,14 @@ restart:
 	more_work = false;
 
 	/* accumulate pending writes into iovec */
+	n_iov = 0;
 	list_for_each_entry(tmp, &cli->write_q, node) {
+		if (n_iov == CLI_MAX_WR_IOV)
+			break;
 		/* bleh, struct iovec should declare iov_base const */
 		iov[n_iov].iov_base = (void *) tmp->buf;
-		iov[n_iov].iov_len = tmp->len;
+		iov[n_iov].iov_len = tmp->togo;
 		n_iov++;
-		if (n_iov == CLI_MAX_WR_IOV)
-			break;
 	}
 
 	/* execute non-blocking write */
@@ -527,14 +528,15 @@ do_write:
 		tmp = list_entry(cli->write_q.next, struct client_write, node);
 
 		/* mark data consumed by decreasing tmp->len */
-		sz = (tmp->len < rc) ? tmp->len : rc;
-		tmp->len -= sz;
+		sz = (tmp->togo < rc) ? tmp->togo : rc;
+		tmp->togo -= sz;
+		tmp->buf += sz;
 		rc -= sz;
 
 		/* if tmp->len reaches zero, write is complete,
 		 * call callback and clean up
 		 */
-		if (tmp->len == 0)
+		if (tmp->togo == 0)
 			if (cli_write_free(cli, tmp, true))
 				more_work = true;
 	}
@@ -600,11 +602,12 @@ int cli_writeq(struct client *cli, const void *buf, unsigned int buflen,
 		return -ENOMEM;
 
 	wr->buf = buf;
-	wr->len = buflen;
+	wr->togo = buflen;
+	wr->length = buflen;
 	wr->cb = cb;
 	wr->cb_data = cb_data;
 	list_add_tail(&wr->node, &cli->write_q);
-	cli->write_cnt += wr->len;
+	cli->write_cnt += buflen;
 
 	return 0;
 }
@@ -645,11 +648,9 @@ do_read:
 	return 0;
 }
 
-bool cli_cb_free(struct client *cli, struct client_write *wr,
-			bool done)
+bool cli_cb_free(struct client *cli, void *cb_data, bool done)
 {
-	free(wr->cb_data);
-
+	free(cb_data);
 	return false;
 }
 
diff --git a/server/tabled.h b/server/tabled.h
index 59c474a..a0d3400 100644
--- a/server/tabled.h
+++ b/server/tabled.h
@@ -103,11 +103,13 @@ struct storage_node {
 };
 
 typedef bool (*cli_evt_func)(struct client *, unsigned int);
-typedef bool (*cli_write_func)(struct client *, struct client_write *, bool);
+typedef bool (*cli_write_func)(struct client *, void *, bool);
 
 struct client_write {
-	const void		*buf;		/* write buffer */
-	int			len;		/* write buffer length */
+	const void		*buf;		/* write buffer pointer */
+	int			togo;		/* write buffer remainder */
+
+	int			length;		/* length for accounting */
 	cli_write_func		cb;		/* callback */
 	void			*cb_data;	/* data passed to cb */
 
@@ -314,8 +316,7 @@ extern bool cli_resp_xml(struct client *cli, int http_status,
 extern int cli_writeq(struct client *cli, const void *buf, unsigned int buflen,
 		     cli_write_func cb, void *cb_data);
 extern size_t cli_wqueued(struct client *cli);
-extern bool cli_cb_free(struct client *cli, struct client_write *wr,
-			bool done);
+extern bool cli_cb_free(struct client *cli, void *cb_data, bool done);
 extern bool cli_write_start(struct client *cli);
 extern int cli_req_avail(struct client *cli);
 extern void applog(int prio, const char *fmt, ...);
--
To unsubscribe from this list: send the line "unsubscribe hail-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Photo]     [Yosemite News]    [Yosemite Photos]    [Free Online Dating]     [Linux Kernel]     [Linux SCSI]     [XFree86]

Add to Google Powered by Linux