/* Copyright 2013 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Persistence module for emulator */ /* This provides storage that can be opened, closed and reopened by the * current process at will, whose naming even remains stable across multiple * invocations of the same executable, while providing a unique name for * each executable (as determined by path) that uses these routines. * * Useful when semi-permanent storage is required even with many * similar processes running in parallel (e.g. in a highly parallel * test suite run. * * mkstemp and friends don't provide these properties which is why we have * this homegrown implementation of something similar-yet-different. */ #include "builtin/assert.h" #include "util.h" #include #include #include #include /* The longest path in a chroot seems to be about 280 characters (as of * April 2021) so define a cut-off instead of just hoping for the best: * If we were to run into a path that is nearly PATH_MAX bytes long, * file names could end up being reused inadvertedly because the various * snprintf calls would cut off the trailing characters, so the "tag" (and * maybe more) is gone even though it only exists for differentiation. * * Instead bail out if we encounter a path (to an executable using these * routines) that is longer than we expect. * * Round up for some spare room because why not? */ static const int max_len = 300; /* This must be at least the size of the prefix added in get_storage_path */ static const int max_prefix_len = 25; static void get_storage_path(char *out) { char buf[PATH_MAX]; int sz; char *current; sz = readlink("/proc/self/exe", buf, PATH_MAX - 1); buf[sz] = '\0'; ASSERT(sz <= max_len); /* replace / by underscores in the path to get the shared memory name */ current = strchr(buf, '/'); while (current) { *current = '_'; current = strchr(current, '/'); } sz = snprintf(out, PATH_MAX - 1, "/dev/shm/EC_persist_%.*s", max_len, buf); ASSERT(sz > 0); out[PATH_MAX - 1] = '\0'; ASSERT(sz <= max_len + max_prefix_len); } FILE *get_persistent_storage(const char *tag, const char *mode) { char buf[PATH_MAX]; char path[PATH_MAX]; int sz; /* There's no longer tag in use right now, and there shouldn't be. */ ASSERT(strlen(tag) < 32); /* * The persistent storage with tag 'foo' for test 'bar' would * be named 'bar_persist_foo' */ get_storage_path(buf); sz = snprintf(path, PATH_MAX - 1, "%.*s_%32s", max_len + max_prefix_len, buf, tag); ASSERT(sz > 0); path[PATH_MAX - 1] = '\0'; return fopen(path, mode); } void release_persistent_storage(FILE *ps) { fclose(ps); } void remove_persistent_storage(const char *tag) { char buf[PATH_MAX]; char path[PATH_MAX]; int sz; /* There's no longer tag in use right now, and there shouldn't be. */ ASSERT(strlen(tag) < 32); get_storage_path(buf); sz = snprintf(path, PATH_MAX - 1, "%.*s_%32s", max_len + max_prefix_len, buf, tag); ASSERT(sz > 0); path[PATH_MAX - 1] = '\0'; unlink(path); }