[pacman-dev] [PATCH v3] Allow querying directory ownership
The restriction of not checking the ownership of a directory is unnecessary given that all the package filelists contain this information. Remove this restriction, with the expectation that you might get multiple packages returned for a given directory. Additionally attempt to minimise the number of files getting through to the slow realpath call. This combines ideas from two patches that have been around for a long time. Original-work-by: Andrew Gregory <andrew.gregory.8@gmail.com> Original-work-by: Dan McGee <dan@archlinux.org> Signed-off-by: Allan McRae <allan@archlinux.org> --- v3 - Now with less segfaults on the rather silly "pacman -Qo /"... src/pacman/query.c | 70 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/src/pacman/query.c b/src/pacman/query.c index 07509fa..67dd11c 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -113,6 +113,7 @@ static int query_fileowner(alpm_list_t *targets) size_t rootlen; alpm_list_t *t; alpm_db_t *db_local; + alpm_list_t *packages; /* This code is here for safety only */ if(targets == NULL) { @@ -133,13 +134,14 @@ static int query_fileowner(alpm_list_t *targets) strcpy(path, root); db_local = alpm_get_localdb(config->handle); + packages = alpm_db_get_pkgcache(db_local); for(t = targets; t; t = alpm_list_next(t)) { char *filename, *dname, *rpath; - const char *bname; + const char *bname, *rpath_bname = NULL; struct stat buf; alpm_list_t *i; - int found = 0; + size_t found = 0, isdir, bname_len, rpath_len = 0, rpath_bname_len = 0; filename = strdup(t->data); @@ -162,16 +164,19 @@ static int query_fileowner(alpm_list_t *targets) } } - if(S_ISDIR(buf.st_mode)) { - pm_printf(ALPM_LOG_ERROR, - _("cannot determine ownership of directory '%s'\n"), filename); - ret++; - free(filename); - continue; + /* make sure directories have a trailing '/' */ + if((isdir = S_ISDIR(buf.st_mode))) { + size_t len = strlen(filename); + if(filename[len-1] != '/') { + filename = realloc(filename, sizeof(char) * (len + 2)); + strcat(filename, "/"); + } } bname = mbasename(filename); + bname_len = strlen(bname); dname = mdirname(filename); + /* for files in '/', there is no directory name to match */ if(strcmp(dname, "") == 0) { rpath = NULL; @@ -181,16 +186,26 @@ static int query_fileowner(alpm_list_t *targets) if(!rpath) { pm_printf(ALPM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"), filename, strerror(errno)); - free(filename); free(dname); - free(rpath); ret++; - continue; + goto cleanup; } } free(dname); - for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) { + if(isdir) { + if(!rpath) { + pm_printf(ALPM_LOG_ERROR, _("cannot determine ownership of root directory\n")); + ret++; + goto cleanup; + } else { + rpath_len = strlen(rpath); + rpath_bname = mbasename(rpath); + rpath_bname_len = strlen(rpath_bname); + } + } + + for(i = packages; i && (!found || isdir); i = alpm_list_next(i)) { alpm_pkg_t *info = i->data; alpm_filelist_t *filelist = alpm_pkg_get_files(info); size_t j; @@ -199,17 +214,40 @@ static int query_fileowner(alpm_list_t *targets) const alpm_file_t *file = filelist->files + j; char *ppath, *pdname; const char *pkgfile = file->name; + size_t pkgfile_len = strlen(pkgfile); - /* avoid the costly resolve_path usage if the basenames don't match */ - if(strcmp(mbasename(pkgfile), bname) != 0) { + /* avoid the costly resolve_path usage if the basenames don't match; + * we can also cheat by comparing the final characters first and avoid + * a full string comparison */ + if(!isdir && (pkgfile[pkgfile_len - 1] != bname[bname_len - 1] || + strcmp(mbasename(pkgfile), bname) != 0)) { continue; } + if(isdir) { + /* check the filelist entry is a directory using its + * trailing '/' and compare final characters of directory + * names. */ + if(pkgfile[pkgfile_len - 1] != '/' || + pkgfile[pkgfile_len - 2] != rpath[rpath_len - 1]) { + continue; + } + + /* compare final directory portion */ + if(pkgfile_len - 1 < rpath_bname_len || + (pkgfile_len - 2 >= rpath_bname_len && + pkgfile[pkgfile_len - 2 - rpath_bname_len] != '/') || + strncmp(pkgfile + pkgfile_len - 1 - rpath_bname_len, + rpath_bname, rpath_bname_len)) { + continue; + } + } + /* for files in '/', there is no directory name to match */ if(!rpath) { print_query_fileowner(filename, info); found = 1; - continue; + break; } if(rootlen + 1 + strlen(pkgfile) > PATH_MAX) { @@ -233,6 +271,8 @@ static int query_fileowner(alpm_list_t *targets) pm_printf(ALPM_LOG_ERROR, _("No package owns %s\n"), filename); ret++; } + +cleanup: free(filename); free(rpath); } -- 1.7.10.2
From: Andrew Gregory <andrew.gregory.8@gmail.com> --- I did this as a patch against your working branch since it requires changes outside the scope of your original patch. This adds a check that all files/directories are within root before we bother looking for an owner which removes the need for a special check just for '/'. I didn't test this extensively, but it handled `./pacman -Qo / firefox /usr/bin/firefox /tmp /tmp/` correctly. src/pacman/query.c | 51 +++++++++++++++++++++++++++------------------------ src/pacman/util.c | 4 ++++ 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/pacman/query.c b/src/pacman/query.c index 67dd11c..d99d5df 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -109,7 +109,7 @@ static int query_fileowner(alpm_list_t *targets) { int ret = 0; char path[PATH_MAX]; - const char *root; + char *root; size_t rootlen; alpm_list_t *t; alpm_db_t *db_local; @@ -124,9 +124,15 @@ static int query_fileowner(alpm_list_t *targets) /* Set up our root path buffer. We only need to copy the location of root in * once, then we can just overwrite whatever file was there on the previous * iteration. */ - root = alpm_option_get_root(config->handle); + /* I don't know if we can assume that root doesn't contain symlinks, + * if not we should go ahead and resolve it */ + root = strdup(resolve_path(alpm_option_get_root(config->handle))); rootlen = strlen(root); - if(rootlen + 1 > PATH_MAX) { + if(root[rootlen - 1] != '/' && rootlen + 1 < PATH_MAX){ + strcat(root, "/"); + rootlen++; + } + if(rootlen + 1 > PATH_MAX || root[rootlen - 1] != '/') { /* we are in trouble here */ pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, ""); return 1; @@ -177,32 +183,28 @@ static int query_fileowner(alpm_list_t *targets) bname_len = strlen(bname); dname = mdirname(filename); - /* for files in '/', there is no directory name to match */ - if(strcmp(dname, "") == 0) { - rpath = NULL; - } else { - rpath = resolve_path(dname); + rpath = resolve_path(dname); - if(!rpath) { - pm_printf(ALPM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"), - filename, strerror(errno)); - free(dname); - ret++; - goto cleanup; - } + if(!rpath) { + pm_printf(ALPM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"), + dname, strerror(errno)); + free(dname); + ret++; + goto cleanup; } free(dname); + if(strstr(root, rpath) && *bname == '\0') { + /* path is either outside root or *is* root */ + pm_printf(ALPM_LOG_ERROR, _("cannot determine ownership of file outside root directory\n")); + ret++; + goto cleanup; + } + if(isdir) { - if(!rpath) { - pm_printf(ALPM_LOG_ERROR, _("cannot determine ownership of root directory\n")); - ret++; - goto cleanup; - } else { - rpath_len = strlen(rpath); - rpath_bname = mbasename(rpath); - rpath_bname_len = strlen(rpath_bname); - } + rpath_len = strlen(rpath); + rpath_bname = mbasename(rpath); + rpath_bname_len = strlen(rpath_bname); } for(i = packages; i && (!found || isdir); i = alpm_list_next(i)) { @@ -276,6 +278,7 @@ cleanup: free(filename); free(rpath); } + free(root); return ret; } diff --git a/src/pacman/util.c b/src/pacman/util.c index 7f7f6a7..1914b70 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -242,6 +242,10 @@ char *mdirname(const char *path) ret = strdup(path); last = strrchr(ret, '/'); + if(last == ret){ + free(ret); + return strdup("/"); + } if(last != NULL) { /* we found a '/', so terminate our string */ *last = '\0'; -- 1.7.10.2
participants (2)
-
Allan McRae
-
Andrew Gregory