Commit Diff


commit - 7afec891490adf4fd7cdcfe668cba0d7b2401de5
commit + e0857cfe46baca2f87986268f7d819b8bd9591ff
blob - 6eac9e32b7726497913a225ac7e1e99a4d727ece
blob + 6a927f23e171745f70fa35dc25632bbd016df870
--- gotweb/gotweb.c
+++ gotweb/gotweb.c
@@ -167,8 +167,7 @@ static const struct got_error	*gw_get_repo_age(char **
 				    char *, char *, int);
 static const struct got_error	*gw_get_file_blame_blob(char **,
 				    struct gw_trans *);
-static const struct got_error	*gw_get_file_read_blob(char **, size_t *,
-				    struct gw_trans *);
+static const struct got_error	*gw_output_blob_buf(struct gw_trans *);
 static const struct got_error	*gw_get_repo_tree(char **, struct gw_trans *);
 static const struct got_error	*gw_get_diff(struct gw_trans *,
 				    struct gw_header *);
@@ -330,9 +329,17 @@ gw_empty_string(char **s)
 }
 
 static int
-isbinary(const char *buf, size_t n)
+isbinary(const uint8_t *buf, size_t n)
 {
-	return (memchr(buf, '\0', n) != NULL);
+	size_t i;
+
+	if (n == 0)
+		return 0;
+
+	for (i = 0; i < n; i++)
+		if (buf[i] == 0)
+			return 1;
+	return 0;
 }
 
 static const struct got_error *
@@ -403,9 +410,6 @@ gw_blob(struct gw_trans *gw_trans)
 {
 	const struct got_error *error = NULL;
 	struct gw_header *header = NULL;
-	char *content = NULL;
-	size_t filesize = 0;
-	enum kcgi_err kerr;
 
 	if (pledge("stdio rpath wpath cpath proc exec sendfd unveil",
 	    NULL) == -1)
@@ -422,26 +426,10 @@ gw_blob(struct gw_trans *gw_trans)
 	if (error)
 		goto done;
 
-	error = gw_get_file_read_blob(&content, &filesize, gw_trans);
-	if (error)
-		goto done;
-
-	if (isbinary(content, filesize))
-		gw_trans->mime = KMIME_APP_OCTET_STREAM;
-	else
-		gw_trans->mime = KMIME_TEXT_PLAIN;
-
-	error = gw_display_index(gw_trans);
-	if (error)
-		goto done;
-
-	kerr = khttp_write(gw_trans->gw_req, content, filesize);
-	if (kerr != KCGI_OK)
-		error = gw_kcgi_error(kerr);
+	error = gw_output_blob_buf(gw_trans);
 done:
 	got_ref_list_free(&header->refs);
 	gw_free_headers(header);
-	free(content);
 	return error;
 }
 
@@ -3112,7 +3100,7 @@ done:
 }
 
 static const struct got_error *
-gw_get_file_read_blob(char **blobstr, size_t *filesize, struct gw_trans *gw_trans)
+gw_output_blob_buf(struct gw_trans *gw_trans)
 {
 	const struct got_error *error = NULL;
 	struct got_repository *repo = NULL;
@@ -3120,13 +3108,11 @@ gw_get_file_read_blob(char **blobstr, size_t *filesize
 	struct got_object_id *commit_id = NULL;
 	struct got_blob_object *blob = NULL;
 	char *path = NULL, *in_repo_path = NULL;
-	int obj_type;
-	size_t n;
-	FILE *f = NULL;
+	int obj_type, set_mime = 0;
+	size_t len, hdrlen;
+	const uint8_t *buf;
+	enum kcgi_err kerr = KCGI_OK;
 
-	*blobstr = NULL;
-	*filesize = 0;
-
 	error = got_repo_open(&repo, gw_trans->repo_path, NULL);
 	if (error)
 		return error;
@@ -3167,31 +3153,36 @@ gw_get_file_read_blob(char **blobstr, size_t *filesize
 	}
 
 	error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
-	if (error)
-		goto done;
-
-	f = got_opentemp();
-	if (f == NULL) {
-		error = got_error_from_errno("got_opentemp");
-		goto done;
-	}
-	error = got_object_blob_dump_to_file(filesize, NULL, NULL, f, blob);
 	if (error)
 		goto done;
 
-	/* XXX This will fail on large files... */
-	*blobstr = calloc(*filesize + 1, sizeof(**blobstr));
-	if (*blobstr == NULL) {
-		error = got_error_from_errno("calloc");
-		goto done;
-	}
+	hdrlen = got_object_blob_get_hdrlen(blob);
+	do {
+		error = got_object_blob_read_block(&len, blob);
+		if (error)
+			goto done;
+		if (len == 0)
+			break;
+		buf = got_object_blob_get_read_buf(blob);
 
-	n = fread(*blobstr, 1, *filesize, f);
-	if (n == 0) {
-		if (ferror(f))
-			error = got_ferror(f, GOT_ERR_IO);
-		goto done;
-	}
+		/*
+		 * Skip blob object header first time around,
+		 * which also contains a zero byte.
+		 */
+		buf += hdrlen;
+		if (set_mime == 0) {
+			if (isbinary(buf, len - hdrlen))
+				gw_trans->mime = KMIME_APP_OCTET_STREAM;
+			else
+				gw_trans->mime = KMIME_TEXT_PLAIN;
+			set_mime = 1;
+			error = gw_display_index(gw_trans);
+			if (error)
+				goto done;
+		}
+		khttp_write(gw_trans->gw_req, buf, len - hdrlen);
+		hdrlen = 0;
+	} while (len != 0);
 done:
 	free(in_repo_path);
 	free(commit_id);
@@ -3201,13 +3192,8 @@ done:
 		got_object_blob_close(blob);
 	if (repo)
 		got_repo_close(repo);
-	if (f != NULL && fclose(f) == -1 && error == NULL)
-		error = got_error_from_errno("fclose");
-	if (error) {
-		free(*blobstr);
-		*blobstr = NULL;
-		*filesize = 0;
-	}
+	if (error == NULL && kerr != KCGI_OK)
+		error = gw_kcgi_error(kerr);
 	return error;
 }