1
0
mirror of https://github.com/systemd/systemd synced 2025-10-03 02:34:45 +02:00

Compare commits

...

6 Commits

Author SHA1 Message Date
Luca Boccassi
294f1b40ab
Merge pull request #18834 from poettering/path-extract
Beef up path_extract_filename() a bit, and introduce path_extract_directory()
2021-03-02 19:22:53 +00:00
Lennart Poettering
312dff1760 util: add helpers for generating colored check mark glyphs from bools 2021-03-03 03:08:31 +09:00
Lennart Poettering
62a88d7a3e tmpfile: port tempfn_*() to path_extract_*() 2021-03-02 15:07:52 +01:00
Lennart Poettering
7fc607637f machinectl: make sure of path_extract_filename() returning O_DIRECTORY 2021-03-02 15:07:48 +01:00
Lennart Poettering
ee277c6bc7 path-util: return O_DIRECTORY from path_extract_filename() when path ends in slash
Let's fine-tune the path_extract_filename() interface: on succes return
O_DIRECTORY as indicator that the input path was slash-suffixed, and
regular 0 otherwise. This is useful since in many cases it is useful to
filter out paths that must refer to dirs early on.

I opted for O_DIRECTORY instead of the following other ideas:

1. return -EISDIR: I think the function should return an extracted
   filename even when referring to an obvious dir, so this is not an
   option.

2. S_ISDIR, this was a strong contender, but I think O_DIRECTORY is a
   tiny bit nicer since quite likely we will go on and open the thing,
   maybe with openat(), and hence it's quite nice to be able to OR in
   the return value into the flags argument of openat().

3. A new enum defined with two values "dont-know" and
   "definitely-directory". But I figured this was unnecessary, given we
   have other options too, that reuse existing definitions for very
   similar purposes.
2021-03-02 15:07:44 +01:00
Lennart Poettering
8dcb891c19 path-util: add path_extract_directory(), to match path_extract_filename()
These two together are a lot like dirname() + basename() but have the
benefit that they return clear errors when one passes a special case
path to them where the extraction doesn't make sense, i.e. "", "/",
"foo", "foo/" and so on.

Sooner or later we should probably port all our uses of
dirname()/basename() over to this, to catch these special cases more
safely.
2021-03-02 15:07:11 +01:00
10 changed files with 292 additions and 49 deletions

View File

@ -90,3 +90,7 @@ void locale_variables_free(char* l[_VARIABLE_LC_MAX]);
static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) { static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) {
locale_variables_free(*l); locale_variables_free(*l);
} }
static inline const char *special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}

View File

