ww: preserve compiler sink outputs
This commit is contained in:
@@ -13,6 +13,7 @@ struct publication {
|
||||
char *backup;
|
||||
int had_old;
|
||||
int installed;
|
||||
int passthrough;
|
||||
};
|
||||
|
||||
static char *
|
||||
@@ -30,9 +31,37 @@ publish_path(const char *dst, const char *kind)
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
copy_stream(FILE *src, FILE *out)
|
||||
{
|
||||
int bad = fflush(src) != 0 || fseek(src, 0, SEEK_SET) != 0;
|
||||
unsigned char buf[65536];
|
||||
while (!bad) {
|
||||
size_t n = fread(buf, 1, sizeof buf, src);
|
||||
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
|
||||
if (n < sizeof buf) {
|
||||
if (ferror(src)) bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bad ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
materialize_stream(FILE *src, struct publication *p)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(p->dst, &st) == 0 && S_ISCHR(st.st_mode)) {
|
||||
FILE *out = fopen(p->dst, "wb");
|
||||
int bad = out == NULL || copy_stream(src, out) < 0;
|
||||
if (out != NULL && fclose(out) != 0) bad = 1;
|
||||
if (bad) {
|
||||
fprintf(stderr, "w6c: cannot write %s\n", p->dst);
|
||||
return -1;
|
||||
}
|
||||
p->passthrough = 1;
|
||||
return 0;
|
||||
}
|
||||
p->stage = publish_path(p->dst, "new");
|
||||
p->backup = publish_path(p->dst, "old");
|
||||
if (p->stage == NULL || p->backup == NULL) {
|
||||
@@ -50,16 +79,7 @@ materialize_stream(FILE *src, struct publication *p)
|
||||
unlink(p->stage);
|
||||
return -1;
|
||||
}
|
||||
int bad = fflush(src) != 0 || fseek(src, 0, SEEK_SET) != 0;
|
||||
unsigned char buf[65536];
|
||||
while (!bad) {
|
||||
size_t n = fread(buf, 1, sizeof buf, src);
|
||||
if (n != 0 && fwrite(buf, 1, n, out) != n) bad = 1;
|
||||
if (n < sizeof buf) {
|
||||
if (ferror(src)) bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int bad = copy_stream(src, out) < 0;
|
||||
if (fclose(out) != 0) bad = 1;
|
||||
if (bad) {
|
||||
unlink(p->stage);
|
||||
@@ -100,6 +120,7 @@ static int
|
||||
publish_all(struct publication *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (p[i].passthrough) continue;
|
||||
if (!path_is_regular_nofollow(p[i].stage)) {
|
||||
fprintf(stderr, "w6c: publication stage is not a regular file for %s\n",
|
||||
p[i].dst);
|
||||
@@ -120,6 +141,7 @@ publish_all(struct publication *p, int n)
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (p[i].passthrough) continue;
|
||||
int exists = path_exists_nofollow(p[i].backup);
|
||||
if (exists != 0) {
|
||||
if (exists < 0)
|
||||
@@ -132,6 +154,7 @@ publish_all(struct publication *p, int n)
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (p[i].passthrough) continue;
|
||||
if (rename(p[i].dst, p[i].backup) == 0)
|
||||
p[i].had_old = 1;
|
||||
else if (errno != ENOENT) {
|
||||
@@ -140,6 +163,7 @@ publish_all(struct publication *p, int n)
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (p[i].passthrough) continue;
|
||||
if (rename(p[i].stage, p[i].dst) != 0) {
|
||||
fprintf(stderr, "w6c: cannot publish %s\n", p[i].dst);
|
||||
goto rollback;
|
||||
@@ -149,13 +173,15 @@ publish_all(struct publication *p, int n)
|
||||
/* Installation is the commit point. Backup cleanup cannot truthfully
|
||||
* turn a completely installed pair into a rejected compilation. */
|
||||
for (int i = 0; i < n; i++)
|
||||
if (p[i].had_old && unlink(p[i].backup) != 0) {
|
||||
if (!p[i].passthrough && p[i].had_old
|
||||
&& unlink(p[i].backup) != 0) {
|
||||
fprintf(stderr, "w6c: cannot remove backup for %s\n", p[i].dst);
|
||||
}
|
||||
return 0;
|
||||
|
||||
rollback:
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
if (p[i].passthrough) continue;
|
||||
if (p[i].installed) (void)unlink(p[i].dst);
|
||||
if (p[i].had_old) (void)rename(p[i].backup, p[i].dst);
|
||||
if (p[i].stage != NULL) (void)unlink(p[i].stage);
|
||||
|
||||
Reference in New Issue
Block a user