Commit Diff


commit - d5e57bf6e1f3359aa57a5535d382d3509e1515a3
commit + 12670a1d14b664c94b403255368880acf880ccf2
blob - dfeb75234cd47546b69f831bdd53ad9733c02a09
blob + 635b94127c330ea75a825fa89c393fbaa65b18c3
--- regress/artifact_result/artifact_result_test.c
+++ regress/artifact_result/artifact_result_test.c
@@ -21,6 +21,7 @@
 #include "json.h"
 #include "log.h"
 #include "regress.h"
+#include "xmalloc.h"
 
 static const char good_id[] = "0123456789abcdef0123456789abcdef";
 
@@ -412,10 +413,7 @@ test_exact_inline_boundaries(void)
 	u_char *raw;
 	size_t i, max;
 
-	raw = malloc(ARTIFACT_RESULT_MAX + 1);
-	CHECK(raw != NULL);
-	if (raw == NULL)
-		return;
+	raw = xmalloc(ARTIFACT_RESULT_MAX + 1);
 	buf_init(&out);
 	for (i = 0; i < sizeof(shapes) / sizeof(shapes[0]); i++) {
 		max = shapes[i].head_raw_max + shapes[i].tail_raw_max;
@@ -472,10 +470,7 @@ test_multibyte_inline_boundary(void)
 	tail_len = ARTIFACT_PREVIEW_TAIL_ENCODED_MAX / sizeof(snowman) *
 	    sizeof(snowman);
 	max = head_len + tail_len;
-	raw = malloc(max + sizeof(heart));
-	CHECK(raw != NULL);
-	if (raw == NULL)
-		return;
+	raw = xmalloc(max + sizeof(heart));
 	for (i = 0; i < head_len; i += sizeof(euro))
 		memcpy(raw + i, euro, sizeof(euro));
 	for (i = 0; i < tail_len; i += sizeof(snowman))
@@ -521,6 +516,38 @@ test_multibyte_inline_boundary(void)
 	    ARTIFACT_PREVIEW_HEAD_ENCODED_MAX / 4 * 3,
 	    ARTIFACT_PREVIEW_TAIL_ENCODED_MAX / 4 * 3, "base64", 1);
 	streamed_matches_contiguous(raw, in.raw_len, 12289);
+
+	/* Fill the two bytes left by whole three-byte runes with ASCII.  Inserting
+	 * one more ASCII byte is still valid UTF-8 but must cross the exact encoded
+	 * head-plus-tail admission boundary. */
+	head_len = ARTIFACT_PREVIEW_HEAD_ENCODED_MAX;
+	tail_len = ARTIFACT_PREVIEW_TAIL_ENCODED_MAX;
+	max = head_len + tail_len;
+	for (i = 0; i < head_len - 2; i += sizeof(euro))
+		memcpy(raw + i, euro, sizeof(euro));
+	raw[head_len - 2] = 'x';
+	raw[head_len - 1] = 'y';
+	for (i = 0; i < tail_len; i += sizeof(snowman))
+		memcpy(raw + head_len + i, snowman, sizeof(snowman));
+	CHECK(artifact_result_inline_fits(raw, max));
+	in.raw_len = max;
+	in.observed_bytes = max;
+	in.retained_bytes = max;
+	in.id = NULL;
+	CHECK(artifact_result_build(&out, &in) == 0);
+	check_preview_result(&out, raw, max, head_len, tail_len, "utf-8", 0);
+	streamed_matches_contiguous(raw, max, 4093);
+	memmove(raw + head_len + 1, raw + head_len, tail_len);
+	raw[head_len] = 'z';
+	CHECK(!artifact_result_inline_fits(raw, max + 1));
+	in.raw_len = max + 1;
+	in.observed_bytes = in.raw_len;
+	in.retained_bytes = in.raw_len;
+	in.id = good_id;
+	CHECK(artifact_result_build(&out, &in) == 0);
+	check_preview_result(&out, raw, in.raw_len, head_len, tail_len,
+	    "utf-8", 1);
+	streamed_matches_contiguous(raw, in.raw_len, 8191);
 	buf_free(&out);
 	free(raw);
 }
