Handle foreign-owned compile artifacts
This commit is contained in:
+127
-8
@@ -386,6 +386,97 @@ static bool compiler_publish_staged_artifacts(SharedUnit* su, String staged_pre_
|
||||
file_unlink(it->previous);
|
||||
}
|
||||
};
|
||||
auto copy_previous = [&](const Artifact& artifact, int link_error) {
|
||||
struct stat path_info;
|
||||
if(lstat(artifact.canonical.c_str(), &path_info) != 0 || !S_ISREG(path_info.st_mode))
|
||||
{
|
||||
error = "refusing non-regular bounded compile artifact " + artifact.canonical;
|
||||
return(false);
|
||||
}
|
||||
int input = open(artifact.canonical.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
|
||||
if(input < 0)
|
||||
{
|
||||
error = "could not read previous bounded compile artifact " + artifact.canonical +
|
||||
" after hard-link failure " + String(std::strerror(link_error)) + ": " + std::strerror(errno);
|
||||
return(false);
|
||||
}
|
||||
struct stat source_info;
|
||||
if(fstat(input, &source_info) != 0)
|
||||
{
|
||||
error = "could not inspect previous bounded compile artifact " + artifact.canonical + ": " + std::strerror(errno);
|
||||
close(input);
|
||||
return(false);
|
||||
}
|
||||
if(source_info.st_dev != path_info.st_dev || source_info.st_ino != path_info.st_ino || !S_ISREG(source_info.st_mode))
|
||||
{
|
||||
error = "bounded compile artifact changed while preserving " + artifact.canonical;
|
||||
close(input);
|
||||
return(false);
|
||||
}
|
||||
int output = open(artifact.previous.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, source_info.st_mode & 07777);
|
||||
if(output < 0)
|
||||
{
|
||||
error = "could not create bounded compile rollback copy " + artifact.previous + ": " + std::strerror(errno);
|
||||
close(input);
|
||||
return(false);
|
||||
}
|
||||
bool copied = true;
|
||||
char buffer[65536];
|
||||
while(copied)
|
||||
{
|
||||
if(deadline && deadline->expire_if_needed())
|
||||
{
|
||||
error = "bounded compile deadline expired while copying previous artifact " + artifact.canonical;
|
||||
copied = false;
|
||||
break;
|
||||
}
|
||||
ssize_t got = read(input, buffer, sizeof(buffer));
|
||||
if(got == 0)
|
||||
break;
|
||||
if(got < 0)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
error = "could not read previous bounded compile artifact " + artifact.canonical + ": " + std::strerror(errno);
|
||||
copied = false;
|
||||
break;
|
||||
}
|
||||
ssize_t written = 0;
|
||||
while(written < got)
|
||||
{
|
||||
if(deadline && deadline->expire_if_needed())
|
||||
{
|
||||
error = "bounded compile deadline expired while copying previous artifact " + artifact.canonical;
|
||||
copied = false;
|
||||
break;
|
||||
}
|
||||
ssize_t amount = write(output, buffer + written, got - written);
|
||||
if(amount < 0 && errno == EINTR)
|
||||
continue;
|
||||
if(amount <= 0)
|
||||
{
|
||||
error = "could not copy previous bounded compile artifact " + artifact.canonical + ": " + std::strerror(errno);
|
||||
copied = false;
|
||||
break;
|
||||
}
|
||||
written += amount;
|
||||
}
|
||||
}
|
||||
if(copied && fchmod(output, source_info.st_mode & 07777) != 0)
|
||||
{
|
||||
error = "could not preserve mode for bounded compile artifact " + artifact.canonical + ": " + std::strerror(errno);
|
||||
copied = false;
|
||||
}
|
||||
if(close(output) != 0 && copied)
|
||||
{
|
||||
error = "could not close bounded compile rollback copy " + artifact.previous + ": " + std::strerror(errno);
|
||||
copied = false;
|
||||
}
|
||||
close(input);
|
||||
if(!copied)
|
||||
file_unlink(artifact.previous);
|
||||
return(copied);
|
||||
};
|
||||
for(auto& artifact : artifacts)
|
||||
{
|
||||
if(deadline && deadline->expire_if_needed())
|
||||
@@ -399,10 +490,20 @@ static bool compiler_publish_staged_artifacts(SharedUnit* su, String staged_pre_
|
||||
artifact.existed = file_exists(artifact.canonical);
|
||||
if(artifact.existed && link(artifact.canonical.c_str(), artifact.previous.c_str()) != 0)
|
||||
{
|
||||
error = "could not preserve previous bounded compile artifacts: " + String(std::strerror(errno));
|
||||
for(auto& cleanup : artifacts)
|
||||
file_unlink(cleanup.previous);
|
||||
return(false);
|
||||
int link_error = errno;
|
||||
if(link_error != EPERM && link_error != EACCES && link_error != EMLINK && link_error != EXDEV)
|
||||
{
|
||||
error = "could not preserve previous bounded compile artifact " + artifact.canonical + ": " + std::strerror(link_error);
|
||||
for(auto& cleanup : artifacts)
|
||||
file_unlink(cleanup.previous);
|
||||
return(false);
|
||||
}
|
||||
if(!copy_previous(artifact, link_error))
|
||||
{
|
||||
for(auto& cleanup : artifacts)
|
||||
file_unlink(cleanup.previous);
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto& artifact : artifacts)
|
||||
@@ -417,7 +518,7 @@ static bool compiler_publish_staged_artifacts(SharedUnit* su, String staged_pre_
|
||||
file_unlink(artifact.canonical);
|
||||
else if(rename(artifact.staged.c_str(), artifact.canonical.c_str()) != 0)
|
||||
{
|
||||
error = "could not publish bounded compile artifacts: " + String(std::strerror(errno));
|
||||
error = "could not publish bounded compile artifact " + artifact.canonical + ": " + std::strerror(errno);
|
||||
rollback();
|
||||
return(false);
|
||||
}
|
||||
@@ -547,6 +648,8 @@ int compiler_open_lock_file(String file_name, String purpose, bool nonblocking =
|
||||
if(lock_dir != "")
|
||||
mkdir(lock_dir);
|
||||
int fdlock = open(file_name.c_str(), O_RDWR | O_CREAT, 0666);
|
||||
if(fdlock == -1 && (errno == EACCES || errno == EPERM))
|
||||
fdlock = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if(fdlock == -1)
|
||||
{
|
||||
printf("(!) Could not open lock file %s\n", file_name.c_str());
|
||||
@@ -575,6 +678,8 @@ int compiler_open_lock_file_bounded(String file_name, String purpose, CompilerDe
|
||||
if(lock_dir != "")
|
||||
mkdir(lock_dir);
|
||||
int fdlock = open(file_name.c_str(), O_RDWR | O_CREAT, 0666);
|
||||
if(fdlock == -1 && (errno == EACCES || errno == EPERM))
|
||||
fdlock = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if(fdlock == -1)
|
||||
return(-1);
|
||||
fcntl(fdlock, F_SETFD, FD_CLOEXEC);
|
||||
@@ -604,6 +709,16 @@ void compiler_close_lock_file(int fdlock)
|
||||
close(fdlock);
|
||||
}
|
||||
|
||||
static void compiler_publish_source_generation(String file_name)
|
||||
{
|
||||
String staged_file_name = file_name + ".stage-" + std::to_string((u64)getpid());
|
||||
file_unlink(staged_file_name);
|
||||
if(file_put_contents(staged_file_name, std::to_string(getpid()) + ":" + std::to_string((u64)(time_precise() * 1000000.0)) + "\n") &&
|
||||
rename(staged_file_name.c_str(), file_name.c_str()) != 0)
|
||||
printf("(!) Could not publish %s: %s\n", file_name.c_str(), std::strerror(errno));
|
||||
file_unlink(staged_file_name);
|
||||
}
|
||||
|
||||
static void compiler_mark_source_generation_nonblocking(Request* context)
|
||||
{
|
||||
if(!context || !context->server)
|
||||
@@ -612,7 +727,7 @@ static void compiler_mark_source_generation_nonblocking(Request* context)
|
||||
int fdlock = compiler_open_lock_file(file_name + ".lock", "source-generation", true);
|
||||
if(fdlock < 0)
|
||||
return;
|
||||
file_put_contents(file_name, std::to_string(getpid()) + ":" + std::to_string((u64)(time_precise() * 1000000.0)) + "\n");
|
||||
compiler_publish_source_generation(file_name);
|
||||
compiler_close_lock_file(fdlock);
|
||||
}
|
||||
|
||||
@@ -1520,7 +1635,7 @@ void compiler_mark_source_generation(Request* context)
|
||||
int fdlock = compiler_open_lock_file(file_name + ".lock", "source-generation");
|
||||
if(fdlock < 0)
|
||||
return;
|
||||
file_put_contents(file_name, std::to_string(getpid()) + ":" + std::to_string((u64)(time_precise() * 1000000.0)) + "\n");
|
||||
compiler_publish_source_generation(file_name);
|
||||
compiler_close_lock_file(fdlock);
|
||||
}
|
||||
|
||||
@@ -1983,10 +2098,12 @@ bool unit_compile(String path)
|
||||
return(su && trim(su->compiler_messages) == "" && file_exists(su->wasm_name));
|
||||
}
|
||||
|
||||
bool unit_compile_bounded(Request* request, String path, u64 timeout_ms, bool* timed_out)
|
||||
bool unit_compile_bounded(Request* request, String path, u64 timeout_ms, bool* timed_out, String* error)
|
||||
{
|
||||
if(timed_out)
|
||||
*timed_out = false;
|
||||
if(error)
|
||||
error->clear();
|
||||
if(!request || timeout_ms == 0)
|
||||
{
|
||||
if(timed_out)
|
||||
@@ -2006,5 +2123,7 @@ bool unit_compile_bounded(Request* request, String path, u64 timeout_ms, bool* t
|
||||
auto su = compiler_get_shared_unit_internal(request, resolved_path, true, false, &deadline);
|
||||
if(timed_out)
|
||||
*timed_out = deadline.timed_out;
|
||||
if(error)
|
||||
*error = first(deadline.operational_error, su ? trim(su->compiler_messages) : "");
|
||||
return(su && trim(su->compiler_messages) == "" && file_exists(su->wasm_name));
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ SharedUnit* get_shared_unit(Request* context, String file_name);
|
||||
#ifndef __UCE_WASM_UNIT__
|
||||
SharedUnit* get_shared_unit_for_preprocess(Request* context, String file_name);
|
||||
SharedUnit* get_shared_unit_bounded(Request* context, String file_name, u64 timeout_ms, bool* timed_out);
|
||||
bool unit_compile_bounded(Request* context, String path, u64 timeout_ms, bool* timed_out);
|
||||
bool unit_compile_bounded(Request* context, String path, u64 timeout_ms, bool* timed_out, String* error = 0);
|
||||
#endif
|
||||
String compiler_error_page_unit(Request* context, String config_key);
|
||||
bool compiler_unit_compile_pending(Request* context, String file_name);
|
||||
|
||||
+7
-1
@@ -3801,9 +3801,15 @@ private:
|
||||
{
|
||||
u64 remaining_ms = self->invocation_remaining_ms();
|
||||
bool timed_out = false;
|
||||
bool ok = remaining_ms > 0 && unit_compile_bounded(context, request["path"].to_string(), remaining_ms, &timed_out);
|
||||
String compile_error;
|
||||
bool ok = remaining_ms > 0 && unit_compile_bounded(context, request["path"].to_string(), remaining_ms, &timed_out, &compile_error);
|
||||
if(timed_out || remaining_ms == 0)
|
||||
return(Trap(self->invocation_timeout_error()));
|
||||
if(!ok && compile_error != "")
|
||||
{
|
||||
response["error"] = compile_error;
|
||||
printf("(!) unit_compile failed for %s: %s\n", request["path"].to_string().c_str(), compile_error.c_str());
|
||||
}
|
||||
response["ok"].set_bool(ok);
|
||||
}
|
||||
else if(op == "call")
|
||||
|
||||
Reference in New Issue
Block a user