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)