@ -819,11 +819,21 @@ const char *last_path_component(const char *path) {
int path_extract_filename(const char *p, char **ret) { int path_extract_filename(const char *p, char **ret) {
_cleanup_free_ char *a = NULL; _cleanup_free_ char *a = NULL;
const char *c; const char *c;
size_t n;
/* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. Returns * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
* -EADDRNOTAVAIL if specified parameter includes no filename (i.e. is "/" or so). Returns -EINVAL if * slashes. Returns:
* not a valid path in the first place. */ *
* -EINVAL if the passed in path is not a valid path
* -EADDRNOTAVAIL if only a directory was specified, but no filename, i.e. the root dir itself is specified
* -ENOMEM no memory
*
* Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to indicate
* the referenced file must be a directory.
*
* This function guarantees to return a fully valid filename, i.e. one that passes
* filename_is_valid() this means "." and ".." are not accepted. */
if (!path_is_valid(p)) if (!path_is_valid(p))
return -EINVAL; return -EINVAL;
@ -834,14 +844,57 @@ int path_extract_filename(const char *p, char **ret) {
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
c = last_path_component(p); c = last_path_component(p);
n = strcspn(c, "/");
a = strndup(c, strcspn(c, "/")); a = strndup(c, n);
if (!a) if (!a)
return -ENOMEM; return -ENOMEM;
if (!filename_is_valid(a)) if (!filename_is_valid(a))
return -EINVAL; return -EINVAL;
*ret = TAKE_PTR(a);
return c[n] == '/' ? O_DIRECTORY : 0;
}
int path_extract_directory(const char *p, char **ret) {
_cleanup_free_ char *a = NULL;
const char *c;
/* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
*
* -EINVAL if the passed in path is not a valid path
* -EDESTADDRREQ if no directory was specified in the passed in path, i.e. only a filename was passed
* -EADDRNOTAVAIL if the passed in parameter had no filename but did have a directory, i.e. the root dir itself was specified
* -ENOMEM no memory (surprise!)
*
* This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
*/
if (!path_is_valid(p))
return -EINVAL;
/* Special case the root dir, because otherwise for an input of "///" last_path_component() returns
* the pointer to the last slash only, which might be seen as a valid path below. */
if (path_equal(p, "/"))
return -EADDRNOTAVAIL;
c = last_path_component(p);
/* Delete trailing slashes, but keep one */
while (c > p+1 && c[-1] == '/')
c--;
if (p == c) /* No path whatsoever? Then return a recognizable error */
return -EDESTADDRREQ;
a = strndup(p, c - p);
if (!a)
return -ENOMEM;
if (!path_is_valid(a))
return -EINVAL;
*ret = TAKE_PTR(a); *ret = TAKE_PTR(a);
return 0; return 0;
} }

View File

@ -147,6 +147,7 @@ int fsck_exists(const char *fstype);
char* dirname_malloc(const char *path); char* dirname_malloc(const char *path);
const char *last_path_component(const char *path); const char *last_path_component(const char *path);
int path_extract_filename(const char *p, char **ret); int path_extract_filename(const char *p, char **ret);
int path_extract_directory(const char *p, char **ret);
bool filename_is_valid(const char *p) _pure_; bool filename_is_valid(const char *p) _pure_;
bool path_is_valid(const char *p) _pure_; bool path_is_valid(const char *p) _pure_;

View File

@ -253,5 +253,9 @@ int vt_release(int fd, bool restore_vt);
void get_log_colors(int priority, const char **on, const char **off, const char **highlight); void get_log_colors(int priority, const char **on, const char **off, const char **highlight);
static inline const char* ansi_highlight_green_red(bool b) {
return b ? ansi_highlight_green() : ansi_highlight_red();
}
/* This assumes there is a 'tty' group */ /* This assumes there is a 'tty' group */
#define TTY_MODE 0620 #define TTY_MODE 0620

View File

@ -94,16 +94,11 @@ int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
} }
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
const char *fn; _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL;
char *t; int r;
assert(ret); assert(ret);
if (isempty(p))
return -EINVAL;
if (path_equal(p, "/"))
return -EINVAL;
/* /*
* Turns this: * Turns this:
* /foo/bar/waldo * /foo/bar/waldo
@ -112,34 +107,41 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
* /foo/bar/.#<extra>waldoXXXXXX * /foo/bar/.#<extra>waldoXXXXXX
*/ */
fn = basename(p); r = path_extract_directory(p, &d);
if (!filename_is_valid(fn)) if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
return -EINVAL; return r;
extra = strempty(extra); r = path_extract_filename(p, &fn);
if (r < 0)
return r;
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); nf = strjoin(".#", strempty(extra), fn, "XXXXXX");
if (!t) if (!nf)
return -ENOMEM; return -ENOMEM;
strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); if (!filename_is_valid(nf)) /* New name is not valid? (Maybe because too long?) Refuse. */
return -EINVAL;
if (d) {
char *j;
j = path_join(d, nf);
if (!j)
return -ENOMEM;
*ret = path_simplify(j, false);
} else
*ret = TAKE_PTR(nf);
*ret = path_simplify(t, false);
return 0; return 0;
} }
int tempfn_random(const char *p, const char *extra, char **ret) { int tempfn_random(const char *p, const char *extra, char **ret) {
const char *fn; _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL;
char *t, *x; int r;
uint64_t u;
assert(ret); assert(ret);
if (isempty(p))
return -EINVAL;
if (path_equal(p, "/"))
return -EINVAL;
/* /*
* Turns this: * Turns this:
* /foo/bar/waldo * /foo/bar/waldo
@ -148,27 +150,34 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
* /foo/bar/.#<extra>waldobaa2a261115984a9 * /foo/bar/.#<extra>waldobaa2a261115984a9
*/ */
fn = basename(p); r = path_extract_directory(p, &d);
if (!filename_is_valid(fn)) if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
return -EINVAL; return r;
extra = strempty(extra); r = path_extract_filename(p, &fn);
if (r < 0)
return r;
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); if (asprintf(&nf, ".#%s%s%016" PRIx64,
if (!t) strempty(extra),
fn,
random_u64()) < 0)
return -ENOMEM; return -ENOMEM;
x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn); if (!filename_is_valid(nf)) /* Not valid? (maybe because too long now?) — refuse early */
return -EINVAL;
u = random_u64(); if (d) {
for (unsigned i = 0; i < 16; i++) { char *j;
*(x++) = hexchar(u & 0xF);
u >>= 4;
}
*x = 0; j = path_join(d, nf);
if (!j)
return -ENOMEM;
*ret = path_simplify(j, false);
} else
*ret = TAKE_PTR(nf);
*ret = path_simplify(t, false);
return 0; return 0;
} }

