commit: b9eca954b9f0518916f390039b43eff46f0ee67e Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> AuthorDate: Sat Oct 12 12:43:19 2019 +0000 Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> CommitDate: Sat Oct 12 12:43:19 2019 +0000 URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=b9eca954
qfile: fix matching of full paths, bug #697094 - properly handle trailing slashes, this is necessary because we use a custom basename that doesn't strip it - fix bug in creating realpath of dirname, resulting in one path component short - cleanup some match conditions Bug: https://bugs.gentoo.org/697094 Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> qfile.c | 72 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/qfile.c b/qfile.c index 63b47da..8fa6eb4 100644 --- a/qfile.c +++ b/qfile.c @@ -171,6 +171,7 @@ static int qfile_cb(tree_pkg_ctx *pkg_ctx, void *priv) short *non_orphans = args->non_orphans; int *results = args->results; int found = 0; + size_t len; /* If exclude_pkg is not NULL, check it. We are looking for files * collisions, and must exclude one package. */ @@ -223,23 +224,22 @@ static int qfile_cb(tree_pkg_ctx *pkg_ctx, void *priv) if (non_orphans != NULL && non_orphans[i]) continue; - /* For optimization of qfile(), we also give it an array of - * the first char of each basename. This way we avoid - * numerous strcmp() calls. */ + /* Try to avoid numerous strcmp() calls. */ if (base[0] != base_names[i][0] || strcmp(base, base_names[i]) != 0) continue; path_ok = false; - if (dir_names[i] && - strncmp(e->name, dir_names[i], dirname_len) == 0 && - dir_names[i][dirname_len] == '\0') + if (dir_names[i] && (len = strlen(dir_names[i])) > 0 && + len == dirname_len && + memcmp(e->name, dir_names[i], len) == 0) { /* dir_name == dirname(CONTENTS) */ path_ok = true; } else if (real_dir_names[i] && - strncmp(e->name, real_dir_names[i], dirname_len) == 0 && - real_dir_names[i][dirname_len] == '\0') + (len = strlen(real_dir_names[i])) > 0 && + len == dirname_len && + memcmp(e->name, real_dir_names[i], len) == 0) { /* real_dir_name == dirname(CONTENTS) */ path_ok = true; @@ -276,19 +276,19 @@ static int qfile_cb(tree_pkg_ctx *pkg_ctx, void *priv) } } - if (!path_ok && state->basename) { + if (!path_ok && state->basename) path_ok = true; - } if (!path_ok && state->pwd && dir_names[i] == NULL) { /* try to match file in current directory */ - if (strncmp(e->name, state->pwd, dirname_len) == 0 && - state->pwd[dirname_len] == '\0') + if ((len = strlen(state->pwd)) > 0 && + len == dirname_len && + memcmp(e->name, state->pwd, len) == 0) path_ok = true; } if (!path_ok && dir_names[i] == NULL && real_dir_names[i] == NULL) { - /* try basename match */ + /* basename match */ if (e->type != CONTENTS_DIR) path_ok = true; } @@ -357,7 +357,7 @@ prepare_qfile_args(const int argc, const char **argv, struct qfile_opt_state *st char **realdirnames = NULL; int *results = NULL; char tmppath[_Q_PATH_MAX]; - char abspath[_Q_PATH_MAX]; + char abspath[_Q_PATH_MAX * 2]; /* For each argument, we store its basename, its absolute dirname, * and the realpath of its dirname. Dirnames and their realpaths @@ -370,11 +370,15 @@ prepare_qfile_args(const int argc, const char **argv, struct qfile_opt_state *st results = xcalloc(argc, sizeof(int)); for (i = 0; i < argc; ++i) { - /* Record basename, but if it is ".", ".." or "/" */ - /* copy so that "argv" can be "const" */ - snprintf(tmppath, sizeof(tmppath), "%s", argv[i]); + /* copy so that "argv" can be "const", but skip trailing / + * because our basename doesn't modify its input */ + len = strlen(argv[i]); + if (len > 1 && argv[i][len - 1] == '/') + len--; + snprintf(tmppath, sizeof(tmppath), "%.*s", (int)len, argv[i]); p = basename(tmppath); - len = strlen(p); + + /* record basename, but if it is ".", ".." or "/" */ if ((len > 2) || (strncmp(tmppath, "..", len) != 0 && strncmp(tmppath, "/", len) != 0)) @@ -388,15 +392,15 @@ prepare_qfile_args(const int argc, const char **argv, struct qfile_opt_state *st /* Make sure we have an absolute path available (with * "realpath(ROOT)" prefix) */ - if (argv[i][0] == '/') { + if (tmppath[0] == '/') { snprintf(abspath, sizeof(abspath), "%s%s", - state->assume_root_prefix ? "" : real_root, argv[i]); + state->assume_root_prefix ? "" : real_root, tmppath); } else if (pwd) { if (state->assume_root_prefix) - snprintf(abspath, sizeof(abspath), "%s/%s", pwd, argv[i]); + snprintf(abspath, sizeof(abspath), "%s/%s", pwd, tmppath); else snprintf(abspath, sizeof(abspath), "%s%s/%s", - real_root, pwd, argv[i]); + real_root, pwd, tmppath); } else { warn("$PWD was not found in environment, " "or is not an absolute path"); @@ -424,11 +428,8 @@ prepare_qfile_args(const int argc, const char **argv, struct qfile_opt_state *st tmppath, abspath); goto skip_query_item; } - snprintf(tmppath, sizeof(tmppath), "%s%s", - dirname(abspath), - abspath[real_root_len] == '\0' ? "/" : ""); - if (strcmp(dirnames[i], tmppath + real_root_len)) - realdirnames[i] = xstrdup(tmppath + real_root_len); + if (strcmp(dirnames[i], abspath + real_root_len)) + realdirnames[i] = xstrdup(abspath + real_root_len); } else { /* No basename means we are looking for something like "/foo/bar/.." * Dirname is meaningless here, we can only get realpath of the full @@ -442,8 +443,7 @@ prepare_qfile_args(const int argc, const char **argv, struct qfile_opt_state *st abspath, tmppath); goto skip_query_item; } - snprintf(abspath, sizeof(abspath), "%s", tmppath); - basenames[i] = xstrdup(basename(abspath)); + basenames[i] = xstrdup(basename(tmppath)); snprintf(abspath, sizeof(abspath), "%s%s", dirname(tmppath), tmppath[real_root_len] == '\0' ? "/" : ""); @@ -451,13 +451,13 @@ prepare_qfile_args(const int argc, const char **argv, struct qfile_opt_state *st } continue; - skip_query_item: - --nb_of_queries; - warn("Skipping query item \"%s\".", argv[i]); - free(basenames[i]); - free(dirnames[i]); - free(realdirnames[i]); - basenames[i] = dirnames[i] = realdirnames[i] = NULL; + skip_query_item: + --nb_of_queries; + warn("Skipping query item \"%s\".", argv[i]); + free(basenames[i]); + free(dirnames[i]); + free(realdirnames[i]); + basenames[i] = dirnames[i] = realdirnames[i] = NULL; } args->basenames = basenames;