commit - d5e57bf6e1f3359aa57a5535d382d3509e1515a3
commit + 12670a1d14b664c94b403255368880acf880ccf2
blob - dfeb75234cd47546b69f831bdd53ad9733c02a09
blob + 635b94127c330ea75a825fa89c393fbaa65b18c3
--- regress/artifact_result/artifact_result_test.c
+++ regress/artifact_result/artifact_result_test.c
#include "json.h"
#include "log.h"
#include "regress.h"
+#include "xmalloc.h"
static const char good_id[] = "0123456789abcdef0123456789abcdef";
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;
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))
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
}
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
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
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 $?)
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" $?
# 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
"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"