blob - c82aa3744f59ce154cc0ce1ad52633a12258b09c
blob + 604fbce56358f2809e76af29679909716fe1be11
--- regress/artifact_store/artifact_store_test.c
+++ regress/artifact_store/artifact_store_test.c
@@ -61,26 +61,28 @@ exists(const char *root, const char *session, const ch
 }
 
 static int
+artifact_namespace_name(const struct dirent *de)
+{
+	return (strncmp(de->d_name, ".pending-", 9) == 0 ||
+	    strncmp(de->d_name, ".order-", 7) == 0 ||
+	    artifact_id_valid(de->d_name));
+}
+
+static int
 artifact_namespace_empty(const char *root, const char *session)
 {
 	char path[PATH_MAX];
-	struct dirent *de;
-	DIR *dir;
-	int empty = 1;
+	struct dirent **names = NULL;
+	int count, i;
 
 	if ((size_t)snprintf(path, sizeof(path), "%s/%s", root, session) >=
-	    sizeof(path) || (dir = opendir(path)) == NULL)
+	    sizeof(path) || (count = scandirat(AT_FDCWD, path, &names,
+	    artifact_namespace_name, NULL)) == -1)
 		return (0);
-	while ((de = readdir(dir)) != NULL) {
-		if (strncmp(de->d_name, ".pending-", 9) == 0 ||
-		    strncmp(de->d_name, ".order-", 7) == 0 ||
-		    artifact_id_valid(de->d_name)) {
-			empty = 0;
-			break;
-		}
-	}
-	closedir(dir);
-	return (empty);
+	for (i = 0; i < count; i++)
+		free(names[i]);
+	free(names);
+	return (count == 0);
 }
 
 static void
@@ -714,10 +716,14 @@ test_enospc_storage_errors(const char *root)
 	close(write_fd);
 	CHECK(rc == -1 && terminal.status == ARTIFACT_STORE_ERROR &&
 	    terminal.retained_bytes == 0);
-	(void)snprintf(name, sizeof(name), ".pending-%s", stream.id);
+	if ((size_t)snprintf(name, sizeof(name), ".pending-%s", stream.id) >=
+	    sizeof(name))
+		errx(1, "pending name too long");
 	CHECK(exists(root, "enospc-write", name) &&
 	    !exists(root, "enospc-write", stream.id));
-	(void)snprintf(name, sizeof(name), ".order-%s", stream.id);
+	if ((size_t)snprintf(name, sizeof(name), ".order-%s", stream.id) >=
+	    sizeof(name))
+		errx(1, "order name too long");
 	CHECK(exists(root, "enospc-write", name));
 	CHECK(artifact_store_retain(store, 1, stream.id) == -1 &&
 	    artifact_store_turn_prepare(store) == -1);
blob - b316e27616cc32b9578a41eb88eb89cb87e15434
blob + 4aa6cbfc990bfd2484d1c0005273f56e13d04b82
--- regress/turn/run.sh
+++ regress/turn/run.sh
@@ -334,6 +334,7 @@ ok "Session lock: owner reaches an idle line-mode prom
 
 cp "$journal" "$dir/session-journal.before"
 cp "$dir/reqlog" "$dir/session-requests.before"
+LC_ALL=C ls -1A "$sessions" > "$dir/session-namespace.before"
 run_fugu -r "$sid" -p "losing same-Session contender"
 ok "Session lock: contender is refused" \
     $([ "$rc" -ne 0 ]; echo $?)
@@ -343,6 +344,9 @@ kill -0 "$fugupid" 2>/dev/null
 ok "Session lock: original owner remains live" $?
 cmp -s "$dir/session-journal.before" "$journal"
 ok "Session lock: losing contender leaves the Journal byte-identical" $?