View File

@ -1153,8 +1153,8 @@ static void read_loader_efi_var(const char *name, char **var) {
static void print_yes_no_line(bool first, bool good, const char *name) { static void print_yes_no_line(bool first, bool good, const char *name) {
printf("%s%s%s%s %s\n", printf("%s%s%s%s %s\n",
first ? " Features: " : " ", first ? " Features: " : " ",
good ? ansi_highlight_green() : ansi_highlight_red(), ansi_highlight_green_red(good),
good ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK), special_glyph_check_mark(good),
ansi_normal(), ansi_normal(),
name); name);
} }

View File

@ -1872,6 +1872,9 @@ static int import_tar(int argc, char *argv[], void *userdata) {
r = path_extract_filename(path, &fn); r = path_extract_filename(path, &fn);
if (r < 0) if (r < 0)
return log_error_errno(r, "Cannot extract container name from filename: %m"); return log_error_errno(r, "Cannot extract container name from filename: %m");
if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
"Path '%s' refers to directory, but we need a regular file: %m", path);
local = fn; local = fn;
} }
@ -1932,6 +1935,9 @@ static int import_raw(int argc, char *argv[], void *userdata) {
r = path_extract_filename(path, &fn); r = path_extract_filename(path, &fn);
if (r < 0) if (r < 0)
return log_error_errno(r, "Cannot extract container name from filename: %m"); return log_error_errno(r, "Cannot extract container name from filename: %m");
if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
"Path '%s' refers to directory, but we need a regular file: %m", path);
local = fn; local = fn;
} }

View File

