blob: 5917243db28aef5ed99b87cd567fb57509371131 [file]
#define _GNU_SOURCE
#include <dlfcn.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int path_for_stream(FILE* stream, char* out, size_t out_size) {
char proc_path[64];
int fd = fileno(stream);
ssize_t len;
if (fd < 0) {
return 0;
}
snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd);
len = readlink(proc_path, out, out_size - 1);
if (len < 0 || (size_t)len >= out_size) {
return 0;
}
out[len] = '\0';
return 1;
}
int fclose(FILE* stream) {
static int (*real_fclose)(FILE*) = NULL;
static int swap_done = 0;
const char* output_path = getenv("BROTLI_SWAP_OUTPUT_ABS");
const char* target_path = getenv("BROTLI_SWAP_TARGET_ABS");
char actual_path[PATH_MAX];
int matched = 0;
int rc;
if (real_fclose == NULL) {
real_fclose = (int (*)(FILE*))dlsym(RTLD_NEXT, "fclose");
if (real_fclose == NULL) {
fprintf(stderr, "fclose_swap: failed to resolve libc fclose\n");
_exit(127);
}
}
if (!swap_done && stream != NULL && output_path != NULL && target_path != NULL &&
path_for_stream(stream, actual_path, sizeof(actual_path))) {
matched = strcmp(actual_path, output_path) == 0;
}
rc = real_fclose(stream);
if (rc != 0 || !matched || swap_done) {
return rc;
}
if (unlink(output_path) != 0) {
perror("fclose_swap: unlink");
return rc;
}
if (symlink(target_path, output_path) != 0) {
perror("fclose_swap: symlink");
return rc;
}
swap_done = 1;
return rc;
}