Merge pull request #18834 from poettering/path-extract
Beef up path_extract_filename() a bit, and introduce path_extract_directory()
This commit is contained in:
+57
-4
@@ -819,11 +819,21 @@ const char *last_path_component(const char *path) {
|
||||
int path_extract_filename(const char *p, char **ret) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
const char *c;
|
||||
size_t n;
|
||||
|
||||
/* 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
|
||||
* -EADDRNOTAVAIL if specified parameter includes no filename (i.e. is "/" or so). Returns -EINVAL if
|
||||
* not a valid path in the first place. */
|
||||
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
|
||||
* slashes. Returns:
|
||||
*
|
||||
* -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))
|
||||
return -EINVAL;
|
||||
@@ -834,14 +844,57 @@ int path_extract_filename(const char *p, char **ret) {
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
c = last_path_component(p);
|
||||
n = strcspn(c, "/");
|
||||
|
||||
a = strndup(c, strcspn(c, "/"));
|
||||
a = strndup(c, n);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!filename_is_valid(a))
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ int fsck_exists(const char *fstype);
|
||||
char* dirname_malloc(const char *path);
|
||||
const char *last_path_component(const char *path);
|
||||
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 path_is_valid(const char *p) _pure_;
|
||||
|
||||
+46
-37
@@ -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) {
|
||||
const char *fn;
|
||||
char *t;
|
||||
_cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (isempty(p))
|
||||
return -EINVAL;
|
||||
if (path_equal(p, "/"))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Turns this:
|
||||
* /foo/bar/waldo
|
||||
@@ -112,34 +107,41 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
|
||||
* /foo/bar/.#<extra>waldoXXXXXX
|
||||
*/
|
||||
|
||||
fn = basename(p);
|
||||
if (!filename_is_valid(fn))
|
||||
return -EINVAL;
|
||||
r = path_extract_directory(p, &d);
|
||||
if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
|
||||
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);
|
||||
if (!t)
|
||||
nf = strjoin(".#", strempty(extra), fn, "XXXXXX");
|
||||
if (!nf)
|
||||
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;
|
||||
}
|
||||
|
||||
int tempfn_random(const char *p, const char *extra, char **ret) {
|
||||
const char *fn;
|
||||
char *t, *x;
|
||||
uint64_t u;
|
||||
_cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (isempty(p))
|
||||
return -EINVAL;
|
||||
if (path_equal(p, "/"))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Turns this:
|
||||
* /foo/bar/waldo
|
||||
@@ -148,27 +150,34 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
|
||||
* /foo/bar/.#<extra>waldobaa2a261115984a9
|
||||
*/
|
||||
|
||||
fn = basename(p);
|
||||
if (!filename_is_valid(fn))
|
||||
return -EINVAL;
|
||||
r = path_extract_directory(p, &d);
|
||||
if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
|
||||
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 (!t)
|
||||
if (asprintf(&nf, ".#%s%s%016" PRIx64,
|
||||
strempty(extra),
|
||||
fn,
|
||||
random_u64()) < 0)
|
||||
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();
|
||||
for (unsigned i = 0; i < 16; i++) {
|
||||
*(x++) = hexchar(u & 0xF);
|
||||
u >>= 4;
|
||||
}
|
||||
if (d) {
|
||||
char *j;
|
||||
|
||||
*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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user