@ -379,6 +379,8 @@ tests += [
[['src/test/test-clock.c']], [['src/test/test-clock.c']],
[['src/test/test-tmpfile-util.c']],
[['src/test/test-architecture.c']], [['src/test/test-architecture.c']],
[['src/test/test-log.c']], [['src/test/test-log.c']],

View File

@ -570,7 +570,10 @@ static void test_path_extract_filename_one(const char *input, const char *output
int r; int r;
r = path_extract_filename(input, &k); r = path_extract_filename(input, &k);
log_info("%s → %s/%s [expected: %s/%s]", strnull(input), strnull(k), strerror_safe(r), strnull(output), strerror_safe(ret)); log_info_errno(r, "%s → %s/%m [expected: %s/%s]",
strnull(input),
strnull(k), /* strerror(r) is printed via %m, to avoid that the two strerror()'s overwrite each other's buffers */
strnull(output), ret < 0 ? strerror_safe(ret) : "-");
assert_se(streq_ptr(k, output)); assert_se(streq_ptr(k, output));
assert_se(r == ret); assert_se(r == ret);
} }
@ -580,7 +583,7 @@ static void test_path_extract_filename(void) {
test_path_extract_filename_one(NULL, NULL, -EINVAL); test_path_extract_filename_one(NULL, NULL, -EINVAL);
test_path_extract_filename_one("a/b/c", "c", 0); test_path_extract_filename_one("a/b/c", "c", 0);
test_path_extract_filename_one("a/b/c/", "c", 0); test_path_extract_filename_one("a/b/c/", "c", O_DIRECTORY);
test_path_extract_filename_one("/", NULL, -EADDRNOTAVAIL); test_path_extract_filename_one("/", NULL, -EADDRNOTAVAIL);
test_path_extract_filename_one("//", NULL, -EADDRNOTAVAIL); test_path_extract_filename_one("//", NULL, -EADDRNOTAVAIL);
test_path_extract_filename_one("///", NULL, -EADDRNOTAVAIL); test_path_extract_filename_one("///", NULL, -EADDRNOTAVAIL);
@ -589,13 +592,13 @@ static void test_path_extract_filename(void) {
test_path_extract_filename_one("././", NULL, -EINVAL); test_path_extract_filename_one("././", NULL, -EINVAL);
test_path_extract_filename_one("././/", NULL, -EINVAL); test_path_extract_filename_one("././/", NULL, -EINVAL);
test_path_extract_filename_one("/foo/a", "a", 0); test_path_extract_filename_one("/foo/a", "a", 0);
test_path_extract_filename_one("/foo/a/", "a", 0); test_path_extract_filename_one("/foo/a/", "a", O_DIRECTORY);
test_path_extract_filename_one("", NULL, -EINVAL); test_path_extract_filename_one("", NULL, -EINVAL);
test_path_extract_filename_one("a", "a", 0); test_path_extract_filename_one("a", "a", 0);
test_path_extract_filename_one("a/", "a", 0); test_path_extract_filename_one("a/", "a", O_DIRECTORY);
test_path_extract_filename_one("/a", "a", 0); test_path_extract_filename_one("/a", "a", 0);
test_path_extract_filename_one("/a/", "a", 0); test_path_extract_filename_one("/a/", "a", O_DIRECTORY);
test_path_extract_filename_one("/////////////a/////////////", "a", 0); test_path_extract_filename_one("/////////////a/////////////", "a", O_DIRECTORY);
test_path_extract_filename_one("xx/.", NULL, -EINVAL); test_path_extract_filename_one("xx/.", NULL, -EINVAL);
test_path_extract_filename_one("xx/..", NULL, -EINVAL); test_path_extract_filename_one("xx/..", NULL, -EINVAL);
test_path_extract_filename_one("..", NULL, -EINVAL); test_path_extract_filename_one("..", NULL, -EINVAL);
@ -606,6 +609,64 @@ static void test_path_extract_filename(void) {
test_path_extract_filename_one("./", NULL, -EINVAL); test_path_extract_filename_one("./", NULL, -EINVAL);
} }
static void test_path_extract_directory_one(const char *input, const char *output, int ret) {
_cleanup_free_ char *k = NULL;
int r;
r = path_extract_directory(input, &k);
log_info_errno(r, "%s → %s/%m [expected: %s/%s]",
strnull(input),
strnull(k), /* we output strerror_safe(r) via %m here, since otherwise the error buffer might be overwritten twice */
strnull(output), strerror_safe(ret));
assert_se(streq_ptr(k, output));
assert_se(r == ret);
/* Extra safety check: let's make sure that if we split out the filename too (and it works) the
* joined parts are identical to the original again */
if (r >= 0) {
_cleanup_free_ char *f = NULL;
r = path_extract_filename(input, &f);
if (r >= 0) {
_cleanup_free_ char *j = NULL;
assert_se(j = path_join(k, f));
assert_se(path_equal(input, j));
}
}
}
static void test_path_extract_directory(void) {
log_info("/* %s */", __func__);
test_path_extract_directory_one(NULL, NULL, -EINVAL);
test_path_extract_directory_one("a/b/c", "a/b", 0);
test_path_extract_directory_one("a/b/c/", "a/b", 0);
test_path_extract_directory_one("/", NULL, -EADDRNOTAVAIL);
test_path_extract_directory_one("//", NULL, -EADDRNOTAVAIL);
test_path_extract_directory_one("///", NULL, -EADDRNOTAVAIL);
test_path_extract_directory_one(".", NULL, -EDESTADDRREQ);
test_path_extract_directory_one("./.", ".", 0);
test_path_extract_directory_one("././", ".", 0);
test_path_extract_directory_one("././/", ".", 0);
test_path_extract_directory_one("/foo/a", "/foo", 0);
test_path_extract_directory_one("/foo/a/", "/foo", 0);
test_path_extract_directory_one("", NULL, -EINVAL);
test_path_extract_directory_one("a", NULL, -EDESTADDRREQ);
test_path_extract_directory_one("a/", NULL, -EDESTADDRREQ);
test_path_extract_directory_one("/a", "/", 0);
test_path_extract_directory_one("/a/", "/", 0);
test_path_extract_directory_one("/////////////a/////////////", "/", 0);
test_path_extract_directory_one("xx/.", "xx", 0);
test_path_extract_directory_one("xx/..", "xx", 0);
test_path_extract_directory_one("..", NULL, -EDESTADDRREQ);
test_path_extract_directory_one("/..", "/", 0);
test_path_extract_directory_one("../", NULL, -EDESTADDRREQ);
test_path_extract_directory_one(".", NULL, -EDESTADDRREQ);
test_path_extract_directory_one("/.", "/", 0);
test_path_extract_directory_one("./", NULL, -EDESTADDRREQ);
}
static void test_filename_is_valid(void) { static void test_filename_is_valid(void) {
char foo[NAME_MAX+2]; char foo[NAME_MAX+2];
@ -793,6 +854,7 @@ int main(int argc, char **argv) {
test_file_in_same_dir(); test_file_in_same_dir();
test_last_path_component(); test_last_path_component();
test_path_extract_filename(); test_path_extract_filename();
test_path_extract_directory();
test_filename_is_valid(); test_filename_is_valid();
test_path_is_valid(); test_path_is_valid();
test_hidden_or_backup_file(); test_hidden_or_backup_file();

View File

@ -0,0 +1,102 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "errno-util.h"
#include "log.h"
#include "string-util.h"
#include "tests.h"
#include "tmpfile-util.h"
static void test_tempfn_random_one(const char *p, const char *extra, const char *expect, int ret) {
_cleanup_free_ char *s = NULL;
int r;
r = tempfn_random(p, extra, &s);
log_info_errno(r, "%s+%s → %s vs. %s (%i/%m vs. %i/%s)", p, strna(extra), strna(s), strna(expect), r, ret, strerror_safe(ret));
assert(!s == !expect);
if (s) {
const char *suffix;
assert_se(suffix = startswith(s, expect));
assert_se(in_charset(suffix, HEXDIGITS));
assert_se(strlen(suffix) == 16);
}
assert(ret == r);
}
static void test_tempfn_random(void) {
test_tempfn_random_one("", NULL, NULL, -EINVAL);
test_tempfn_random_one(".", NULL, NULL, -EINVAL);
test_tempfn_random_one("..", NULL, NULL, -EINVAL);
test_tempfn_random_one("/", NULL, NULL, -EADDRNOTAVAIL);
test_tempfn_random_one("foo", NULL, ".#foo", 0);
test_tempfn_random_one("foo", "bar", ".#barfoo", 0);
test_tempfn_random_one("/tmp/foo", NULL, "/tmp/.#foo", 0);
test_tempfn_random_one("/tmp/foo", "bar", "/tmp/.#barfoo", 0);
test_tempfn_random_one("./foo", NULL, "./.#foo", 0);
test_tempfn_random_one("./foo", "bar", "./.#barfoo", 0);
test_tempfn_random_one("../foo", NULL, "../.#foo", 0);
test_tempfn_random_one("../foo", "bar", "../.#barfoo", 0);
test_tempfn_random_one("foo/", NULL, ".#foo", 0);
test_tempfn_random_one("foo/", "bar", ".#barfoo", 0);
test_tempfn_random_one("/tmp/foo/", NULL, "/tmp/.#foo", 0);
test_tempfn_random_one("/tmp/foo/", "bar", "/tmp/.#barfoo", 0);
test_tempfn_random_one("./foo/", NULL, "./.#foo", 0);
test_tempfn_random_one("./foo/", "bar", "./.#barfoo", 0);
test_tempfn_random_one("../foo/", NULL, "../.#foo", 0);
test_tempfn_random_one("../foo/", "bar", "../.#barfoo", 0);
}
static void test_tempfn_xxxxxx_one(const char *p, const char *extra, const char *expect, int ret) {
_cleanup_free_ char *s = NULL;
int r;
r = tempfn_xxxxxx(p, extra, &s);
log_info_errno(r, "%s+%s → %s vs. %s (%i/%m vs. %i/%s)", p, strna(extra), strna(s), strna(expect), r, ret, strerror_safe(ret));
assert(!s == !expect);
if (s) {
const char *suffix;
assert_se(suffix = startswith(s, expect));
assert_se(streq(suffix, "XXXXXX"));
}
assert(ret == r);
}
static void test_tempfn_xxxxxx(void) {
test_tempfn_xxxxxx_one("", NULL, NULL, -EINVAL);
test_tempfn_xxxxxx_one(".", NULL, NULL, -EINVAL);
test_tempfn_xxxxxx_one("..", NULL, NULL, -EINVAL);
test_tempfn_xxxxxx_one("/", NULL, NULL, -EADDRNOTAVAIL);
test_tempfn_xxxxxx_one("foo", NULL, ".#foo", 0);
test_tempfn_xxxxxx_one("foo", "bar", ".#barfoo", 0);
test_tempfn_xxxxxx_one("/tmp/foo", NULL, "/tmp/.#foo", 0);
test_tempfn_xxxxxx_one("/tmp/foo", "bar", "/tmp/.#barfoo", 0);
test_tempfn_xxxxxx_one("./foo", NULL, "./.#foo", 0);
test_tempfn_xxxxxx_one("./foo", "bar", "./.#barfoo", 0);
test_tempfn_xxxxxx_one("../foo", NULL, "../.#foo", 0);
test_tempfn_xxxxxx_one("../foo", "bar", "../.#barfoo", 0);
test_tempfn_xxxxxx_one("foo/", NULL, ".#foo", 0);
test_tempfn_xxxxxx_one("foo/", "bar", ".#barfoo", 0);
test_tempfn_xxxxxx_one("/tmp/foo/", NULL, "/tmp/.#foo", 0);
test_tempfn_xxxxxx_one("/tmp/foo/", "bar", "/tmp/.#barfoo", 0);
test_tempfn_xxxxxx_one("./foo/", NULL, "./.#foo", 0);
test_tempfn_xxxxxx_one("./foo/", "bar", "./.#barfoo", 0);
test_tempfn_xxxxxx_one("../foo/", NULL, "../.#foo", 0);
test_tempfn_xxxxxx_one("../foo/", "bar", "../.#barfoo", 0);
}
int main(int argc, char **argv) {
test_setup_logging(LOG_DEBUG);
test_tempfn_random();
test_tempfn_xxxxxx();
return 0;
}