From 9c160cf3f3cbda5fe5b6188e9e31775b71fc5bab Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Sat, 8 Sep 2012 17:14:19 -0400 Subject: 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. Original-work-by: Dan McGee Original-work-by: Allan McRae Signed-off-by: Andrew Gregory Signed-off-by: Allan McRae --- src/pacman/query.c | 60 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 19 deletions(-) (limited to 'src/pacman/query.c') diff --git a/src/pacman/query.c b/src/pacman/query.c index d1887e3a..ef279d97 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -97,6 +97,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) { @@ -129,13 +130,14 @@ static int query_fileowner(alpm_list_t *targets) } 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 = NULL, *dname = NULL, *rpath = NULL; const char *bname; struct stat buf; alpm_list_t *i; - size_t len; + size_t len, is_dir, bname_len, pbname_len; unsigned int found = 0; if((filename = strdup(t->data)) == NULL) { @@ -163,15 +165,21 @@ 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); - goto targcleanup; + is_dir = S_ISDIR(buf.st_mode) ? 1 : 0; + if(is_dir) { + /* the entire filename is safe to resolve if we know it points to a dir, + * and it's necessary in case it ends in . or .. */ + dname = realpath(filename, NULL); + bname = mbasename(dname); + rpath = mdirname(dname); + } else { + bname = mbasename(filename); + dname = mdirname(filename); + rpath = realpath(dname, NULL); } - bname = mbasename(filename); - dname = mdirname(filename); - rpath = realpath(dname, NULL); + bname_len = strlen(bname); + pbname_len = bname_len + is_dir; if(!dname || !rpath) { pm_printf(ALPM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"), @@ -179,33 +187,47 @@ static int query_fileowner(alpm_list_t *targets) goto targcleanup; } - for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) { + for(i = packages; i && (!found || is_dir); i = alpm_list_next(i)) { alpm_pkg_t *info = i->data; alpm_filelist_t *filelist = alpm_pkg_get_files(info); size_t j; for(j = 0; j < filelist->count; j++) { const alpm_file_t *file = filelist->files + j; - char *ppath, *pdname; + char *ppath; const char *pkgfile = file->name; + size_t pkgfile_len = strlen(pkgfile); + + /* make sure pkgfile and target are of the same type */ + if(is_dir != (pkgfile[pkgfile_len - 1] == '/')) { + continue; + } + + /* make sure pkgfile is long enough */ + if(pkgfile_len < pbname_len) { + continue; + } + + /* make sure pbname_len takes us to the start of a path component */ + if(pbname_len != pkgfile_len && pkgfile[pkgfile_len - pbname_len - 1] != '/') { + continue; + } - /* avoid the costly realpath usage if the basenames don't match */ - if(strcmp(mbasename(pkgfile), bname) != 0) { + /* compare basename with bname */ + if(strncmp(pkgfile + pkgfile_len - pbname_len, bname, bname_len) != 0) { continue; } - /* concatenate our file and the root path */ - if(rootlen + 1 + strlen(pkgfile) > PATH_MAX) { + /* concatenate our dirname and the root path */ + if(rootlen + 1 + pkgfile_len - pbname_len > PATH_MAX) { path[rootlen] = '\0'; /* reset path for error message */ pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), path, pkgfile); continue; } - strcpy(path + rootlen, pkgfile); - - pdname = mdirname(path); - ppath = realpath(pdname, NULL); - free(pdname); + strncpy(path + rootlen, pkgfile, pkgfile_len - pbname_len); + path[rootlen + pkgfile_len - pbname_len] = '\0'; + ppath = realpath(path, NULL); if(!ppath) { pm_printf(ALPM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"), path, strerror(errno)); -- cgit v1.2.3-24-g4f1b