+LC_ALL=C ls -1A "$sessions" > "$dir/session-namespace.after"
+cmp -s "$dir/session-namespace.before" "$dir/session-namespace.after"
+ok "Session lock: losing contender leaves the Journal namespace unchanged" $?
 cmp -s "$dir/session-requests.before" "$dir/reqlog" &&
     ! test -s "$dir/reqlog"
 ok "Session lock: losing contender sends zero Provider requests" $?
@@ -738,20 +742,38 @@ stop_stub
 # foreground executor, reports storage_error to the next Generation, and rolls
 # back every private staging/final name instead of journaling an Artifact.
 rm -rf "$HOME/.fugu"
+mkdir -p "$dir/enospc-project"
 start_stub "STORAGEFULL OK"
 write_conf "$stubport"
 t0=$(date +%s)
-FUGU_SELFTEST=1 FUGU_ARTIFACT_ENOSPC_WRITE=1 \
+( cd "$dir/enospc-project" && \
+    FUGU_SELFTEST=1 FUGU_ARTIFACT_ENOSPC_WRITE=1 \
     FUGU_CONF=$dir/fugu.conf FUGU_LIBEXEC=$dir/libexec/fugu \
     FUGU_CA_FILE=$dir/cert.pem FUGU_RETRY_BASE_MS=50 \
     "$fugu" -p "exercise storage full" </dev/null \
-    >"$dir/out" 2>"$dir/errout"
+    >"$dir/out" 2>"$dir/errout" )
 rc=$?
 t1=$(date +%s)
 ok "Artifact ENOSPC: Turn recovers through the next Generation" \
     $([ "$rc" -eq 0 ]; echo $?)
 ok "Artifact ENOSPC: live foreground executor is cancelled promptly" \
     $([ $((t1 - t0)) -lt 15 ]; echo $?)
+enospc_pid=$(cat "$dir/enospc-project/enospc.pid" 2>/dev/null || true)
+case "$enospc_pid" in
+''|*[!0-9]*)	fail "Artifact ENOSPC: fixture records the live child pid" ;;
+*)	pass "Artifact ENOSPC: fixture records the live child pid" ;;
+esac
+i=0
+while [ -n "$enospc_pid" ] && /bin/kill -0 "$enospc_pid" 2>/dev/null &&
+    [ "$i" -lt 100 ]; do
+	sleep 0.02
+	i=$((i + 1))
+done
+if [ -n "$enospc_pid" ] && /bin/kill -0 "$enospc_pid" 2>/dev/null; then
+	fail "Artifact ENOSPC: executor process group has no surviving child"
+else
+	pass "Artifact ENOSPC: executor process group has no surviving child"
+fi
 request_log_entry 2 "$dir/enospc-request"
 grep -Fq '\"termination\":\"storage_error\"' "$dir/enospc-request"
 ok "Artifact ENOSPC: the continuation receives storage_error" $?
blob - 6fbb99b07377c4135999435bef4335f28887d4fc
blob + 45c12b71a2088f40f83ca91b6dd43a40fc6122f5
--- regress/turn/stub.c
+++ regress/turn/stub.c
@@ -1072,8 +1072,9 @@ static const char sse_storagefull[] =
 "event: content_block_delta\n"
 "data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":"
     "{\"type\":\"input_json_delta\",\"partial_json\":"
-    "\"{\\\"command\\\":\\\"printf ENOSPC-WRITE-START; sleep 30; "
-    "printf ENOSPC-WRITE-NOT-CANCELLED\\\"}\"}}\n\n"
+    "\"{\\\"command\\\":\\\"sleep 30 & child=$!; echo $child > "
+    "enospc.pid; printf ENOSPC-WRITE-START; wait $child; printf "
+    "ENOSPC-WRITE-NOT-CANCELLED\\\"}\"}}\n\n"
 "event: content_block_stop\n"
 "data: {\"type\":\"content_block_stop\",\"index\":0}\n\n"
 "event: message_delta\n"