[pacman-dev] [PATCH 0/8] Package file validation using mtree data
This patchset still has a bit of polish needed (there are a few TODOs scattered throughout). But it is now working quite well, so I think a bit of a review would be useful. The following fields are checked for each file: Directory: uid, gid, mode File: uid, gid, mode, size, (m)time Symbolic Link: uid, gid, mode, link, (m)time The main issus so far is that libarchive does not read any checksums from the mtree file. I will approach the libarchive devs to see how we can improve this situation. Allan McRae (8): makepkg: add mtree file into package Extract .MTREE file into local package database Add internal functions for reading mtree file from local db Add public functions for accessing mtree data Move check function into its own file Separate checking a files existence into a function Basic running of pacman -Qkk to check mtree files Perform full checking of files with -Qkk lib/libalpm/add.c | 5 + lib/libalpm/alpm.h | 12 ++ lib/libalpm/be_local.c | 57 ++++++++++ lib/libalpm/package.c | 44 ++++++++ lib/libalpm/package.h | 8 ++ scripts/makepkg.sh.in | 10 +- src/pacman/Makefile.am | 1 + src/pacman/check.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pacman/check.h | 30 +++++ src/pacman/pacman.c | 2 +- src/pacman/query.c | 62 +---------- 11 files changed, 457 insertions(+), 59 deletions(-) create mode 100644 src/pacman/check.c create mode 100644 src/pacman/check.h -- 1.7.10.1
Add an mtree file to the package with all file information. This can be added to the local pacman database on install allowing full package verification. Signed-off-by: Allan McRae <allan@archlinux.org> --- scripts/makepkg.sh.in | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in index d36dbd6..08f4b62 100644 --- a/scripts/makepkg.sh.in +++ b/scripts/makepkg.sh.in @@ -1351,8 +1351,6 @@ create_package() { done # tar it up - msg2 "$(gettext "Compressing package...")" - local fullver=$(get_full_version) local pkg_file="$PKGDEST/${nameofpkg}-${fullver}-${pkgarch}${PKGEXT}" local ret=0 @@ -1363,6 +1361,14 @@ create_package() { # when fileglobbing, we want * in an empty directory to expand to # the null string rather than itself shopt -s nullglob + + msg2 "$(gettext "Generating .MTREE file...")" + bsdtar -czf .MTREE --format=mtree \ + --options='!all,use-set,type,uid,gid,mode,time,size,md5,link' \ + "${comp_files[@]}" * + comp_files+=(".MTREE") + + msg2 "$(gettext "Compressing package...")" # TODO: Maybe this can be set globally for robustness shopt -s -o pipefail # bsdtar's gzip compression always saves the time stamp, making one -- 1.7.10.1
Signed-off-by: Allan McRae <allan@archlinux.org> --- lib/libalpm/add.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c index b007766..b98346e 100644 --- a/lib/libalpm/add.c +++ b/lib/libalpm/add.c @@ -164,6 +164,11 @@ static int extract_single_file(alpm_handle_t *handle, struct archive *archive, snprintf(filename, PATH_MAX, "%s%s-%s/changelog", _alpm_db_path(handle->db_local), newpkg->name, newpkg->version); archive_entry_set_perm(entry, 0644); + } else if(strcmp(entryname, ".MTREE") == 0) { + /* the mtree file goes inside the db */ + snprintf(filename, PATH_MAX, "%s%s-%s/mtree", + _alpm_db_path(handle->db_local), newpkg->name, newpkg->version); + archive_entry_set_perm(entry, 0644); } else if(*entryname == '.') { /* for now, ignore all files starting with '.' that haven't * already been handled (for future possibilities) */ -- 1.7.10.1
Signed-off-by: Allan McRae <allan@archlinux.org> --- lib/libalpm/be_local.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/libalpm/package.c | 21 +++++++++++++++++ lib/libalpm/package.h | 8 +++++++ 3 files changed, 88 insertions(+) diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c index 30e59d0..402637a 100644 --- a/lib/libalpm/be_local.c +++ b/lib/libalpm/be_local.c @@ -28,6 +28,10 @@ #include <dirent.h> #include <limits.h> /* PATH_MAX */ +/* libarchive */ +#include <archive.h> +#include <archive_entry.h> + /* libalpm */ #include "db.h" #include "alpm_list.h" @@ -210,6 +214,57 @@ static int _cache_changelog_close(const alpm_pkg_t UNUSED *pkg, void *fp) return fclose((FILE *)fp); } +static struct archive *_cache_mtree_open(alpm_pkg_t *pkg) +{ + int r; + struct archive *mtree; + + pkg->handle->pm_errno = 0; + + alpm_db_t *db = alpm_pkg_get_db(pkg); + char *mtfile = _alpm_local_db_pkgpath(db, pkg, "mtree"); + + if(access(mtfile, F_OK) != 0) { + /* there is no mtree file for this package */ + goto error; + } + + if((mtree = archive_read_new()) == NULL) { + pkg->handle->pm_errno = ALPM_ERR_LIBARCHIVE; + goto error; + } + + archive_read_support_filter_gzip(mtree); + archive_read_support_format_mtree(mtree); + + if((r = archive_read_open_file(mtree, mtfile, ALPM_BUFFER_SIZE))) { + _alpm_log(pkg->handle, ALPM_LOG_ERROR, _("error while reading file %s: %s\n"), + mtfile, archive_error_string(mtree)); + pkg->handle->pm_errno = ALPM_ERR_LIBARCHIVE; + archive_read_finish(mtree); + goto error; + } + + free(mtfile); + return mtree; + +error: + free(mtfile); + return NULL; +} + +static int _cache_mtree_next(const alpm_pkg_t UNUSED *pkg, + struct archive *mtree, struct archive_entry **entry) +{ + return archive_read_next_header(mtree, entry); +} + +static int _cache_mtree_close(const alpm_pkg_t UNUSED *pkg, + struct archive *mtree) +{ + return archive_read_finish(mtree); +} + static int _cache_force_load(alpm_pkg_t *pkg) { return local_db_read(pkg, INFRQ_ALL); @@ -245,6 +300,10 @@ static struct pkg_operations local_pkg_ops = { .changelog_read = _cache_changelog_read, .changelog_close = _cache_changelog_close, + .mtree_open = _cache_mtree_open, + .mtree_next = _cache_mtree_next, + .mtree_close = _cache_mtree_close, + .force_load = _cache_force_load, }; diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c index 46d1473..3906b0f 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -121,6 +121,23 @@ static int _pkg_changelog_close(const alpm_pkg_t UNUSED *pkg, return EOF; } +static struct archive *_pkg_mtree_open(alpm_pkg_t UNUSED *pkg) +{ + return NULL; +} + +static int _pkg_mtree_next(const alpm_pkg_t UNUSED *pkg, + struct archive UNUSED *archive, struct archive_entry UNUSED **entry) +{ + return -1; +} + +static int _pkg_mtree_close(const alpm_pkg_t UNUSED *pkg, + struct archive UNUSED *archive) +{ + return -1; +} + static int _pkg_force_load(alpm_pkg_t UNUSED *pkg) { return 0; } /** The standard package operations struct. Get fields directly from the @@ -152,6 +169,10 @@ struct pkg_operations default_pkg_ops = { .changelog_read = _pkg_changelog_read, .changelog_close = _pkg_changelog_close, + .mtree_open = _pkg_mtree_open, + .mtree_next = _pkg_mtree_next, + .mtree_close = _pkg_mtree_close, + .force_load = _pkg_force_load, }; diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h index c473549..542a6d9 100644 --- a/lib/libalpm/package.h +++ b/lib/libalpm/package.h @@ -26,6 +26,10 @@ #include <sys/types.h> /* off_t */ +/* libarchive */ +#include <archive.h> +#include <archive_entry.h> + #include "alpm.h" #include "backup.h" #include "db.h" @@ -64,6 +68,10 @@ struct pkg_operations { size_t (*changelog_read) (void *, size_t, const alpm_pkg_t *, void *); int (*changelog_close) (const alpm_pkg_t *, void *); + struct archive *(*mtree_open) (alpm_pkg_t *); + int (*mtree_next) (const alpm_pkg_t *, struct archive *, struct archive_entry **); + int (*mtree_close) (const alpm_pkg_t *, struct archive *); + int (*force_load) (alpm_pkg_t *); }; -- 1.7.10.1
Signed-off-by: Allan McRae <allan@archlinux.org> --- lib/libalpm/alpm.h | 12 ++++++++++++ lib/libalpm/be_local.c | 2 -- lib/libalpm/package.c | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 023c53e..c2761a5 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -31,6 +31,10 @@ extern "C" { #include <sys/types.h> /* off_t */ #include <stdarg.h> /* va_list */ +/* libarchive */ +#include <archive.h> +#include <archive_entry.h> + #include <alpm_list.h> /* @@ -930,6 +934,14 @@ size_t alpm_pkg_changelog_read(void *ptr, size_t size, int alpm_pkg_changelog_close(const alpm_pkg_t *pkg, void *fp); + +struct archive *alpm_pkg_mtree_open(alpm_pkg_t *pkg); + +int alpm_pkg_mtree_next(const alpm_pkg_t *pkg, struct archive *archive, + struct archive_entry **entry); + +int alpm_pkg_mtree_close(const alpm_pkg_t *pkg, struct archive *archive); + /** Returns whether the package has an install scriptlet. * @return 0 if FALSE, TRUE otherwise */ diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c index 402637a..3b78b99 100644 --- a/lib/libalpm/be_local.c +++ b/lib/libalpm/be_local.c @@ -219,8 +219,6 @@ static struct archive *_cache_mtree_open(alpm_pkg_t *pkg) int r; struct archive *mtree; - pkg->handle->pm_errno = 0; - alpm_db_t *db = alpm_pkg_get_db(pkg); char *mtfile = _alpm_local_db_pkgpath(db, pkg, "mtree"); diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c index 3906b0f..bdc2899 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -403,6 +403,29 @@ int SYMEXPORT alpm_pkg_changelog_close(const alpm_pkg_t *pkg, void *fp) return pkg->ops->changelog_close(pkg, fp); } +struct archive SYMEXPORT *alpm_pkg_mtree_open(alpm_pkg_t * pkg) +{ + ASSERT(pkg != NULL, return NULL); + pkg->handle->pm_errno = 0; + return pkg->ops->mtree_open(pkg); +} + +int SYMEXPORT alpm_pkg_mtree_next(const alpm_pkg_t * pkg, struct archive *archive, + struct archive_entry **entry) +{ + ASSERT(pkg != NULL, return -1); + pkg->handle->pm_errno = 0; + return pkg->ops->mtree_next(pkg, archive, entry); +} + +int SYMEXPORT alpm_pkg_mtree_close(const alpm_pkg_t * pkg, struct archive *archive) +{ + ASSERT(pkg != NULL, return -1); + pkg->handle->pm_errno = 0; + return pkg->ops->mtree_close(pkg, archive); +} + + int SYMEXPORT alpm_pkg_has_scriptlet(alpm_pkg_t *pkg) { ASSERT(pkg != NULL, return -1); -- 1.7.10.1
There is going to be a lot of overlap in the code for the quick and full checks that can be abstracted into their own functions. Also many other file checking functions will be needed for the full check. Put all these in a separate source file. Signed-off-by: Allan McRae <allan@archlinux.org> --- src/pacman/Makefile.am | 1 + src/pacman/check.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pacman/check.h | 29 ++++++++++++++++++ src/pacman/query.c | 56 +-------------------------------- 4 files changed, 111 insertions(+), 55 deletions(-) create mode 100644 src/pacman/check.c create mode 100644 src/pacman/check.h diff --git a/src/pacman/Makefile.am b/src/pacman/Makefile.am index c8ce977..84cb74b 100644 --- a/src/pacman/Makefile.am +++ b/src/pacman/Makefile.am @@ -29,6 +29,7 @@ DEFS += -DGIT_VERSION=\"$(GIT_VERSION)\" endif pacman_SOURCES = \ + check.h check.c \ conf.h conf.c \ database.c \ deptest.c \ diff --git a/src/pacman/check.c b/src/pacman/check.c new file mode 100644 index 0000000..89a4248 --- /dev/null +++ b/src/pacman/check.c @@ -0,0 +1,80 @@ +/* + * check.c + * + * Copyright (c) 2012 Pacman Development Team <pacman-dev@archlinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <limits.h> +#include <string.h> +#include <errno.h> + +/* pacman */ +#include "check.h" +#include "conf.h" +#include "util.h" + +/* Loop through the files of the package to check if they exist. */ +int check(alpm_pkg_t *pkg) +{ + const char *root, *pkgname; + size_t errors = 0; + size_t rootlen; + char f[PATH_MAX]; + alpm_filelist_t *filelist; + size_t i; + + root = alpm_option_get_root(config->handle); + rootlen = strlen(root); + if(rootlen + 1 > PATH_MAX) { + /* we are in trouble here */ + pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, ""); + return 1; + } + strcpy(f, root); + + pkgname = alpm_pkg_get_name(pkg); + filelist = alpm_pkg_get_files(pkg); + for(i = 0; i < filelist->count; i++) { + const alpm_file_t *file = filelist->files + i; + struct stat st; + const char *path = file->name; + + if(rootlen + 1 + strlen(path) > PATH_MAX) { + pm_printf(ALPM_LOG_WARNING, _("path too long: %s%s\n"), root, path); + continue; + } + strcpy(f + rootlen, path); + /* use lstat to prevent errors from symlinks */ + if(lstat(f, &st) != 0) { + if(config->quiet) { + printf("%s %s\n", pkgname, f); + } else { + pm_printf(ALPM_LOG_WARNING, "%s: %s (%s)\n", + pkgname, f, strerror(errno)); + } + errors++; + } + } + + if(!config->quiet) { + printf(_n("%s: %jd total file, ", "%s: %jd total files, ", + (unsigned long)filelist->count), pkgname, (intmax_t)filelist->count); + printf(_n("%jd missing file\n", "%jd missing files\n", + (unsigned long)errors), (intmax_t)errors); + } + + return (errors != 0 ? 1 : 0); +} diff --git a/src/pacman/check.h b/src/pacman/check.h new file mode 100644 index 0000000..b107a7f --- /dev/null +++ b/src/pacman/check.h @@ -0,0 +1,29 @@ +/* + * check.h + * + * Copyright (c) 2012 Pacman Development Team <pacman-dev@archlinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _PM_CHECK_H +#define _PM_CHECK_H + +#include <alpm.h> + +int check(alpm_pkg_t *pkg); + +#endif /* _PM_CHECK_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/pacman/query.c b/src/pacman/query.c index a1cc1cf..12c3412 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -22,7 +22,6 @@ #include <stdio.h> #include <stdint.h> #include <limits.h> -#include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> @@ -33,6 +32,7 @@ /* pacman */ #include "pacman.h" #include "package.h" +#include "check.h" #include "conf.h" #include "util.h" @@ -401,60 +401,6 @@ static int filter(alpm_pkg_t *pkg) return 1; } -/* Loop through the packages. For each package, - * loop through files to check if they exist. */ -static int check(alpm_pkg_t *pkg) -{ - const char *root, *pkgname; - size_t errors = 0; - size_t rootlen; - char f[PATH_MAX]; - alpm_filelist_t *filelist; - size_t i; - - root = alpm_option_get_root(config->handle); - rootlen = strlen(root); - if(rootlen + 1 > PATH_MAX) { - /* we are in trouble here */ - pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, ""); - return 1; - } - strcpy(f, root); - - pkgname = alpm_pkg_get_name(pkg); - filelist = alpm_pkg_get_files(pkg); - for(i = 0; i < filelist->count; i++) { - const alpm_file_t *file = filelist->files + i; - struct stat st; - const char *path = file->name; - - if(rootlen + 1 + strlen(path) > PATH_MAX) { - pm_printf(ALPM_LOG_WARNING, _("path too long: %s%s\n"), root, path); - continue; - } - strcpy(f + rootlen, path); - /* use lstat to prevent errors from symlinks */ - if(lstat(f, &st) != 0) { - if(config->quiet) { - printf("%s %s\n", pkgname, f); - } else { - pm_printf(ALPM_LOG_WARNING, "%s: %s (%s)\n", - pkgname, f, strerror(errno)); - } - errors++; - } - } - - if(!config->quiet) { - printf(_n("%s: %jd total file, ", "%s: %jd total files, ", - (unsigned long)filelist->count), pkgname, (intmax_t)filelist->count); - printf(_n("%jd missing file\n", "%jd missing files\n", - (unsigned long)errors), (intmax_t)errors); - } - - return (errors != 0 ? 1 : 0); -} - static int display(alpm_pkg_t *pkg) { int ret = 0; -- 1.7.10.1
Signed-off-by: Allan McRae <allan@archlinux.org> --- src/pacman/check.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/pacman/check.c b/src/pacman/check.c index 89a4248..8317f22 100644 --- a/src/pacman/check.c +++ b/src/pacman/check.c @@ -26,13 +26,30 @@ #include "conf.h" #include "util.h" +static int check_file_exists(const char *pkgname, const char * filepath, + struct stat * st) +{ + /* use lstat to prevent errors from symlinks */ + if(lstat(filepath, st) != 0) { + if(config->quiet) { + printf("%s %s\n", pkgname, filepath); + } else { + pm_printf(ALPM_LOG_WARNING, "%s: %s (%s)\n", + pkgname, filepath, strerror(errno)); + } + return 1; + } + + return 0; +} + /* Loop through the files of the package to check if they exist. */ int check(alpm_pkg_t *pkg) { const char *root, *pkgname; size_t errors = 0; size_t rootlen; - char f[PATH_MAX]; + char filepath[PATH_MAX]; alpm_filelist_t *filelist; size_t i; @@ -43,7 +60,7 @@ int check(alpm_pkg_t *pkg) pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, ""); return 1; } - strcpy(f, root); + strcpy(filepath, root); pkgname = alpm_pkg_get_name(pkg); filelist = alpm_pkg_get_files(pkg); @@ -56,17 +73,9 @@ int check(alpm_pkg_t *pkg) pm_printf(ALPM_LOG_WARNING, _("path too long: %s%s\n"), root, path); continue; } - strcpy(f + rootlen, path); - /* use lstat to prevent errors from symlinks */ - if(lstat(f, &st) != 0) { - if(config->quiet) { - printf("%s %s\n", pkgname, f); - } else { - pm_printf(ALPM_LOG_WARNING, "%s: %s (%s)\n", - pkgname, f, strerror(errno)); - } - errors++; - } + strcpy(filepath + rootlen, path); + + errors += check_file_exists(pkgname, filepath, &st); } if(!config->quiet) { -- 1.7.10.1
If a package has an mtree file, using pacman -Qkk will read that file and use it to perform more in depth package checking. Currently this only checks for file presence. Signed-off-by: Allan McRae <allan@archlinux.org> --- src/pacman/check.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/pacman/check.h | 3 ++- src/pacman/pacman.c | 2 +- src/pacman/query.c | 6 ++++- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/pacman/check.c b/src/pacman/check.c index 8317f22..ba9036a 100644 --- a/src/pacman/check.c +++ b/src/pacman/check.c @@ -44,7 +44,7 @@ static int check_file_exists(const char *pkgname, const char * filepath, } /* Loop through the files of the package to check if they exist. */ -int check(alpm_pkg_t *pkg) +int check_pkg_fast(alpm_pkg_t *pkg) { const char *root, *pkgname; size_t errors = 0; @@ -87,3 +87,70 @@ int check(alpm_pkg_t *pkg) return (errors != 0 ? 1 : 0); } + +int check_pkg_full(alpm_pkg_t *pkg) +{ + const char *root, *pkgname; + size_t errors = 0; + size_t rootlen; + char filepath[PATH_MAX]; + struct archive *mtree; + struct archive_entry *entry = NULL; + size_t file_count = 0; + + root = alpm_option_get_root(config->handle); + rootlen = strlen(root); + if(rootlen + 1 > PATH_MAX) { + /* we are in trouble here */ + pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, ""); + return 1; + } + strcpy(filepath, root); + + pkgname = alpm_pkg_get_name(pkg); + mtree = alpm_pkg_mtree_open(pkg); + if(mtree == NULL) { + /* TODO: check error to confirm failure due to no mtree file */ + if(!config->quiet) { + printf(_("%s: no mtree file\n"), pkgname); + } + return 0; + } + + while(alpm_pkg_mtree_next(pkg, mtree, &entry) == ARCHIVE_OK) { + struct stat st; + const char *path = archive_entry_pathname(entry); + + /* ignore special files for the moment */ + if(*path == '.') { + continue; + } + + file_count++; + + if(rootlen + 1 + strlen(path) > PATH_MAX) { + pm_printf(ALPM_LOG_WARNING, _("path too long: %s%s\n"), root, path); + continue; + } + strcpy(filepath + rootlen, path); + + if(check_file_exists(pkgname, filepath, &st) == 1) + { + errors++; + continue; + } + + /* TODO: check file properties */ + } + + alpm_pkg_mtree_close(pkg, mtree); + + if(!config->quiet) { + printf(_n("%s: %jd total file, ", "%s: %jd total files, ", + (unsigned long)file_count), pkgname, (intmax_t)file_count); + printf(_n("%jd altered file\n", "%jd altered files\n", + (unsigned long)errors), (intmax_t)errors); + } + + return (errors != 0 ? 1 : 0); +} diff --git a/src/pacman/check.h b/src/pacman/check.h index b107a7f..f71637c 100644 --- a/src/pacman/check.h +++ b/src/pacman/check.h @@ -22,7 +22,8 @@ #include <alpm.h> -int check(alpm_pkg_t *pkg); +int check_pkg_fast(alpm_pkg_t *pkg); +int check_pkg_full(alpm_pkg_t *pkg); #endif /* _PM_CHECK_H */ diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 73d5be9..d79f2f9 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -459,7 +459,7 @@ static int parsearg_query(int opt) case 'e': config->op_q_explicit = 1; break; case 'g': (config->group)++; break; case 'i': (config->op_q_info)++; break; - case 'k': config->op_q_check = 1; break; + case 'k': (config->op_q_check)++; break; case 'l': config->op_q_list = 1; break; case 'm': config->op_q_foreign = 1; break; case 'o': config->op_q_owns = 1; break; diff --git a/src/pacman/query.c b/src/pacman/query.c index 12c3412..6b3c3e6 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -419,7 +419,11 @@ static int display(alpm_pkg_t *pkg) dump_pkg_changelog(pkg); } if(config->op_q_check) { - ret = check(pkg); + if(config->op_q_check == 1) { + ret = check_pkg_fast(pkg); + } else { + ret = check_pkg_full(pkg); + } } if(!config->op_q_info && !config->op_q_list && !config->op_q_changelog && !config->op_q_check) { -- 1.7.10.1
The follow fields are checked: Directory: uid, gid, mode File: uid, gid, mode, size, time Symbolic Link: uid, gid, mode, link, time Signed-off-by: Allan McRae <allan@archlinux.org> --- src/pacman/check.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 4 deletions(-) diff --git a/src/pacman/check.c b/src/pacman/check.c index ba9036a..be84dd6 100644 --- a/src/pacman/check.c +++ b/src/pacman/check.c @@ -43,6 +43,104 @@ static int check_file_exists(const char *pkgname, const char * filepath, return 0; } +static int check_file_permissions(const char *pkgname, const char *filepath, + struct stat *st, struct archive_entry *entry) +{ + int errors = 0; + mode_t fsmode; + + /* uid */ + if(st->st_uid != archive_entry_uid(entry)) { + errors++; + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, "%s: %s (UID mismatch)\n", + pkgname, filepath); + } + } + + /* gid */ + if(st->st_gid != archive_entry_gid(entry)) { + errors++; + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, "%s: %s (GID mismatch)\n", + pkgname, filepath); + } + } + + /* mode */ + fsmode = st->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO); + if(fsmode != archive_entry_perm(entry)) { + errors++; + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, "%s: %s (Permissions mismatch)\n", + pkgname, filepath); + } + } + + return (errors != 0 ? 1 : 0); +} + +static int check_file_time(const char *pkgname, const char *filepath, + struct stat *st, struct archive_entry *entry) +{ + if(st->st_mtime != archive_entry_mtime(entry)) { + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, "%s: %s (Modification time mismatch)\n", + pkgname, filepath); + } + return 1; + } + + return 0; +} + +static int check_file_link(const char *pkgname, const char *filepath, + struct stat *st, struct archive_entry *entry) +{ + /* TODO - fail early if file is not a symlink */ + size_t length = st->st_size + 1; + char link[length]; + + if(readlink(filepath, link, length) != st->st_size) { + /* this should not happen */ + pm_printf(ALPM_LOG_ERROR, _("unable to read symlink contents: %s\n"), filepath); + return 1; + } + link[length - 1] = '\0'; + + if(strcmp(link, archive_entry_symlink(entry)) != 0) { + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, "%s: %s (Symlink path mismatch)\n", + pkgname, filepath); + } + return 1; + } + + return 0; +} + +static int check_file_size(const char *pkgname, const char *filepath, + struct stat *st, struct archive_entry *entry) +{ + if(st->st_size != archive_entry_size(entry)) { + if(!config->quiet) { + pm_printf(ALPM_LOG_WARNING, "%s: %s (Size mismatch)\n", + pkgname, filepath); + } + return 1; + } + + return 0; +} + +/* placeholder - libarchive currently does not read checksums from mtree files +static int check_file_md5sum(const char *pkgname, const char *filepath, + struct stat *st, struct archive_entry *entry) +{ + return 0; +} +*/ + /* Loop through the files of the package to check if they exist. */ int check_pkg_fast(alpm_pkg_t *pkg) { @@ -120,8 +218,10 @@ int check_pkg_full(alpm_pkg_t *pkg) while(alpm_pkg_mtree_next(pkg, mtree, &entry) == ARCHIVE_OK) { struct stat st; const char *path = archive_entry_pathname(entry); + mode_t type; + size_t file_errors = 0; - /* ignore special files for the moment */ + /* TODO: ignoring special files for the moment */ if(*path == '.') { continue; } @@ -134,13 +234,40 @@ int check_pkg_full(alpm_pkg_t *pkg) } strcpy(filepath + rootlen, path); - if(check_file_exists(pkgname, filepath, &st) == 1) - { + if(check_file_exists(pkgname, filepath, &st) == 1) { errors++; continue; } - /* TODO: check file properties */ + type = archive_entry_filetype(entry); + + if(type != AE_IFDIR && type != AE_IFREG && type != AE_IFLNK) { + pm_printf(ALPM_LOG_WARNING, _("file type not recognized: %s%s\n"), root, path); + continue; + } + + file_errors += check_file_permissions(pkgname, filepath, &st, entry); + + if(type != AE_IFDIR) { + /* file or symbolic link */ + file_errors += check_file_time(pkgname, filepath, &st, entry); + } + + if(type == AE_IFLNK) { + file_errors += check_file_link(pkgname, filepath, &st, entry); + } + + if(type == AE_IFREG) { + /* TODO: these are expected to be changed with backup files */ + file_errors += check_file_size(pkgname, filepath, &st, entry); + //file_errors += check_file_md5sum(pkgname, filepath, &st, entry); + } + + if(config->quiet && file_errors) { + printf("%s %s\n", pkgname, filepath); + } + + errors += (file_errors != 0 ? 1 : 0); } alpm_pkg_mtree_close(pkg, mtree); @@ -154,3 +281,5 @@ int check_pkg_full(alpm_pkg_t *pkg) return (errors != 0 ? 1 : 0); } + +/* vim: set ts=2 sw=2 noet: */ -- 1.7.10.1
participants (1)
-
Allan McRae