path-util: rework find_binary(), fsck_exists() and mkfs_exists()

Modernize the code a bit:

- Get rid of FOREACH_WORD_SEPARATOR() loop in favour of a
  extract_first_word() loop.

- Remove find_binary()'s "local" flag. It's not reasonably possible to
  look for binaries on remote systems, we hence should not pretend we
  could.

- When we cannot find a suitable binary, return the last error returned
  from access() rather than ENOENT unconditionally.

- Rework fsck_exists() and mkfs_exists() to return 1 on success, 0 if
  the implementation is missing and negative on real errors. This is
  more like we do it in other functions.

- Make sure we also detect direct fsck symlinks to "true", rather than
  just absolute ones to /bin/true.
This commit is contained in:
Lennart Poettering
2015-10-24 23:03:49 +02:00
parent 7f66eb931d
commit 85eca92e20
6 changed files with 96 additions and 72 deletions
+60 -38
View File
@@ -698,7 +698,6 @@ int path_is_os_tree(const char *path) {
/* We use /usr/lib/os-release as flag file if something is an OS */
p = strjoina(path, "/usr/lib/os-release");
r = access(p, F_OK);
if (r >= 0)
return 1;
@@ -709,56 +708,67 @@ int path_is_os_tree(const char *path) {
return r >= 0;
}
int find_binary(const char *name, bool local, char **filename) {
int find_binary(const char *name, char **ret) {
int last_error, r;
const char *p;
assert(name);
if (is_path(name)) {
if (local && access(name, X_OK) < 0)
if (access(name, X_OK) < 0)
return -errno;
if (filename) {
char *p;
if (ret) {
char *rs;
p = path_make_absolute_cwd(name);
if (!p)
rs = path_make_absolute_cwd(name);
if (!rs)
return -ENOMEM;
*filename = p;
*ret = rs;
}
return 0;
} else {
const char *path;
const char *word, *state;
size_t l;
}
/**
* Plain getenv, not secure_getenv, because we want
* to actually allow the user to pick the binary.
*/
path = getenv("PATH");
if (!path)
path = DEFAULT_PATH;
/**
* Plain getenv, not secure_getenv, because we want
* to actually allow the user to pick the binary.
*/
p = getenv("PATH");
if (!p)
p = DEFAULT_PATH;
FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
_cleanup_free_ char *p = NULL;
last_error = -ENOENT;
if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
return -ENOMEM;
for (;;) {
_cleanup_free_ char *j = NULL, *element = NULL;
if (access(p, X_OK) < 0)
continue;
r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
break;
if (filename) {
*filename = path_kill_slashes(p);
p = NULL;
j = strjoin(element, "/", name, NULL);
if (!j)
return -ENOMEM;
if (access(j, X_OK) >= 0) {
/* Found it! */
if (ret) {
*ret = path_kill_slashes(j);
j = NULL;
}
return 0;
}
return -ENOENT;
last_error = -errno;
}
return last_error;
}
bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
@@ -800,7 +810,9 @@ static int binary_is_good(const char *binary) {
_cleanup_free_ char *p = NULL, *d = NULL;
int r;
r = find_binary(binary, true, &p);
r = find_binary(binary, &p);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
@@ -808,28 +820,38 @@ static int binary_is_good(const char *binary) {
* fsck */
r = readlink_malloc(p, &d);
if (r >= 0 &&
(path_equal(d, "/bin/true") ||
path_equal(d, "/usr/bin/true") ||
path_equal(d, "/dev/null")))
return -ENOENT;
if (r == -EINVAL) /* not a symlink */
return 1;
if (r < 0)
return r;
return 0;
return !path_equal(d, "true") &&
!path_equal(d, "/bin/true") &&
!path_equal(d, "/usr/bin/true") &&
!path_equal(d, "/dev/null");
}
int fsck_exists(const char *fstype) {
const char *checker;
checker = strjoina("fsck.", fstype);
assert(fstype);
if (streq(fstype, "auto"))
return -EINVAL;
checker = strjoina("fsck.", fstype);
return binary_is_good(checker);
}
int mkfs_exists(const char *fstype) {
const char *mkfs;
mkfs = strjoina("mkfs.", fstype);
assert(fstype);
if (streq(fstype, "auto"))
return -EINVAL;
mkfs = strjoina("mkfs.", fstype);
return binary_is_good(mkfs);
}