44 std::string message(what);
45 const auto suffix =
": " + std::error_code(errno_value, std::generic_category()).message();
46 if (message.size() >= suffix.size()
47 && message.compare(message.size() - suffix.size(), suffix.size(), suffix) == 0)
48 message.resize(message.size() - suffix.size());
49 constexpr char create_prefix[] =
"scratch create; ";
50 constexpr char refusal_prefix[] =
"scratch requires disk backing; ";
51 if (message.compare(0,
sizeof(create_prefix) - 1, create_prefix) == 0)
52 return message.substr(
sizeof(create_prefix) - 1);
54 if (message.compare(0,
sizeof(refusal_prefix) - 1, refusal_prefix) == 0)
66#if defined(__linux__) || defined(__APPLE__)
70 struct stat directory{};
72 do { status = ::stat(path, &directory); }
while (status != 0 && errno == EINTR);
73 if (status != 0)
return errno;
74 if (!S_ISDIR(directory.st_mode))
return ENOTDIR;
75#if defined(AT_EACCESS)
77 do { status = ::faccessat(AT_FDCWD, path, X_OK, AT_EACCESS); }
78 while (status != 0 && errno == EINTR);
81 do { status = ::access(path, X_OK); }
while (status != 0 && errno == EINTR);
83 if (status != 0)
return errno;
84 struct statfs filesystem{};
85 do { status = ::statfs(path, &filesystem); }
while (status != 0 && errno == EINTR);
86 if (status != 0)
return errno;
87 type =
static_cast<std::uint32_t
>(filesystem.f_type);
111 std::string& refusal,
int& error,
112 bool& proven_memory, Probe probe) {
115 proven_memory =
false;
117 const auto reject = [&](
const std::string& path,
const char*
source) {
118 std::uint64_t type = 0;
119 query_error = probe(path.c_str(), type);
121 if (query_error == ENOTSUP) {
127 if (query_error == 0 && !proven_memory)
return false;
128 if (!refusal.empty()) refusal +=
"; ";
133 if (query_error != 0) {
135 refusal +=
"directory check failed (errno ";
136 refusal += std::to_string(query_error);
138 refusal += std::error_code(query_error, std::generic_category()).message();
143 refusal +=
" requires disk backing, not tmpfs or ramfs";
147 if (!configured.empty()) {
148 if (!reject(configured,
"explicit directory"))
return configured;
151 if (query_error != 0) {
156 refusal.insert(0,
"scratch requires disk backing; ");
159 const char* temporary = std::getenv(
"TMPDIR");
160 const bool has_temporary = temporary && *temporary;
161 const std::string first = has_temporary ? temporary :
"/tmp";
162 if (!reject(first, has_temporary ?
"TMPDIR" :
"legacy /tmp (TMPDIR unset or empty)"))
166 if (query_error != 0) {
167 refusal.insert(0,
"scratch create; ");
170 if (!reject(
"/var/tmp",
"fallback")) {
171 if (query_error != ENOTSUP)
172 refusal +=
"; selected fallback '/var/tmp'; set TMPDIR to a disk-backed directory";
176 if (query_error != 0) {
177 refusal.insert(0,
"scratch create; ");
183 refusal.insert(0,
"scratch requires disk backing; ");
184 refusal +=
"; set TMPDIR to a disk-backed directory";