util-lib: rework /tmp and /var/tmp handling code
Beef up the existing var_tmp() call, rename it to var_tmp_dir() and add a matching tmp_dir() call (the former looks for the place for /var/tmp, the latter for /tmp). Both calls check $TMPDIR, $TEMP, $TMP, following the algorithm Python3 uses. All dirs are validated before use. secure_getenv() is used in order to limite exposure in suid binaries. This also ports a couple of users over to these new APIs. The var_tmp() return parameter is changed from an allocated buffer the caller will own to a const string either pointing into environ[], or into a static const buffer. Given that environ[] is mostly considered constant (and this is exposed in the very well-known getenv() call), this should be OK behaviour and allows us to avoid memory allocations in most cases. Note that $TMPDIR and friends override both /var/tmp and /tmp usage if set.
This commit is contained in:
+13
-4
@@ -1161,8 +1161,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
|
||||
char *t, *x;
|
||||
uint64_t u;
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
/* Turns this:
|
||||
@@ -1171,6 +1171,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
|
||||
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
|
||||
*/
|
||||
|
||||
if (!p) {
|
||||
r = tmp_dir(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!extra)
|
||||
extra = "";
|
||||
|
||||
@@ -1257,10 +1263,13 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
|
||||
|
||||
int open_tmpfile_unlinkable(const char *directory, int flags) {
|
||||
char *p;
|
||||
int fd;
|
||||
int fd, r;
|
||||
|
||||
if (!directory)
|
||||
directory = "/tmp";
|
||||
if (!directory) {
|
||||
r = tmp_dir(&directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
|
||||
|
||||
|
||||
+79
-19
@@ -496,34 +496,94 @@ int get_files_in_directory(const char *path, char ***list) {
|
||||
return n;
|
||||
}
|
||||
|
||||
int var_tmp(char **ret) {
|
||||
const char *tmp_dir = NULL;
|
||||
const char *env_tmp_dir = NULL;
|
||||
char *c = NULL;
|
||||
int r;
|
||||
static int getenv_tmp_dir(const char **ret_path) {
|
||||
const char *n;
|
||||
int r, ret = 0;
|
||||
|
||||
assert(ret);
|
||||
assert(ret_path);
|
||||
|
||||
env_tmp_dir = getenv("TMPDIR");
|
||||
if (env_tmp_dir != NULL) {
|
||||
r = is_dir(env_tmp_dir, true);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
if (r > 0)
|
||||
tmp_dir = env_tmp_dir;
|
||||
/* We use the same order of environment variables python uses in tempfile.gettempdir():
|
||||
* https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
|
||||
FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
|
||||
const char *e;
|
||||
|
||||
e = secure_getenv(n);
|
||||
if (!e)
|
||||
continue;
|
||||
if (!path_is_absolute(e)) {
|
||||
r = -ENOTDIR;
|
||||
goto next;
|
||||
}
|
||||
if (!path_is_safe(e)) {
|
||||
r = -EPERM;
|
||||
goto next;
|
||||
}
|
||||
|
||||
r = is_dir(e, true);
|
||||
if (r < 0)
|
||||
goto next;
|
||||
if (r == 0) {
|
||||
r = -ENOTDIR;
|
||||
goto next;
|
||||
}
|
||||
|
||||
*ret_path = e;
|
||||
return 1;
|
||||
|
||||
next:
|
||||
/* Remember first error, to make this more debuggable */
|
||||
if (ret >= 0)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
if (!tmp_dir)
|
||||
tmp_dir = "/var/tmp";
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
c = strdup(tmp_dir);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
*ret = c;
|
||||
*ret_path = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tmp_dir_internal(const char *def, const char **ret) {
|
||||
const char *e;
|
||||
int r, k;
|
||||
|
||||
assert(def);
|
||||
assert(ret);
|
||||
|
||||
r = getenv_tmp_dir(&e);
|
||||
if (r > 0) {
|
||||
*ret = e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
k = is_dir(def, true);
|
||||
if (k == 0)
|
||||
k = -ENOTDIR;
|
||||
if (k < 0)
|
||||
return r < 0 ? r : k;
|
||||
|
||||
*ret = def;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int var_tmp_dir(const char **ret) {
|
||||
|
||||
/* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
|
||||
* even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
|
||||
* returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
|
||||
* making it a variable that overrides all temporary file storage locations. */
|
||||
|
||||
return tmp_dir_internal("/var/tmp", ret);
|
||||
}
|
||||
|
||||
int tmp_dir(const char **ret) {
|
||||
|
||||
/* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
|
||||
* backed by an in-memory file system: /tmp. */
|
||||
|
||||
return tmp_dir_internal("/tmp", ret);
|
||||
}
|
||||
|
||||
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
|
||||
char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
|
||||
int r;
|
||||
|
||||
+2
-1
@@ -61,7 +61,8 @@ int mkfifo_atomic(const char *path, mode_t mode);
|
||||
|
||||
int get_files_in_directory(const char *path, char ***list);
|
||||
|
||||
int var_tmp(char **ret);
|
||||
int tmp_dir(const char **ret);
|
||||
int var_tmp_dir(const char **ret);
|
||||
|
||||
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user