19#define SDEBUG(...) Ase::debug ("storage", __VA_ARGS__)
20#define return_with_errno(ERRNO, RETVAL) ({ errno = ERRNO; return RETVAL; })
28 static String boot_id = []() {
29 auto id =
string_strip (Path::stringread (
"/proc/sys/kernel/random/boot_id"));
31 id =
string_strip (Path::stringread (
"/etc/machine-id"));
38 if (exename.empty() &&
Path::check (
"/proc/self/exe",
"r"))
41 char exepath[max_size + 1 + 1] = { 0, };
47 else if (exename.empty() &&
Path::check (
"/proc/self/comm",
"r"))
48 exename = Path::stringread (
string_format (
"/proc/%u/comm", pid));
51 if (getpgid (pid) >= 0 || errno != ESRCH)
68anklang_cachedir_base (
bool createbase =
false)
76 int err =
mkdir (basedir.c_str(), 0700);
77 SDEBUG (
"mkdir: %s: %s", basedir, strerror (err ? errno : 0));
93atexit_clean_cachedirs()
96 while (cachedirs_list.
size())
108 String cachedir = anklang_cachedir_base (
true);
109 if (cachedir.
empty())
111 cachedir +=
"/" + tmpdir_prefix() +
"XXXXXX";
112 char *tmpchars = cachedir.
data();
113 char *result =
mkdtemp (tmpchars);
114 SDEBUG (
"mkdtemp: %s: %s", tmpchars,
strerror (result ? 0 :
errno));
117 String guardfile = cachedir +
"/guard.pid";
119 const int guardfd =
open (guardfile.
c_str(), O_EXCL | O_CREAT, 0600);
120 if (guardfd >= 0 && Path::stringwrite (guardfile, guardstring))
125 static bool needatexit =
true;
128 SDEBUG (
"create: %s: %s", guardfile,
strerror (0));
132 const int err =
errno;
144 const String cachedir_base = anklang_cachedir_base (
false);
148 const String guardfile = cachedir +
"/guard.pid";
151 String guardstring = Path::stringread (guardfile, 3 * 4096);
153 if (guardpid > 0 && guardstring == pid_string (guardpid))
163 const String cachedir = anklang_cachedir_base (
false);
164 const String tmpprefix = tmpdir_prefix();
165 if (!cachedir.
empty())
167 if (direntry.is_directory())
172 const String guardfile = direntry.path() /
"guard.pid";
175 String guardstring = Path::stringread (guardfile, 3 * 4096);
176 if (guardstring == pid_string (
getpid()))
178 SDEBUG (
"skipping dir (pid=self): %s", guardfile);
184 SDEBUG (
"skipping dir (live pid=%u): %s", guardpid, guardfile);
200 void *writer =
nullptr;
206 warning (
"Ase::StorageWriter: ZIP file left open: %s", zipname);
213 int mzerr = mz_zip_writer_close (writer);
214 const int saved_errno =
errno;
215 if (mzerr != MZ_OK && !zipname.
empty())
217 mz_zip_writer_delete (&writer);
220 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (
errno);
228 if (!zipname.
empty())
234 open_for_writing (
const String &filename)
238 mz_zip_writer_create (&writer);
239 mz_zip_writer_set_zip_cd (writer,
false);
240 mz_zip_writer_set_password (writer,
nullptr);
241 mz_zip_writer_set_store_links (writer,
false);
242 mz_zip_writer_set_follow_links (writer,
true);
243 mz_zip_writer_set_compress_level (writer, MZ_COMPRESS_LEVEL_BEST);
244 mz_zip_writer_set_compress_method (writer, MZ_COMPRESS_METHOD_DEFLATE);
245 int mzerr = mz_zip_writer_open_file (writer, zipname.
c_str(), 0,
false);
248 const int saved_errno =
errno;
249 mz_zip_writer_delete (&writer);
252 return ase_error_from_errno (saved_errno);
257 store_file_data (
const String &filename,
const String &buffer,
bool compress,
int64_t epoch_seconds)
259 assert_return (mz_zip_writer_is_open (writer) == MZ_OK, Error::INTERNAL);
260 const uint32 attrib = S_IFREG | 0664;
261 const time_t fdate = epoch_seconds;
262 mz_zip_file file_info = {
263 .version_madeby = MZ_VERSION_MADEBY,
264 .flag = MZ_ZIP_FLAG_UTF8,
265 .compression_method =
uint16_t (compress ? MZ_COMPRESS_METHOD_DEFLATE : MZ_COMPRESS_METHOD_STORE),
266 .modified_date = fdate,
267 .accessed_date = fdate,
270 .external_fa = attrib,
271 .filename = filename.
c_str(),
272 .zip64 = buffer.
size() > 4294967295,
275 if (MZ_HOST_SYSTEM (file_info.version_madeby) != MZ_HOST_SYSTEM_MSDOS &&
276 MZ_HOST_SYSTEM (file_info.version_madeby) != MZ_HOST_SYSTEM_WINDOWS_NTFS)
279 mzerr = mz_zip_attrib_convert (MZ_HOST_SYSTEM (file_info.version_madeby), attrib, MZ_HOST_SYSTEM_MSDOS, &target_attrib);
280 file_info.external_fa = target_attrib;
281 file_info.external_fa |= attrib << 16;
284 mzerr = mz_zip_writer_add_buffer (writer, (
void*) buffer.
data(), buffer.
size(), &file_info);
285 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (
errno);
288 store_file (
const String &filename,
const String &ondiskpath,
bool maycompress)
290 assert_return (mz_zip_writer_is_open (writer) == MZ_OK, Error::INTERNAL);
292 maycompress = !is_compressed (Path::stringread (ondiskpath, 1024));
294 mz_zip_writer_set_compress_method (writer, MZ_COMPRESS_METHOD_STORE);
295 int32_t mzerr = mz_zip_writer_add_file (writer, ondiskpath.
c_str(), filename.
c_str());
297 mz_zip_writer_set_compress_method (writer, MZ_COMPRESS_METHOD_DEFLATE);
298 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (
errno);
302StorageWriter::StorageWriter (StorageFlags sflags) :
305 impl_->flags = sflags;
308StorageWriter::~StorageWriter ()
312StorageWriter::open_for_writing (
const String &filename)
315 return impl_->open_for_writing (filename);
319StorageWriter::open_with_mimetype (
const String &filename,
const String &mimetype)
321 Error err = open_for_writing (filename);
322 if (err == Error::NONE)
324 const int64_t ase_project_start = 844503962;
325 err = impl_->store_file_data (
"mimetype", mimetype,
false, ase_project_start);
326 if (err != Error::NONE && impl_)
327 impl_->remove_opened();
333StorageWriter::store_file_data (
const String &filename,
const String &buffer,
bool alwayscompress)
336 const bool compressed = is_compressed (buffer);
337 if (!compressed && (alwayscompress || impl_->flags & AUTO_ZSTD))
339 const String cdata = zstd_compress (buffer);
340 if (alwayscompress || cdata.size() + 128 <= buffer.size())
341 return impl_->store_file_data (filename +
".zst", cdata,
false, time (
nullptr));
343 return impl_->store_file_data (filename, buffer,
344 compressed ?
false : true,
349StorageWriter::store_file (
const String &filename,
const String &ondiskpath,
bool maycompress)
352 return impl_->store_file (filename, ondiskpath, maycompress);
356StorageWriter::close ()
359 return impl_->close();
363StorageWriter::remove_opened ()
366 return impl_->remove_opened();
372 void *reader =
nullptr;
383 int mzerr = mz_zip_reader_close (reader);
384 const int saved_errno =
errno;
385 mz_zip_reader_delete (&reader);
388 return mzerr == MZ_OK ? Error::NONE : ase_error_from_errno (
errno);
391 open_for_reading (
const String &filename)
396 mz_zip_reader_create (&reader);
398 mz_zip_reader_set_password (reader,
nullptr);
399 mz_zip_reader_set_encoding (reader, MZ_ENCODING_UTF8);
401 int err = mz_zip_reader_open_file (reader, zipname.
c_str());
404 const int saved_errno =
errno;
405 mz_zip_reader_delete (&reader);
407 if (saved_errno == ELIBBAD ||
408 (saved_errno ==
ENOENT && Path::check (zipname,
"f")))
409 return Error::BROKEN_ARCHIVE;
410 return ase_error_from_errno (saved_errno);
421 int err = mz_zip_reader_goto_first_entry (reader);
424 mz_zip_file *file_info =
nullptr;
425 if (MZ_OK == mz_zip_reader_entry_get_info (reader, &file_info) && file_info->filename && file_info->filename[0])
427 if (!
strchr (file_info->filename,
'/') &&
428 !
strchr (file_info->filename,
'\\'))
431 err = mz_zip_reader_goto_next_entry (reader);
436 has_file (
const String &filename)
438 return_unless (mz_zip_reader_is_open (reader) == MZ_OK,
false);
439 String fname = Path::normalize (filename);
440 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.
c_str(),
false))
442 if (flags & AUTO_ZSTD)
445 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.
c_str(),
false))
451 stringread (
const String &filename)
455 String fname = Path::normalize (filename);
457 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.c_str(),
false))
459 else if (flags & AUTO_ZSTD)
462 if (MZ_OK == mz_zip_reader_locate_entry (reader, fname.c_str(),
false))
465 return_with_errno (
ENOENT, {});
468 return_with_errno (
ENOENT, {});
469 ssize_t len = mz_zip_reader_entry_save_buffer_length (reader);
473 if (MZ_OK == mz_zip_reader_entry_save_buffer (reader, &buffer[0], buffer.
size()))
476 return uncompress ? zstd_uncompress (buffer) : buffer;
479 return_with_errno (
ENOENT, {});
483StorageReader::StorageReader (StorageFlags sflags) :
486 impl_->flags = sflags;
489StorageReader::~StorageReader ()
493StorageReader::open_for_reading (
const String &filename)
496 return impl_->open_for_reading (filename);
500StorageReader::list_files ()
503 return impl_->list_files();
507StorageReader::close ()
510 return impl_->close();
514StorageReader::has_file (
const String &filename)
517 return impl_->has_file (filename);
521StorageReader::stringread (
const String &filename, ssize_t maxlength)
526 if (maxlength >= 0 && maxlength < int64_t (
data.size()))
527 data.resize (maxlength);
532StorageReader::search_dir (
const String &dirname)
538StreamReader::~StreamReader()
542 FILE *file_ =
nullptr;
555 return file_ !=
nullptr;
558 read (
void *buffer,
size_t len)
override
563 return fread (buffer, 1, len, file_);
569 const int e =
fclose (file_);
574 name()
const override
581stream_reader_from_file (
const String &file)
584 if (readerp->open (file))
590 void *reader_ =
nullptr;
591 bool entry_opened_ =
false;
599 open_zip (
const String &zipname)
603 mz_zip_reader_create (&reader_);
604 mz_zip_reader_set_password (reader_,
nullptr);
605 mz_zip_reader_set_encoding (reader_, MZ_ENCODING_UTF8);
607 int err = mz_zip_reader_open_file (reader_, name_.
c_str());
610 const int saved_errno =
errno;
611 mz_zip_reader_delete (&reader_);
613 return saved_errno == ELIBBAD ? Error::BROKEN_ARCHIVE : ase_error_from_errno (saved_errno);
618 open_entry (
const String &member)
621 assert_return (mz_zip_reader_is_open (reader_) == MZ_OK, Error::INTERNAL);
623 String membername = Path::normalize (member);
624 if (MZ_OK != mz_zip_reader_locate_entry (reader_, membername.
c_str(),
false) ||
625 MZ_OK != mz_zip_reader_entry_open (reader_))
626 return Error::FILE_NOT_FOUND;
627 entry_opened_ =
true;
628 member_ = membername;
632 read (
void *buffer,
size_t len)
override
637 ssize_t n = mz_zip_reader_entry_read (reader_, buffer, len);
640 mz_zip_reader_entry_close (reader_);
641 entry_opened_ =
false;
649 const int mzerr = mz_zip_reader_close (reader_);
650 const int saved_errno =
errno;
651 mz_zip_reader_delete (&reader_);
653 entry_opened_ =
false;
655 return mzerr == MZ_OK;
658 name()
const override
660 return name_ + (member_.
empty() ?
"" :
"/./" + member_);
665stream_reader_zip_member (
const String &archive,
const String &member, Storage::StorageFlags f)
668 if (Error::NONE == readerp->open_zip (archive))
670 Error error = readerp->open_entry (member);
671 if (Error::NONE == error)
673 if (error == Error::FILE_NOT_FOUND && f & Storage::AUTO_ZSTD)
675 const String memberzstd = member +
".zst";
676 if (Error::NONE == readerp->open_entry (memberzstd))
678 StreamReaderP istream = readerp;
679 return stream_reader_zstd (istream);
687StreamWriter::~StreamWriter ()
703 name()
const override
712 fd_ =
open (name_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, mode);
716 write (
const void *buffer,
size_t len)
override
722 const char *p = (
const char*) buffer, *
const e = p + len;
727 l = ::write (fd_, p, e - p);
751stream_writer_create_file (
const String &filename,
int mode)
754 if (fw->create (mode) ==
false)
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
bool check(const String &file, const String &mode)
String cache_home()
Get the $XDG_CACHE_HOME directory, see: https://specifications.freedesktop.org/basedir-spec/latest.
void rmrf(const String &dir)
Recursively delete directory tree.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
String anklang_cachedir_create()
Create exclusive cache directory for this process' runtime.
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
Error
Enum representing Error states.
void anklang_cachedir_clean_stale()
Clean stale cache directories from past runtimes, may be called from any thread.
int64 string_to_int(const String &string, size_t *consumed, uint base)
Parse a string into a 64bit integer, optionally specifying the expected number base.
String program_alias()
Retrieve the program name as used for logging or debug messages.
std::string String
Convenience alias for std::string.
String string_strip(const String &input)
Strip whitespaces from the left and right of a string.
uint32_t uint32
A 32-bit unsigned integer.
void anklang_cachedir_cleanup(const String &cachedir)
Cleanup a cachedir previously created with anklang_cachedir_create().
bool string_startswith(const String &string, const String &fragment)
Returns whether string starts with fragment.
T temp_directory_path(T... args)