diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2019-11-26 14:00:30 +0300 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2019-11-26 14:00:30 +0300 |
commit | 414ea1706306e061fc44a8b5ce3042d4f0728489 (patch) | |
tree | ef0b2c4eac79e479ed686a5d88d7b3b954717824 /lib | |
parent | ed2b463626bd721942143baa6207f2ccac67a616 (diff) | |
parent | 89afa9af7cd589eb8384ed96b6d86dd59d56bdf5 (diff) | |
download | dpkg-414ea1706306e061fc44a8b5ce3042d4f0728489.tar.gz |
Merge https://salsa.debian.org/dpkg-team/dpkg
Diffstat (limited to 'lib')
103 files changed, 4889 insertions, 606 deletions
diff --git a/lib/compat/Makefile.am b/lib/compat/Makefile.am index 1d5840a25..4477c27b4 100644 --- a/lib/compat/Makefile.am +++ b/lib/compat/Makefile.am @@ -10,6 +10,8 @@ noinst_LTLIBRARIES = libcompat-test.la libcompat.la libcompat_test_la_CPPFLAGS = $(AM_CPPFLAGS) -DTEST_LIBCOMPAT=1 libcompat_test_la_SOURCES = \ compat.h \ + md5.c md5.h \ + strchrnul.c \ strnlen.c \ strndup.c \ strsignal.c \ @@ -56,6 +58,10 @@ if !HAVE_STRNLEN libcompat_la_SOURCES += strnlen.c endif +if !HAVE_STRCHRNUL +libcompat_la_SOURCES += strchrnul.c +endif + if !HAVE_STRNDUP libcompat_la_SOURCES += strndup.c endif diff --git a/lib/compat/compat.h b/lib/compat/compat.h index 320ffdbc9..c1aa60180 100644 --- a/lib/compat/compat.h +++ b/lib/compat/compat.h @@ -109,6 +109,8 @@ extern "C" { #define asprintf test_asprintf #undef vasprintf #define vasprintf test_vasprintf +#undef strchrnul +#define strchrnul test_strchrnul #undef strndup #define strndup test_strndup #undef strnlen @@ -139,6 +141,10 @@ int vasprintf(char **str, const char *fmt, va_list args) LIBCOMPAT_ATTR_VPRINTF(2); #endif +#if TEST_LIBCOMPAT || !defined(HAVE_STRCHRNUL) +char *strchrnul(const char *s, int c); +#endif + #if TEST_LIBCOMPAT || !defined(HAVE_STRNLEN) size_t strnlen(const char *s, size_t n); #endif diff --git a/lib/compat/getopt.c b/lib/compat/getopt.c index 4c52b8b82..b13f81454 100644 --- a/lib/compat/getopt.c +++ b/lib/compat/getopt.c @@ -88,7 +88,7 @@ USA. */ #endif /* XXX: Disable intl support, because we do not carry the translations anyway - * and this pulls indirectly libintl, wich we do not want to impose. */ + * and this pulls indirectly libintl, which we do not want to impose. */ #ifndef _ #define _(msgid) (msgid) #endif diff --git a/lib/compat/md5.c b/lib/compat/md5.c index 3da18c98e..7da974635 100644 --- a/lib/compat/md5.c +++ b/lib/compat/md5.c @@ -41,7 +41,7 @@ (cp)[1] = (value) >> 8; \ (cp)[0] = (value); } while (0) -static u_int8_t PADDING[MD5_BLOCK_LENGTH] = { +static uint8_t PADDING[MD5_BLOCK_LENGTH] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -75,7 +75,7 @@ MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) need = MD5_BLOCK_LENGTH - have; /* Update bitcount */ - ctx->count += (u_int64_t)len << 3; + ctx->count += (uint64_t)len << 3; if (len >= need) { if (have != 0) { @@ -106,7 +106,7 @@ MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) void MD5Pad(MD5_CTX *ctx) { - u_int8_t count[8]; + uint8_t count[8]; size_t padlen; /* Convert count to 8 bytes in little endian order. */ @@ -156,19 +156,19 @@ MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) * the data and converts bytes into longwords for this routine. */ void -MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH]) +MD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_LENGTH]) { - u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; + uint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; #ifndef WORDS_BIGENDIAN memcpy(in, block, sizeof(in)); #else for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { - in[a] = (u_int32_t)( - (u_int32_t)(block[a * 4 + 0]) | - (u_int32_t)(block[a * 4 + 1]) << 8 | - (u_int32_t)(block[a * 4 + 2]) << 16 | - (u_int32_t)(block[a * 4 + 3]) << 24); + in[a] = (uint32_t)( + (uint32_t)(block[a * 4 + 0]) | + (uint32_t)(block[a * 4 + 1]) << 8 | + (uint32_t)(block[a * 4 + 2]) << 16 | + (uint32_t)(block[a * 4 + 3]) << 24); } #endif diff --git a/lib/compat/md5.h b/lib/compat/md5.h index f6243603d..b5247d527 100644 --- a/lib/compat/md5.h +++ b/lib/compat/md5.h @@ -15,20 +15,22 @@ #ifndef _MD5_H_ #define _MD5_H_ +#include <stdint.h> + #define MD5_BLOCK_LENGTH 64 #define MD5_DIGEST_LENGTH 16 #define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) typedef struct MD5Context { - u_int32_t state[4]; /* state */ - u_int64_t count; /* number of bits, mod 2^64 */ - u_int8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ + uint32_t state[4]; /* state */ + uint64_t count; /* number of bits, mod 2^64 */ + uint8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX *); -void MD5Update(MD5_CTX *, const u_int8_t *, size_t); +void MD5Update(MD5_CTX *, const uint8_t *, size_t); void MD5Pad(MD5_CTX *); -void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *); -void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]); +void MD5Final(uint8_t [MD5_DIGEST_LENGTH], MD5_CTX *); +void MD5Transform(uint32_t [4], const uint8_t [MD5_BLOCK_LENGTH]); #endif /* _MD5_H_ */ diff --git a/lib/compat/scandir.c b/lib/compat/scandir.c index 4765d7dcb..8771de093 100644 --- a/lib/compat/scandir.c +++ b/lib/compat/scandir.c @@ -72,13 +72,13 @@ scandir(const char *dir, struct dirent ***namelist, avail *= 2; else avail = 20; - newlist = realloc(list, avail * sizeof(struct dirent *)); + newlist = realloc(list, avail * sizeof(*newlist)); if (!newlist) return cleanup(d, list, used); list = newlist; } - m = malloc(sizeof(struct dirent) + strlen(e->d_name)); + m = malloc(sizeof(*m) + strlen(e->d_name)); if (!m) return cleanup(d, list, used); *m = *e; @@ -91,7 +91,7 @@ scandir(const char *dir, struct dirent ***namelist, closedir(d); if (list != NULL && cmp != NULL) - qsort(list, used, sizeof(struct dirent *), cmp); + qsort(list, used, sizeof(list[0]), cmp); *namelist = list; diff --git a/lib/compat/strchrnul.c b/lib/compat/strchrnul.c new file mode 100644 index 000000000..b072e1ad3 --- /dev/null +++ b/lib/compat/strchrnul.c @@ -0,0 +1,37 @@ +/* + * libcompat - system compatibility library + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include "compat.h" + +char * +strchrnul(const char *s, int c) +{ + char *match; + + match = strchr(s, c); + if (match) + return match; + + return (char *)s + strlen(s); +} diff --git a/lib/dpkg/Makefile.am b/lib/dpkg/Makefile.am index 215d12ffb..3a3bd64b4 100644 --- a/lib/dpkg/Makefile.am +++ b/lib/dpkg/Makefile.am @@ -53,6 +53,13 @@ libdpkg_la_SOURCES = \ compress.c \ dbdir.c \ dbmodify.c \ + db-ctrl-access.c \ + db-ctrl-format.c \ + db-ctrl-upgrade.c \ + db-fsys-digest.c \ + db-fsys-divert.c \ + db-fsys-files.c \ + db-fsys-override.c \ deb-version.c \ debug.c \ depcon.c \ @@ -63,6 +70,9 @@ libdpkg_la_SOURCES = \ fdio.c \ file.c \ fields.c \ + fsys-dir.c\ + fsys-iter.c \ + fsys-hash.c \ glob.c \ i18n.c i18n.h \ log.c \ @@ -71,14 +81,17 @@ libdpkg_la_SOURCES = \ nfmalloc.c \ options.c \ options-parsers.c \ + pager.c \ parse.c \ parsehelp.c \ path.c \ path-remove.c \ + perf.h \ pkg.c \ - pkg-db.c \ pkg-array.c \ + pkg-files.c \ pkg-format.c \ + pkg-hash.c \ pkg-list.c \ pkg-namevalue.c \ pkg-queue.c \ @@ -112,6 +125,8 @@ pkginclude_HEADERS = \ color.h \ command.h \ compress.h \ + db-ctrl.h \ + db-fsys.h \ deb-version.h \ debug.h \ dir.h \ @@ -121,14 +136,17 @@ pkginclude_HEADERS = \ error.h \ fdio.h \ file.h \ + fsys.h \ glob.h \ macros.h \ namevalue.h \ options.h \ + pager.h \ parsedump.h \ path.h \ pkg.h \ pkg-array.h \ + pkg-files.h \ pkg-format.h \ pkg-list.h \ pkg-queue.h \ diff --git a/lib/dpkg/ar.h b/lib/dpkg/ar.h index 116703d84..55bfa6e99 100644 --- a/lib/dpkg/ar.h +++ b/lib/dpkg/ar.h @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * ar.c - primitives for ar handling + * ar.h - primitives for ar handling * * Copyright © 2010 Guillem Jover <guillem@debian.org> * diff --git a/lib/dpkg/arch.c b/lib/dpkg/arch.c index 099957170..96f261704 100644 --- a/lib/dpkg/arch.c +++ b/lib/dpkg/arch.c @@ -23,7 +23,6 @@ #include <config.h> #include <compat.h> -#include <assert.h> #include <limits.h> #include <string.h> #include <stdbool.h> @@ -59,7 +58,8 @@ dpkg_arch_name_is_illegal(const char *name) static char buf[150]; const char *p = name; - assert(name); + if (p == NULL) + internerr("arch name argument is NULL"); if (!*p) return _("may not be empty string"); if (!c_isalnum(*p)) @@ -124,10 +124,11 @@ dpkg_arch_new(const char *name, enum dpkg_arch_type type) /** * Retrieve the struct dpkg_arch for the given architecture. * - * Create a new structure for the architecture if it's not yet known from - * the system, in that case it will have arch->type == arch_unknown, if the - * architecture is illegal it will have arch->type == arch_illegal and if - * name is NULL or an empty string then it will have arch->type == arch_none. + * Create a new structure for the architecture if it is not yet known from + * the system, in that case it will have type == DPKG_ARCH_UNKNOWN, if the + * architecture is illegal it will have type == DPKG_ARCH_ILLEGAL, if name + * is an empty string it will have type == DPKG_ARCH_EMPTY, and if it is + * NULL then it will have type == DPKG_ARCH_NONE. * * @param name The architecture name. */ diff --git a/lib/dpkg/atomic-file.c b/lib/dpkg/atomic-file.c index b49a47b9b..20b1fb37e 100644 --- a/lib/dpkg/atomic-file.c +++ b/lib/dpkg/atomic-file.c @@ -58,7 +58,7 @@ atomic_file_open(struct atomic_file *file) file->name_new); fchmod(fileno(file->fp), 0644); - push_cleanup(cu_closestream, ~ehflag_normaltidy, NULL, 0, 1, file->fp); + push_cleanup(cu_closestream, ~ehflag_normaltidy, 1, file->fp); } void diff --git a/lib/dpkg/buffer.c b/lib/dpkg/buffer.c index c6c53eb7b..9faa12d93 100644 --- a/lib/dpkg/buffer.c +++ b/lib/dpkg/buffer.c @@ -38,7 +38,7 @@ #include <dpkg/buffer.h> struct buffer_md5_ctx { - struct MD5Context ctx; + MD5_CTX ctx; char *hash; }; @@ -225,6 +225,8 @@ buffer_copy(struct buffer_data *read_data, if (bytesread < 0 || byteswritten < 0) return -1; + if (totalread != totalwritten) + return -1; if (limit > 0) return dpkg_put_error(err, _("unexpected end of file or stream")); diff --git a/lib/dpkg/command.c b/lib/dpkg/command.c index b6a79d7c8..ab89d4b9b 100644 --- a/lib/dpkg/command.c +++ b/lib/dpkg/command.c @@ -51,7 +51,7 @@ command_init(struct command *cmd, const char *filename, const char *name) cmd->name = name; cmd->argc = 0; cmd->argv_size = 10; - cmd->argv = m_malloc(cmd->argv_size * sizeof(const char *)); + cmd->argv = m_malloc(cmd->argv_size * sizeof(cmd->argv[0])); cmd->argv[0] = NULL; } @@ -85,7 +85,7 @@ command_grow_argv(struct command *cmd, int need) return; cmd->argv_size = (cmd->argv_size + need) * 2; - cmd->argv = m_realloc(cmd->argv, cmd->argv_size * sizeof(const char *)); + cmd->argv = m_realloc(cmd->argv, cmd->argv_size * sizeof(cmd->argv[0])); } /** @@ -182,27 +182,6 @@ command_exec(struct command *cmd) ohshite(_("unable to execute %s (%s)"), cmd->name, cmd->filename); } - -/** - * Get a suitable pager. - * - * @return A string representing a pager. - */ -const char * -command_get_pager(void) -{ - const char *pager; - - if (!isatty(1)) - return CAT; - - pager = getenv("PAGER"); - if (str_is_unset(pager)) - pager = DEFAULTPAGER; - - return pager; -} - /** * Execute a shell with a possible command. * diff --git a/lib/dpkg/command.h b/lib/dpkg/command.h index 8cd2af9f1..7d2098a29 100644 --- a/lib/dpkg/command.h +++ b/lib/dpkg/command.h @@ -54,7 +54,6 @@ void command_add_args(struct command *cmd, ...) DPKG_ATTR_SENTINEL; void command_exec(struct command *cmd) DPKG_ATTR_NORET; -const char *command_get_pager(void); void command_shell(const char *cmd, const char *name) DPKG_ATTR_NORET; /** @} */ diff --git a/lib/dpkg/db-ctrl-access.c b/lib/dpkg/db-ctrl-access.c new file mode 100644 index 000000000..d31f5466f --- /dev/null +++ b/lib/dpkg/db-ctrl-access.c @@ -0,0 +1,120 @@ +/* + * dpkg - main program for package management + * db-ctrl-access.c - package control information database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <dirent.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/debug.h> + +bool +pkg_infodb_has_file(struct pkginfo *pkg, struct pkgbin *pkgbin, + const char *name) +{ + const char *filename; + struct stat stab; + + filename = pkg_infodb_get_file(pkg, pkgbin, name); + if (lstat(filename, &stab) == 0) + return true; + else if (errno == ENOENT) + return false; + else + ohshite(_("unable to check existence of '%.250s'"), filename); +} + +void +pkg_infodb_foreach(struct pkginfo *pkg, struct pkgbin *pkgbin, + pkg_infodb_file_func *func) +{ + DIR *db_dir; + struct dirent *db_de; + struct varbuf_state db_path_state; + struct varbuf db_path = VARBUF_INIT; + const char *pkgname; + enum pkg_infodb_format db_format; + + /* Make sure to always read and verify the format version. */ + db_format = pkg_infodb_get_format(); + + if (pkgbin->multiarch == PKG_MULTIARCH_SAME && + db_format == PKG_INFODB_FORMAT_MULTIARCH) + pkgname = pkgbin_name(pkg, pkgbin, pnaw_always); + else + pkgname = pkgbin_name(pkg, pkgbin, pnaw_never); + + varbuf_add_str(&db_path, pkg_infodb_get_dir()); + varbuf_add_char(&db_path, '/'); + varbuf_end_str(&db_path); + varbuf_snapshot(&db_path, &db_path_state); + + db_dir = opendir(db_path.buf); + if (!db_dir) + ohshite(_("cannot read info directory")); + + push_cleanup(cu_closedir, ~0, 1, (void *)db_dir); + while ((db_de = readdir(db_dir)) != NULL) { + const char *filename, *filetype, *dot; + + debug(dbg_veryverbose, "infodb foreach info file '%s'", + db_de->d_name); + + /* Ignore dotfiles, including ‘.’ and ‘..’. */ + if (db_de->d_name[0] == '.') + continue; + + /* Ignore anything odd. */ + dot = strrchr(db_de->d_name, '.'); + if (dot == NULL) + continue; + + /* Ignore files from other packages. */ + if (strlen(pkgname) != (size_t)(dot - db_de->d_name) || + strncmp(db_de->d_name, pkgname, dot - db_de->d_name)) + continue; + + debug(dbg_stupidlyverbose, "infodb foreach file this pkg"); + + /* Skip past the full stop. */ + filetype = dot + 1; + + varbuf_rollback(&db_path, &db_path_state); + varbuf_add_str(&db_path, db_de->d_name); + varbuf_end_str(&db_path); + filename = db_path.buf; + + func(filename, filetype); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + varbuf_destroy(&db_path); +} diff --git a/lib/dpkg/db-ctrl-format.c b/lib/dpkg/db-ctrl-format.c new file mode 100644 index 000000000..a545ec5e7 --- /dev/null +++ b/lib/dpkg/db-ctrl-format.c @@ -0,0 +1,158 @@ +/* + * dpkg - main program for package management + * db-ctrl-format.c - package control information database format + * + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/varbuf.h> +#include <dpkg/db-ctrl.h> + +static enum pkg_infodb_format db_format = PKG_INFODB_FORMAT_UNKNOWN; +static bool db_upgrading; +static char *db_infodir; + +static enum pkg_infodb_format +pkg_infodb_parse_format(const char *file) +{ + FILE *fp; + unsigned int format; + + fp = fopen(file, "r"); + if (fp == NULL) { + /* A missing format file means legacy format (0). */ + if (errno == ENOENT) + return PKG_INFODB_FORMAT_LEGACY; + ohshite(_("error trying to open %.250s"), file); + } + + if (fscanf(fp, "%u", &format) != 1) + ohshit(_("corrupt info database format file '%s'"), file); + + fclose(fp); + + return format; +} + +static enum pkg_infodb_format +pkg_infodb_read_format(void) +{ + struct atomic_file *file; + struct stat st; + char *filename; + + filename = dpkg_db_get_path(INFODIR "/format"); + file = atomic_file_new(filename, 0); + + db_format = pkg_infodb_parse_format(file->name); + + /* Check if a previous upgrade got interrupted. Because we are only + * supposed to upgrade the db layout one format at a time, if the + * new file exists that means the new format is just one ahead, + * we don't try to read it because it contains unreliable data. */ + if (stat(file->name_new, &st) == 0) { + db_format++; + db_upgrading = true; + } + + atomic_file_free(file); + free(filename); + + if (db_format < 0 || db_format >= PKG_INFODB_FORMAT_LAST) + ohshit(_("info database format (%d) is bogus or too new; " + "try getting a newer dpkg"), db_format); + + return db_format; +} + +enum pkg_infodb_format +pkg_infodb_get_format(void) +{ + if (db_format > PKG_INFODB_FORMAT_UNKNOWN) + return db_format; + else + return pkg_infodb_read_format(); +} + +void +pkg_infodb_set_format(enum pkg_infodb_format version) +{ + db_format = version; +} + +bool +pkg_infodb_is_upgrading(void) +{ + if (db_format < 0) + pkg_infodb_read_format(); + + return db_upgrading; +} + +const char * +pkg_infodb_get_dir(void) +{ + if (db_infodir == NULL) + db_infodir = dpkg_db_get_path(INFODIR); + + return db_infodir; +} + +const char * +pkg_infodb_get_file(const struct pkginfo *pkg, const struct pkgbin *pkgbin, + const char *filetype) +{ + static struct varbuf vb; + enum pkg_infodb_format format; + + /* Make sure to always read and verify the format version. */ + format = pkg_infodb_get_format(); + + varbuf_reset(&vb); + varbuf_add_str(&vb, pkg_infodb_get_dir()); + varbuf_add_char(&vb, '/'); + varbuf_add_str(&vb, pkg->set->name); + if (pkgbin->multiarch == PKG_MULTIARCH_SAME && + format == PKG_INFODB_FORMAT_MULTIARCH) + varbuf_add_archqual(&vb, pkgbin->arch); + varbuf_add_char(&vb, '.'); + varbuf_add_str(&vb, filetype); + varbuf_end_str(&vb); + + return vb.buf; +} + +const char * +pkg_infodb_reset_dir(void) +{ + free(db_infodir); + db_infodir = NULL; + + return pkg_infodb_get_dir(); +} diff --git a/lib/dpkg/db-ctrl-upgrade.c b/lib/dpkg/db-ctrl-upgrade.c new file mode 100644 index 000000000..9b6d735cc --- /dev/null +++ b/lib/dpkg/db-ctrl-upgrade.c @@ -0,0 +1,252 @@ +/* + * dpkg - main program for package management + * db-ctrl-upgrade.c - package control information database format upgrade + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/path.h> +#include <dpkg/dir.h> + +struct rename_node { + struct rename_node *next; + char *old; + char *new; +}; + +/* Global variables. */ +static struct rename_node *rename_head = NULL; + +static struct rename_node * +rename_node_new(const char *old, const char *new, struct rename_node *next) +{ + struct rename_node *node; + + node = m_malloc(sizeof(*node)); + node->next = next; + node->old = m_strdup(old); + node->new = m_strdup(new); + + return node; +} + +static void +rename_node_free(struct rename_node *node) +{ + free(node->old); + free(node->new); + free(node); +} + +static void +pkg_infodb_link_multiarch_files(void) +{ + DIR *db_dir; + struct dirent *db_de; + struct varbuf pkgname = VARBUF_INIT; + struct varbuf oldname = VARBUF_INIT; + struct varbuf newname = VARBUF_INIT; + struct varbuf_state db_path_state; + + varbuf_add_str(&oldname, pkg_infodb_get_dir()); + varbuf_add_char(&oldname, '/'); + varbuf_end_str(&oldname); + varbuf_snapshot(&oldname, &db_path_state); + + varbuf_add_buf(&newname, oldname.buf, oldname.used); + varbuf_end_str(&newname); + + db_dir = opendir(oldname.buf); + if (!db_dir) + ohshite(_("cannot read info directory")); + + push_cleanup(cu_closedir, ~0, 1, (void *)db_dir); + while ((db_de = readdir(db_dir)) != NULL) { + const char *filetype, *dot; + struct pkginfo *pkg; + struct pkgset *set; + + /* Ignore dotfiles, including ‘.’ and ‘..’. */ + if (db_de->d_name[0] == '.') + continue; + + /* Ignore anything odd. */ + dot = strrchr(db_de->d_name, '.'); + if (dot == NULL) + continue; + + varbuf_reset(&pkgname); + varbuf_add_buf(&pkgname, db_de->d_name, dot - db_de->d_name); + varbuf_end_str(&pkgname); + + /* Skip files already converted. */ + if (strchr(pkgname.buf, ':')) + continue; + + set = pkg_hash_find_set(pkgname.buf); + for (pkg = &set->pkg; pkg; pkg = pkg->arch_next) + if (pkg->status != PKG_STAT_NOTINSTALLED) + break; + if (!pkg) { + warning(_("info file %s/%s not associated to any package"), + pkg_infodb_get_dir(), db_de->d_name); + continue; + } + + /* Does it need to be upgraded? */ + if (pkg->installed.multiarch != PKG_MULTIARCH_SAME) + continue; + + /* Skip past the full stop. */ + filetype = dot + 1; + + varbuf_rollback(&oldname, &db_path_state); + varbuf_add_str(&oldname, db_de->d_name); + varbuf_end_str(&oldname); + + varbuf_rollback(&newname, &db_path_state); + varbuf_add_pkgbin_name(&newname, pkg, &pkg->installed, pnaw_always); + varbuf_add_char(&newname, '.'); + varbuf_add_str(&newname, filetype); + varbuf_end_str(&newname); + + if (link(oldname.buf, newname.buf) && errno != EEXIST) + ohshite(_("error creating hard link '%.255s'"), + newname.buf); + rename_head = rename_node_new(oldname.buf, newname.buf, rename_head); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + varbuf_destroy(&pkgname); + varbuf_destroy(&newname); + varbuf_destroy(&oldname); +} + +static void +cu_abort_db_upgrade(int argc, void **argv) +{ + struct atomic_file *file = argv[0]; + struct rename_node *next; + + /* Restore the old files if needed and drop the newly created files. */ + while (rename_head) { + next = rename_head->next; + if (link(rename_head->new, rename_head->old) && errno != EEXIST) + ohshite(_("error creating hard link '%.255s'"), + rename_head->old); + if (unlink(rename_head->new)) + ohshite(_("cannot remove '%.250s'"), rename_head->new); + rename_node_free(rename_head); + rename_head = next; + } + if (unlink(file->name_new) && errno != ENOENT) + ohshite(_("cannot remove '%.250s'"), file->name_new); + + atomic_file_free(file); +} + +static void +pkg_infodb_write_format(struct atomic_file *file, int version) +{ + if (fprintf(file->fp, "%d\n", version) < 0) + ohshite(_("error while writing '%s'"), file->name_new); + + atomic_file_sync(file); + atomic_file_close(file); + dir_sync_path_parent(file->name); + + pkg_infodb_set_format(version); +} + +static void +pkg_infodb_unlink_monoarch_files(void) +{ + struct rename_node *next; + + while (rename_head) { + next = rename_head->next; + if (unlink(rename_head->old)) + ohshite(_("cannot remove '%.250s'"), rename_head->old); + rename_node_free(rename_head); + rename_head = next; + } +} + +static void +pkg_infodb_upgrade_to_multiarch(void) +{ + struct atomic_file *db_file; + char *db_format_file; + + db_format_file = dpkg_db_get_path(INFODIR "/format"); + db_file = atomic_file_new(db_format_file, 0); + atomic_file_open(db_file); + + push_cleanup(cu_abort_db_upgrade, ehflag_bombout, 1, db_file); + + pkg_infodb_link_multiarch_files(); + pkg_infodb_write_format(db_file, 1); + + pkg_infodb_unlink_monoarch_files(); + atomic_file_commit(db_file); + dir_sync_path(pkg_infodb_get_dir()); + + pop_cleanup(ehflag_normaltidy); + + atomic_file_free(db_file); + free(db_format_file); +} + +/** + * Upgrade the infodb if there's the need and possibility. + * + * Currently this implies, that the modstatdb was opened for writing and: + * - previous upgrade has not been completed; or + * - current format is not the latest one. + */ +void +pkg_infodb_upgrade(void) +{ + enum pkg_infodb_format db_format; + + /* Make sure to always read and verify the format version. */ + db_format = pkg_infodb_get_format(); + + if (modstatdb_get_status() < msdbrw_write) + return; + + if (db_format < PKG_INFODB_FORMAT_MULTIARCH || + pkg_infodb_is_upgrading()) + pkg_infodb_upgrade_to_multiarch(); +} diff --git a/lib/dpkg/db-ctrl.h b/lib/dpkg/db-ctrl.h new file mode 100644 index 000000000..e4dccd285 --- /dev/null +++ b/lib/dpkg/db-ctrl.h @@ -0,0 +1,52 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-ctrl.h - package control information database + * + * Copyright © 2011-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DB_CTRL_H +#define LIBDPKG_DB_CTRL_H + +#include <stdbool.h> + +#include <dpkg/dpkg-db.h> + +enum pkg_infodb_format { + PKG_INFODB_FORMAT_UNKNOWN = -1, + PKG_INFODB_FORMAT_LEGACY = 0, + PKG_INFODB_FORMAT_MULTIARCH = 1, + PKG_INFODB_FORMAT_LAST, +}; + +enum pkg_infodb_format pkg_infodb_get_format(void); +void pkg_infodb_set_format(enum pkg_infodb_format format); +bool pkg_infodb_is_upgrading(void); +void pkg_infodb_upgrade(void); + +const char *pkg_infodb_get_dir(void); +const char *pkg_infodb_get_file(const struct pkginfo *pkg, const struct pkgbin *pkgbin, + const char *filetype); +const char *pkg_infodb_reset_dir(void); +bool pkg_infodb_has_file(struct pkginfo *pkg, struct pkgbin *pkgbin, + const char *name); + +typedef void pkg_infodb_file_func(const char *filename, const char *filetype); + +void pkg_infodb_foreach(struct pkginfo *pkg, struct pkgbin *pkgbin, + pkg_infodb_file_func *func); + +#endif /* LIBDPKG_DB_CTRL_H */ diff --git a/lib/dpkg/db-fsys-digest.c b/lib/dpkg/db-fsys-digest.c new file mode 100644 index 000000000..5b32d9179 --- /dev/null +++ b/lib/dpkg/db-fsys-digest.c @@ -0,0 +1,151 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-digest.c - management of filesystem digests database + * + * Copyright © 2012-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/debug.h> +#include <dpkg/fdio.h> +#include <dpkg/dir.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> + +/* + * If mask is nonzero, will not write any file whose fsys_namenode + * has any flag bits set in mask. + */ +void +write_filehash_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask) +{ + struct atomic_file *file; + const char *hashfile; + + debug(dbg_general, "generating infodb hashfile"); + + if (pkg_infodb_has_file(pkg, &pkg->available, HASHFILE)) + return; + + hashfile = pkg_infodb_get_file(pkg, pkgbin, HASHFILE); + + file = atomic_file_new(hashfile, 0); + atomic_file_open(file); + + for (; list; list = list->next) { + struct fsys_namenode *namenode = list->namenode; + + if (mask && (namenode->flags & mask)) + continue; + if (strcmp(namenode->newhash, EMPTYHASHFLAG) == 0) + continue; + + fprintf(file->fp, "%s %s\n", + namenode->newhash, namenode->name + 1); + } + + atomic_file_sync(file); + atomic_file_close(file); + atomic_file_commit(file); + atomic_file_free(file); + + dir_sync_path(pkg_infodb_get_dir()); +} + +static void +parse_filehash_buffer(struct varbuf *buf, + struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + char *thisline, *nextline; + const char *pkgname = pkg_name(pkg, pnaw_nonambig); + const char *buf_end = buf->buf + buf->used; + + for (thisline = buf->buf; thisline < buf_end; thisline = nextline) { + struct fsys_namenode *namenode; + char *endline, *hash_end, *filename; + + endline = memchr(thisline, '\n', buf_end - thisline); + if (endline == NULL) + ohshit(_("control file '%s' for package '%s' is " + "missing final newline"), HASHFILE, pkgname); + + /* The md5sum hash has a constant length. */ + hash_end = thisline + MD5HASHLEN; + + filename = hash_end + 2; + if (filename + 1 > endline) + ohshit(_("control file '%s' for package '%s' is " + "missing value"), HASHFILE, pkgname); + + if (hash_end[0] != ' ' || hash_end[1] != ' ') + ohshit(_("control file '%s' for package '%s' is " + "missing value separator"), HASHFILE, pkgname); + hash_end[0] = '\0'; + + /* Where to start next time around. */ + nextline = endline + 1; + + /* Strip trailing ‘/’. */ + if (endline > thisline && endline[-1] == '/') + endline--; + *endline = '\0'; + + if (endline == thisline) + ohshit(_("control file '%s' for package '%s' " + "contains empty filename"), HASHFILE, pkgname); + + debug(dbg_eachfiledetail, "load hash '%s' for filename '%s'", + thisline, filename); + + /* Add the file to the list. */ + namenode = fsys_hash_find_node(filename, 0); + namenode->newhash = nfstrsave(thisline); + } +} + +void +parse_filehash(struct pkginfo *pkg, struct pkgbin *pkgbin) +{ + const char *hashfile; + struct varbuf buf = VARBUF_INIT; + struct dpkg_error err = DPKG_ERROR_INIT; + + hashfile = pkg_infodb_get_file(pkg, pkgbin, HASHFILE); + + if (file_slurp(hashfile, &buf, &err) < 0 && err.syserrno != ENOENT) + dpkg_error_print(&err, + _("loading control file '%s' for package '%s'"), + HASHFILE, pkg_name(pkg, pnaw_nonambig)); + + if (buf.used > 0) + parse_filehash_buffer(&buf, pkg, pkgbin); + + varbuf_destroy(&buf); +} diff --git a/lib/dpkg/db-fsys-divert.c b/lib/dpkg/db-fsys-divert.c new file mode 100644 index 000000000..bf0b7ff5a --- /dev/null +++ b/lib/dpkg/db-fsys-divert.c @@ -0,0 +1,132 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-divert.c - management of filesystem diverted files database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/debug.h> +#include <dpkg/db-fsys.h> + +static struct fsys_diversion *diversions = NULL; +static char *diversionsname; + +void +ensure_diversions(void) +{ + static struct stat sb_prev; + struct stat sb_next; + char linebuf[MAXDIVERTFILENAME]; + static FILE *file_prev; + FILE *file; + struct fsys_diversion *ov, *oicontest, *oialtname; + + if (diversionsname == NULL) + diversionsname = dpkg_db_get_path(DIVERSIONSFILE); + + onerr_abort++; + + file = fopen(diversionsname, "r"); + if (!file) { + if (errno != ENOENT) + ohshite(_("failed to open diversions file")); + } else { + setcloexec(fileno(file), diversionsname); + + if (fstat(fileno(file), &sb_next)) + ohshite(_("failed to fstat diversions file")); + + /* + * We need to keep the database file open so that the + * filesystem cannot reuse the inode number (f.ex. during + * multiple dpkg-divert invocations in a maintainer script), + * otherwise the following check might turn true, and we + * would skip reloading a modified database. + */ + if (file_prev && + sb_prev.st_dev == sb_next.st_dev && + sb_prev.st_ino == sb_next.st_ino) { + fclose(file); + onerr_abort--; + debug(dbg_general, "%s: same, skipping", __func__); + return; + } + sb_prev = sb_next; + } + if (file_prev) + fclose(file_prev); + file_prev = file; + + for (ov = diversions; ov; ov = ov->next) { + ov->useinstead->divert->camefrom->divert = NULL; + ov->useinstead->divert = NULL; + } + diversions = NULL; + if (!file) { + onerr_abort--; + debug(dbg_general, "%s: none, resetting", __func__); + return; + } + debug(dbg_general, "%s: new, (re)loading", __func__); + + while (fgets_checked(linebuf, sizeof(linebuf), file, diversionsname) >= 0) { + oicontest = nfmalloc(sizeof(*oicontest)); + oialtname = nfmalloc(sizeof(*oialtname)); + + oialtname->camefrom = fsys_hash_find_node(linebuf, 0); + oialtname->useinstead = NULL; + + fgets_must(linebuf, sizeof(linebuf), file, diversionsname); + oicontest->useinstead = fsys_hash_find_node(linebuf, 0); + oicontest->camefrom = NULL; + + fgets_must(linebuf, sizeof(linebuf), file, diversionsname); + oicontest->pkgset = strcmp(linebuf, ":") ? + pkg_hash_find_set(linebuf) : NULL; + oialtname->pkgset = oicontest->pkgset; + + if (oialtname->camefrom->divert || + oicontest->useinstead->divert) + ohshit(_("conflicting diversions involving '%.250s' or '%.250s'"), + oialtname->camefrom->name, oicontest->useinstead->name); + + oialtname->camefrom->divert = oicontest; + oicontest->useinstead->divert = oialtname; + + oicontest->next = diversions; + diversions = oicontest; + } + + onerr_abort--; +} diff --git a/lib/dpkg/db-fsys-files.c b/lib/dpkg/db-fsys-files.c new file mode 100644 index 000000000..ffd26cee2 --- /dev/null +++ b/lib/dpkg/db-fsys-files.c @@ -0,0 +1,324 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-files.c - management of filesystem files database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#ifdef HAVE_LINUX_FIEMAP_H +#include <linux/fiemap.h> +#include <linux/fs.h> +#include <sys/ioctl.h> +#include <sys/vfs.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/string.h> +#include <dpkg/path.h> +#include <dpkg/dir.h> +#include <dpkg/fdio.h> +#include <dpkg/pkg-array.h> +#include <dpkg/pkg-files.h> +#include <dpkg/progress.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> + +/*** Generic data structures and routines. ***/ + +static bool allpackagesdone = false; + +void note_must_reread_files_inpackage(struct pkginfo *pkg) { + allpackagesdone = false; + pkg->files_list_valid = false; +} + +enum pkg_filesdb_load_status { + PKG_FILESDB_LOAD_NONE = 0, + PKG_FILESDB_LOAD_INPROGRESS = 1, + PKG_FILESDB_LOAD_DONE = 2, +}; + +static enum pkg_filesdb_load_status saidread = PKG_FILESDB_LOAD_NONE; + +/** + * Load the list of files in this package into memory, or update the + * list if it is there but stale. + */ +void +ensure_packagefiles_available(struct pkginfo *pkg) +{ + const char *filelistfile; + struct fsys_namenode_list **lendp; + char *loaded_list_end, *thisline, *nextline, *ptr; + struct varbuf buf = VARBUF_INIT; + struct dpkg_error err = DPKG_ERROR_INIT; + + if (pkg->files_list_valid) + return; + + /* Throw away any stale data, if there was any. */ + pkg_files_blank(pkg); + + /* Packages which aren't installed don't have a files list. */ + if (pkg->status == PKG_STAT_NOTINSTALLED) { + pkg->files_list_valid = true; + return; + } + + filelistfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); + + onerr_abort++; + + if (file_slurp(filelistfile, &buf, &err) < 0) { + if (err.syserrno != ENOENT) + dpkg_error_print(&err, _("loading files list file for package '%s'"), + pkg_name(pkg, pnaw_nonambig)); + + onerr_abort--; + if (pkg->status != PKG_STAT_CONFIGFILES && + dpkg_version_is_informative(&pkg->configversion)) { + warning(_("files list file for package '%.250s' missing; assuming " + "package has no files currently installed"), + pkg_name(pkg, pnaw_nonambig)); + } + pkg->files = NULL; + pkg->files_list_valid = true; + return; + } + + if (buf.used) { + loaded_list_end = buf.buf + buf.used; + + lendp = &pkg->files; + thisline = buf.buf; + while (thisline < loaded_list_end) { + struct fsys_namenode *namenode; + + ptr = memchr(thisline, '\n', loaded_list_end - thisline); + if (ptr == NULL) + ohshit(_("files list file for package '%.250s' is missing final newline"), + pkg_name(pkg, pnaw_nonambig)); + /* Where to start next time around. */ + nextline = ptr + 1; + /* Strip trailing ‘/’. */ + if (ptr > thisline && ptr[-1] == '/') ptr--; + /* Add the file to the list. */ + if (ptr == thisline) + ohshit(_("files list file for package '%.250s' contains empty filename"), + pkg_name(pkg, pnaw_nonambig)); + *ptr = '\0'; + + namenode = fsys_hash_find_node(thisline, 0); + lendp = pkg_files_add_file(pkg, namenode, lendp); + thisline = nextline; + } + } + + varbuf_destroy(&buf); + + onerr_abort--; + + pkg->files_list_valid = true; +} + +#if defined(HAVE_LINUX_FIEMAP_H) +static int +pkg_sorter_by_files_list_phys_offs(const void *a, const void *b) +{ + const struct pkginfo *pa = *(const struct pkginfo **)a; + const struct pkginfo *pb = *(const struct pkginfo **)b; + + /* We can't simply subtract, because the difference may be greater than + * INT_MAX. */ + if (pa->files_list_phys_offs < pb->files_list_phys_offs) + return -1; + else if (pa->files_list_phys_offs > pb->files_list_phys_offs) + return 1; + else + return 0; +} + +static void +pkg_files_optimize_load(struct pkg_array *array) +{ + struct statfs fs; + int i; + + /* Get the filesystem block size. */ + if (statfs(pkg_infodb_get_dir(), &fs) < 0) + return; + + /* Sort packages by the physical location of their list files, so that + * scanning them later will minimize disk drive head movements. */ + for (i = 0; i < array->n_pkgs; i++) { + struct pkginfo *pkg = array->pkgs[i]; + struct { + struct fiemap fiemap; + struct fiemap_extent extent; + } fm; + const char *listfile; + int fd; + + if (pkg->status == PKG_STAT_NOTINSTALLED || + pkg->files_list_phys_offs != 0) + continue; + + pkg->files_list_phys_offs = -1; + + listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); + + fd = open(listfile, O_RDONLY); + if (fd < 0) + continue; + + memset(&fm, 0, sizeof(fm)); + fm.fiemap.fm_start = 0; + fm.fiemap.fm_length = fs.f_bsize; + fm.fiemap.fm_flags = 0; + fm.fiemap.fm_extent_count = 1; + + if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)&fm) == 0) + pkg->files_list_phys_offs = fm.fiemap.fm_extents[0].fe_physical; + + close(fd); + } + + pkg_array_sort(array, pkg_sorter_by_files_list_phys_offs); +} +#elif defined(HAVE_POSIX_FADVISE) +static void +pkg_files_optimize_load(struct pkg_array *array) +{ + int i; + + /* Ask the kernel to start preloading the list files, so as to get a + * boost when later we actually load them. */ + for (i = 0; i < array->n_pkgs; i++) { + struct pkginfo *pkg = array->pkgs[i]; + const char *listfile; + int fd; + + listfile = pkg_infodb_get_file(pkg, &pkg->installed, LISTFILE); + + fd = open(listfile, O_RDONLY | O_NONBLOCK); + if (fd != -1) { + posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED); + close(fd); + } + } +} +#else +static void +pkg_files_optimize_load(struct pkg_array *array) +{ +} +#endif + +void ensure_allinstfiles_available(void) { + struct pkg_array array; + struct pkginfo *pkg; + struct progress progress; + int i; + + if (allpackagesdone) return; + if (saidread < PKG_FILESDB_LOAD_DONE) { + int max = pkg_hash_count_pkg(); + + saidread = PKG_FILESDB_LOAD_INPROGRESS; + progress_init(&progress, _("(Reading database ... "), max); + } + + pkg_array_init_from_hash(&array); + + pkg_files_optimize_load(&array); + + for (i = 0; i < array.n_pkgs; i++) { + pkg = array.pkgs[i]; + ensure_packagefiles_available(pkg); + + if (saidread == PKG_FILESDB_LOAD_INPROGRESS) + progress_step(&progress); + } + + pkg_array_destroy(&array); + + allpackagesdone = true; + + if (saidread == PKG_FILESDB_LOAD_INPROGRESS) { + progress_done(&progress); + printf(P_("%d file or directory currently installed.)\n", + "%d files and directories currently installed.)\n", + fsys_hash_entries()), + fsys_hash_entries()); + saidread = PKG_FILESDB_LOAD_DONE; + } +} + +void ensure_allinstfiles_available_quiet(void) { + saidread = PKG_FILESDB_LOAD_DONE; + ensure_allinstfiles_available(); +} + +/* + * If mask is nonzero, will not write any file whose fsys_namenode + * has any flag bits set in mask. + */ +void +write_filelist_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask) +{ + struct atomic_file *file; + struct fsys_namenode_list *node; + const char *listfile; + + listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE); + + file = atomic_file_new(listfile, 0); + atomic_file_open(file); + + for (node = list; node; node = node->next) { + if (!(mask && (node->namenode->flags & mask))) { + fputs(node->namenode->name, file->fp); + putc('\n', file->fp); + } + } + + atomic_file_sync(file); + atomic_file_close(file); + atomic_file_commit(file); + atomic_file_free(file); + + dir_sync_path(pkg_infodb_get_dir()); + + note_must_reread_files_inpackage(pkg); +} diff --git a/lib/dpkg/db-fsys-override.c b/lib/dpkg/db-fsys-override.c new file mode 100644 index 000000000..e079c5ffb --- /dev/null +++ b/lib/dpkg/db-fsys-override.c @@ -0,0 +1,268 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys-override.c - management of filesystem stat overrides database + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2012 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/fdio.h> +#include <dpkg/debug.h> +#include <dpkg/db-fsys.h> + +static char *statoverridename; + +uid_t +statdb_parse_uid(const char *str) +{ + char *endptr; + uid_t uid; + + if (str[0] == '#') { + long int value; + + errno = 0; + value = strtol(str + 1, &endptr, 10); + if (str + 1 == endptr || *endptr || value < 0 || errno != 0) + ohshit(_("invalid statoverride uid %s"), str); + uid = (uid_t)value; + } else { + struct passwd *pw = getpwnam(str); + + if (pw == NULL) + uid = (uid_t)-1; + else + uid = pw->pw_uid; + } + + return uid; +} + +gid_t +statdb_parse_gid(const char *str) +{ + char *endptr; + gid_t gid; + + if (str[0] == '#') { + long int value; + + errno = 0; + value = strtol(str + 1, &endptr, 10); + if (str + 1 == endptr || *endptr || value < 0 || errno != 0) + ohshit(_("invalid statoverride gid %s"), str); + gid = (gid_t)value; + } else { + struct group *gr = getgrnam(str); + + if (gr == NULL) + gid = (gid_t)-1; + else + gid = gr->gr_gid; + } + + return gid; +} + +mode_t +statdb_parse_mode(const char *str) +{ + char *endptr; + long int mode; + + mode = strtol(str, &endptr, 8); + if (str == endptr || *endptr || mode < 0 || mode > 07777) + ohshit(_("invalid statoverride mode %s"), str); + + return (mode_t)mode; +} + +void +ensure_statoverrides(enum statdb_parse_flags flags) +{ + static struct stat sb_prev; + struct stat sb_next; + static FILE *file_prev; + FILE *file; + char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr; + struct file_stat *fso; + struct fsys_namenode *fnn; + struct fsys_hash_iter *iter; + + if (statoverridename == NULL) + statoverridename = dpkg_db_get_path(STATOVERRIDEFILE); + + onerr_abort++; + + file = fopen(statoverridename, "r"); + if (!file) { + if (errno != ENOENT) + ohshite(_("failed to open statoverride file")); + } else { + setcloexec(fileno(file), statoverridename); + + if (fstat(fileno(file), &sb_next)) + ohshite(_("failed to fstat statoverride file")); + + /* + * We need to keep the database file open so that the + * filesystem cannot reuse the inode number (f.ex. during + * multiple dpkg-statoverride invocations in a maintainer + * script), otherwise the following check might turn true, + * and we would skip reloading a modified database. + */ + if (file_prev && + sb_prev.st_dev == sb_next.st_dev && + sb_prev.st_ino == sb_next.st_ino) { + fclose(file); + onerr_abort--; + debug(dbg_general, "%s: same, skipping", __func__); + return; + } + sb_prev = sb_next; + } + if (file_prev) + fclose(file_prev); + file_prev = file; + + /* Reset statoverride information. */ + iter = fsys_hash_iter_new(); + while ((fnn = fsys_hash_iter_next(iter))) + fnn->statoverride = NULL; + fsys_hash_iter_free(iter); + + if (!file) { + onerr_abort--; + debug(dbg_general, "%s: none, resetting", __func__); + return; + } + debug(dbg_general, "%s: new, (re)loading", __func__); + + /* If the statoverride list is empty we don't need to bother + * reading it. */ + if (!sb_next.st_size) { + onerr_abort--; + return; + } + + loaded_list = m_malloc(sb_next.st_size); + loaded_list_end = loaded_list + sb_next.st_size; + + if (fd_read(fileno(file), loaded_list, sb_next.st_size) < 0) + ohshite(_("reading statoverride file '%.250s'"), statoverridename); + + thisline = loaded_list; + while (thisline < loaded_list_end) { + fso = nfmalloc(sizeof(*fso)); + + ptr = memchr(thisline, '\n', loaded_list_end - thisline); + if (ptr == NULL) + ohshit(_("statoverride file is missing final newline")); + /* Where to start next time around. */ + nextline = ptr + 1; + if (ptr == thisline) + ohshit(_("statoverride file contains empty line")); + *ptr = '\0'; + + /* Extract the uid. */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->uid = statdb_parse_uid(thisline); + if (fso->uid == (uid_t)-1) + fso->uname = nfstrsave(thisline); + else + fso->uname = NULL; + + if (fso->uid == (uid_t)-1 && !(flags & STATDB_PARSE_LAX)) + ohshit(_("unknown system user '%s' in statoverride file; the system user got removed\n" + "before the override, which is most probably a packaging bug, to recover you\n" + "can remove the override manually with %s"), thisline, DPKGSTAT); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + /* Extract the gid */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->gid = statdb_parse_gid(thisline); + if (fso->gid == (gid_t)-1) + fso->gname = nfstrsave(thisline); + else + fso->gname = NULL; + + if (fso->gid == (gid_t)-1 && !(flags & STATDB_PARSE_LAX)) + ohshit(_("unknown system group '%s' in statoverride file; the system group got removed\n" + "before the override, which is most probably a packaging bug, to recover you\n" + "can remove the override manually with %s"), thisline, DPKGSTAT); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + /* Extract the mode */ + ptr = memchr(thisline, ' ', nextline - thisline); + if (ptr == NULL) + ohshit(_("syntax error in statoverride file")); + *ptr = '\0'; + + fso->mode = statdb_parse_mode(thisline); + + /* Move to the next bit */ + thisline = ptr + 1; + if (thisline >= loaded_list_end) + ohshit(_("unexpected end of line in statoverride file")); + + fnn = fsys_hash_find_node(thisline, 0); + if (fnn->statoverride) + ohshit(_("multiple statoverrides present for file '%.250s'"), + thisline); + fnn->statoverride = fso; + + /* Moving on... */ + thisline = nextline; + } + + free(loaded_list); + + onerr_abort--; +} diff --git a/lib/dpkg/db-fsys.h b/lib/dpkg/db-fsys.h new file mode 100644 index 000000000..339a0fb45 --- /dev/null +++ b/lib/dpkg/db-fsys.h @@ -0,0 +1,75 @@ +/* + * libdpkg - Debian packaging suite library routines + * db-fsys.h - management of database of files installed on system + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_DB_FSYS_H +#define LIBDPKG_DB_FSYS_H + +#include <dpkg/file.h> +#include <dpkg/fsys.h> + +/* + * Data structure here is as follows: + * + * For each package we have a ‘struct fsys_namenode_list *’, the head of a list of + * files in that package. They are in ‘forwards’ order. Each entry has a + * pointer to the ‘struct fsys_namenode’. + * + * The struct fsys_namenodes are in a hash table, indexed by name. + * (This hash table is not visible to callers.) + * + * Each fsys_namenode has a (possibly empty) list of ‘struct filepackage’, + * giving a list of the packages listing that filename. + * + * When we read files contained info about a particular package we set the + * ‘files’ member of the clientdata struct to the appropriate thing. When + * not yet set the files pointer is made to point to ‘fileslist_uninited’ + * (this is available only internally, within filesdb.c - the published + * interface is ensure_*_available). + */ + +struct pkginfo; + +void ensure_diversions(void); + +enum statdb_parse_flags { + STATDB_PARSE_NORMAL = 0, + STATDB_PARSE_LAX = 1, +}; + +uid_t statdb_parse_uid(const char *str); +gid_t statdb_parse_gid(const char *str); +mode_t statdb_parse_mode(const char *str); +void ensure_statoverrides(enum statdb_parse_flags flags); + +#define LISTFILE "list" +#define HASHFILE "md5sums" + +void ensure_packagefiles_available(struct pkginfo *pkg); +void ensure_allinstfiles_available(void); +void ensure_allinstfiles_available_quiet(void); +void note_must_reread_files_inpackage(struct pkginfo *pkg); +void parse_filehash(struct pkginfo *pkg, struct pkgbin *pkgbin); +void write_filelist_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask); +void write_filehash_except(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct fsys_namenode_list *list, enum fsys_namenode_flags mask); + +#endif /* LIBDPKG_DB_FSYS_H */ diff --git a/lib/dpkg/dbmodify.c b/lib/dpkg/dbmodify.c index ec1102107..763293170 100644 --- a/lib/dpkg/dbmodify.c +++ b/lib/dpkg/dbmodify.c @@ -27,7 +27,6 @@ #include <sys/types.h> #include <sys/wait.h> -#include <assert.h> #include <errno.h> #include <limits.h> #include <string.h> @@ -51,6 +50,7 @@ static bool db_initialized; static enum modstatdb_rw cstatus=-1, cflags=0; static char *lockfile; +static char *frontendlockfile; static char *statusfile, *availablefile; static char *importanttmpfile=NULL; static FILE *importanttmp; @@ -140,6 +140,7 @@ static const struct fni { char **store; } fnis[] = { { LOCKFILE, &lockfile }, + { FRONTENDLOCKFILE, &frontendlockfile }, { STATUSFILE, &statusfile }, { AVAILFILE, &availablefile }, { UPDATESDIR, &updatesdir }, @@ -185,6 +186,7 @@ modstatdb_done(void) } static int dblockfd = -1; +static int frontendlockfd = -1; bool modstatdb_is_locked(void) @@ -195,7 +197,8 @@ modstatdb_is_locked(void) if (dblockfd == -1) { lockfd = open(lockfile, O_RDONLY); if (lockfd == -1) - ohshite(_("unable to open lock file %s for testing"), lockfile); + ohshite(_("unable to check lock file for dpkg database directory %s"), + dpkg_db_get_dir()); } else { lockfd = dblockfd; } @@ -216,12 +219,26 @@ modstatdb_can_lock(void) if (dblockfd >= 0) return true; + if (getenv("DPKG_FRONTEND_LOCKED") == NULL) { + frontendlockfd = open(frontendlockfile, O_RDWR | O_CREAT | O_TRUNC, 0660); + if (frontendlockfd == -1) { + if (errno == EACCES || errno == EPERM) + return false; + else + ohshite(_("unable to open/create dpkg frontend lock for directory %s"), + dpkg_db_get_dir()); + } + } else { + frontendlockfd = -1; + } + dblockfd = open(lockfile, O_RDWR | O_CREAT | O_TRUNC, 0660); if (dblockfd == -1) { if (errno == EACCES || errno == EPERM) return false; else - ohshite(_("unable to open/create status database lockfile")); + ohshite(_("unable to open/create dpkg database lock file for directory %s"), + dpkg_db_get_dir()); } return true; @@ -231,9 +248,14 @@ void modstatdb_lock(void) { if (!modstatdb_can_lock()) - ohshit(_("you do not have permission to lock the dpkg status database")); - - file_lock(&dblockfd, FILE_LOCK_NOWAIT, lockfile, _("dpkg status database")); + ohshit(_("you do not have permission to lock the dpkg database directory %s"), + dpkg_db_get_dir()); + + if (frontendlockfd != -1) + file_lock(&frontendlockfd, FILE_LOCK_NOWAIT, frontendlockfile, + _("dpkg frontend lock")); + file_lock(&dblockfd, FILE_LOCK_NOWAIT, lockfile, + _("dpkg database lock")); } void @@ -241,8 +263,11 @@ modstatdb_unlock(void) { /* Unlock. */ pop_cleanup(ehflag_normaltidy); + if (frontendlockfd != -1) + pop_cleanup(ehflag_normaltidy); dblockfd = -1; + frontendlockfd = -1; } enum modstatdb_rw @@ -262,9 +287,11 @@ modstatdb_open(enum modstatdb_rw readwritereq) case msdbrw_write: case msdbrw_writeifposs: if (access(dpkg_db_get_dir(), W_OK)) { if (errno != EACCES) - ohshite(_("unable to access dpkg status area")); + ohshite(_("unable to access the dpkg database directory %s"), + dpkg_db_get_dir()); else if (readwritereq == msdbrw_write) - ohshit(_("operation requires read/write access to dpkg status area")); + ohshit(_("required read/write access to the dpkg database directory %s"), + dpkg_db_get_dir()); cstatus= msdbrw_readonly; } else { modstatdb_lock(); @@ -307,13 +334,19 @@ modstatdb_get_status(void) void modstatdb_checkpoint(void) { int i; - assert(cstatus >= msdbrw_write); + if (cstatus < msdbrw_write) + internerr("modstatdb status '%d' is not writable", cstatus); + writedb(statusfile, wdb_must_sync); for (i=0; i<nextupdate; i++) { sprintf(updatefnrest, IMPORTANTFMT, i); + /* Have we made a real mess? */ - assert(strlen(updatefnrest) <= IMPORTANTMAXLEN); + if (strlen(updatefnrest) > IMPORTANTMAXLEN) + internerr("modstatdb update entry name '%s' longer than %d", + updatefnrest, IMPORTANTMAXLEN); + if (unlink(updatefnbuf)) ohshite(_("failed to remove my own update file %.255s"),updatefnbuf); } @@ -341,7 +374,7 @@ void modstatdb_shutdown(void) { break; } - pkg_db_reset(); + pkg_hash_reset(); modstatdb_done(); } @@ -349,7 +382,8 @@ void modstatdb_shutdown(void) { static void modstatdb_note_core(struct pkginfo *pkg) { - assert(cstatus >= msdbrw_write); + if (cstatus < msdbrw_write) + internerr("modstatdb status '%d' is not writable", cstatus); varbuf_reset(&uvb); varbufrecord(&uvb, pkg, &pkg->installed); @@ -377,7 +411,9 @@ modstatdb_note_core(struct pkginfo *pkg) dir_sync_path(updatesdir); /* Have we made a real mess? */ - assert(strlen(updatefnrest) <= IMPORTANTMAXLEN); + if (strlen(updatefnrest) > IMPORTANTMAXLEN) + internerr("modstatdb update entry name '%s' longer than %d", + updatefnrest, IMPORTANTMAXLEN); nextupdate++; @@ -413,11 +449,15 @@ void modstatdb_note(struct pkginfo *pkg) { pkg->trigaw.head = pkg->trigaw.tail = NULL; } - log_message("status %s %s %s", pkg_status_name(pkg), - pkg_name(pkg, pnaw_always), - versiondescribe(&pkg->installed.version, vdew_nonambig)); - statusfd_send("status: %s: %s", pkg_name(pkg, pnaw_nonambig), - pkg_status_name(pkg)); + if (pkg->status_dirty) { + log_message("status %s %s %s", pkg_status_name(pkg), + pkg_name(pkg, pnaw_always), + versiondescribe_c(&pkg->installed.version, vdew_nonambig)); + statusfd_send("status: %s: %s", pkg_name(pkg, pnaw_nonambig), + pkg_status_name(pkg)); + + pkg->status_dirty = false; + } if (cstatus >= msdbrw_write) modstatdb_note_core(pkg); diff --git a/lib/dpkg/dpkg-db.h b/lib/dpkg/dpkg-db.h index bffd317ab..10de6d3e2 100644 --- a/lib/dpkg/dpkg-db.h +++ b/lib/dpkg/dpkg-db.h @@ -84,8 +84,8 @@ struct conffile { bool obsolete; }; -struct filedetails { - struct filedetails *next; +struct archivedetails { + struct archivedetails *next; const char *name; const char *msdosname; const char *size; @@ -204,11 +204,12 @@ struct pkginfo { const char *otherpriority; const char *section; struct dpkg_version configversion; - struct filedetails *files; struct pkgbin installed; struct pkgbin available; struct perpackagestate *clientdata; + struct archivedetails *archives; + struct { /* ->aw == this */ struct trigaw *head, *tail; @@ -217,6 +218,22 @@ struct pkginfo { /* ->pend == this, non-NULL for us when Triggers-Pending. */ struct trigaw *othertrigaw_head; struct trigpend *trigpend_head; + + /** + * files_list_valid files Meaning + * ---------------- ----- ------- + * false NULL Not read yet, must do so if want them. + * false !NULL Read, but rewritten and now out of date. If want + * info must throw away old and reread file. + * true !NULL Read, all is OK. + * true NULL Read OK, but, there were no files. + */ + struct fsys_namenode_list *files; + off_t files_list_phys_offs; + bool files_list_valid; + + /* The status has changed, it needs to be logged. */ + bool status_dirty; }; /** @@ -277,21 +294,34 @@ void pkg_blank(struct pkginfo *pp); void pkgbin_blank(struct pkgbin *pkgbin); bool pkg_is_informative(struct pkginfo *pkg, struct pkgbin *info); -struct pkgset *pkg_db_find_set(const char *name); -struct pkginfo *pkg_db_get_singleton(struct pkgset *set); -struct pkginfo *pkg_db_find_singleton(const char *name); -struct pkginfo *pkg_db_get_pkg(struct pkgset *set, const struct dpkg_arch *arch); -struct pkginfo *pkg_db_find_pkg(const char *name, const struct dpkg_arch *arch); -int pkg_db_count_set(void); -int pkg_db_count_pkg(void); -void pkg_db_reset(void); - -struct pkgiterator *pkg_db_iter_new(void); -struct pkgset *pkg_db_iter_next_set(struct pkgiterator *iter); -struct pkginfo *pkg_db_iter_next_pkg(struct pkgiterator *iter); -void pkg_db_iter_free(struct pkgiterator *iter); +struct pkgset * +pkg_hash_find_set(const char *name); +struct pkginfo * +pkg_hash_get_singleton(struct pkgset *set); +struct pkginfo * +pkg_hash_find_singleton(const char *name); +struct pkginfo * +pkg_hash_get_pkg(struct pkgset *set, const struct dpkg_arch *arch); +struct pkginfo * +pkg_hash_find_pkg(const char *name, const struct dpkg_arch *arch); +int +pkg_hash_count_set(void); +int +pkg_hash_count_pkg(void); +void +pkg_hash_reset(void); + +struct pkg_hash_iter * +pkg_hash_iter_new(void); +struct pkgset * +pkg_hash_iter_next_set(struct pkg_hash_iter *iter); +struct pkginfo * +pkg_hash_iter_next_pkg(struct pkg_hash_iter *iter); +void +pkg_hash_iter_free(struct pkg_hash_iter *iter); -void pkg_db_report(FILE *); +void +pkg_hash_report(FILE *); /*** from parse.c ***/ @@ -304,8 +334,8 @@ enum parsedbflags { pdb_rejectstatus = DPKG_BIT(2), /** Ignore priority/section info if we already have any. */ pdb_weakclassification = DPKG_BIT(3), - /** Ignore files info if we already have them. */ - pdb_ignorefiles = DPKG_BIT(4), + /** Ignore archives info if we already have them. */ + pdb_ignore_archives = DPKG_BIT(4), /** Ignore packages with older versions already read. */ pdb_ignoreolder = DPKG_BIT(5), /** Perform laxer version parsing. */ @@ -362,6 +392,8 @@ int parseversion(struct dpkg_version *version, const char *, struct dpkg_error *err); const char *versiondescribe(const struct dpkg_version *, enum versiondisplayepochwhen); +const char *versiondescribe_c(const struct dpkg_version *version, + enum versiondisplayepochwhen vdew); enum pkg_name_arch_when { /** Never display arch. */ @@ -377,9 +409,21 @@ enum pkg_name_arch_when { void varbuf_add_pkgbin_name(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum pkg_name_arch_when pnaw); -const char *pkgbin_name(struct pkginfo *pkg, struct pkgbin *pkgbin, - enum pkg_name_arch_when pnaw); -const char *pkg_name(struct pkginfo *pkg, enum pkg_name_arch_when pnaw); + +const char * +pkgbin_name_archqual(const struct pkginfo *pkg, const struct pkgbin *pkgbin); + +const char * +pkgbin_name(struct pkginfo *pkg, struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw); +const char * +pkg_name(struct pkginfo *pkg, enum pkg_name_arch_when pnaw); + +const char * +pkgbin_name_const(const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw); +const char * +pkg_name_const(const struct pkginfo *pkg, enum pkg_name_arch_when pnaw); void pkg_source_version(struct dpkg_version *version, @@ -407,6 +451,7 @@ enum writedb_flags { wdb_must_sync = DPKG_BIT(1), }; +void writedb_records(FILE *fp, const char *filename, enum writedb_flags flags); void writedb(const char *filename, enum writedb_flags flags); /* Note: The varbufs must have been initialized and will not be diff --git a/lib/dpkg/dpkg.h b/lib/dpkg/dpkg.h index 19b7914f4..2bb067af3 100644 --- a/lib/dpkg/dpkg.h +++ b/lib/dpkg/dpkg.h @@ -78,6 +78,7 @@ DPKG_BEGIN_DECLS #define STATUSFILE "status" #define AVAILFILE "available" #define LOCKFILE "lock" +#define FRONTENDLOCKFILE "lock-frontend" #define DIVERSIONSFILE "diversions" #define STATOVERRIDEFILE "statoverride" #define UPDATESDIR "updates/" @@ -145,6 +146,7 @@ char *m_strndup(const char *str, size_t n); int m_asprintf(char **strp, const char *fmt, ...) DPKG_ATTR_PRINTF(2); int m_vasprintf(char **strp, const char *fmt, va_list args) DPKG_ATTR_VPRINTF(2); +int m_dup(int oldfd); void m_dup2(int oldfd, int newfd); void m_pipe(int fds[2]); void m_output(FILE *f, const char *name); diff --git a/lib/dpkg/dump.c b/lib/dpkg/dump.c index b13eb6e79..48f0beb03 100644 --- a/lib/dpkg/dump.c +++ b/lib/dpkg/dump.c @@ -30,7 +30,6 @@ #include <sys/types.h> #include <sys/stat.h> -#include <assert.h> #include <errno.h> #include <string.h> #include <unistd.h> @@ -41,6 +40,8 @@ #include <dpkg/i18n.h> #include <dpkg/dpkg.h> #include <dpkg/dpkg-db.h> +#include <dpkg/pkg-array.h> +#include <dpkg/pkg-show.h> #include <dpkg/string.h> #include <dpkg/dir.h> #include <dpkg/parsedump.h> @@ -57,7 +58,9 @@ w_name(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { - assert(pkg->set->name); + if (pkg->set->name == NULL) + internerr("pkgset has no name"); + if (flags&fw_printheader) varbuf_add_str(vb, "Package: "); varbuf_add_str(vb, pkg->set->name); @@ -139,16 +142,16 @@ w_charfield(struct varbuf *vb, } void -w_filecharf(struct varbuf *vb, - const struct pkginfo *pkg, const struct pkgbin *pkgbin, - enum fwriteflags flags, const struct fieldinfo *fip) +w_archives(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) { - struct filedetails *fdp; + struct archivedetails *archive; if (pkgbin != &pkg->available) return; - fdp = pkg->files; - if (!fdp || !STRUCTFIELD(fdp, fip->integer, const char *)) + archive = pkg->archives; + if (!archive || !STRUCTFIELD(archive, fip->integer, const char *)) return; if (flags&fw_printheader) { @@ -156,10 +159,10 @@ w_filecharf(struct varbuf *vb, varbuf_add_char(vb, ':'); } - while (fdp) { + while (archive) { varbuf_add_char(vb, ' '); - varbuf_add_str(vb, STRUCTFIELD(fdp, fip->integer, const char *)); - fdp= fdp->next; + varbuf_add_str(vb, STRUCTFIELD(archive, fip->integer, const char *)); + archive = archive->next; } if (flags&fw_printheader) @@ -230,7 +233,11 @@ w_priority(struct varbuf *vb, { if (pkg->priority == PKG_PRIO_UNKNOWN) return; - assert(pkg->priority <= PKG_PRIO_UNKNOWN); + + if (pkg->priority > PKG_PRIO_UNKNOWN) + internerr("package %s has out-of-range priority %d", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->priority); + if (flags&fw_printheader) varbuf_add_str(vb, "Priority: "); varbuf_add_str(vb, pkg_priority_name(pkg)); @@ -245,38 +252,46 @@ w_status(struct varbuf *vb, { if (pkgbin != &pkg->installed) return; - assert(pkg->want <= PKG_WANT_PURGE); - assert(pkg->eflag <= PKG_EFLAG_REINSTREQ); -#define PEND pkg->trigpend_head -#define AW pkg->trigaw.head + if (pkg->want > PKG_WANT_PURGE) + internerr("package %s has unknown want state %d", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->want); + if (pkg->eflag > PKG_EFLAG_REINSTREQ) + internerr("package %s has unknown error state %d", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->eflag); + switch (pkg->status) { case PKG_STAT_NOTINSTALLED: case PKG_STAT_CONFIGFILES: - assert(!PEND); - assert(!AW); + if (pkg->trigpend_head || pkg->trigaw.head) + internerr("package %s in state %s, has awaited or pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_HALFINSTALLED: case PKG_STAT_UNPACKED: case PKG_STAT_HALFCONFIGURED: - assert(!PEND); + if (pkg->trigpend_head) + internerr("package %s in state %s, has pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_TRIGGERSAWAITED: - assert(AW); + if (pkg->trigaw.head == NULL) + internerr("package %s in state %s, has no awaited triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_TRIGGERSPENDING: - assert(PEND); - assert(!AW); + if (pkg->trigpend_head == NULL || pkg->trigaw.head) + internerr("package %s in stata %s, has awaited or no pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_INSTALLED: - assert(!PEND); - assert(!AW); + if (pkg->trigpend_head || pkg->trigaw.head) + internerr("package %s in state %s, has awaited or pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; default: internerr("unknown package status '%d'", pkg->status); } -#undef PEND -#undef AW if (flags&fw_printheader) varbuf_add_str(vb, "Status: "); @@ -295,7 +310,9 @@ void varbufdependency(struct varbuf *vb, struct dependency *dep) { possdel= ""; for (dop= dep->list; dop; dop= dop->next) { - assert(dop->up == dep); + if (dop->up != dep) + internerr("dependency and deppossi not linked properly"); + varbuf_add_str(vb, possdel); possdel = " | "; varbuf_add_str(vb, dop->ed->name); @@ -339,7 +356,10 @@ w_dependency(struct varbuf *vb, for (dyp = pkgbin->depends; dyp; dyp = dyp->next) { if (dyp->type != fip->integer) continue; - assert(dyp->up == pkg); + + if (dyp->up != pkg) + internerr("dependency and package %s not linked properly", + pkgbin_name_const(pkg, pkgbin, pnaw_always)); if (dep_found) { varbuf_add_str(vb, ", "); @@ -389,8 +409,10 @@ w_trigpend(struct varbuf *vb, if (pkgbin == &pkg->available || !pkg->trigpend_head) return; - assert(pkg->status >= PKG_STAT_TRIGGERSAWAITED && - pkg->status <= PKG_STAT_TRIGGERSPENDING); + if (pkg->status < PKG_STAT_TRIGGERSAWAITED || + pkg->status > PKG_STAT_TRIGGERSPENDING) + internerr("package %s in non-trigger state %s, has pending triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); if (flags & fw_printheader) varbuf_add_str(vb, "Triggers-Pending:"); @@ -412,8 +434,10 @@ w_trigaw(struct varbuf *vb, if (pkgbin == &pkg->available || !pkg->trigaw.head) return; - assert(pkg->status > PKG_STAT_CONFIGFILES && - pkg->status <= PKG_STAT_TRIGGERSAWAITED); + if (pkg->status <= PKG_STAT_CONFIGFILES || + pkg->status > PKG_STAT_TRIGGERSAWAITED) + internerr("package %s in state %s, has awaited triggers", + pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); if (flags & fw_printheader) varbuf_add_str(vb, "Triggers-Awaited:"); @@ -461,55 +485,69 @@ writerecord(FILE *file, const char *filename, varbufrecord(&vb, pkg, pkgbin); varbuf_end_str(&vb); - if (fputs(vb.buf,file) < 0) { - struct varbuf pkgname = VARBUF_INIT; - int errno_saved = errno; - varbuf_add_pkgbin_name(&pkgname, pkg, pkgbin, pnaw_nonambig); - - errno = errno_saved; + if (fputs(vb.buf, file) < 0) ohshite(_("failed to write details of '%.50s' to '%.250s'"), - pkgname.buf, filename); - } + pkgbin_name_const(pkg, pkgbin, pnaw_nonambig), filename); varbuf_destroy(&vb); } void -writedb(const char *filename, enum writedb_flags flags) +writedb_records(FILE *fp, const char *filename, enum writedb_flags flags) { static char writebuf[8192]; - struct pkgiterator *iter; + struct pkg_array array; struct pkginfo *pkg; struct pkgbin *pkgbin; const char *which; - struct atomic_file *file; struct varbuf vb = VARBUF_INIT; + int i; which = (flags & wdb_dump_available) ? "available" : "status"; - file = atomic_file_new(filename, ATOMIC_FILE_BACKUP); - atomic_file_open(file); - if (setvbuf(file->fp, writebuf, _IOFBF, sizeof(writebuf))) + if (setvbuf(fp, writebuf, _IOFBF, sizeof(writebuf))) ohshite(_("unable to set buffering on %s database file"), which); - iter = pkg_db_iter_new(); - while ((pkg = pkg_db_iter_next_pkg(iter)) != NULL) { + pkg_array_init_from_hash(&array); + pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); + + for (i = 0; i < array.n_pkgs; i++) { + pkg = array.pkgs[i]; pkgbin = (flags & wdb_dump_available) ? &pkg->available : &pkg->installed; + /* Don't dump records which have no useful content. */ if (!pkg_is_informative(pkg, pkgbin)) continue; + varbufrecord(&vb, pkg, pkgbin); varbuf_add_char(&vb, '\n'); varbuf_end_str(&vb); - if (fputs(vb.buf, file->fp) < 0) + if (fputs(vb.buf, fp) < 0) ohshite(_("failed to write %s database record about '%.50s' to '%.250s'"), which, pkgbin_name(pkg, pkgbin, pnaw_nonambig), filename); varbuf_reset(&vb); } - pkg_db_iter_free(iter); + + pkg_array_destroy(&array); varbuf_destroy(&vb); +} + +void +writedb(const char *filename, enum writedb_flags flags) +{ + struct atomic_file *file; + enum atomic_file_flags atomic_flags = ATOMIC_FILE_BACKUP; + + if (flags & wdb_dump_available) + atomic_flags = 0; + + file = atomic_file_new(filename, atomic_flags); + atomic_file_open(file); + + writedb_records(file->fp, filename, flags); + if (flags & wdb_must_sync) atomic_file_sync(file); diff --git a/lib/dpkg/ehandle.c b/lib/dpkg/ehandle.c index f21fb07e9..e525db003 100644 --- a/lib/dpkg/ehandle.c +++ b/lib/dpkg/ehandle.c @@ -135,7 +135,7 @@ error_context_new(void) { struct error_context *necp; - necp = malloc(sizeof(struct error_context)); + necp = malloc(sizeof(*necp)); if (!necp) ohshite(_("out of memory for new error context")); necp->next= econtext; @@ -190,7 +190,7 @@ error_context_errmsg_format(const char *fmt, va_list args) int rc; va_copy(args_copy, args); - rc = vasprintf(&errmsg, fmt, args); + rc = vasprintf(&errmsg, fmt, args_copy); va_end(args_copy); /* If the message was constructed successfully, at least we have some @@ -311,7 +311,7 @@ void push_checkpoint(int mask, int value) { struct cleanup_entry *cep; int i; - cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *)); + cep = malloc(sizeof(*cep) + sizeof(void *)); if (cep == NULL) { onerr_abort++; ohshite(_("out of memory for new cleanup entry")); @@ -324,9 +324,11 @@ void push_checkpoint(int mask, int value) { econtext->cleanups= cep; } -void push_cleanup(void (*call1)(int argc, void **argv), int mask1, +static void +cleanup_entry_new(void (*call1)(int argc, void **argv), int mask1, void (*call2)(int argc, void **argv), int mask2, - unsigned int nargs, ...) { + unsigned int nargs, va_list vargs) +{ struct cleanup_entry *cep; void **argv; int e = 0; @@ -334,7 +336,7 @@ void push_cleanup(void (*call1)(int argc, void **argv), int mask1, onerr_abort++; - cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *) * (nargs + 1)); + cep = malloc(sizeof(*cep) + sizeof(void *) * (nargs + 1)); if (!cep) { if (nargs > array_count(emergency.args)) ohshite(_("out of memory for new cleanup entry with many arguments")); @@ -343,7 +345,8 @@ void push_cleanup(void (*call1)(int argc, void **argv), int mask1, cep->calls[0].call= call1; cep->calls[0].mask= mask1; cep->calls[1].call= call2; cep->calls[1].mask= mask2; cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs; - va_start(args, nargs); + + va_copy(args, vargs); argv = cep->argv; while (nargs-- > 0) *argv++ = va_arg(args, void *); @@ -359,6 +362,29 @@ void push_cleanup(void (*call1)(int argc, void **argv), int mask1, onerr_abort--; } +void +push_cleanup(void (*call)(int argc, void **argv), int mask, + unsigned int nargs, ...) +{ + va_list args; + + va_start(args, nargs); + cleanup_entry_new(call, mask, NULL, 0, nargs, args); + va_end(args); +} + +void +push_cleanup_fallback(void (*call1)(int argc, void **argv), int mask1, + void (*call2)(int argc, void **argv), int mask2, + unsigned int nargs, ...) +{ + va_list args; + + va_start(args, nargs); + cleanup_entry_new(call1, mask1, call2, mask2, nargs, args); + va_end(args); +} + void pop_cleanup(int flagset) { struct cleanup_entry *cep; int i; diff --git a/lib/dpkg/ehandle.h b/lib/dpkg/ehandle.h index 370614812..36059048a 100644 --- a/lib/dpkg/ehandle.h +++ b/lib/dpkg/ehandle.h @@ -59,8 +59,10 @@ void push_error_context_func(error_handler_func *handler, void push_error_context(void); void pop_error_context(int flagset); -void push_cleanup(void (*f1)(int argc, void **argv), int flagmask1, - void (*f2)(int argc, void **argv), int flagmask2, +void push_cleanup_fallback(void (*f1)(int argc, void **argv), int flagmask1, + void (*f2)(int argc, void **argv), int flagmask2, + unsigned int nargs, ...); +void push_cleanup(void (*call)(int argc, void **argv), int flagmask, unsigned int nargs, ...); void push_checkpoint(int mask, int value); void pop_cleanup(int flagset); diff --git a/lib/dpkg/error.c b/lib/dpkg/error.c index b2b0e3f9c..025c558ac 100644 --- a/lib/dpkg/error.c +++ b/lib/dpkg/error.c @@ -29,8 +29,9 @@ #include <dpkg/varbuf.h> #include <dpkg/error.h> -static void DPKG_ATTR_VPRINTF(3) -dpkg_error_set(struct dpkg_error *err, int type, const char *fmt, va_list args) +static void DPKG_ATTR_VPRINTF(4) +dpkg_error_set(struct dpkg_error *err, enum dpkg_msg_type type, int syserrno, + const char *fmt, va_list args) { struct varbuf str = VARBUF_INIT; @@ -38,18 +39,25 @@ dpkg_error_set(struct dpkg_error *err, int type, const char *fmt, va_list args) return; err->type = type; + err->syserrno = syserrno; varbuf_vprintf(&str, fmt, args); err->str = str.buf; } +bool +dpkg_has_error(struct dpkg_error *err) +{ + return err != NULL && err->type != DPKG_MSG_NONE; +} + int dpkg_put_warn(struct dpkg_error *err, const char *fmt, ...) { va_list args; va_start(args, fmt); - dpkg_error_set(err, DPKG_MSG_WARN, fmt, args); + dpkg_error_set(err, DPKG_MSG_WARN, 0, fmt, args); va_end(args); return -1; @@ -61,7 +69,7 @@ dpkg_put_error(struct dpkg_error *err, const char *fmt, ...) va_list args; va_start(args, fmt); - dpkg_error_set(err, DPKG_MSG_ERROR, fmt, args); + dpkg_error_set(err, DPKG_MSG_ERROR, 0, fmt, args); va_end(args); return -1; @@ -72,11 +80,12 @@ dpkg_put_errno(struct dpkg_error *err, const char *fmt, ...) { va_list args; char *new_fmt; + int syserrno = errno; new_fmt = str_fmt("%s (%s)", fmt, strerror(errno)); va_start(args, fmt); - dpkg_error_set(err, DPKG_MSG_ERROR, new_fmt, args); + dpkg_error_set(err, DPKG_MSG_ERROR, syserrno, new_fmt, args); va_end(args); free(new_fmt); @@ -103,9 +112,19 @@ dpkg_error_print(struct dpkg_error *err, const char *fmt, ...) } void +dpkg_error_move(struct dpkg_error *dst, struct dpkg_error *src) +{ + dst->type = src->type; + src->type = DPKG_MSG_NONE; + dst->str = src->str; + src->str = NULL; +} + +void dpkg_error_destroy(struct dpkg_error *err) { err->type = DPKG_MSG_NONE; + err->syserrno = 0; free(err->str); err->str = NULL; } diff --git a/lib/dpkg/error.h b/lib/dpkg/error.h index 4d8b048df..9fd7ead21 100644 --- a/lib/dpkg/error.h +++ b/lib/dpkg/error.h @@ -21,6 +21,8 @@ #ifndef LIBDPKG_ERROR_H #define LIBDPKG_ERROR_H +#include <stdbool.h> + #include <dpkg/macros.h> DPKG_BEGIN_DECLS @@ -40,10 +42,15 @@ enum dpkg_msg_type { struct dpkg_error { enum dpkg_msg_type type; + int syserrno; char *str; }; -#define DPKG_ERROR_INIT { DPKG_MSG_NONE, NULL } +#define DPKG_ERROR_INIT { DPKG_MSG_NONE, 0, NULL } + +#define DPKG_ERROR_OBJECT (struct dpkg_error)DPKG_ERROR_INIT + +bool dpkg_has_error(struct dpkg_error *err); int dpkg_put_warn(struct dpkg_error *err, const char *fmt, ...) DPKG_ATTR_PRINTF(2); @@ -55,6 +62,7 @@ int dpkg_put_errno(struct dpkg_error *err, const char *fmt, ...) void dpkg_error_print(struct dpkg_error *err, const char *fmt, ...) DPKG_ATTR_PRINTF(2); +void dpkg_error_move(struct dpkg_error *dst, struct dpkg_error *src); void dpkg_error_destroy(struct dpkg_error *err); /** @} */ diff --git a/lib/dpkg/fields.c b/lib/dpkg/fields.c index aeb22b46c..8294149b9 100644 --- a/lib/dpkg/fields.c +++ b/lib/dpkg/fields.c @@ -62,20 +62,22 @@ enum parse_nv_flags { */ static int parse_nv(struct parsedb_state *ps, enum parse_nv_flags flags, - const char **strp, const struct namevalue *nv_head, const char *what) + const char **strp, const struct namevalue *nv_head) { const char *str_start = *strp, *str_end; const struct namevalue *nv; int value; + dpkg_error_destroy(&ps->err); + if (str_start[0] == '\0') - parse_error(ps, _("%s is missing"), what); + return dpkg_put_error(&ps->err, _("is missing a value")); nv = namevalue_find_by_name(nv_head, str_start); if (nv == NULL) { /* We got no match, skip further string validation. */ if (!(flags & PARSE_NV_FALLBACK)) - parse_error(ps, _("'%.50s' is not allowed for %s"), str_start, what); + return dpkg_put_error(&ps->err, _("has invalid value '%.50s'"), str_start); str_end = NULL; value = -1; @@ -87,7 +89,7 @@ parse_nv(struct parsedb_state *ps, enum parse_nv_flags flags, } if (!(flags & PARSE_NV_NEXT) && str_is_set(str_end)) - parse_error(ps, _("junk after %s"), what); + return dpkg_put_error(&ps->err, _("has trailing junk")); *strp = str_end; @@ -103,28 +105,28 @@ f_name(struct pkginfo *pkg, struct pkgbin *pkgbin, e = pkg_name_is_illegal(value); if (e != NULL) - parse_error(ps, _("invalid package name (%.250s)"), e); - /* We use the new name, as pkg_db_find_set() may have done a tolower for us. */ - pkg->set->name = pkg_db_find_set(value)->name; + parse_error(ps, _("invalid package name in '%s' field: %s"), fip->name, e); + /* We use the new name, as pkg_hash_find_set() may have done a tolower for us. */ + pkg->set->name = pkg_hash_find_set(value)->name; } void -f_filecharf(struct pkginfo *pkg, struct pkgbin *pkgbin, - struct parsedb_state *ps, - const char *value, const struct fieldinfo *fip) +f_archives(struct pkginfo *pkg, struct pkgbin *pkgbin, + struct parsedb_state *ps, + const char *value, const struct fieldinfo *fip) { - struct filedetails *fdp, **fdpp; + struct archivedetails *fdp, **fdpp; char *cpos, *space; int allowextend; if (!*value) - parse_error(ps, _("empty file details field '%s'"), fip->name); + parse_error(ps, _("empty archive details '%s' field"), fip->name); if (!(ps->flags & pdb_recordavailable)) parse_error(ps, - _("file details field '%s' not allowed in status file"), + _("archive details '%s' field not allowed in status file"), fip->name); - allowextend = !pkg->files; - fdpp = &pkg->files; + allowextend = !pkg->archives; + fdpp = &pkg->archives; cpos= nfstrsave(value); while (*cpos) { space = cpos; @@ -136,9 +138,9 @@ f_filecharf(struct pkginfo *pkg, struct pkgbin *pkgbin, if (!fdp) { if (!allowextend) parse_error(ps, - _("too many values in file details field '%s' " + _("too many values in archive details '%s' field " "(compared to others)"), fip->name); - fdp= nfmalloc(sizeof(struct filedetails)); + fdp = nfmalloc(sizeof(*fdp)); fdp->next= NULL; fdp->name= fdp->msdosname= fdp->size= fdp->md5sum= NULL; *fdpp= fdp; @@ -151,7 +153,7 @@ f_filecharf(struct pkginfo *pkg, struct pkgbin *pkgbin, } if (*fdpp) parse_error(ps, - _("too few values in file details field '%s' " + _("too few values in archive details '%s' field " "(compared to others)"), fip->name); } @@ -174,8 +176,11 @@ f_boolean(struct pkginfo *pkg, struct pkgbin *pkgbin, if (!*value) return; - boolean = parse_nv(ps, PARSE_NV_LAST, &value, booleaninfos, - _("yes/no in boolean field")); + boolean = parse_nv(ps, PARSE_NV_LAST, &value, booleaninfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("boolean (yes/no) '%s' field: %s"), + fip->name, ps->err.str); + STRUCTFIELD(pkgbin, fip->integer, bool) = boolean; } @@ -189,8 +194,10 @@ f_multiarch(struct pkginfo *pkg, struct pkgbin *pkgbin, if (!*value) return; - multiarch = parse_nv(ps, PARSE_NV_LAST, &value, multiarchinfos, - _("foreign/allowed/same/no in quadstate field")); + multiarch = parse_nv(ps, PARSE_NV_LAST, &value, multiarchinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("quadstate (foreign/allowed/same/no) '%s' field: %s"), + fip->name, ps->err.str); STRUCTFIELD(pkgbin, fip->integer, int) = multiarch; } @@ -201,8 +208,8 @@ f_architecture(struct pkginfo *pkg, struct pkgbin *pkgbin, { pkgbin->arch = dpkg_arch_find(value); if (pkgbin->arch->type == DPKG_ARCH_ILLEGAL) - parse_warn(ps, _("'%s' is not a valid architecture name: %s"), - value, dpkg_arch_name_is_illegal(value)); + parse_warn(ps, _("'%s' is not a valid architecture name in '%s' field: %s"), + value, fip->name, dpkg_arch_name_is_illegal(value)); } void @@ -225,7 +232,9 @@ f_priority(struct pkginfo *pkg, struct pkgbin *pkgbin, if (!*value) return; priority = parse_nv(ps, PARSE_NV_LAST | PARSE_NV_FALLBACK, &str, - priorityinfos, _("word in 'Priority' field")); + priorityinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("word in '%s' field: %s"), fip->name, ps->err.str); if (str == NULL) { pkg->priority = PKG_PRIO_OTHER; @@ -243,16 +252,22 @@ f_status(struct pkginfo *pkg, struct pkgbin *pkgbin, if (ps->flags & pdb_rejectstatus) parse_error(ps, _("value for '%s' field not allowed in this context"), - "Status"); + fip->name); if (ps->flags & pdb_recordavailable) return; - pkg->want = parse_nv(ps, PARSE_NV_NEXT, &value, wantinfos, - _("first (want) word in 'Status' field")); - pkg->eflag = parse_nv(ps, PARSE_NV_NEXT, &value, eflaginfos, - _("second (error) word in 'Status' field")); - pkg->status = parse_nv(ps, PARSE_NV_LAST, &value, statusinfos, - _("third (status) word in 'Status' field")); + pkg->want = parse_nv(ps, PARSE_NV_NEXT, &value, wantinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("first (want) word in '%s' field: %s"), + fip->name, ps->err.str); + pkg->eflag = parse_nv(ps, PARSE_NV_NEXT, &value, eflaginfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("second (error) word in '%s' field: %s"), + fip->name, ps->err.str); + pkg->status = parse_nv(ps, PARSE_NV_LAST, &value, statusinfos); + if (dpkg_has_error(&ps->err)) + parse_error(ps, _("third (status) word in '%s' field: %s"), + fip->name, ps->err.str); } void @@ -260,9 +275,8 @@ f_version(struct pkginfo *pkg, struct pkgbin *pkgbin, struct parsedb_state *ps, const char *value, const struct fieldinfo *fip) { - parse_db_version(ps, &pkgbin->version, value, - _("error in '%s' field string '%.250s'"), - "Version", value); + if (parse_db_version(ps, &pkgbin->version, value) < 0) + parse_problem(ps, _("'%s' field value '%.250s'"), fip->name, value); } void @@ -272,9 +286,7 @@ f_revision(struct pkginfo *pkg, struct pkgbin *pkgbin, { char *newversion; - parse_warn(ps, - _("obsolete '%s' or '%s' field used"), - "Revision", "Package-Revision"); + parse_warn(ps, _("obsolete '%s' field used"), fip->name); if (!*value) return; if (str_is_set(pkgbin->version.revision)) { newversion = nfmalloc(strlen(pkgbin->version.version) + @@ -294,14 +306,12 @@ f_configversion(struct pkginfo *pkg, struct pkgbin *pkgbin, if (ps->flags & pdb_rejectstatus) parse_error(ps, _("value for '%s' field not allowed in this context"), - "Config-Version"); + fip->name); if (ps->flags & pdb_recordavailable) return; - parse_db_version(ps, &pkg->configversion, value, - _("error in '%s' field string '%.250s'"), - "Config-Version", value); - + if (parse_db_version(ps, &pkg->configversion, value) < 0) + parse_problem(ps, _("'%s' field value '%.250s'"), fip->name, value); } /* @@ -348,8 +358,8 @@ f_conffiles(struct pkginfo *pkg, struct pkgbin *pkgbin, if (c == '\n') continue; if (c != ' ') parse_error(ps, - _("value for '%s' has line starting with non-space '%c'"), - "Conffiles", c); + _("value for '%s' field has line starting with non-space '%c'"), + fip->name, c); for (endent = value; (c = *endent) != '\0' && c != '\n'; endent++) ; conffvalue_lastword(value, endent, endent, &hashstart, &hashlen, &endfn, @@ -360,12 +370,13 @@ f_conffiles(struct pkginfo *pkg, struct pkgbin *pkgbin, conffvalue_lastword(value, endfn, endent, &hashstart, &hashlen, &endfn, ps); - newlink= nfmalloc(sizeof(struct conffile)); + newlink = nfmalloc(sizeof(*newlink)); value = path_skip_slash_dotslash(value); namelen= (int)(endfn-value); if (namelen <= 0) parse_error(ps, - _("root or null directory is listed as a conffile")); + _("root or empty directory listed as a conffile in '%s' field"), + fip->name); newptr = nfmalloc(namelen+2); newptr[0]= '/'; memcpy(newptr+1,value,namelen); @@ -408,7 +419,7 @@ f_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin, /* Loop creating new struct dependency's. */ for (;;) { - dyp= nfmalloc(sizeof(struct dependency)); + dyp = nfmalloc(sizeof(*dyp)); /* Set this to NULL for now, as we don't know what our real * struct pkginfo address (in the database) is going to be yet. */ dyp->up = NULL; @@ -438,9 +449,9 @@ f_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin, parse_error(ps, _("'%s' field, invalid package name '%.255s': %s"), fip->name, depname.buf, emsg); - dop= nfmalloc(sizeof(struct deppossi)); + dop = nfmalloc(sizeof(*dop)); dop->up= dyp; - dop->ed = pkg_db_find_set(depname.buf); + dop->ed = pkg_hash_find_set(depname.buf); dop->next= NULL; *ldopp= dop; ldopp= &dop->next; /* Don't link this (which is after all only ‘new_pkg’ from @@ -540,7 +551,7 @@ f_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin, if ((dop->verrel != DPKG_RELATION_EQ) && (fip->integer == dep_provides)) parse_warn(ps, _("only exact versions may be used for '%s' field"), - "Provides"); + fip->name); if (!c_isspace(*p) && !c_isalnum(*p)) { parse_warn(ps, @@ -577,9 +588,10 @@ f_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin, varbuf_reset(&version); varbuf_add_buf(&version, versionstart, versionlength); varbuf_end_str(&version); - parse_db_version(ps, &dop->version, version.buf, - _("'%s' field, reference to '%.255s': " - "error in version"), fip->name, depname.buf); + if (parse_db_version(ps, &dop->version, version.buf) < 0) + parse_problem(ps, + _("'%s' field, reference to '%.255s': version '%s'"), + fip->name, depname.buf, version.buf); p++; while (c_isspace(*p)) p++; @@ -596,8 +608,8 @@ f_dependency(struct pkginfo *pkg, struct pkgbin *pkgbin, fip->integer == dep_breaks || fip->integer == dep_provides || fip->integer == dep_replaces) - parse_error(ps, - _("alternatives ('|') not allowed in %s field"), fip->name); + parse_error(ps, _("alternatives ('|') not allowed in '%s' field"), + fip->name); p++; while (c_isspace(*p)) p++; @@ -656,7 +668,7 @@ f_trigpend(struct pkginfo *pend, struct pkgbin *pkgbin, if (ps->flags & pdb_rejectstatus) parse_error(ps, _("value for '%s' field not allowed in this context"), - "Triggers-Pending"); + fip->name); while ((word = scan_word(&value))) { emsg = trig_name_is_illegal(word); @@ -681,7 +693,7 @@ f_trigaw(struct pkginfo *aw, struct pkgbin *pkgbin, if (ps->flags & pdb_rejectstatus) parse_error(ps, _("value for '%s' field not allowed in this context"), - "Triggers-Awaited"); + fip->name); while ((word = scan_word(&value))) { struct dpkg_error err; diff --git a/lib/dpkg/file.c b/lib/dpkg/file.c index f8afdeb8a..ceda51047 100644 --- a/lib/dpkg/file.c +++ b/lib/dpkg/file.c @@ -25,15 +25,15 @@ #include <sys/types.h> #include <sys/stat.h> -#include <assert.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <dpkg/dpkg.h> #include <dpkg/i18n.h> -#include <dpkg/subproc.h> -#include <dpkg/command.h> +#include <dpkg/pager.h> +#include <dpkg/fdio.h> +#include <dpkg/buffer.h> #include <dpkg/file.h> /** @@ -61,6 +61,49 @@ file_copy_perms(const char *src, const char *dst) ohshite(_("unable to set mode of target file '%.250s'"), dst); } +static int +file_slurp_fd(int fd, const char *filename, struct varbuf *vb, + struct dpkg_error *err) +{ + struct stat st; + + if (fstat(fd, &st) < 0) + return dpkg_put_errno(err, _("cannot stat %s"), filename); + + if (!S_ISREG(st.st_mode)) + return dpkg_put_error(err, _("%s is not a regular file"), + filename); + + if (st.st_size == 0) + return 0; + + varbuf_init(vb, st.st_size); + if (fd_read(fd, vb->buf, st.st_size) < 0) + return dpkg_put_errno(err, _("cannot read %s"), filename); + vb->used = st.st_size; + + return 0; +} + +int +file_slurp(const char *filename, struct varbuf *vb, struct dpkg_error *err) +{ + int fd; + int rc; + + varbuf_init(vb, 0); + + fd = open(filename, O_RDONLY); + if (fd < 0) + return dpkg_put_errno(err, _("cannot open %s"), filename); + + rc = file_slurp_fd(fd, filename, vb, err); + + (void)close(fd); + + return rc; +} + static void file_lock_setup(struct flock *fl, short type) { @@ -75,25 +118,27 @@ file_lock_setup(struct flock *fl, short type) * Unlock a previously locked file. */ void -file_unlock(int lockfd, const char *lock_desc) +file_unlock(int lockfd, const char *lockfile, const char *lockdesc) { struct flock fl; - assert(lockfd >= 0); + if (lockfd < 0) + internerr("%s (%s) fd is %d < 0", lockdesc, lockfile, lockfd); file_lock_setup(&fl, F_UNLCK); if (fcntl(lockfd, F_SETLK, &fl) == -1) - ohshite(_("unable to unlock %s"), lock_desc); + ohshite(_("unable to unlock %s"), lockdesc); } static void file_unlock_cleanup(int argc, void **argv) { int lockfd = *(int *)argv[0]; - const char *lock_desc = argv[1]; + const char *lockfile = argv[1]; + const char *lockdesc = argv[2]; - file_unlock(lockfd, lock_desc); + file_unlock(lockfd, lockfile, lockdesc); } /** @@ -144,34 +189,50 @@ file_lock(int *lockfd, enum file_lock_flags flags, const char *filename, lock_cmd = F_SETLK; if (fcntl(*lockfd, lock_cmd, &fl) == -1) { - if (errno == EACCES || errno == EAGAIN) - ohshit(_("%s is locked by another process"), desc); - else + const char *warnmsg; + + if (errno != EACCES && errno != EAGAIN) ohshite(_("unable to lock %s"), desc); + + warnmsg = _("Note: removing the lock file is always wrong, " + "and can end up damaging the\n" + "locked area and the entire system. " + "See <https://wiki.debian.org/Teams/Dpkg/FAQ>."); + + file_lock_setup(&fl, F_WRLCK); + if (fcntl(*lockfd, F_GETLK, &fl) == -1) + ohshit(_("%s was locked by another process\n%s"), + desc, warnmsg); + + ohshit(_("%s was locked by another process with pid %d\n%s"), + desc, fl.l_pid, warnmsg); } - push_cleanup(file_unlock_cleanup, ~0, NULL, 0, 2, lockfd, desc); + push_cleanup(file_unlock_cleanup, ~0, 3, lockfd, filename, desc); } void file_show(const char *filename) { - pid_t pid; + struct pager *pager; + struct dpkg_error err; + int fd, rc; if (filename == NULL) - internerr("file '%s' does not exist", filename); + internerr("filename is NULL"); + + fd = open(filename, O_RDONLY); + if (fd < 0) + ohshite(_("cannot open file %s"), filename); - pid = subproc_fork(); - if (pid == 0) { - struct command cmd; - const char *pager; + pager = pager_spawn(_("pager to show file")); + rc = fd_fd_copy(fd, STDOUT_FILENO, -1, &err); + pager_reap(pager); - pager = command_get_pager(); + close(fd); - command_init(&cmd, pager, _("showing file on pager")); - command_add_arg(&cmd, pager); - command_add_arg(&cmd, filename); - command_exec(&cmd); + if (rc < 0 && err.syserrno != EPIPE) { + errno = err.syserrno; + ohshite(_("cannot write file %s into the pager"), filename); } - subproc_reap(pid, _("showing file on pager"), SUBPROC_NOCHECK); } diff --git a/lib/dpkg/file.h b/lib/dpkg/file.h index a1dd270ec..ead11c73a 100644 --- a/lib/dpkg/file.h +++ b/lib/dpkg/file.h @@ -26,6 +26,8 @@ #include <stdbool.h> #include <dpkg/macros.h> +#include <dpkg/error.h> +#include <dpkg/varbuf.h> DPKG_BEGIN_DECLS @@ -48,6 +50,9 @@ struct file_stat { void file_copy_perms(const char *src, const char *dst); +int +file_slurp(const char *filename, struct varbuf *vb, struct dpkg_error *err); + enum file_lock_flags { FILE_LOCK_NOWAIT, FILE_LOCK_WAIT, @@ -55,8 +60,8 @@ enum file_lock_flags { bool file_is_locked(int lockfd, const char *filename); void file_lock(int *lockfd, enum file_lock_flags flags, const char *filename, - const char *desc); -void file_unlock(int fd, const char *desc); + const char *filedesc); +void file_unlock(int fd, const char *filename, const char *filedesc); void file_show(const char *filename); /** @} */ diff --git a/lib/dpkg/fsys-dir.c b/lib/dpkg/fsys-dir.c new file mode 100644 index 000000000..15335b23e --- /dev/null +++ b/lib/dpkg/fsys-dir.c @@ -0,0 +1,93 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys-dir.c - filesystem root directory functions + * + * Copyright © 2011, 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/string.h> +#include <dpkg/path.h> +#include <dpkg/fsys.h> + +static const char *fsys_dir = ""; + +/** + * Set current on-disk filesystem root directory. + * + * The directory is initially set to "", this function can be used to + * set the directory to a new value, or to set it to a default value if dir + * is NULL. For the latter the order is, value from environment variable + * DPKG_ROOT, and then the built-in default "", + * + * @param dir The new filesystem root directory, or NULL to set to default. + * + * @return The new filesystem root directory. + */ +const char * +dpkg_fsys_set_dir(const char *dir) +{ + char *new_dir; + + if (dir == NULL) { + const char *env; + + env = getenv("DPKG_ROOT"); + if (env) + dir = env; + else + dir = ""; + } + + new_dir = m_strdup(dir); + path_trim_slash_slashdot(new_dir); + + fsys_dir = new_dir; + + return fsys_dir; +} + +/** + * Get current on-disk filesystem root directory. + * + * @return The current filesystem root directory. + */ +const char * +dpkg_fsys_get_dir(void) +{ + return fsys_dir; +} + +/** + * Get a pathname to the current on-disk filesystem root directory. + * + * This function returns an allocated string, which should be freed with + * free(2). + * + * @param pathpart The pathpart to append to the new pathname. + * + * @return The newly allocated pathname. + */ +char * +dpkg_fsys_get_path(const char *pathpart) +{ + return str_fmt("%s/%s", fsys_dir, pathpart); +} diff --git a/lib/dpkg/fsys-hash.c b/lib/dpkg/fsys-hash.c new file mode 100644 index 000000000..88ec47e4a --- /dev/null +++ b/lib/dpkg/fsys-hash.c @@ -0,0 +1,210 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys-hash.c - filesystem nodes hash table + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/string.h> +#include <dpkg/path.h> + +#include "fsys.h" + +/* This must always be a prime for optimal performance. + * This is the closest one to 2^18 (262144). */ +#define BINS 262139 + +static struct fsys_namenode *bins[BINS]; +static int nfiles = 0; + +void +fsys_hash_init(void) +{ + struct fsys_namenode *fnn; + int i; + + for (i = 0; i < BINS; i++) { + for (fnn = bins[i]; fnn; fnn = fnn->next) { + fnn->flags = 0; + fnn->oldhash = NULL; + fnn->newhash = EMPTYHASHFLAG; + fnn->file_ondisk_id = NULL; + } + } +} + +void +fsys_hash_reset(void) +{ + int i; + + for (i = 0; i < BINS; i++) + bins[i] = NULL; + + nfiles = 0; +} + +int +fsys_hash_entries(void) +{ + return nfiles; +} + +struct fsys_namenode * +fsys_hash_find_node(const char *name, enum fsys_hash_find_flags flags) +{ + struct fsys_namenode **pointerp, *newnode; + const char *orig_name = name; + + /* We skip initial slashes and ‘./’ pairs, and add our own single + * leading slash. */ + name = path_skip_slash_dotslash(name); + + pointerp = bins + (str_fnv_hash(name) % (BINS)); + while (*pointerp) { + /* XXX: This should not be needed, but it has been a constant + * source of assertions over the years. Hopefully with the + * internerr() we will get better diagnostics. */ + if ((*pointerp)->name[0] != '/') + internerr("filename node '%s' does not start with '/'", + (*pointerp)->name); + + if (strcmp((*pointerp)->name + 1, name) == 0) + break; + pointerp = &(*pointerp)->next; + } + if (*pointerp) + return *pointerp; + + if (flags & FHFF_NONE) + return NULL; + + newnode = nfmalloc(sizeof(*newnode)); + newnode->packages = NULL; + if ((flags & FHFF_NOCOPY) && name > orig_name && name[-1] == '/') { + newnode->name = name - 1; + } else { + char *newname = nfmalloc(strlen(name) + 2); + + newname[0] = '/'; + strcpy(newname + 1, name); + newnode->name = newname; + } + newnode->flags = 0; + newnode->next = NULL; + newnode->divert = NULL; + newnode->statoverride = NULL; + newnode->oldhash = NULL; + newnode->newhash = EMPTYHASHFLAG; + newnode->file_ondisk_id = NULL; + newnode->trig_interested = NULL; + *pointerp = newnode; + nfiles++; + + return newnode; +} + +void +fsys_hash_report(FILE *file) +{ + struct fsys_namenode *node; + int i, c; + int *freq; + int empty = 0, used = 0, collided = 0; + + freq = m_malloc(sizeof(freq[0]) * nfiles + 1); + for (i = 0; i <= nfiles; i++) + freq[i] = 0; + for (i = 0; i < BINS; i++) { + for (c = 0, node = bins[i]; node; c++, node = node->next); + fprintf(file, "fsys-hash: bin %5d has %7d\n", i, c); + if (c == 0) + empty++; + else if (c == 1) + used++; + else { + used++; + collided++; + } + freq[c]++; + } + for (i = nfiles; i > 0 && freq[i] == 0; i--); + while (i >= 0) { + fprintf(file, "fsys-hash: size %7d occurs %5d times\n", + i, freq[i]); + i--; + } + fprintf(file, "fsys-hash: bins empty %d\n", empty); + fprintf(file, "fsys-hash: bins used %d (collided %d)\n", used, + collided); + + m_output(file, "<hash report>"); + + free(freq); +} + +/* + * Forward iterator. + */ + +struct fsys_hash_iter { + struct fsys_namenode *namenode; + int nbinn; +}; + +struct fsys_hash_iter * +fsys_hash_iter_new(void) +{ + struct fsys_hash_iter *iter; + + iter = m_malloc(sizeof(*iter)); + iter->namenode = NULL; + iter->nbinn = 0; + + return iter; +} + +struct fsys_namenode * +fsys_hash_iter_next(struct fsys_hash_iter *iter) +{ + struct fsys_namenode *fnn = NULL; + + while (!iter->namenode) { + if (iter->nbinn >= BINS) + return NULL; + iter->namenode = bins[iter->nbinn++]; + } + fnn = iter->namenode; + iter->namenode = fnn->next; + + return fnn; +} + +void +fsys_hash_iter_free(struct fsys_hash_iter *iter) +{ + free(iter); +} diff --git a/lib/dpkg/fsys-iter.c b/lib/dpkg/fsys-iter.c new file mode 100644 index 000000000..807aabbb0 --- /dev/null +++ b/lib/dpkg/fsys-iter.c @@ -0,0 +1,126 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys-iter.c - filesystem nodes iterators + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-list.h> + +#include "fsys.h" + +/* + * Reverse iterator. + */ + +/* + * Initializes an iterator that appears to go through the file list ‘files’ + * in reverse order, returning the namenode from each. What actually happens + * is that we walk the list here, building up a reverse list, and then peel + * it apart one entry at a time. + */ +void +fsys_hash_rev_iter_init(struct fsys_hash_rev_iter *iter, + struct fsys_namenode_list *files) +{ + struct fsys_namenode_list *newent; + + iter->todo = NULL; + while (files) { + newent = m_malloc(sizeof(*newent)); + newent->namenode = files->namenode; + newent->next = iter->todo; + iter->todo = newent; + files = files->next; + } +} + +struct fsys_namenode * +fsys_hash_rev_iter_next(struct fsys_hash_rev_iter *iter) +{ + struct fsys_namenode *next; + struct fsys_namenode_list *todo; + + todo = iter->todo; + if (!todo) + return NULL; + next = todo->namenode; + iter->todo = todo->next; + free(todo); + + return next; +} + +/* + * Clients must call this function to clean up the fsys_hash_rev_iter + * if they wish to break out of the iteration before it is all done. + * Calling this function is not necessary if fsys_hash_rev_iter_next() has + * been called until it returned 0. + */ +void +fsys_hash_rev_iter_abort(struct fsys_hash_rev_iter *iter) +{ + while (fsys_hash_rev_iter_next(iter)) + ; +} + +/* + * Iterator for packages owning a file. + */ + +struct fsys_node_pkgs_iter { + struct pkg_list *pkg_node; +}; + +struct fsys_node_pkgs_iter * +fsys_node_pkgs_iter_new(struct fsys_namenode *fnn) +{ + struct fsys_node_pkgs_iter *iter; + + iter = m_malloc(sizeof(*iter)); + iter->pkg_node = fnn->packages; + + return iter; +} + +struct pkginfo * +fsys_node_pkgs_iter_next(struct fsys_node_pkgs_iter *iter) +{ + struct pkg_list *pkg_node; + + if (iter->pkg_node == NULL) + return NULL; + + pkg_node = iter->pkg_node; + iter->pkg_node = pkg_node->next; + + return pkg_node->pkg; +} + +void +fsys_node_pkgs_iter_free(struct fsys_node_pkgs_iter *iter) +{ + free(iter); +} diff --git a/lib/dpkg/fsys.h b/lib/dpkg/fsys.h new file mode 100644 index 000000000..8b9107472 --- /dev/null +++ b/lib/dpkg/fsys.h @@ -0,0 +1,204 @@ +/* + * libdpkg - Debian packaging suite library routines + * fsys.h - filesystem nodes hash table + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_FSYS_H +#define LIBDPKG_FSYS_H + +#include <stdio.h> + +#include <dpkg/file.h> + +/* + * Data structure here is as follows: + * + * For each package we have a ‘struct fsys_namenode_list *’, the head of a list of + * files in that package. They are in ‘forwards’ order. Each entry has a + * pointer to the ‘struct fsys_namenode’. + * + * The struct fsys_namenodes are in a hash table, indexed by name. + * (This hash table is not visible to callers.) + * + * Each fsys_namenode has a (possibly empty) list of ‘struct filepackage’, + * giving a list of the packages listing that filename. + * + * When we read files contained info about a particular package we set the + * ‘files’ member of the clientdata struct to the appropriate thing. When + * not yet set the files pointer is made to point to ‘fileslist_uninited’ + * (this is available only internally, within filesdb.c - the published + * interface is ensure_*_available). + */ + +struct pkginfo; + +/** + * Flags to fsys_hash_find_node(). + */ +enum fsys_hash_find_flags { + /** Do not need to copy filename. */ + FHFF_NOCOPY = DPKG_BIT(0), + /** The find function might return NULL. */ + FHFF_NONE = DPKG_BIT(1), +}; + +enum fsys_namenode_flags { + /** In the newconffiles list. */ + FNNF_NEW_CONFF = DPKG_BIT(0), + /** In the new filesystem archive. */ + FNNF_NEW_INARCHIVE = DPKG_BIT(1), + /** In the old package's conffiles list. */ + FNNF_OLD_CONFF = DPKG_BIT(2), + /** Obsolete conffile. */ + FNNF_OBS_CONFF = DPKG_BIT(3), + /** Must remove from other packages' lists. */ + FNNF_ELIDE_OTHER_LISTS = DPKG_BIT(4), + /** >= 1 instance is a dir, cannot rename over. */ + FNNF_NO_ATOMIC_OVERWRITE = DPKG_BIT(5), + /** New file has been placed on the disk. */ + FNNF_PLACED_ON_DISK = DPKG_BIT(6), + FNNF_DEFERRED_FSYNC = DPKG_BIT(7), + FNNF_DEFERRED_RENAME = DPKG_BIT(8), + /** Path being filtered. */ + FNNF_FILTERED = DPKG_BIT(9), +}; + +/** + * Stores information to uniquely identify an on-disk file. + */ +struct file_ondisk_id { + dev_t id_dev; + ino_t id_ino; +}; + +struct fsys_namenode { + struct fsys_namenode *next; + const char *name; + struct pkg_list *packages; + struct fsys_diversion *divert; + + /** We allow the administrator to override the owner, group and mode + * of a file. If such an override is present we use that instead of + * the stat information stored in the archive. + * + * This functionality used to be in the suidmanager package. */ + struct file_stat *statoverride; + + struct trigfileint *trig_interested; + + /* + * Fields from here on are used by archives.c &c, and cleared by + * fsys_hash_init(). + */ + + /** Set to zero when a new node is created. */ + enum fsys_namenode_flags flags; + + /** Valid iff this namenode is in the newconffiles list. */ + const char *oldhash; + + /** Valid iff the file was unpacked and hashed on this run. */ + const char *newhash; + + struct file_ondisk_id *file_ondisk_id; +}; + +struct fsys_namenode_list { + struct fsys_namenode_list *next; + struct fsys_namenode *namenode; +}; + +/** + * Queue of fsys_namenode entries. + */ +struct fsys_namenode_queue { + struct fsys_namenode_list *head, **tail; +}; + +/** + * When we deal with an ‘overridden’ file, every package except the + * overriding one is considered to contain the other file instead. Both + * files have entries in the filesdb database, and they refer to each other + * via these diversion structures. + * + * The contested filename's fsys_namenode has an diversion entry with + * useinstead set to point to the redirected filename's fsys_namenode; the + * redirected fsys_namenode has camefrom set to the contested fsys_namenode. + * Both sides' diversion entries will have pkg set to the package (if any) + * which is allowed to use the contended filename. + * + * Packages that contain either version of the file will all refer to the + * contested fsys_namenode in their per-file package lists (both in core and + * on disk). References are redirected to the other fsys_namenode's filename + * where appropriate. + */ +struct fsys_diversion { + struct fsys_namenode *useinstead; + struct fsys_namenode *camefrom; + struct pkgset *pkgset; + + /** The ‘contested’ halves are in this list for easy cleanup. */ + struct fsys_diversion *next; +}; + +struct fsys_node_pkgs_iter; +struct fsys_node_pkgs_iter * +fsys_node_pkgs_iter_new(struct fsys_namenode *fnn); +struct pkginfo * +fsys_node_pkgs_iter_next(struct fsys_node_pkgs_iter *iter); +void +fsys_node_pkgs_iter_free(struct fsys_node_pkgs_iter *iter); + +void +fsys_hash_init(void); +void +fsys_hash_reset(void); +void +fsys_hash_report(FILE *file); +int +fsys_hash_entries(void); + +struct fsys_hash_iter; +struct fsys_hash_iter * +fsys_hash_iter_new(void); +struct fsys_namenode * +fsys_hash_iter_next(struct fsys_hash_iter *iter); +void +fsys_hash_iter_free(struct fsys_hash_iter *iter); + +struct fsys_namenode * +fsys_hash_find_node(const char *filename, enum fsys_hash_find_flags flags); + +struct fsys_hash_rev_iter { + struct fsys_namenode_list *todo; +}; + +void +fsys_hash_rev_iter_init(struct fsys_hash_rev_iter *iter, + struct fsys_namenode_list *files); +struct fsys_namenode * +fsys_hash_rev_iter_next(struct fsys_hash_rev_iter *iter); +void +fsys_hash_rev_iter_abort(struct fsys_hash_rev_iter *iter); + +const char *dpkg_fsys_set_dir(const char *dir); +const char *dpkg_fsys_get_dir(void); +char *dpkg_fsys_get_path(const char *pathpart); + +#endif /* LIBDPKG_FSYS_H */ diff --git a/lib/dpkg/i18n.c b/lib/dpkg/i18n.c index 147c5756e..495270003 100644 --- a/lib/dpkg/i18n.c +++ b/lib/dpkg/i18n.c @@ -23,6 +23,10 @@ #include <dpkg/i18n.h> +#ifdef HAVE_USELOCALE +static locale_t dpkg_C_locale; +#endif + void dpkg_locales_init(const char *package) { @@ -30,6 +34,10 @@ dpkg_locales_init(const char *package) bindtextdomain(package, LOCALEDIR); textdomain(package); +#ifdef HAVE_USELOCALE + dpkg_C_locale = newlocale(LC_ALL_MASK, "C", (locale_t)0); +#endif + #if defined(__APPLE__) && defined(__MACH__) /* * On Mac OS X, the libintl code needs to call into the CoreFoundation @@ -44,3 +52,37 @@ dpkg_locales_init(const char *package) gettext(""); #endif } + +void +dpkg_locales_done(void) +{ +#ifdef HAVE_USELOCALE + freelocale(dpkg_C_locale); + dpkg_C_locale = (locale_t)0; +#endif +} + +struct dpkg_locale +dpkg_locale_switch_C(void) +{ + struct dpkg_locale loc; + +#ifdef HAVE_USELOCALE + loc.oldloc = uselocale(dpkg_C_locale); +#else + loc.oldloc = setlocale(LC_ALL, NULL); + setlocale(LC_ALL, "C"); +#endif + + return loc; +} + +void +dpkg_locale_switch_back(struct dpkg_locale loc) +{ +#ifdef HAVE_USELOCALE + uselocale(loc.oldloc); +#else + setlocale(LC_ALL, loc.oldloc); +#endif +} diff --git a/lib/dpkg/i18n.h b/lib/dpkg/i18n.h index e92158f15..f574433a2 100644 --- a/lib/dpkg/i18n.h +++ b/lib/dpkg/i18n.h @@ -43,6 +43,14 @@ DPKG_BEGIN_DECLS #define C_(ctxt, str) pgettext(ctxt, str) void dpkg_locales_init(const char *package); +void dpkg_locales_done(void); + +struct dpkg_locale { + void *oldloc; +}; + +struct dpkg_locale dpkg_locale_switch_C(void); +void dpkg_locale_switch_back(struct dpkg_locale loc); /** @} */ diff --git a/lib/dpkg/libdpkg.map b/lib/dpkg/libdpkg.map index 5e685c948..33297a8bf 100644 --- a/lib/dpkg/libdpkg.map +++ b/lib/dpkg/libdpkg.map @@ -1,14 +1,17 @@ LIBDPKG_0 { global: # Error reporting + dpkg_has_error; dpkg_put_warn; dpkg_put_error; dpkg_put_errno; dpkg_error_print; + dpkg_error_move; dpkg_error_destroy; # Charset and string functions dpkg_locales_init; + dpkg_locales_done; # Program name dpkg_set_progname; @@ -46,6 +49,7 @@ LIBDPKG_PRIVATE { catch_fatal_error; push_checkpoint; push_cleanup; + push_cleanup_fallback; pop_cleanup; onerr_abort; # XXX variable, do not export ohshitv; @@ -54,6 +58,8 @@ LIBDPKG_PRIVATE { do_internerr; dpkg_set_report_piped_mode; dpkg_set_report_buffer; + dpkg_warning_printer; + dpkg_set_warning_printer; warning_get_count; warningv; warning; @@ -79,6 +85,7 @@ LIBDPKG_PRIVATE { m_strdup; m_vasprintf; m_asprintf; + m_dup; m_dup2; m_pipe; m_output; @@ -90,6 +97,7 @@ LIBDPKG_PRIVATE { str_match_end; str_fnv_hash; + str_concat; str_fmt; str_escape_fmt; str_strip_quotes; @@ -98,6 +106,7 @@ LIBDPKG_PRIVATE { str_gen_crop; # Variable buffer support + varbuf_new; varbuf_init; varbuf_reset; varbuf_grow; @@ -114,6 +123,7 @@ LIBDPKG_PRIVATE { varbuf_snapshot; varbuf_rollback; varbuf_destroy; + varbuf_free; # Path, directory and file functions secure_unlink_statted; @@ -130,6 +140,7 @@ LIBDPKG_PRIVATE { dir_sync_path_parent; dir_sync_contents; + treenode_get_name; treenode_get_mode; treenode_get_virtname; treenode_get_pathname; @@ -137,9 +148,11 @@ LIBDPKG_PRIVATE { treewalk_node; treewalk_next; treewalk_close; + treewalk; file_copy_perms; file_show; + file_slurp; atomic_file_new; atomic_file_open; @@ -176,10 +189,14 @@ LIBDPKG_PRIVATE { command_add_argv; command_add_args; command_exec; - command_get_pager; command_shell; command_destroy; + pager_get_exec; + pager_spawn; + pager_reap; + pager_enable; + setcloexec; # Compression support @@ -243,6 +260,7 @@ LIBDPKG_PRIVATE { dpkg_version_compare; dpkg_version_relate; versiondescribe; + versiondescribe_c; parseversion; # Architecture database @@ -278,8 +296,11 @@ LIBDPKG_PRIVATE { varbuf_add_source_version; pkgbin_name; pkg_name; + pkgbin_name_const; + pkg_name_const; pkg_source_version; - pkgbin_summary; + pkgbin_synopsis; + pkg_synopsis; pkg_abbrev_want; pkg_abbrev_status; pkg_abbrev_eflag; @@ -294,7 +315,7 @@ LIBDPKG_PRIVATE { pkg_list_prepend; # Package array handling - pkg_array_init_from_db; + pkg_array_init_from_hash; pkg_array_init_from_names; pkg_array_foreach; pkg_array_sort; @@ -307,7 +328,21 @@ LIBDPKG_PRIVATE { pkg_queue_push; pkg_queue_pop; + # Package in-core database functions + pkg_hash_find_set; + pkg_hash_find_singleton; + pkg_hash_find_pkg; + pkg_hash_get_singleton; + pkg_hash_count_set; + pkg_hash_count_pkg; + pkg_hash_reset; + pkg_hash_iter_new; + pkg_hash_iter_next_set; + pkg_hash_iter_next_pkg; + pkg_hash_iter_free; + # Package field handling + booleaninfos; # XXX variable, do not export fieldinfos; # XXX variable, do not export find_field_info; find_arbfield_info; @@ -330,18 +365,6 @@ LIBDPKG_PRIVATE { pkg_spec_iter_next_pkg; pkg_spec_iter_destroy; - # Package in-core database functions - pkg_db_find_set; - pkg_db_find_singleton; - pkg_db_find_pkg; - pkg_db_count_set; - pkg_db_count_pkg; - pkg_db_reset; - pkg_db_iter_new; - pkg_db_iter_next_set; - pkg_db_iter_next_pkg; - pkg_db_iter_free; - # Dependency and Conflict functions pkg_virtual_deppossi_satisfied; deparchsatisfied; @@ -360,6 +383,7 @@ LIBDPKG_PRIVATE { parsedb_parse; parsedb_close; parsedb; + writedb_records; writedb; dpkg_db_set_dir; @@ -380,6 +404,53 @@ LIBDPKG_PRIVATE { modstatdb_shutdown; modstatdb_done; + # Package on-disk control database support + pkg_infodb_foreach; + pkg_infodb_get_dir; + pkg_infodb_get_file; + pkg_infodb_has_file; + pkg_infodb_upgrade; + + # Package on-disk diversion database support + ensure_diversions; + + # Filesystem node hash support + fsys_hash_init; + fsys_hash_reset; + fsys_hash_entries; + fsys_hash_find_node; + fsys_hash_report; + + fsys_hash_iter_new; + fsys_hash_iter_next; + fsys_hash_iter_free; + + fsys_hash_rev_iter_init; + fsys_hash_rev_iter_next; + fsys_hash_rev_iter_abort; + + fsys_node_pkgs_iter_new; + fsys_node_pkgs_iter_next; + fsys_node_pkgs_iter_free; + + dpkg_fsys_set_dir; + dpkg_fsys_get_dir; + dpkg_fsys_get_path; + + # Package on-disk filesystem database support + parse_filehash; + write_filelist_except; + write_filehash_except; + ensure_packagefiles_available; + ensure_allinstfiles_available; + ensure_allinstfiles_available_quiet; + + # Package on-disk stat override database support + statdb_parse_uid; + statdb_parse_gid; + statdb_parse_mode; + ensure_statoverrides; + # Triggers support trig_name_is_illegal; trigdef_set_methods; diff --git a/lib/dpkg/log.c b/lib/dpkg/log.c index 3079f3ca6..c2221c819 100644 --- a/lib/dpkg/log.c +++ b/lib/dpkg/log.c @@ -83,7 +83,7 @@ statusfd_add(int fd) setcloexec(fd, _("<package status and progress file descriptor>")); - pipe_new = nfmalloc(sizeof(struct pipef)); + pipe_new = nfmalloc(sizeof(*pipe_new)); pipe_new->fd = fd; pipe_new->next = status_pipes; status_pipes = pipe_new; diff --git a/lib/dpkg/mlib.c b/lib/dpkg/mlib.c index 1470a7f61..4e5e5b1a8 100644 --- a/lib/dpkg/mlib.c +++ b/lib/dpkg/mlib.c @@ -24,6 +24,7 @@ #include <sys/types.h> +#include <errno.h> #include <string.h> #include <fcntl.h> #include <unistd.h> @@ -110,6 +111,19 @@ m_asprintf(char **strp, const char *fmt, ...) return n; } +int +m_dup(int oldfd) +{ + int newfd; + + newfd = dup(oldfd); + if (newfd >= 0) + return newfd; + + onerr_abort++; + ohshite(_("failed to dup for fd %d"), oldfd); +} + void m_dup2(int oldfd, int newfd) { const char *const stdstrings[]= { "in", "out", "err" }; @@ -130,7 +144,7 @@ void m_output(FILE *f, const char *name) { fflush(f); - if (ferror(f)) + if (ferror(f) && errno != EPIPE) ohshite(_("error writing to '%s'"), name); } diff --git a/lib/dpkg/options-parsers.c b/lib/dpkg/options-parsers.c index b65215027..c26864278 100644 --- a/lib/dpkg/options-parsers.c +++ b/lib/dpkg/options-parsers.c @@ -1,6 +1,6 @@ /* * dpkg - main program for package management - * options-helper.c - command-line options parser helpers + * options-parsers.c - command-line options parser helpers * * Copyright © 2014 Guillem Jover <guillem@debian.org> * diff --git a/lib/dpkg/pager.c b/lib/dpkg/pager.c new file mode 100644 index 000000000..4eab2f09e --- /dev/null +++ b/lib/dpkg/pager.c @@ -0,0 +1,145 @@ +/* + * libdpkg - Debian packaging suite library routines + * pager.c - pager execution support + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +#include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/string.h> +#include <dpkg/subproc.h> +#include <dpkg/command.h> +#include <dpkg/pager.h> + +static bool pager_enabled = true; + +void +pager_enable(bool enable) +{ + pager_enabled = enable; +} + +/** + * Get a suitable pager. + * + * @return A string representing a pager. + */ +const char * +pager_get_exec(void) +{ + const char *pager; + + if (!isatty(0) || !isatty(1)) + return CAT; + + pager = getenv("DPKG_PAGER"); + if (str_is_unset(pager)) + pager = getenv("PAGER"); + if (str_is_unset(pager)) + pager = DEFAULTPAGER; + + return pager; +} + +struct pager { + bool used; + const char *desc; + pid_t pid; + struct sigaction sigpipe; + int stdout_old; + int pipe[2]; +}; + +struct pager * +pager_spawn(const char *desc) +{ + struct sigaction sa; + struct pager *pager; + const char *exec; + + pager = m_calloc(1, sizeof(*pager)); + pager->used = isatty(0) && isatty(1); + pager->desc = desc; + + exec = pager_get_exec(); + if (strcmp(exec, CAT) == 0) + pager->used = false; + + if (!pager_enabled) + pager->used = false; + + if (!pager->used) + return pager; + + m_pipe(pager->pipe); + + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + + sigaction(SIGPIPE, &sa, &pager->sigpipe); + + pager->pid = subproc_fork(); + if (pager->pid == 0) { + /* Set better defaults for less if not already set. */ + setenv("LESS", "-FRSXMQ", 0); + + m_dup2(pager->pipe[0], 0); + close(pager->pipe[0]); + close(pager->pipe[1]); + + command_shell(exec, desc); + } + + pager->stdout_old = m_dup(1); + m_dup2(pager->pipe[1], 1); + close(pager->pipe[0]); + close(pager->pipe[1]); + + /* Force the output to fully buffered, because originally stdout was + * a tty, so it was set as line buffered. This way we send as much as + * possible to the pager, which will handle the output by itself. */ + setvbuf(stdout, NULL, _IOFBF, 0); + + return pager; +} + +void +pager_reap(struct pager *pager) +{ + if (!pager->used) + return; + + m_dup2(pager->stdout_old, 1); + subproc_reap(pager->pid, pager->desc, SUBPROC_NOPIPE); + + sigaction(SIGPIPE, &pager->sigpipe, NULL); + + free(pager); +} diff --git a/lib/dpkg/pager.h b/lib/dpkg/pager.h new file mode 100644 index 000000000..f9029d0a5 --- /dev/null +++ b/lib/dpkg/pager.h @@ -0,0 +1,54 @@ +/* + * libdpkg - Debian packaging suite library routines + * pager.h - pager execution support + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PAGER_H +#define LIBDPKG_PAGER_H + +#include <stdbool.h> + +#include <dpkg/macros.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pager Pager execution + * @ingroup dpkg-internal + * @{ + */ + +struct pager; + +void +pager_enable(bool enable); + +const char * +pager_get_exec(void); + +struct pager * +pager_spawn(const char *desc); + +void +pager_reap(struct pager *pager); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PAGER_H */ diff --git a/lib/dpkg/parse.c b/lib/dpkg/parse.c index 1b3bf13d8..f7979b8e5 100644 --- a/lib/dpkg/parse.c +++ b/lib/dpkg/parse.c @@ -28,7 +28,6 @@ #include <sys/mman.h> #endif -#include <assert.h> #include <fcntl.h> #include <string.h> #include <unistd.h> @@ -65,7 +64,6 @@ const struct fieldinfo fieldinfos[]= { { FIELD("Multi-Arch"), f_multiarch, w_multiarch, PKGIFPOFF(multiarch) }, { FIELD("Source"), f_charfield, w_charfield, PKGIFPOFF(source) }, { FIELD("Version"), f_version, w_version, PKGIFPOFF(version) }, - { FIELD("Revision"), f_revision, w_null }, { FIELD("Config-Version"), f_configversion, w_configversion }, { FIELD("Replaces"), f_dependency, w_dependency, dep_replaces }, { FIELD("Provides"), f_dependency, w_dependency, dep_provides }, @@ -77,25 +75,21 @@ const struct fieldinfo fieldinfos[]= { { FIELD("Conflicts"), f_dependency, w_dependency, dep_conflicts }, { FIELD("Enhances"), f_dependency, w_dependency, dep_enhances }, { FIELD("Conffiles"), f_conffiles, w_conffiles }, - { FIELD("Filename"), f_filecharf, w_filecharf, FILEFOFF(name) }, - { FIELD("Size"), f_filecharf, w_filecharf, FILEFOFF(size) }, - { FIELD("MD5sum"), f_filecharf, w_filecharf, FILEFOFF(md5sum) }, - { FIELD("MSDOS-Filename"), f_filecharf, w_filecharf, FILEFOFF(msdosname) }, + { FIELD("Filename"), f_archives, w_archives, ARCHIVEFOFF(name) }, + { FIELD("Size"), f_archives, w_archives, ARCHIVEFOFF(size) }, + { FIELD("MD5sum"), f_archives, w_archives, ARCHIVEFOFF(md5sum) }, + { FIELD("MSDOS-Filename"), f_archives, w_archives, ARCHIVEFOFF(msdosname) }, { FIELD("Description"), f_charfield, w_charfield, PKGIFPOFF(description) }, { FIELD("Triggers-Pending"), f_trigpend, w_trigpend }, { FIELD("Triggers-Awaited"), f_trigaw, w_trigaw }, /* Note that aliases are added to the nicknames table. */ - { NULL } -}; - -static const struct nickname nicknames[] = { - /* Note: Capitalization of these strings is important. */ - { NICK("Recommended"), .canon = "Recommends" }, - { NICK("Optional"), .canon = "Suggests" }, - { NICK("Class"), .canon = "Priority" }, - { NICK("Package-Revision"), .canon = "Revision" }, - { NICK("Package_Revision"), .canon = "Revision" }, - { .nick = NULL } + { FIELD("Revision"), f_revision, w_null }, + { FIELD("Recommended"), f_dependency, w_null }, + { FIELD("Optional"), f_dependency, w_null }, + { FIELD("Class"), f_priority, w_null }, + { FIELD("Package-Revision"), f_revision, w_null }, + { FIELD("Package_Revision"), f_revision, w_null }, + { NULL } }; /** @@ -117,19 +111,9 @@ pkg_parse_field(struct parsedb_state *ps, struct field_state *fs, void *parse_obj) { struct pkg_parse_object *pkg_obj = parse_obj; - const struct nickname *nick; const struct fieldinfo *fip; int *ip; - for (nick = nicknames; nick->nick; nick++) - if (nick->nicklen == (size_t)fs->fieldlen && - strncasecmp(nick->nick, fs->fieldstart, fs->fieldlen) == 0) - break; - if (nick->nick) { - fs->fieldstart = nick->canon; - fs->fieldlen = strlen(fs->fieldstart); - } - for (fip = fieldinfos, ip = fs->fieldencountered; fip->name; fip++, ip++) if (fip->namelen == (size_t)fs->fieldlen && strncasecmp(fip->name, fs->fieldstart, fs->fieldlen) == 0) @@ -160,7 +144,7 @@ pkg_parse_field(struct parsedb_state *ps, struct field_state *fs, fs->fieldlen, fs->fieldstart); larpp = &arp->next; } - arp = nfmalloc(sizeof(struct arbitraryfield)); + arp = nfmalloc(sizeof(*arp)); arp->name = nfstrnsave(fs->fieldstart, fs->fieldlen); arp->value = nfstrnsave(fs->valuestart, fs->valuelen); arp->next = NULL; @@ -178,7 +162,7 @@ pkg_parse_verify(struct parsedb_state *ps, struct dependency *dep; struct deppossi *dop; - parse_must_have_field(ps, pkg->set->name, "package name"); + parse_must_have_field(ps, pkg->set->name, "Package"); /* XXX: We need to check for status != PKG_STAT_HALFINSTALLED as while * unpacking an unselected package, it will not have yet all data in @@ -187,9 +171,9 @@ pkg_parse_verify(struct parsedb_state *ps, if ((ps->flags & pdb_recordavailable) || (pkg->status != PKG_STAT_NOTINSTALLED && pkg->status != PKG_STAT_HALFINSTALLED)) { - parse_ensure_have_field(ps, &pkgbin->description, "description"); - parse_ensure_have_field(ps, &pkgbin->maintainer, "maintainer"); - parse_must_have_field(ps, pkgbin->version.version, "version"); + parse_ensure_have_field(ps, &pkgbin->description, "Description"); + parse_ensure_have_field(ps, &pkgbin->maintainer, "Maintainer"); + parse_must_have_field(ps, pkgbin->version.version, "Version"); } /* XXX: Versions before dpkg 1.10.19 did not preserve the Architecture @@ -201,9 +185,9 @@ pkg_parse_verify(struct parsedb_state *ps, * is in such a state that it make sense), so that it can be used safely * on string comparisons and the like. */ if (pkgbin->arch->type == DPKG_ARCH_NONE) - parse_warn(ps, _("missing %s"), "architecture"); + parse_warn(ps, _("missing '%s' field"), "Architecture"); else if (pkgbin->arch->type == DPKG_ARCH_EMPTY) - parse_warn(ps, _("empty value for %s"), "architecture"); + parse_warn(ps, _("empty value for '%s' field"), "Architecture"); } /* Mark missing architectures as empty, to distinguish these from * unused slots in the db. */ @@ -212,12 +196,15 @@ pkg_parse_verify(struct parsedb_state *ps, if (pkgbin->arch->type == DPKG_ARCH_EMPTY && pkgbin->multiarch == PKG_MULTIARCH_SAME) - parse_error(ps, _("package has field '%s' but is missing architecture"), + parse_error(ps, _("package has '%s' field but is missing architecture"), "Multi-Arch: same"); if (pkgbin->arch->type == DPKG_ARCH_ALL && pkgbin->multiarch == PKG_MULTIARCH_SAME) - parse_error(ps, _("package has field '%s' but is architecture all"), - "Multi-Arch: same"); + parse_error(ps, _("package has '%s' field but is architecture '%s'"), + "Multi-Arch: same", "all"); + + /* Generate the cached fully qualified package name representation. */ + pkgbin->pkgname_archqual = pkgbin_name_archqual(pkg, pkgbin); /* Initialize deps to be arch-specific unless stated otherwise. */ for (dep = pkgbin->depends; dep; dep = dep->next) @@ -239,7 +226,8 @@ pkg_parse_verify(struct parsedb_state *ps, pkg->status == PKG_STAT_NOTINSTALLED || pkg->status == PKG_STAT_TRIGGERSPENDING) parse_error(ps, - _("Config-Version for package with inappropriate Status")); + _("'%s' field present for package with inappropriate '%s' field"), + "Config-Version", "Status"); } else { if (pkg->status == PKG_STAT_INSTALLED || pkg->status == PKG_STAT_TRIGGERSPENDING) @@ -255,7 +243,8 @@ pkg_parse_verify(struct parsedb_state *ps, pkg_status_name(pkg)); else if (pkg->status == PKG_STAT_TRIGGERSAWAITED && !pkg->trigaw.head) parse_error(ps, - _("package has status triggers-awaited but no triggers awaited")); + _("package has status %s but no triggers awaited"), + pkg_status_name(pkg)); if (pkg->trigpend_head && !(pkg->status == PKG_STAT_TRIGGERSPENDING || @@ -265,8 +254,8 @@ pkg_parse_verify(struct parsedb_state *ps, pkg_status_name(pkg)); else if (pkg->status == PKG_STAT_TRIGGERSPENDING && !pkg->trigpend_head) parse_error(ps, - _("package has status triggers-pending but no triggers " - "pending")); + _("package has status %s but no triggers pending"), + pkg_status_name(pkg)); /* FIXME: There was a bug that could make a not-installed package have * conffiles, so we check for them here and remove them (rather than @@ -275,8 +264,8 @@ pkg_parse_verify(struct parsedb_state *ps, pkg->status == PKG_STAT_NOTINSTALLED && pkgbin->conffiles) { parse_warn(ps, - _("Package which in state not-installed has conffiles, " - "forgetting them")); + _("package has status %s and has conffiles, forgetting them"), + pkg_status_name(pkg)); pkgbin->conffiles = NULL; } @@ -302,7 +291,18 @@ pkg_parse_verify(struct parsedb_state *ps, pkg->eflag == PKG_EFLAG_OK && pkg->want == PKG_WANT_INSTALL && pkgbin->arch->type == DPKG_ARCH_EMPTY) - pkg->want = PKG_WANT_UNKNOWN; + pkg_set_want(pkg, PKG_WANT_UNKNOWN); + + /* XXX: Versions before dpkg 1.13.10 did not blank the Origin and Bugs + * fields, so there can be packages that should be garbage collected but + * are lingering around. Blank them to make sure we will forget all about + * them on the next database dump. */ + if (!(ps->flags & pdb_recordavailable) && + pkg->status == PKG_STAT_NOTINSTALLED && + pkg->eflag == PKG_EFLAG_OK && + pkg->want == PKG_WANT_UNKNOWN) { + pkgbin_blank(pkgbin); + } } struct pkgcount { @@ -340,7 +340,7 @@ parse_find_set_slot(struct parsedb_state *ps, struct pkgset *set; struct pkginfo *pkg; - set = pkg_db_find_set(new_pkg->set->name); + set = pkg_hash_find_set(new_pkg->set->name); /* Sanity checks: verify that the db is in a consistent state. */ @@ -403,9 +403,9 @@ parse_find_pkg_slot(struct parsedb_state *ps, * possible architecture switch, for example from native to all. */ if (pkgset_installed_instances(db_set) == 1 && new_pkgbin->multiarch != PKG_MULTIARCH_SAME) - return pkg_db_get_singleton(db_set); + return pkg_hash_get_singleton(db_set); else - return pkg_db_get_pkg(db_set, new_pkgbin->arch); + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); } else { bool selection = false; @@ -417,28 +417,29 @@ parse_find_pkg_slot(struct parsedb_state *ps, /* Verify we don't allow something that will mess up the db. */ if (pkgset_installed_instances(db_set) > 1 && !selection && new_pkgbin->multiarch != PKG_MULTIARCH_SAME) - ohshit(_("%s %s (Multi-Arch: %s) is not co-installable with " - "%s which has multiple installed instances"), + ohshit(_("package %s (%s) with field '%s: %s' is not co-installable " + "with %s which has multiple installed instances"), pkgbin_name(new_pkg, new_pkgbin, pnaw_always), versiondescribe(&new_pkgbin->version, vdew_nonambig), - multiarchinfos[new_pkgbin->multiarch].name, db_set->name); + "Multi-Arch", multiarchinfos[new_pkgbin->multiarch].name, + db_set->name); /* If we are parsing the status file, use a slot per arch. */ if (ps->type == pdb_file_status) - return pkg_db_get_pkg(db_set, new_pkgbin->arch); + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); /* If we are doing an update, from the log or a new package, then * handle cross-grades. */ if (pkgset_installed_instances(db_set) == 1) { - db_pkg = pkg_db_get_singleton(db_set); + db_pkg = pkg_hash_get_singleton(db_set); if (db_pkg->installed.multiarch == PKG_MULTIARCH_SAME && new_pkgbin->multiarch == PKG_MULTIARCH_SAME) - return pkg_db_get_pkg(db_set, new_pkgbin->arch); + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); else return db_pkg; } else { - return pkg_db_get_pkg(db_set, new_pkgbin->arch); + return pkg_hash_get_pkg(db_set, new_pkgbin->arch); } } } @@ -478,18 +479,21 @@ pkg_parse_copy(struct parsedb_state *ps, pkg_copy_eflags(dst_pkg, src_pkg); pkg_set_status(dst_pkg, src_pkg->status); dst_pkg->configversion = src_pkg->configversion; - dst_pkg->files = NULL; + dst_pkg->archives = NULL; dst_pkg->trigpend_head = src_pkg->trigpend_head; dst_pkg->trigaw = src_pkg->trigaw; for (ta = dst_pkg->trigaw.head; ta; ta = ta->sameaw.next) { - assert(ta->aw == src_pkg); + if (ta->aw != src_pkg) + internerr("trigger awaited package %s and origin package %s not linked properly", + pkg_name(ta->aw, pnaw_always), + pkgbin_name(src_pkg, src_pkgbin, pnaw_always)); ta->aw = dst_pkg; - /* ->othertrigaw_head is updated by trig_note_aw in *(pkg_db_find()) + /* ->othertrigaw_head is updated by trig_note_aw in *(pkg_hash_find()) * rather than in dst_pkg. */ } - } else if (!(ps->flags & pdb_ignorefiles)) { - dst_pkg->files = src_pkg->files; + } else if (!(ps->flags & pdb_ignore_archives)) { + dst_pkg->archives = src_pkg->archives; } } @@ -521,6 +525,7 @@ parsedb_new(const char *filename, int fd, enum parsedbflags flags) struct parsedb_state *ps; ps = m_malloc(sizeof(*ps)); + ps->err = DPKG_ERROR_OBJECT; ps->filename = filename; ps->type = parse_get_type(ps, flags); ps->flags = flags; @@ -552,7 +557,7 @@ parsedb_open(const char *filename, enum parsedbflags flags) ps = parsedb_new(filename, fd, flags | pdb_close_fd); - push_cleanup(cu_closefd, ~ehflag_normaltidy, NULL, 0, 1, &ps->fd); + push_cleanup(cu_closefd, ~ehflag_normaltidy, 1, &ps->fd); return ps; } diff --git a/lib/dpkg/parsedump.h b/lib/dpkg/parsedump.h index 336f35f7f..ea29ff317 100644 --- a/lib/dpkg/parsedump.h +++ b/lib/dpkg/parsedump.h @@ -25,6 +25,8 @@ #include <stdint.h> +#include <dpkg/error.h> + /** * @defgroup parsedump In-core package database parsing and reading * @ingroup dpkg-public @@ -46,6 +48,7 @@ enum parsedbtype { struct parsedb_state { enum parsedbtype type; enum parsedbflags flags; + struct dpkg_error err; struct pkginfo *pkg; struct pkgbin *pkgbin; char *data; @@ -89,17 +92,22 @@ bool parse_stanza(struct parsedb_state *ps, struct field_state *fs, #define STRUCTFIELD(klass, off, type) (*(type *)((uintptr_t)(klass) + (off))) #define PKGIFPOFF(f) (offsetof(struct pkgbin, f)) -#define FILEFOFF(f) (offsetof(struct filedetails, f)) +#define ARCHIVEFOFF(f) (offsetof(struct archivedetails, f)) typedef void freadfunction(struct pkginfo *pkg, struct pkgbin *pkgbin, struct parsedb_state *ps, const char *value, const struct fieldinfo *fip); -freadfunction f_name, f_charfield, f_priority, f_section, f_status, f_filecharf; +freadfunction f_name; +freadfunction f_charfield; +freadfunction f_priority; +freadfunction f_section; +freadfunction f_status; freadfunction f_boolean, f_dependency, f_conffiles, f_version, f_revision; freadfunction f_configversion; freadfunction f_multiarch; freadfunction f_architecture; freadfunction f_trigpend, f_trigaw; +freadfunction f_archives; enum fwriteflags { /** Print field header and trailing newline. */ @@ -113,8 +121,8 @@ fwritefunction w_name, w_charfield, w_priority, w_section, w_status, w_configver fwritefunction w_version, w_null, w_booleandefno, w_dependency, w_conffiles; fwritefunction w_multiarch; fwritefunction w_architecture; -fwritefunction w_filecharf; fwritefunction w_trigpend, w_trigaw; +fwritefunction w_archives; void varbuf_add_arbfield(struct varbuf *vb, const struct arbitraryfield *arbfield, @@ -130,14 +138,19 @@ struct fieldinfo { size_t integer; }; -void parse_db_version(struct parsedb_state *ps, - struct dpkg_version *version, const char *value, - const char *fmt, ...) DPKG_ATTR_PRINTF(4); +int +parse_db_version(struct parsedb_state *ps, + struct dpkg_version *version, const char *value) + DPKG_ATTR_REQRET; void parse_error(struct parsedb_state *ps, const char *fmt, ...) DPKG_ATTR_NORET DPKG_ATTR_PRINTF(2); void parse_warn(struct parsedb_state *ps, const char *fmt, ...) DPKG_ATTR_PRINTF(2); +void +parse_problem(struct parsedb_state *ps, const char *fmt, ...) + DPKG_ATTR_PRINTF(2); + void parse_must_have_field(struct parsedb_state *ps, const char *value, const char *what); void parse_ensure_have_field(struct parsedb_state *ps, @@ -145,14 +158,6 @@ void parse_ensure_have_field(struct parsedb_state *ps, #define MSDOS_EOF_CHAR '\032' /* ^Z */ -#define NICK(name) .nick = name, .nicklen = sizeof(name) - 1 - -struct nickname { - const char *nick; - const char *canon; - size_t nicklen; -}; - extern const struct fieldinfo fieldinfos[]; /** @} */ diff --git a/lib/dpkg/parsehelp.c b/lib/dpkg/parsehelp.c index 453077fd9..34247269e 100644 --- a/lib/dpkg/parsehelp.c +++ b/lib/dpkg/parsehelp.c @@ -77,6 +77,24 @@ parse_warn(struct parsedb_state *ps, const char *fmt, ...) va_end(args); } +void +parse_problem(struct parsedb_state *ps, const char *fmt, ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + m_vasprintf(&str, parse_error_msg(ps, fmt), args); + va_end(args); + + if (ps->err.type == DPKG_MSG_WARN) + warning("%s: %s", str, ps->err.str); + else + ohshit("%s: %s", str, ps->err.str); + + free(str); +} + const struct fieldinfo * find_field_info(const struct fieldinfo *fields, const char *fieldname) { @@ -170,6 +188,20 @@ const char *versiondescribe return vb->buf; } +const char * +versiondescribe_c(const struct dpkg_version *version, + enum versiondisplayepochwhen vdew) +{ + struct dpkg_locale oldloc; + const char *str; + + oldloc = dpkg_locale_switch_C(); + str = versiondescribe(version, vdew); + dpkg_locale_switch_back(oldloc); + + return str; +} + /** * Parse a version string and check for invalid syntax. * @@ -266,29 +298,24 @@ parseversion(struct dpkg_version *rversion, const char *string, * @param ps The parsedb state. * @param version The version to parse into. * @param value The version string to parse from. - * @param fmt The error format string. + * + * @retval 0 On success, and err is reset. + * @retval -1 On failure, and err is set accordingly. */ -void +int parse_db_version(struct parsedb_state *ps, struct dpkg_version *version, - const char *value, const char *fmt, ...) + const char *value) { - struct dpkg_error err; - va_list args; - char buf[1000]; - - if (parseversion(version, value, &err) == 0) - return; + dpkg_error_destroy(&ps->err); - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); + if (parseversion(version, value, &ps->err) == 0) + return 0; - if (err.type == DPKG_MSG_WARN && (ps->flags & pdb_lax_version_parser)) - parse_warn(ps, "%s: %.250s", buf, err.str); - else - parse_error(ps, "%s: %.250s", buf, err.str); + /* If not in lax mode, turn everything into an error. */ + if (!(ps->flags & pdb_lax_version_parser)) + ps->err.type = DPKG_MSG_ERROR; - dpkg_error_destroy(&err); + return -1; } void @@ -296,9 +323,9 @@ parse_must_have_field(struct parsedb_state *ps, const char *value, const char *what) { if (!value) - parse_error(ps, _("missing %s"), what); + parse_error(ps, _("missing '%s' field"), what); else if (!*value) - parse_error(ps, _("empty value for %s"), what); + parse_error(ps, _("empty value for '%s' field"), what); } void @@ -308,9 +335,9 @@ parse_ensure_have_field(struct parsedb_state *ps, static const char empty[] = ""; if (!*value) { - parse_warn(ps, _("missing %s"), what); + parse_warn(ps, _("missing '%s' field"), what); *value = empty; } else if (!**value) { - parse_warn(ps, _("empty value for %s"), what); + parse_warn(ps, _("empty value for '%s' field"), what); } } diff --git a/lib/dpkg/path-remove.c b/lib/dpkg/path-remove.c index ab26b4a8d..6afb37607 100644 --- a/lib/dpkg/path-remove.c +++ b/lib/dpkg/path-remove.c @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * path-remove.c - path removal functionss + * path-remove.c - path removal functions * * Copyright © 1994-1995 Ian Jackson <ijackson@chiark.greenend.org.uk> * Copyright © 2007-2015 Guillem Jover <guillem@debian.org> @@ -24,7 +24,6 @@ #include <sys/stat.h> -#include <assert.h> #include <errno.h> #include <string.h> #include <unistd.h> @@ -122,7 +121,8 @@ path_remove_tree(const char *pathname) const char *u; u = path_skip_slash_dotslash(pathname); - assert(*u); + if (u[0] == '\0') + internerr("pathname '%s' reduces to nothing", pathname); debug(dbg_eachfile, "%s '%s'", __func__, pathname); if (!rmdir(pathname)) diff --git a/lib/dpkg/perf.h b/lib/dpkg/perf.h new file mode 100644 index 000000000..c7d9301a8 --- /dev/null +++ b/lib/dpkg/perf.h @@ -0,0 +1,71 @@ +/* + * libdpkg - Debian packaging suite library routines + * perf.h - performance testing support + * + * Copyright © 2009-2019 Guillem Jover <guillem@debian.org> + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef LIBDPKG_PERF_H +#define LIBDPKG_PERF_H + +#include <config.h> +#include <compat.h> + +#include <time.h> +#include <stdio.h> + +struct perf_slot { + struct timespec t_ini, t_end; +}; + +static inline void +perf_ts_sub(struct timespec *a, struct timespec *b, struct timespec *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + if (res->tv_nsec < 0) { + res->tv_sec--; + res->tv_nsec += 1000000000; + } +} + +static void +perf_ts_mark_print(const char *str) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + printf("%lu.%.9lu: %s\n", ts.tv_sec, ts.tv_nsec, str); +} + +static void +perf_ts_slot_print(struct perf_slot *ps, const char *str) +{ + struct timespec t_res; + + perf_ts_sub(&ps->t_end, &ps->t_ini, &t_res); + + printf("%lu.%.9lu: %s (%lu.%.9lu sec)\n", + ps->t_end.tv_sec, ps->t_end.tv_nsec, + str, t_res.tv_sec, t_res.tv_nsec); +} + +#define perf_ts_slot_start(ps) clock_gettime(CLOCK_MONOTONIC, &((ps)->t_ini)) +#define perf_ts_slot_stop(ps) clock_gettime(CLOCK_MONOTONIC, &((ps)->t_end)) + +#endif diff --git a/lib/dpkg/pkg-array.c b/lib/dpkg/pkg-array.c index 24c6dc0e2..0ce285e92 100644 --- a/lib/dpkg/pkg-array.c +++ b/lib/dpkg/pkg-array.c @@ -22,7 +22,6 @@ #include <config.h> #include <compat.h> -#include <assert.h> #include <string.h> #include <stdlib.h> @@ -60,21 +59,23 @@ pkg_array_init_from_names(struct pkg_array *a, pkg_mapper_func pkg_mapper, * @param a The array to initialize. */ void -pkg_array_init_from_db(struct pkg_array *a) +pkg_array_init_from_hash(struct pkg_array *a) { - struct pkgiterator *iter; + struct pkg_hash_iter *iter; struct pkginfo *pkg; int i; - a->n_pkgs = pkg_db_count_pkg(); + a->n_pkgs = pkg_hash_count_pkg(); a->pkgs = m_malloc(sizeof(a->pkgs[0]) * a->n_pkgs); - iter = pkg_db_iter_new(); - for (i = 0; (pkg = pkg_db_iter_next_pkg(iter)); i++) + iter = pkg_hash_iter_new(); + for (i = 0; (pkg = pkg_hash_iter_next_pkg(iter)); i++) a->pkgs[i] = pkg; - pkg_db_iter_free(iter); + pkg_hash_iter_free(iter); - assert(i == a->n_pkgs); + if (i != a->n_pkgs) + internerr("inconsistent state in pkg array: i=%d != npkgs=%d", + i, a->n_pkgs); } /** diff --git a/lib/dpkg/pkg-array.h b/lib/dpkg/pkg-array.h index 858b4fe46..f9ea385b6 100644 --- a/lib/dpkg/pkg-array.h +++ b/lib/dpkg/pkg-array.h @@ -44,7 +44,7 @@ typedef struct pkginfo *pkg_mapper_func(const char *name); typedef void pkg_array_visitor_func(struct pkg_array *a, struct pkginfo *pkg, void *pkg_data); -void pkg_array_init_from_db(struct pkg_array *a); +void pkg_array_init_from_hash(struct pkg_array *a); void pkg_array_init_from_names(struct pkg_array *a, pkg_mapper_func *pkg_mapper, const char **pkg_names); void pkg_array_foreach(struct pkg_array *a, pkg_array_visitor_func *pkg_visitor, diff --git a/lib/dpkg/pkg-files.c b/lib/dpkg/pkg-files.c new file mode 100644 index 000000000..f02213270 --- /dev/null +++ b/lib/dpkg/pkg-files.c @@ -0,0 +1,95 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-files.c - handle list of filesystem files per package + * + * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-list.h> +#include <dpkg/pkg-files.h> + +/** + * Erase the files saved in pkg. + */ +void +pkg_files_blank(struct pkginfo *pkg) +{ + struct fsys_namenode_list *current; + + for (current = pkg->files; + current; + current = current->next) { + struct pkg_list **pkg_prev = ¤t->namenode->packages; + struct pkg_list *pkg_node; + + /* For each file that used to be in the package, go through + * looking for this package's entry in the list of packages + * containing this file, and blank it out. */ + for (pkg_node = current->namenode->packages; + pkg_node; + pkg_node = pkg_node->next) { + if (pkg_node->pkg == pkg) { + *pkg_prev = pkg_node->next; + + /* The actual filelist links were allocated + * w/ nfmalloc, so we should not free them. */ + break; + } + + pkg_prev = &pkg_node->next; + } + } + pkg->files = NULL; +} + +struct fsys_namenode_list ** +pkg_files_add_file(struct pkginfo *pkg, struct fsys_namenode *namenode, + struct fsys_namenode_list **file_tail) +{ + struct fsys_namenode_list *newent; + struct pkg_list *pkg_node; + + if (file_tail == NULL) + file_tail = &pkg->files; + + /* Make sure we're at the end. */ + while ((*file_tail) != NULL) + file_tail = &((*file_tail)->next); + + /* Create a new node. */ + newent = nfmalloc(sizeof(*newent)); + newent->namenode = namenode; + newent->next = NULL; + *file_tail = newent; + file_tail = &newent->next; + + /* Add pkg to newent's package list. */ + pkg_node = nfmalloc(sizeof(*pkg_node)); + pkg_node->pkg = pkg; + pkg_node->next = newent->namenode->packages; + newent->namenode->packages = pkg_node; + + /* Return the position for the next guy. */ + return file_tail; +} diff --git a/lib/dpkg/pkg-files.h b/lib/dpkg/pkg-files.h new file mode 100644 index 000000000..bee137a16 --- /dev/null +++ b/lib/dpkg/pkg-files.h @@ -0,0 +1,46 @@ +/* + * libdpkg - Debian packaging suite library routines + * pkg-files.h - primitives for pkg files handling + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#ifndef LIBDPKG_PKG_FILES_H +#define LIBDPKG_PKG_FILES_H + +#include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> + +DPKG_BEGIN_DECLS + +/** + * @defgroup pkg-files Package files handling + * @ingroup dpkg-public + * @{ + */ + +void +pkg_files_blank(struct pkginfo *pkg); + +struct fsys_namenode_list ** +pkg_files_add_file(struct pkginfo *pkg, struct fsys_namenode *namenode, + struct fsys_namenode_list **file_tail); + +/** @} */ + +DPKG_END_DECLS + +#endif /* LIBDPKG_PKG_FILES_H */ diff --git a/lib/dpkg/pkg-format.c b/lib/dpkg/pkg-format.c index 40344a829..1985df068 100644 --- a/lib/dpkg/pkg-format.c +++ b/lib/dpkg/pkg-format.c @@ -22,16 +22,22 @@ #include <config.h> #include <compat.h> +#include <sys/types.h> +#include <sys/stat.h> + #include <errno.h> #include <limits.h> #include <string.h> #include <stdlib.h> #include <stdio.h> +#include <unistd.h> #include <dpkg/i18n.h> #include <dpkg/error.h> #include <dpkg/dpkg.h> #include <dpkg/dpkg-db.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> #include <dpkg/parsedump.h> #include <dpkg/pkg-show.h> #include <dpkg/pkg-format.h> @@ -194,11 +200,8 @@ pkg_format_parse(const char *fmt, struct dpkg_error *err) fmtend = fmt; do { fmtend += 1; - fmtend = strchr(fmtend, '$'); - } while (fmtend && fmtend[1] != '{'); - - if (!fmtend) - fmtend = fmt + strlen(fmt); + fmtend = strchrnul(fmtend, '$'); + } while (fmtend[0] && fmtend[1] != '{'); if (!parsestring(node, fmt, fmtend - 1, err)) { pkg_format_free(head); @@ -269,19 +272,69 @@ virt_status_eflag(struct varbuf *vb, } static void -virt_summary(struct varbuf *vb, - const struct pkginfo *pkg, const struct pkgbin *pkgbin, - enum fwriteflags flags, const struct fieldinfo *fip) +virt_synopsis(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) { const char *desc; int len; - desc = pkgbin_summary(pkg, pkgbin, &len); + desc = pkgbin_synopsis(pkg, pkgbin, &len); varbuf_add_buf(vb, desc, len); } static void +virt_fsys_last_modified(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + const char *listfile; + struct stat st; + + if (pkg->status == PKG_STAT_NOTINSTALLED) + return; + + listfile = pkg_infodb_get_file(pkg, pkgbin, LISTFILE); + + if (stat(listfile, &st) < 0) { + if (errno == ENOENT) + return; + + ohshite(_("cannot get package %s filesystem last modification time"), + pkgbin_name_const(pkg, pkgbin, pnaw_nonambig)); + } + + varbuf_printf(vb, "%ld", st.st_mtime); +} + +static void +virt_fsys_files(struct varbuf *vb, + const struct pkginfo *pkg, const struct pkgbin *pkgbin, + enum fwriteflags flags, const struct fieldinfo *fip) +{ + struct fsys_namenode_list *node; + + /* XXX: This cast is so wrong on so many levels, but the alternatives + * are apparently worse. We might need to end up removing the const + * from the arguments. + * + * Ideally loading the entire fsys db would be cheaper, and stored + * in a single file, so we could do it unconditionally, before any + * formatting. */ + ensure_packagefiles_available((struct pkginfo *)pkg); + + if (!pkg->files_list_valid) + return; + + for (node = pkg->files; node; node = node->next) { + varbuf_add_char(vb, ' '); + varbuf_add_str(vb, node->namenode->name); + varbuf_add_char(vb, '\n'); + } +} + +static void virt_source_package(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) @@ -321,11 +374,14 @@ virt_source_upstream_version(struct varbuf *vb, static const struct fieldinfo virtinfos[] = { { FIELD("binary:Package"), NULL, virt_package }, - { FIELD("binary:Summary"), NULL, virt_summary }, + { FIELD("binary:Synopsis"), NULL, virt_synopsis }, + { FIELD("binary:Summary"), NULL, virt_synopsis }, { FIELD("db:Status-Abbrev"), NULL, virt_status_abbrev }, { FIELD("db:Status-Want"), NULL, virt_status_want }, { FIELD("db:Status-Status"), NULL, virt_status_status }, { FIELD("db:Status-Eflag"), NULL, virt_status_eflag }, + { FIELD("db-fsys:Files"), NULL, virt_fsys_files }, + { FIELD("db-fsys:Last-Modified"), NULL, virt_fsys_last_modified }, { FIELD("source:Package"), NULL, virt_source_package }, { FIELD("source:Version"), NULL, virt_source_version }, { FIELD("source:Upstream-Version"), NULL, virt_source_upstream_version }, diff --git a/lib/dpkg/pkg-format.h b/lib/dpkg/pkg-format.h index cba39a147..3e013003d 100644 --- a/lib/dpkg/pkg-format.h +++ b/lib/dpkg/pkg-format.h @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * pkg-format.c - customizable package formatting + * pkg-format.g - customizable package formatting * * Copyright © 2001 Wichert Akkerman <wakkerma@debian.org> * diff --git a/lib/dpkg/pkg-db.c b/lib/dpkg/pkg-hash.c index 300c1cad8..18d642eef 100644 --- a/lib/dpkg/pkg-db.c +++ b/lib/dpkg/pkg-hash.c @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * pkg-db.c - low level package database routines (hash tables, etc.) + * pkg-hash.c - low level package database routines (hash tables, etc.) * * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> @@ -24,7 +24,6 @@ #include <config.h> #include <compat.h> -#include <assert.h> #include <string.h> #include <stdlib.h> @@ -35,11 +34,16 @@ #include <dpkg/string.h> #include <dpkg/arch.h> -/* This must always be a prime for optimal performance. - * With 4093 buckets, we glean a 20% speedup, for 8191 buckets - * we get 23%. The nominal increase in memory usage is a mere - * sizeof(void *) * 8191 (i.e. less than 32 KiB on 32bit systems). */ -#define BINS 8191 +/* + * This must always be a prime for optimal performance. + * + * We use a number that is close to the amount of packages currently present + * in a Debian suite, so that installed and available packages do not add + * tons of collisions. + * + * The memory usage is «BINS * sizeof(void *)». + */ +#define BINS 65521 static struct pkgset *bins[BINS]; static int npkg, nset; @@ -61,7 +65,7 @@ static int npkg, nset; * @return The package set. */ struct pkgset * -pkg_db_find_set(const char *inname) +pkg_hash_find_set(const char *inname) { struct pkgset **setp, *new_set; char *name = m_strdup(inname), *p; @@ -80,7 +84,7 @@ pkg_db_find_set(const char *inname) return *setp; } - new_set = nfmalloc(sizeof(struct pkgset)); + new_set = nfmalloc(sizeof(*new_set)); pkgset_blank(new_set); new_set->name = nfstrsave(name); new_set->next = NULL; @@ -105,7 +109,7 @@ pkg_db_find_set(const char *inname) * @return The singleton package instance. */ struct pkginfo * -pkg_db_get_singleton(struct pkgset *set) +pkg_hash_get_singleton(struct pkgset *set) { struct pkginfo *pkg; @@ -139,13 +143,13 @@ pkg_db_get_singleton(struct pkgset *set) * @return The package instance. */ struct pkginfo * -pkg_db_find_singleton(const char *name) +pkg_hash_find_singleton(const char *name) { struct pkgset *set; struct pkginfo *pkg; - set = pkg_db_find_set(name); - pkg = pkg_db_get_singleton(set); + set = pkg_hash_find_set(name); + pkg = pkg_hash_get_singleton(set); if (pkg == NULL) ohshit(_("ambiguous package name '%s' with more " "than one installed instance"), set->name); @@ -167,17 +171,21 @@ pkg_db_find_singleton(const char *name) * @return The package instance. */ struct pkginfo * -pkg_db_get_pkg(struct pkgset *set, const struct dpkg_arch *arch) +pkg_hash_get_pkg(struct pkgset *set, const struct dpkg_arch *arch) { struct pkginfo *pkg, **pkgp; - assert(arch); - assert(arch->type != DPKG_ARCH_NONE); + if (arch == NULL) + internerr("arch argument is NULL"); + if (arch->type == DPKG_ARCH_NONE) + internerr("arch argument is none"); pkg = &set->pkg; /* If there's a single unused slot, let's use that. */ if (pkg->installed.arch->type == DPKG_ARCH_NONE && pkg->arch_next == NULL) { + /* We can only initialize the arch pkgbin members, because those are used + * to find instances, anything else will be overwritten at parse time. */ pkg->installed.arch = arch; pkg->available.arch = arch; return pkg; @@ -192,10 +200,12 @@ pkg_db_get_pkg(struct pkgset *set, const struct dpkg_arch *arch) } /* Need to create a new instance for the wanted architecture. */ - pkg = nfmalloc(sizeof(struct pkginfo)); + pkg = nfmalloc(sizeof(*pkg)); pkg_blank(pkg); pkg->set = set; pkg->arch_next = NULL; + /* We can only initialize the arch pkgbin members, because those are used + * to find instances, anything else will be overwritten at parse time. */ pkg->installed.arch = arch; pkg->available.arch = arch; *pkgp = pkg; @@ -213,13 +223,13 @@ pkg_db_get_pkg(struct pkgset *set, const struct dpkg_arch *arch) * @return The package instance. */ struct pkginfo * -pkg_db_find_pkg(const char *name, const struct dpkg_arch *arch) +pkg_hash_find_pkg(const char *name, const struct dpkg_arch *arch) { struct pkgset *set; struct pkginfo *pkg; - set = pkg_db_find_set(name); - pkg = pkg_db_get_pkg(set, arch); + set = pkg_hash_find_set(name); + pkg = pkg_hash_get_pkg(set, arch); return pkg; } @@ -230,7 +240,7 @@ pkg_db_find_pkg(const char *name, const struct dpkg_arch *arch) * @return The number of package sets. */ int -pkg_db_count_set(void) +pkg_hash_count_set(void) { return nset; } @@ -241,12 +251,12 @@ pkg_db_count_set(void) * @return The number of package instances. */ int -pkg_db_count_pkg(void) +pkg_hash_count_pkg(void) { return npkg; } -struct pkgiterator { +struct pkg_hash_iter { struct pkginfo *pkg; int nbinn; }; @@ -258,12 +268,12 @@ struct pkgiterator { * * @return The iterator. */ -struct pkgiterator * -pkg_db_iter_new(void) +struct pkg_hash_iter * +pkg_hash_iter_new(void) { - struct pkgiterator *iter; + struct pkg_hash_iter *iter; - iter = m_malloc(sizeof(struct pkgiterator)); + iter = m_malloc(sizeof(*iter)); iter->pkg = NULL; iter->nbinn = 0; @@ -280,7 +290,7 @@ pkg_db_iter_new(void) * @return A package set. */ struct pkgset * -pkg_db_iter_next_set(struct pkgiterator *iter) +pkg_hash_iter_next_set(struct pkg_hash_iter *iter) { struct pkgset *set; @@ -315,7 +325,7 @@ pkg_db_iter_next_set(struct pkgiterator *iter) * @return A package instance. */ struct pkginfo * -pkg_db_iter_next_pkg(struct pkgiterator *iter) +pkg_hash_iter_next_pkg(struct pkg_hash_iter *iter) { struct pkginfo *pkg; @@ -344,13 +354,13 @@ pkg_db_iter_next_pkg(struct pkgiterator *iter) * @name iter The iterator. */ void -pkg_db_iter_free(struct pkgiterator *iter) +pkg_hash_iter_free(struct pkg_hash_iter *iter) { free(iter); } void -pkg_db_reset(void) +pkg_hash_reset(void) { int i; @@ -362,25 +372,36 @@ pkg_db_reset(void) } void -pkg_db_report(FILE *file) +pkg_hash_report(FILE *file) { int i, c; struct pkgset *pkg; int *freq; + int empty = 0, used = 0, collided = 0; freq = m_malloc(sizeof(int) * nset + 1); for (i = 0; i <= nset; i++) freq[i] = 0; for (i=0; i<BINS; i++) { for (c=0, pkg= bins[i]; pkg; c++, pkg= pkg->next); - fprintf(file,"bin %5d has %7d\n",i,c); + fprintf(file, "pkg-hash: bin %5d has %7d\n", i, c); + if (c == 0) + empty++; + else if (c == 1) + used++; + else { + used++; + collided++; + } freq[c]++; } for (i = nset; i > 0 && freq[i] == 0; i--); while (i >= 0) { - fprintf(file, "size %7d occurs %5d times\n", i, freq[i]); + fprintf(file, "pkg-hash: size %7d occurs %5d times\n", i, freq[i]); i--; } + fprintf(file, "pkg-hash: bins empty %d\n", empty); + fprintf(file, "pkg-hash: bins used %d (collided %d)\n", used, collided); m_output(file, "<hash report>"); diff --git a/lib/dpkg/pkg-show.c b/lib/dpkg/pkg-show.c index 6af658c42..67e5af404 100644 --- a/lib/dpkg/pkg-show.c +++ b/lib/dpkg/pkg-show.c @@ -34,6 +34,10 @@ static bool pkgbin_name_needs_arch(const struct pkgbin *pkgbin, enum pkg_name_arch_when pnaw) { + if (pkgbin->arch->type == DPKG_ARCH_NONE || + pkgbin->arch->type == DPKG_ARCH_EMPTY) + return false; + switch (pnaw) { case pnaw_never: break; @@ -43,8 +47,7 @@ pkgbin_name_needs_arch(const struct pkgbin *pkgbin, /* Fall through. */ case pnaw_foreign: if (pkgbin->arch->type == DPKG_ARCH_NATIVE || - pkgbin->arch->type == DPKG_ARCH_ALL || - pkgbin->arch->type == DPKG_ARCH_NONE) + pkgbin->arch->type == DPKG_ARCH_ALL) break; /* Fall through. */ case pnaw_always: @@ -76,12 +79,33 @@ varbuf_add_pkgbin_name(struct varbuf *vb, varbuf_end_str(vb); } +const char * +pkgbin_name_archqual(const struct pkginfo *pkg, const struct pkgbin *pkgbin) +{ + char *pkgname; + + if (pkgbin->arch->type == DPKG_ARCH_NONE || + pkgbin->arch->type == DPKG_ARCH_EMPTY) + return pkg->set->name; + + pkgname = nfmalloc(strlen(pkg->set->name) + 1 + + strlen(pkgbin->arch->name) + 1); + str_concat(pkgname, pkg->set->name, ":", + pkgbin->arch->name, NULL); + + return pkgname; +} + /** * Return a string representation of the package name. * * The returned string must not be freed, and it's permanently allocated so * can be used as long as the non-freeing memory pool has not been freed. * + * Note, that this const variant will "leak" a new non-freeing string on + * each call if the internal cache has not been previously initialized, + * so it is advised to use it only in error reporting code paths. + * * The pnaw parameter should be one of pnaw_never (never print arch), * pnaw_foreign (print arch for foreign packages only), pnaw_nonambig (print * arch for non ambiguous cases) or pnaw_always (always print arch), @@ -93,24 +117,63 @@ varbuf_add_pkgbin_name(struct varbuf *vb, * @return The string representation. */ const char * -pkgbin_name(struct pkginfo *pkg, struct pkgbin *pkgbin, +pkgbin_name_const(const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum pkg_name_arch_when pnaw) { if (!pkgbin_name_needs_arch(pkgbin, pnaw)) return pkg->set->name; - /* Cache the package name representation, for later reuse. */ - if (pkgbin->pkgname_archqual == NULL) { - struct varbuf vb = VARBUF_INIT; + /* Return a non-freeing package name representation, which + * is intended to be used in error-handling code, as we will keep + * "leaking" them until the next memory pool flush. */ + if (pkgbin->pkgname_archqual == NULL) + return pkgbin_name_archqual(pkg, pkgbin); - varbuf_add_str(&vb, pkg->set->name); - varbuf_add_archqual(&vb, pkgbin->arch); - varbuf_end_str(&vb); + return pkgbin->pkgname_archqual; +} - pkgbin->pkgname_archqual = nfstrsave(vb.buf); +/** + * Return a string representation of the installed package name. + * + * This is equivalent to pkgbin_name_const() but just for its installed pkgbin. + * + * @param pkg The package to consider. + * @param pnaw When to display the architecture qualifier. + * + * @return The string representation. + */ +const char * +pkg_name_const(const struct pkginfo *pkg, enum pkg_name_arch_when pnaw) +{ + return pkgbin_name_const(pkg, &pkg->installed, pnaw); +} - varbuf_destroy(&vb); - } +/** + * Return a string representation of the package name. + * + * The returned string must not be freed, and it's permanently allocated so + * can be used as long as the non-freeing memory pool has not been freed. + * + * The pnaw parameter should be one of pnaw_never (never print arch), + * pnaw_foreign (print arch for foreign packages only), pnaw_nonambig (print + * arch for non ambiguous cases) or pnaw_always (always print arch), + * + * @param pkg The package to consider. + * @param pkgbin The binary package instance to consider. + * @param pnaw When to display the architecture qualifier. + * + * @return The string representation. + */ +const char * +pkgbin_name(struct pkginfo *pkg, struct pkgbin *pkgbin, + enum pkg_name_arch_when pnaw) +{ + if (!pkgbin_name_needs_arch(pkgbin, pnaw)) + return pkg->set->name; + + /* Cache the package name representation, for later reuse. */ + if (pkgbin->pkgname_archqual == NULL) + pkgbin->pkgname_archqual = pkgbin_name_archqual(pkg, pkgbin); return pkgbin->pkgname_archqual; } @@ -132,22 +195,22 @@ pkg_name(struct pkginfo *pkg, enum pkg_name_arch_when pnaw) } /** - * Return a string representation of the package summary. + * Return a string representation of the package synopsis. * * The returned string must not be freed, and it's permanently allocated so * can be used as long as the non-freeing memory pool has not been freed. * - * The package summary is the short description, but it is not NUL terminated, + * The package synopsis is the short description, but it is not NUL terminated, * so the output len argument should be used to limit the string length. * * @param pkg The package to consider. * @param pkgbin The binary package instance to consider. - * @param[out] len The length of the summary string within the description. + * @param[out] len The length of the synopsis string within the description. * * @return The string representation. */ const char * -pkgbin_summary(const struct pkginfo *pkg, const struct pkgbin *pkgbin, int *len) +pkgbin_synopsis(const struct pkginfo *pkg, const struct pkgbin *pkgbin, int *len) { const char *pdesc; @@ -161,6 +224,39 @@ pkgbin_summary(const struct pkginfo *pkg, const struct pkgbin *pkgbin, int *len) } /** + * Return a string representation of the package synopsis. + * + * The returned string must not be freed, and it's permanently allocated so + * can be used as long as the non-freeing memory pool has not been freed. + * + * It will try to use the installed version, otherwise it will fallback to + * use the available version. + * + * The package synopsis is the short description, but it is not NUL terminated, + * so the output len argument should be used to limit the string length. + * + * @param pkg The package to consider. + * @param[out] len The length of the synopsis string within the description. + * + * @return The string representation. + */ +const char * +pkg_synopsis(const struct pkginfo *pkg, int *len) +{ + const char *pdesc; + + pdesc = pkg->installed.description; + if (!pdesc) + pdesc = pkg->available.description; + if (!pdesc) + pdesc = _("(no description available)"); + + *len = strcspn(pdesc, "\n"); + + return pdesc; +} + +/** * Return a character abbreviated representation of the package want status. * * @param pkg The package to consider. diff --git a/lib/dpkg/pkg-show.h b/lib/dpkg/pkg-show.h index dd8f666e6..4ac3f5d65 100644 --- a/lib/dpkg/pkg-show.h +++ b/lib/dpkg/pkg-show.h @@ -34,8 +34,10 @@ DPKG_BEGIN_DECLS int pkg_sorter_by_nonambig_name_arch(const void *a, const void *b); -const char *pkgbin_summary(const struct pkginfo *pkg, - const struct pkgbin *pkgbin, int *len_ret); +const char *pkgbin_synopsis(const struct pkginfo *pkg, + const struct pkgbin *pkgbin, int *len_ret); +const char * +pkg_synopsis(const struct pkginfo *pkg, int *len_ret); int pkg_abbrev_want(const struct pkginfo *pkg); int pkg_abbrev_status(const struct pkginfo *pkg); int pkg_abbrev_eflag(const struct pkginfo *pkg); diff --git a/lib/dpkg/pkg-spec.c b/lib/dpkg/pkg-spec.c index 9b4d0da31..3e3f59fa0 100644 --- a/lib/dpkg/pkg-spec.c +++ b/lib/dpkg/pkg-spec.c @@ -97,7 +97,7 @@ pkg_spec_is_illegal(struct pkg_spec *ps) if (!ps->arch_is_pattern && ps->flags & PKG_SPEC_ARCH_SINGLE) { struct pkgset *set; - set = pkg_db_find_set(ps->name); + set = pkg_hash_find_set(ps->name); /* Single instancing only applies with no architecture. */ if (ps->arch->type == DPKG_ARCH_NONE && @@ -195,9 +195,9 @@ static struct pkginfo * pkg_spec_get_pkg(struct pkg_spec *ps) { if (ps->arch->type == DPKG_ARCH_NONE) - return pkg_db_find_singleton(ps->name); + return pkg_hash_find_singleton(ps->name); else - return pkg_db_find_pkg(ps->name, ps->arch); + return pkg_hash_find_pkg(ps->name, ps->arch); } struct pkginfo * @@ -245,9 +245,9 @@ void pkg_spec_iter_init(struct pkg_spec *ps) { if (ps->name_is_pattern) - ps->pkg_iter = pkg_db_iter_new(); + ps->pkg_iter = pkg_hash_iter_new(); else - ps->pkg_next = &pkg_db_find_set(ps->name)->pkg; + ps->pkg_next = &pkg_hash_find_set(ps->name)->pkg; } static struct pkginfo * @@ -255,7 +255,7 @@ pkg_spec_iter_next_pkgname(struct pkg_spec *ps) { struct pkginfo *pkg; - while ((pkg = pkg_db_iter_next_pkg(ps->pkg_iter))) { + while ((pkg = pkg_hash_iter_next_pkg(ps->pkg_iter))) { if (pkg_spec_match_pkg(ps, pkg, &pkg->installed)) return pkg; } @@ -290,7 +290,7 @@ pkg_spec_iter_next_pkg(struct pkg_spec *ps) void pkg_spec_iter_destroy(struct pkg_spec *ps) { - pkg_db_iter_free(ps->pkg_iter); + pkg_hash_iter_free(ps->pkg_iter); pkg_spec_iter_blank(ps); } diff --git a/lib/dpkg/pkg-spec.h b/lib/dpkg/pkg-spec.h index 956b7fe3c..964f0d982 100644 --- a/lib/dpkg/pkg-spec.h +++ b/lib/dpkg/pkg-spec.h @@ -60,7 +60,7 @@ struct pkg_spec { bool arch_is_pattern; /** Used for the pkg_db iterator. */ - struct pkgiterator *pkg_iter; + struct pkg_hash_iter *pkg_iter; /** Used for the pkgset iterator. */ struct pkginfo *pkg_next; }; diff --git a/lib/dpkg/pkg.c b/lib/dpkg/pkg.c index 1fc3d0c21..893d14e18 100644 --- a/lib/dpkg/pkg.c +++ b/lib/dpkg/pkg.c @@ -22,9 +22,9 @@ #include <config.h> #include <compat.h> -#include <assert.h> #include <string.h> +#include <dpkg/ehandle.h> #include <dpkg/string.h> #include <dpkg/dpkg-db.h> #include <dpkg/pkg.h> @@ -42,9 +42,12 @@ pkg_set_status(struct pkginfo *pkg, enum pkgstatus status) else if (status == PKG_STAT_NOTINSTALLED) pkg->set->installed_instances--; - assert(pkg->set->installed_instances >= 0); + if (pkg->set->installed_instances < 0) + internerr("pkgset %s went into negative installed instances %d", + pkg->set->name, pkg->set->installed_instances); pkg->status = status; + pkg->status_dirty = true; } /** @@ -113,13 +116,17 @@ void pkg_blank(struct pkginfo *pkg) { pkg->status = PKG_STAT_NOTINSTALLED; + pkg->status_dirty = false; pkg->eflag = PKG_EFLAG_OK; pkg->want = PKG_WANT_UNKNOWN; pkg->priority = PKG_PRIO_UNKNOWN; pkg->otherpriority = NULL; pkg->section = NULL; dpkg_version_blank(&pkg->configversion); + pkg->files_list_valid = false; + pkg->files_list_phys_offs = 0; pkg->files = NULL; + pkg->archives = NULL; pkg->clientdata = NULL; pkg->trigaw.head = NULL; pkg->trigaw.tail = NULL; diff --git a/lib/dpkg/report.c b/lib/dpkg/report.c index 58df60006..f7763b362 100644 --- a/lib/dpkg/report.c +++ b/lib/dpkg/report.c @@ -51,6 +51,24 @@ dpkg_set_report_buffer(FILE *fp) setvbuf(fp, NULL, piped_mode, 0); } +void +dpkg_warning_printer(const char *msg, void *data) +{ + fprintf(stderr, "%s%s:%s %s%s:%s %s\n", + color_get(COLOR_PROG), dpkg_get_progname(), color_reset(), + color_get(COLOR_WARN), _("warning"), color_reset(), msg); +} + +static dpkg_warning_printer_func *warning_printer_func = dpkg_warning_printer; +static void *warning_printer_data; + +void +dpkg_set_warning_printer(dpkg_warning_printer_func *printer, void *data) +{ + warning_printer_func = printer; + warning_printer_data = data; +} + static int warn_count = 0; int @@ -67,9 +85,7 @@ warningv(const char *fmt, va_list args) warn_count++; m_vasprintf(&buf, fmt, args); - fprintf(stderr, "%s%s:%s %s%s:%s %s\n", - color_get(COLOR_PROG), dpkg_get_progname(), color_reset(), - color_get(COLOR_WARN), _("warning"), color_reset(), buf); + warning_printer_func(buf, warning_printer_data); free(buf); } diff --git a/lib/dpkg/report.h b/lib/dpkg/report.h index 4cadc7fe3..862a8429a 100644 --- a/lib/dpkg/report.h +++ b/lib/dpkg/report.h @@ -38,6 +38,11 @@ DPKG_BEGIN_DECLS void dpkg_set_report_piped_mode(int mode); void dpkg_set_report_buffer(FILE *fp); +typedef void dpkg_warning_printer_func(const char *msg, void *data); + +void dpkg_warning_printer(const char *msg, void *data); +void dpkg_set_warning_printer(dpkg_warning_printer_func *printer, void *data); + int warning_get_count(void); void warningv(const char *fmt, va_list args) DPKG_ATTR_VPRINTF(1); void warning(const char *fmt, ...) DPKG_ATTR_PRINTF(1); diff --git a/lib/dpkg/string.c b/lib/dpkg/string.c index 220e2789f..ff97159f9 100644 --- a/lib/dpkg/string.c +++ b/lib/dpkg/string.c @@ -28,6 +28,25 @@ #include <dpkg/string.h> #include <dpkg/dpkg.h> +char * +str_concat(char *dst, ...) +{ + va_list args; + const char *src; + size_t len; + + va_start(args, dst); + while ((src = va_arg(args, const char *))) { + len = strlen(src); + memcpy(dst, src, len); + dst += len; + } + va_end(args); + *dst = '\0'; + + return dst; +} + /** * Match the end of a string. * diff --git a/lib/dpkg/string.h b/lib/dpkg/string.h index 927aeaee4..d0f6bd7ad 100644 --- a/lib/dpkg/string.h +++ b/lib/dpkg/string.h @@ -56,6 +56,7 @@ bool str_match_end(const char *str, const char *end); unsigned int str_fnv_hash(const char *str); +char *str_concat(char *dst, ...) DPKG_ATTR_SENTINEL; char *str_fmt(const char *fmt, ...) DPKG_ATTR_PRINTF(1); char *str_escape_fmt(char *dest, const char *src, size_t n); char *str_quote_meta(const char *src); diff --git a/lib/dpkg/subproc.c b/lib/dpkg/subproc.c index b2f36ec2e..7b3fb9906 100644 --- a/lib/dpkg/subproc.c +++ b/lib/dpkg/subproc.c @@ -73,7 +73,7 @@ subproc_signals_ignore(const char *name) for (i = 0; i < array_count(signo_ignores); i++) subproc_set_signal(signo_ignores[i], &sa, &sa_save[i], name); - push_cleanup(subproc_signals_cleanup, ~0, NULL, 0, 0); + push_cleanup(subproc_signals_cleanup, ~0, 0); onerr_abort--; } diff --git a/lib/dpkg/t/.gitignore b/lib/dpkg/t/.gitignore index e22bcc9e4..b711dbee7 100644 --- a/lib/dpkg/t/.gitignore +++ b/lib/dpkg/t/.gitignore @@ -1,6 +1,11 @@ +# Benchmarks +b-fsys-hash +b-pkg-hash +# Compiled helpers c-tarextract c-treewalk c-trigdeferred +# Testsuite t-ar t-arch t-buffer @@ -9,12 +14,20 @@ t-command t-deb-version t-ehandle t-error +t-file +t-fsys-dir +t-fsys-hash +t-headers-cpp t-macros t-mod-db +t-namevalue +t-pager t-path t-pkginfo +t-pkg-hash t-pkg-list t-pkg-queue +t-pkg-show t-progname t-string t-subproc diff --git a/lib/dpkg/t/Makefile.am b/lib/dpkg/t/Makefile.am index 12dc4d86a..ab9ce3b4b 100644 --- a/lib/dpkg/t/Makefile.am +++ b/lib/dpkg/t/Makefile.am @@ -17,20 +17,26 @@ TEST_ENV_VARS = \ DPKG_PROGTAR=$(TAR) \ $(nil) +t_headers_cpp_SOURCES = t-headers-cpp.cc + # The tests are sorted in order of increasing complexity. test_programs = \ t-test \ t-test-skip \ t-macros \ + t-headers-cpp \ t-c-ctype \ + t-namevalue \ t-ehandle \ t-error \ t-string \ + t-file \ t-buffer \ t-path \ t-progname \ t-subproc \ t-command \ + t-pager \ t-varbuf \ t-ar \ t-tar \ @@ -40,6 +46,10 @@ test_programs = \ t-pkginfo \ t-pkg-list \ t-pkg-queue \ + t-pkg-hash \ + t-pkg-show \ + t-fsys-dir \ + t-fsys-hash \ t-trigger \ t-mod-db \ $(nil) @@ -50,8 +60,15 @@ test_scripts = \ t-trigdeferred.t \ $(nil) +BENCHMARK_LDADD_FLAGS = -lrt $(LDADD) + +b_fsys_hash_LDADD = $(BENCHMARK_LDADD_FLAGS) +b_pkg_hash_LDADD = $(BENCHMARK_LDADD_FLAGS) + check_PROGRAMS = \ $(test_programs) \ + b-fsys-hash \ + b-pkg-hash \ c-tarextract \ c-treewalk \ c-trigdeferred \ diff --git a/lib/dpkg/t/b-fsys-hash.c b/lib/dpkg/t/b-fsys-hash.c new file mode 100644 index 000000000..373cfc7c6 --- /dev/null +++ b/lib/dpkg/t/b-fsys-hash.c @@ -0,0 +1,80 @@ +/* + * dpkg - main program for package management + * b-fsys-hash.c - test fsys database load and hash performance + * + * Copyright © 2009-2019 Guillem Jover <guillem@debian.org> + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +#include <dpkg/perf.h> + +#include <dpkg/db-fsys.h> + +static const char *admindir; + +int +main(int argc, const char *const *argv) +{ + struct perf_slot ps; + + push_error_context(); + setvbuf(stdout, NULL, _IONBF, 0); + + admindir = dpkg_db_set_dir(admindir); + + perf_ts_mark_print("init"); + + perf_ts_slot_start(&ps); + fsys_hash_init(); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "fsys_hash_init"); + + perf_ts_slot_start(&ps); + modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "modstatdb_init"); + + perf_ts_slot_start(&ps); + ensure_allinstfiles_available_quiet(); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "load .list"); + + pkg_hash_report(stdout); + fsys_hash_report(stdout); + + modstatdb_shutdown(); + pop_error_context(ehflag_normaltidy); + + perf_ts_mark_print("shutdown"); + + return 0; +} diff --git a/lib/dpkg/t/b-pkg-hash.c b/lib/dpkg/t/b-pkg-hash.c new file mode 100644 index 000000000..74e26af7b --- /dev/null +++ b/lib/dpkg/t/b-pkg-hash.c @@ -0,0 +1,65 @@ +/* + * dpkg - main program for package management + * b-pkg-hash.c - test pkg database load and hash performance + * + * Copyright © 2009-2019 Guillem Jover <guillem@debian.org> + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> + +#include <dpkg/perf.h> + +static const char *admindir; + +int +main(int argc, const char *const *argv) +{ + struct perf_slot ps; + + push_error_context(); + setvbuf(stdout, NULL, _IONBF, 0); + + admindir = dpkg_db_set_dir(admindir); + + perf_ts_mark_print("init"); + + perf_ts_slot_start(&ps); + modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); + perf_ts_slot_stop(&ps); + + perf_ts_slot_print(&ps, "modstatdb_init"); + + pkg_hash_report(stdout); + + modstatdb_shutdown(); + pop_error_context(ehflag_normaltidy); + + perf_ts_mark_print("shutdown"); + + return 0; +} diff --git a/lib/dpkg/t/c-tarextract.c b/lib/dpkg/t/c-tarextract.c index 4781c0237..34f3d08fa 100644 --- a/lib/dpkg/t/c-tarextract.c +++ b/lib/dpkg/t/c-tarextract.c @@ -42,16 +42,17 @@ struct tar_context { }; static int -tar_read(void *ctx, char *buffer, int size) +tar_read(struct tar_archive *tar, char *buffer, int size) { - struct tar_context *tc = ctx; + struct tar_context *tc = tar->ctx; return fd_read(tc->tar_fd, buffer, size); } static int -tar_object_skip(struct tar_context *tc, struct tar_entry *te) +tar_object_skip(struct tar_archive *tar, struct tar_entry *te) { + struct tar_context *tc = tar->ctx; off_t size; size = (te->size + TARBLKSZ - 1) / TARBLKSZ * TARBLKSZ; @@ -62,7 +63,7 @@ tar_object_skip(struct tar_context *tc, struct tar_entry *te) } static int -tar_object(void *ctx, struct tar_entry *te) +tar_object(struct tar_archive *tar, struct tar_entry *te) { printf("%s mode=%o time=%ld.%.9d uid=%d gid=%d", te->name, te->stat.mode, te->mtime, 0, te->stat.uid, te->stat.gid); @@ -74,7 +75,7 @@ tar_object(void *ctx, struct tar_entry *te) switch (te->type) { case TAR_FILETYPE_FILE0: case TAR_FILETYPE_FILE: - tar_object_skip(ctx, te); + tar_object_skip(tar, te); printf(" type=file size=%jd", (intmax_t)te->size); break; case TAR_FILETYPE_HARDLINK: @@ -116,7 +117,8 @@ struct tar_operations tar_ops = { int main(int argc, char **argv) { - struct tar_context ctx; + struct tar_archive tar; + struct tar_context tar_ctx; const char *tar_name = argv[1]; setvbuf(stdout, NULL, _IOLBF, 0); @@ -124,18 +126,22 @@ main(int argc, char **argv) push_error_context(); if (tar_name) { - ctx.tar_fd = open(tar_name, O_RDONLY); - if (ctx.tar_fd < 0) + tar_ctx.tar_fd = open(tar_name, O_RDONLY); + if (tar_ctx.tar_fd < 0) ohshite("cannot open file '%s'", tar_name); } else { - ctx.tar_fd = STDIN_FILENO; + tar_ctx.tar_fd = STDIN_FILENO; } - if (tar_extractor(&ctx, &tar_ops)) + tar.err = DPKG_ERROR_OBJECT; + tar.ctx = &tar_ctx; + tar.ops = &tar_ops; + + if (tar_extractor(&tar)) ohshite("extracting tar"); if (tar_name) - close(ctx.tar_fd); + close(tar_ctx.tar_fd); pop_error_context(ehflag_normaltidy); diff --git a/lib/dpkg/t/t-ar.c b/lib/dpkg/t/t-ar.c index 28b5e38f3..88e93872a 100644 --- a/lib/dpkg/t/t-ar.c +++ b/lib/dpkg/t/t-ar.c @@ -21,6 +21,8 @@ #include <config.h> #include <compat.h> +#include <string.h> + #include <dpkg/test.h> #include <dpkg/ar.h> @@ -29,11 +31,11 @@ test_ar_normalize_name(void) { struct dpkg_ar_hdr arh; - strncpy(arh.ar_name, "member-name/ ", sizeof(arh.ar_name)); + memccpy(arh.ar_name, "member-name/ ", '\0', sizeof(arh.ar_name)); dpkg_ar_normalize_name(&arh); test_str(arh.ar_name, ==, "member-name"); - strncpy(arh.ar_name, "member-name ", sizeof(arh.ar_name)); + memccpy(arh.ar_name, "member-name ", '\0', sizeof(arh.ar_name)); dpkg_ar_normalize_name(&arh); test_str(arh.ar_name, ==, "member-name"); } diff --git a/lib/dpkg/t/t-command.c b/lib/dpkg/t/t-command.c index b59abd4e4..099884560 100644 --- a/lib/dpkg/t/t-command.c +++ b/lib/dpkg/t/t-command.c @@ -208,44 +208,9 @@ test_command_shell(void) test_pass(ret == 0); } -static void -test_dup_file(int fd, const char *filename, int flags) -{ - int newfd; - - newfd = open(filename, flags); - dup2(newfd, fd); - close(newfd); -} - -static void -test_command_pager(void) -{ - const char *pager, *default_pager; - int origfd = dup(STDOUT_FILENO); - - /* Test stdout being a tty. */ - test_todo_block("environment might not expose controlling terminal") { - test_dup_file(STDOUT_FILENO, "/dev/tty", O_WRONLY); - setenv("PAGER", "test-pager", 1); - pager = command_get_pager(); - unsetenv("PAGER"); - default_pager = command_get_pager(); - dup2(origfd, STDOUT_FILENO); - test_str(pager, ==, "test-pager"); - test_str(default_pager, ==, DEFAULTPAGER); - } - - /* Test stdout not being a tty. */ - test_dup_file(STDOUT_FILENO, "/dev/null", O_WRONLY); - pager = command_get_pager(); - dup2(origfd, STDOUT_FILENO); - test_str(pager, ==, CAT); -} - TEST_ENTRY(test) { - test_plan(52); + test_plan(49); test_command_init(); test_command_grow_argv(); @@ -254,5 +219,4 @@ TEST_ENTRY(test) test_command_add_args(); test_command_exec(); test_command_shell(); - test_command_pager(); } diff --git a/lib/dpkg/t/t-ehandle.c b/lib/dpkg/t/t-ehandle.c index 5904bfae1..7a20887af 100644 --- a/lib/dpkg/t/t-ehandle.c +++ b/lib/dpkg/t/t-ehandle.c @@ -100,7 +100,7 @@ test_cleanup_error(void) pass = false; push_error_context_jump(&handler_jump, printer_empty, "test cleanup"); - push_cleanup(cleanup_error, ~ehflag_normaltidy, NULL, 0, 0); + push_cleanup(cleanup_error, ~ehflag_normaltidy, 0); pop_error_context(ehflag_bombout); /* We should have recovered from the cleanup handler failing, diff --git a/lib/dpkg/t/t-error.c b/lib/dpkg/t/t-error.c index 5e2f55fcd..e93459e7e 100644 --- a/lib/dpkg/t/t-error.c +++ b/lib/dpkg/t/t-error.c @@ -42,10 +42,12 @@ test_dpkg_error_put(void) test_pass(dpkg_put_errno(NULL, "void error") < 0); test_pass(dpkg_put_warn(&err, "test warning %d", 10) < 0); + test_pass(err.syserrno == 0); test_str(err.str, ==, "test warning 10"); test_warn(err); test_pass(dpkg_put_error(&err, "test error %d", 20) < 0); + test_pass(err.syserrno == 0); test_str(err.str, ==, "test error 20"); test_error(err); @@ -54,6 +56,7 @@ test_dpkg_error_put(void) test_bail("cannot allocate string"); test_pass(dpkg_put_errno(&err, "test errno %d", 30) < 0); test_str(err.str, ==, errstr_ref); + test_pass(err.syserrno == ENOENT); test_error(err); free(errstr_ref); errno = 0; @@ -64,17 +67,20 @@ test_dpkg_error_destroy(void) { struct dpkg_error err = DPKG_ERROR_INIT; - test_pass(dpkg_put_error(&err, "test destroy") < 0); + errno = ENOENT; + test_pass(dpkg_put_errno(&err, "test destroy") < 0); + test_pass(err.syserrno == ENOENT); test_pass(err.type == DPKG_MSG_ERROR); test_pass(err.str != NULL); dpkg_error_destroy(&err); test_pass(err.type == DPKG_MSG_NONE); + test_pass(err.syserrno == 0); test_pass(err.str == NULL); } TEST_ENTRY(test) { - test_plan(19); + test_plan(24); test_dpkg_error_put(); test_dpkg_error_destroy(); diff --git a/lib/dpkg/t/t-file.c b/lib/dpkg/t/t-file.c new file mode 100644 index 000000000..ca68eaefe --- /dev/null +++ b/lib/dpkg/t/t-file.c @@ -0,0 +1,96 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-file.c - test file functions + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/file.h> + +#include <sys/types.h> + +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +static const char ref_data[] = + "this is a test string\n" + "within a test file\n" + "containing multiple lines\n" +; + +static void +test_file_slurp(void) +{ + struct varbuf vb = VARBUF_INIT; + struct dpkg_error err = DPKG_ERROR_INIT; + char *test_file; + char *test_dir; + int fd; + + test_pass(file_slurp("/nonexistent", &vb, &err) < 0); + test_pass(vb.used == 0); + test_pass(vb.buf == NULL); + test_pass(err.syserrno == ENOENT); + test_error(err); + varbuf_destroy(&vb); + + test_dir = test_alloc(strdup("test.XXXXXX")); + test_pass(mkdtemp(test_dir) != NULL); + test_pass(file_slurp(test_dir, &vb, &err) < 0); + test_pass(vb.used == 0); + test_pass(vb.buf == NULL); + test_pass(err.syserrno == 0); + test_error(err); + varbuf_destroy(&vb); + test_pass(rmdir(test_dir) == 0); + + test_file = test_alloc(strdup("test.XXXXXX")); + fd = mkstemp(test_file); + test_pass(fd >= 0); + + test_pass(file_slurp(test_file, &vb, &err) == 0); + test_pass(vb.used == 0); + test_pass(vb.buf == NULL); + test_pass(err.syserrno == 0); + test_pass(err.type == DPKG_MSG_NONE); + varbuf_destroy(&vb); + + test_pass(write(fd, ref_data, strlen(ref_data)) == (ssize_t)strlen(ref_data)); + test_pass(lseek(fd, 0, SEEK_SET) == 0); + + test_pass(file_slurp(test_file, &vb, &err) == 0); + test_pass(vb.used == strlen(ref_data)); + test_mem(vb.buf, ==, ref_data, min(vb.used, strlen(ref_data))); + test_pass(err.syserrno == 0); + test_pass(err.type == DPKG_MSG_NONE); + varbuf_destroy(&vb); + + test_pass(unlink(test_file) == 0); +} + +TEST_ENTRY(test) +{ + test_plan(26); + + test_file_slurp(); +} diff --git a/lib/dpkg/t/t-fsys-dir.c b/lib/dpkg/t/t-fsys-dir.c new file mode 100644 index 000000000..7dbe753d1 --- /dev/null +++ b/lib/dpkg/t/t-fsys-dir.c @@ -0,0 +1,65 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-fsys.c - test filesystem handling + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <stdlib.h> + +#include <dpkg/test.h> +#include <dpkg/fsys.h> + +static void +test_fsys_dir(void) +{ + char *dir; + + test_str(dpkg_fsys_get_dir(), ==, ""); + + dpkg_fsys_set_dir("/testdir"); + test_str(dpkg_fsys_get_dir(), ==, "/testdir"); + + dir = dpkg_fsys_get_path("testfile"); + test_str(dir, ==, "/testdir/testfile"); + free(dir); + + setenv("DPKG_ROOT", "/testenvdir", 1); + dpkg_fsys_set_dir(NULL); + test_str(dpkg_fsys_get_dir(), ==, "/testenvdir"); + + dir = dpkg_fsys_get_path("testfile"); + test_str(dir, ==, "/testenvdir/testfile"); + free(dir); + + unsetenv("DPKG_ROOT"); + dpkg_fsys_set_dir(NULL); + test_str(dpkg_fsys_get_dir(), ==, ""); + + dir = dpkg_fsys_get_path("testfile"); + test_str(dir, ==, "/testfile"); + free(dir); +} + +TEST_ENTRY(test) +{ + test_plan(7); + + test_fsys_dir(); +} diff --git a/lib/dpkg/t/t-fsys-hash.c b/lib/dpkg/t/t-fsys-hash.c new file mode 100644 index 000000000..d80dbe899 --- /dev/null +++ b/lib/dpkg/t/t-fsys-hash.c @@ -0,0 +1,108 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-fsys-hash.c - test fsys-hash implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/fsys.h> + +static void +test_fsys_nodes(void) +{ + struct fsys_namenode *fnn; + struct fsys_hash_iter *iter; + const char *name; + + test_pass(fsys_hash_entries() == 0); + + fsys_hash_init(); + + fnn = fsys_hash_find_node("/nonexistent", FHFF_NONE); + test_pass(fnn == NULL); + test_pass(fsys_hash_entries() == 0); + + name = "/test/path/aa"; + fnn = fsys_hash_find_node(name, FHFF_NOCOPY); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 1); + test_pass(fnn->name == name); + test_str(fnn->name, ==, "/test/path/aa"); + test_pass(fnn->flags == 0); + test_pass(fnn->oldhash == NULL); + test_str(fnn->newhash, ==, EMPTYHASHFLAG); + + fnn = fsys_hash_find_node("//./test/path/bb", 0); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 2); + test_str(fnn->name, ==, "/test/path/bb"); + test_pass(fnn->flags == 0); + test_pass(fnn->oldhash == NULL); + test_str(fnn->newhash, ==, EMPTYHASHFLAG); + + fnn = fsys_hash_find_node("/test/path/cc", 0); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 3); + test_str(fnn->name, ==, "/test/path/cc"); + test_pass(fnn->flags == 0); + test_pass(fnn->oldhash == NULL); + test_str(fnn->newhash, ==, EMPTYHASHFLAG); + + iter = fsys_hash_iter_new(); + while ((fnn = fsys_hash_iter_next(iter))) { + if (strcmp(fnn->name, "/test/path/aa") == 0) + test_str(fnn->name, ==, "/test/path/aa"); + else if (strcmp(fnn->name, "/test/path/bb") == 0) + test_str(fnn->name, ==, "/test/path/bb"); + else if (strcmp(fnn->name, "/test/path/cc") == 0) + test_str(fnn->name, ==, "/test/path/cc"); + else + test_fail("unknown fsys_namenode"); + } + fsys_hash_iter_free(iter); + + fsys_hash_init(); + test_pass(fsys_hash_entries() == 3); + fnn = fsys_hash_find_node("/test/path/aa", FHFF_NONE); + test_pass(fnn != NULL); + fnn = fsys_hash_find_node("/test/path/bb", FHFF_NONE); + test_pass(fnn != NULL); + fnn = fsys_hash_find_node("/test/path/cc", FHFF_NONE); + test_pass(fnn != NULL); + test_pass(fsys_hash_entries() == 3); + + fsys_hash_reset(); + test_pass(fsys_hash_entries() == 0); + fnn = fsys_hash_find_node("/test/path/aa", FHFF_NONE); + test_pass(fnn == NULL); + fnn = fsys_hash_find_node("/test/path/bb", FHFF_NONE); + test_pass(fnn == NULL); + fnn = fsys_hash_find_node("/test/path/cc", FHFF_NONE); + test_pass(fnn == NULL); + test_pass(fsys_hash_entries() == 0); +} + +TEST_ENTRY(test) +{ + test_plan(35); + + test_fsys_nodes(); +} diff --git a/lib/dpkg/t/t-headers-cpp.cc b/lib/dpkg/t/t-headers-cpp.cc new file mode 100644 index 000000000..b5becf9ae --- /dev/null +++ b/lib/dpkg/t/t-headers-cpp.cc @@ -0,0 +1,82 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-headers-cpp.cc - test C++ inclusion of headers + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <cstdbool> + +#include <dpkg/ar.h> +#include <dpkg/arch.h> +#include <dpkg/atomic-file.h> +#include <dpkg/buffer.h> +#include <dpkg/c-ctype.h> +#include <dpkg/color.h> +#include <dpkg/command.h> +#include <dpkg/compress.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> +#include <dpkg/deb-version.h> +#include <dpkg/debug.h> +#include <dpkg/dir.h> +#include <dpkg/dlist.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/dpkg.h> +#include <dpkg/ehandle.h> +#include <dpkg/error.h> +#include <dpkg/fdio.h> +#include <dpkg/file.h> +#include <dpkg/fsys.h> +#include <dpkg/glob.h> +#include <dpkg/i18n.h> +#include <dpkg/macros.h> +#include <dpkg/namevalue.h> +#include <dpkg/options.h> +#include <dpkg/pager.h> +#include <dpkg/parsedump.h> +#include <dpkg/path.h> +#include <dpkg/pkg-array.h> +#include <dpkg/pkg-files.h> +#include <dpkg/pkg-format.h> +#include <dpkg/pkg-list.h> +#include <dpkg/pkg-queue.h> +#include <dpkg/pkg-show.h> +#include <dpkg/pkg-spec.h> +#include <dpkg/pkg.h> +#include <dpkg/progname.h> +#include <dpkg/program.h> +#include <dpkg/progress.h> +#include <dpkg/report.h> +#include <dpkg/string.h> +#include <dpkg/subproc.h> +#include <dpkg/tarfn.h> +#include <dpkg/test.h> +#include <dpkg/treewalk.h> +#include <dpkg/trigdeferred.h> +#include <dpkg/triglib.h> +#include <dpkg/varbuf.h> +#include <dpkg/version.h> + +TEST_ENTRY(test) +{ + test_plan(1); + + test_pass(true); +} diff --git a/lib/dpkg/t/t-mod-db.c b/lib/dpkg/t/t-mod-db.c index 680dab5e5..c40d8e8ae 100644 --- a/lib/dpkg/t/t-mod-db.c +++ b/lib/dpkg/t/t-mod-db.c @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * t-db.c - test database implementation + * t-mod-db.c - test database implementation * * Copyright © 2011 Guillem Jover <guillem@debian.org> * diff --git a/lib/dpkg/t/t-namevalue.c b/lib/dpkg/t/t-namevalue.c new file mode 100644 index 000000000..c8647662e --- /dev/null +++ b/lib/dpkg/t/t-namevalue.c @@ -0,0 +1,48 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-namevalue.c - test name/value implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/namevalue.h> +#include <dpkg/dpkg-db.h> + +static void +test_namevalue(void) +{ + const struct namevalue *nv; + + nv = namevalue_find_by_name(booleaninfos, ""); + test_pass(nv == NULL); + + nv = namevalue_find_by_name(booleaninfos, "no"); + test_pass(nv != NULL); + test_pass(nv->value == false); + test_pass(nv->length == strlen("no")); + test_str(nv->name, ==, "no"); +} + +TEST_ENTRY(test) +{ + test_plan(5); + + test_namevalue(); +} diff --git a/lib/dpkg/t/t-pager.c b/lib/dpkg/t/t-pager.c new file mode 100644 index 000000000..2481e8065 --- /dev/null +++ b/lib/dpkg/t/t-pager.c @@ -0,0 +1,75 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pager.c - test pager implementation + * + * Copyright © 2010-2012 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#include <dpkg/test.h> +#include <dpkg/pager.h> +#include <dpkg/dpkg.h> + +static void +test_dup_file(int fd, const char *filename, int flags) +{ + int newfd; + + newfd = open(filename, flags); + dup2(newfd, fd); + close(newfd); +} + +static void +test_pager_get_exec(void) +{ + const char *pager, *default_pager; + int origfd = dup(STDOUT_FILENO); + + /* Test stdout being a tty. */ + test_todo_block("environment might not expose controlling terminal") { + test_dup_file(STDOUT_FILENO, "/dev/tty", O_WRONLY); + setenv("PAGER", "test-pager", 1); + pager = pager_get_exec(); + unsetenv("PAGER"); + default_pager = pager_get_exec(); + dup2(origfd, STDOUT_FILENO); + test_str(pager, ==, "test-pager"); + test_str(default_pager, ==, DEFAULTPAGER); + } + + /* Test stdout not being a tty. */ + test_dup_file(STDOUT_FILENO, "/dev/null", O_WRONLY); + pager = pager_get_exec(); + dup2(origfd, STDOUT_FILENO); + test_str(pager, ==, CAT); +} + +TEST_ENTRY(test) +{ + test_plan(3); + + test_pager_get_exec(); +} diff --git a/lib/dpkg/t/t-pkg-hash.c b/lib/dpkg/t/t-pkg-hash.c new file mode 100644 index 000000000..72185a2f1 --- /dev/null +++ b/lib/dpkg/t/t-pkg-hash.c @@ -0,0 +1,178 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkg-hash.c - test pkg-hash implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg.h> + +static void +test_pkg_hash(void) +{ + struct dpkg_arch *arch; + struct pkgset *set; + struct pkginfo *pkg; + struct pkg_hash_iter *iter; + int pkginstance; + + test_pass(pkg_hash_count_set() == 0); + test_pass(pkg_hash_count_pkg() == 0); + + set = pkg_hash_find_set("pkg-aa"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-aa"); + test_pass(pkg_hash_count_set() == 1); + test_pass(pkg_hash_count_pkg() == 1); + + set = pkg_hash_find_set("pkg-aa"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-aa"); + test_pass(pkg_hash_count_set() == 1); + test_pass(pkg_hash_count_pkg() == 1); + + set = pkg_hash_find_set("Pkg-AA"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-aa"); + test_pass(pkg_hash_count_set() == 1); + test_pass(pkg_hash_count_pkg() == 1); + + set = pkg_hash_find_set("pkg-bb"); + pkg_set_status(&set->pkg, PKG_STAT_INSTALLED); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-bb"); + test_pass(pkg_hash_count_set() == 2); + test_pass(pkg_hash_count_pkg() == 2); + + set = pkg_hash_find_set("pkg-cc"); + test_pass(set != NULL); + test_str(set->name, ==, "pkg-cc"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 3); + + arch = dpkg_arch_find("arch-xx"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + pkg_set_status(pkg, PKG_STAT_INSTALLED); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-xx"); + test_str(pkg->available.arch->name, ==, "arch-xx"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 3); + + arch = dpkg_arch_find("arch-yy"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-yy"); + test_str(pkg->available.arch->name, ==, "arch-yy"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 4); + + arch = dpkg_arch_find("arch-zz"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + pkg_set_status(pkg, PKG_STAT_UNPACKED); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-zz"); + test_str(pkg->available.arch->name, ==, "arch-zz"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + arch = dpkg_arch_find("arch-xx"); + pkg = pkg_hash_find_pkg("pkg-aa", arch); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-aa"); + test_str(pkg->installed.arch->name, ==, "arch-xx"); + test_str(pkg->available.arch->name, ==, "arch-xx"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + set = pkg_hash_find_set("pkg-aa"); + test_str(set->name, ==, "pkg-aa"); + pkg = pkg_hash_get_singleton(set); + test_pass(pkg == NULL); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + pkg = pkg_hash_find_singleton("pkg-bb"); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-bb"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + pkg = pkg_hash_find_singleton("pkg-cc"); + test_pass(pkg != NULL); + test_str(pkg->set->name, ==, "pkg-cc"); + test_pass(pkg_hash_count_set() == 3); + test_pass(pkg_hash_count_pkg() == 5); + + iter = pkg_hash_iter_new(); + while ((set = pkg_hash_iter_next_set(iter))) { + if (strcmp(set->name, "pkg-aa") == 0) + test_str(set->name, ==, "pkg-aa"); + else if (strcmp(set->name, "pkg-bb") == 0) + test_str(set->name, ==, "pkg-bb"); + else if (strcmp(set->name, "pkg-cc") == 0) + test_str(set->name, ==, "pkg-cc"); + else + test_fail("unknown fsys_namenode"); + } + pkg_hash_iter_free(iter); + + pkginstance = 0; + iter = pkg_hash_iter_new(); + while ((pkg = pkg_hash_iter_next_pkg(iter))) { + pkginstance++; + if (strcmp(pkg->set->name, "pkg-aa") == 0) { + struct pkgbin *pkgbin = &pkg->installed; + + test_str(pkg->set->name, ==, "pkg-aa"); + if (strcmp(pkgbin->arch->name, "arch-xx") == 0) + test_str(pkgbin->arch->name, ==, "arch-xx"); + else if (strcmp(pkgbin->arch->name, "arch-yy") == 0) + test_str(pkgbin->arch->name, ==, "arch-yy"); + else if (strcmp(pkgbin->arch->name, "arch-zz") == 0) + test_str(pkgbin->arch->name, ==, "arch-zz"); + else + test_fail("unknown pkginfo instance"); + } else if (strcmp(pkg->set->name, "pkg-bb") == 0) { + test_str(pkg->set->name, ==, "pkg-bb"); + } else if (strcmp(pkg->set->name, "pkg-cc") == 0) { + test_str(pkg->set->name, ==, "pkg-cc"); + } else { + test_fail("unknown fsys_namenode"); + } + } + pkg_hash_iter_free(iter); + + pkg_hash_reset(); + test_pass(pkg_hash_count_set() == 0); + test_pass(pkg_hash_count_pkg() == 0); +} + +TEST_ENTRY(test) +{ + test_plan(71); + + test_pkg_hash(); +} diff --git a/lib/dpkg/t/t-pkg-show.c b/lib/dpkg/t/t-pkg-show.c new file mode 100644 index 000000000..0f6ece061 --- /dev/null +++ b/lib/dpkg/t/t-pkg-show.c @@ -0,0 +1,61 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-pkg-show.c - test pkg-show implementation + * + * Copyright © 2018 Guillem Jover <guillem@debian.org> + * + * This 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 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/arch.h> + +static void +test_pkg_show_name(void) +{ + struct dpkg_arch *arch; + struct pkginfo *pkg; + const char *pkgname; + + arch = dpkg_arch_find("arch"); + test_pass(arch); + + pkg = pkg_hash_find_pkg("test", arch); + test_pass(pkg); + test_str(pkg->set->name, ==, "test"); + test_pass(pkg->installed.arch->type == DPKG_ARCH_UNKNOWN); + + pkgname = pkg_name(pkg, pnaw_never); + test_pass(pkgname); + test_str(pkgname, ==, "test"); + + pkgname = pkg_name(pkg, pnaw_nonambig); + test_pass(pkgname); + test_str(pkgname, ==, "test:arch"); + + pkgname = pkg_name(pkg, pnaw_always); + test_pass(pkgname); + test_str(pkgname, ==, "test:arch"); +} + +TEST_ENTRY(test) +{ + test_plan(10); + + test_pkg_show_name(); +} diff --git a/lib/dpkg/t/t-string.c b/lib/dpkg/t/t-string.c index 038de84b0..fc2819b55 100644 --- a/lib/dpkg/t/t-string.c +++ b/lib/dpkg/t/t-string.c @@ -77,6 +77,37 @@ test_str_fnv_hash(void) } static void +test_str_concat(void) +{ + char buf[1024], *str; + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, NULL); + test_pass(str == buf); + test_str(buf, ==, ""); + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, "aaa", NULL); + test_str(buf, ==, "aaa"); + test_pass(str == buf + 3); + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, "zzzz", "yy", "xxxx", NULL); + test_str(buf, ==, "zzzzyyxxxx"); + test_pass(str == buf + 10); + + memset(buf, 0, sizeof(buf)); + str = str_concat(buf, "1234", "", "5678", NULL); + test_str(buf, ==, "12345678"); + test_pass(str == buf + 8); + + memset(buf, ' ', sizeof(buf)); + str = str_concat(buf, "eol", NULL, "bom", NULL); + test_str(buf, ==, "eol"); + test_pass(str == buf + 3); +} + +static void test_str_fmt(void) { char *str; @@ -119,6 +150,8 @@ test_str_escape_fmt(void) memset(buf, 'a', sizeof(buf)); q = str_escape_fmt(buf, NULL, 0); test_mem(buf, ==, "aaaa", 4); + test_pass(buf == q); + test_pass(strnlen(buf, sizeof(buf)) == sizeof(buf)); memset(buf, 'a', sizeof(buf)); q = str_escape_fmt(buf, "b", 1); @@ -197,11 +230,12 @@ test_str_strip_quotes(void) TEST_ENTRY(test) { - test_plan(50); + test_plan(62); test_str_is_set(); test_str_match_end(); test_str_fnv_hash(); + test_str_concat(); test_str_fmt(); test_str_escape_fmt(); test_str_quote_meta(); diff --git a/lib/dpkg/t/t-tar.c b/lib/dpkg/t/t-tar.c index 8417ed306..6fa217de5 100644 --- a/lib/dpkg/t/t-tar.c +++ b/lib/dpkg/t/t-tar.c @@ -72,22 +72,22 @@ test_tar_atol8(void) errno = 0; u = tar_atoul(" 11111aaa ", 12, UINTMAX_MAX); test_pass(u == 0); - test_pass(errno == EINVAL); + test_pass(errno == ERANGE); errno = 0; u = tar_atoul(" 8 ", 12, UINTMAX_MAX); test_pass(u == 0); - test_pass(errno == EINVAL); + test_pass(errno == ERANGE); errno = 0; u = tar_atoul(" 18 ", 12, UINTMAX_MAX); test_pass(u == 0); - test_pass(errno == EINVAL); + test_pass(errno == ERANGE); errno = 0; u = tar_atoul(" aa ", 12, UINTMAX_MAX); test_pass(u == 0); - test_pass(errno == EINVAL); + test_pass(errno == ERANGE); } static void diff --git a/lib/dpkg/t/t-tarextract.t b/lib/dpkg/t/t-tarextract.t index 25916bb8d..5fb9afa09 100755 --- a/lib/dpkg/t/t-tarextract.t +++ b/lib/dpkg/t/t-tarextract.t @@ -119,7 +119,7 @@ TAR make_path($tmpdir); - my $cwd = cwd(); + my $cwd = getcwd(); # Check generated tarballs. foreach my $type (qw(v7 ustar oldgnu gnu)) { diff --git a/lib/dpkg/t/t-test.c b/lib/dpkg/t/t-test.c index 62b35ce2f..48ce8726b 100644 --- a/lib/dpkg/t/t-test.c +++ b/lib/dpkg/t/t-test.c @@ -25,7 +25,7 @@ TEST_ENTRY(test) { - test_plan(20); + test_plan(22); test_pass(1); test_fail(0); @@ -58,4 +58,9 @@ TEST_ENTRY(test) test_mem("abcd", ==, "abcd", 5); test_mem("ababcd", ==, "ababff", 4); test_mem("ababcd", !=, "ababff", 6); + + setenv("srcdir", "aaa", 1); + setenv("builddir", "bbb", 1); + test_str(test_get_srcdir(), ==, "aaa"); + test_str(test_get_builddir(), ==, "bbb"); } diff --git a/lib/dpkg/t/t-treewalk.t b/lib/dpkg/t/t-treewalk.t index dcde7901e..6f379c81b 100755 --- a/lib/dpkg/t/t-treewalk.t +++ b/lib/dpkg/t/t-treewalk.t @@ -45,7 +45,7 @@ sub make_file { # Populate the tree hierarchy. sub make_tree { my ($dirtree) = @_; - my $cwd = cwd(); + my $cwd = getcwd(); make_path($dirtree); chdir $dirtree; diff --git a/lib/dpkg/t/t-varbuf.c b/lib/dpkg/t/t-varbuf.c index 3fb4444b2..8df733b9b 100644 --- a/lib/dpkg/t/t-varbuf.c +++ b/lib/dpkg/t/t-varbuf.c @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * t-verbuf.c - test varbuf implementation + * t-varbuf.c - test varbuf implementation * * Copyright © 2009-2011, 2013-2015 Guillem Jover <guillem@debian.org> * @@ -60,6 +60,26 @@ test_varbuf_prealloc(void) } static void +test_varbuf_new(void) +{ + struct varbuf *vb; + + vb = varbuf_new(0); + test_pass(vb != NULL); + test_pass(vb->used == 0); + test_pass(vb->size == 0); + test_pass(vb->buf == NULL); + varbuf_free(vb); + + vb = varbuf_new(10); + test_pass(vb != NULL); + test_pass(vb->used == 0); + test_pass(vb->size >= 10); + test_pass(vb->buf != NULL); + varbuf_free(vb); +} + +static void test_varbuf_grow(void) { struct varbuf vb; @@ -350,10 +370,11 @@ test_varbuf_detach(void) TEST_ENTRY(test) { - test_plan(120); + test_plan(128); test_varbuf_init(); test_varbuf_prealloc(); + test_varbuf_new(); test_varbuf_grow(); test_varbuf_trunc(); test_varbuf_add_buf(); diff --git a/lib/dpkg/tarfn.c b/lib/dpkg/tarfn.c index 27952f926..a0821f217 100644 --- a/lib/dpkg/tarfn.c +++ b/lib/dpkg/tarfn.c @@ -22,6 +22,9 @@ #include <config.h> #include <compat.h> +#if HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif #include <sys/stat.h> #include <errno.h> @@ -35,6 +38,8 @@ #include <dpkg/macros.h> #include <dpkg/dpkg.h> +#include <dpkg/i18n.h> +#include <dpkg/error.h> #include <dpkg/tarfn.h> #define TAR_MAGIC_USTAR "ustar\0" "00" @@ -105,7 +110,7 @@ tar_atol8(const char *s, size_t size) if (*s == '\0' || *s == ' ') break; if (*s < '0' || *s > '7') - return tar_ret_errno(EINVAL, 0); + return tar_ret_errno(ERANGE, 0); n = (n * 010) + (*s++ - '0'); } @@ -118,7 +123,7 @@ tar_atol8(const char *s, size_t size) if (s < end) return tar_ret_errno(EINVAL, 0); - return n; + return tar_ret_errno(0, n); } /** @@ -154,12 +159,12 @@ tar_atol256(const char *s, size_t size, intmax_t min, uintmax_t max) for (;;) { n = (n << 8) | c; - if (--size <= 0) + if (--size == 0) break; c = *s++; } - return n; + return tar_ret_errno(0, n); } static uintmax_t @@ -269,7 +274,7 @@ tar_header_checksum(struct tar_header *h) } static int -tar_header_decode(struct tar_header *h, struct tar_entry *d) +tar_header_decode(struct tar_header *h, struct tar_entry *d, struct dpkg_error *err) { long checksum; @@ -296,7 +301,11 @@ tar_header_decode(struct tar_header *h, struct tar_entry *d) /* Even though off_t is signed, we use an unsigned parser here because * negative offsets are not allowed. */ d->size = TAR_ATOUL(h->size, off_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header size field")); d->mtime = TAR_ATOSL(h->mtime, time_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header mtime field")); if (d->type == TAR_FILETYPE_CHARDEV || d->type == TAR_FILETYPE_BLOCKDEV) d->dev = makedev(TAR_ATOUL(h->devmajor, dev_t), @@ -309,19 +318,25 @@ tar_header_decode(struct tar_header *h, struct tar_entry *d) else d->stat.uname = NULL; d->stat.uid = TAR_ATOUL(h->uid, uid_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header uid field")); if (*h->group) d->stat.gname = m_strndup(h->group, sizeof(h->group)); else d->stat.gname = NULL; d->stat.gid = TAR_ATOUL(h->gid, gid_t); + if (errno) + return dpkg_put_errno(err, _("invalid tar header gid field")); checksum = tar_atol8(h->checksum, sizeof(h->checksum)); - - /* Check for parse errors. */ if (errno) - return 0; - return tar_header_checksum(h) == checksum; + return dpkg_put_errno(err, _("invalid tar header checksum field")); + + if (tar_header_checksum(h) != checksum) + return dpkg_put_error(err, _("invalid tar header checksum")); + + return 0; } /** @@ -336,8 +351,7 @@ tar_header_decode(struct tar_header *h, struct tar_entry *d) * bogus name or link. */ static int -tar_gnu_long(void *ctx, const struct tar_operations *ops, struct tar_entry *te, - char **longp) +tar_gnu_long(struct tar_archive *tar, struct tar_entry *te, char **longp) { char buf[TARBLKSZ]; char *bp; @@ -350,14 +364,15 @@ tar_gnu_long(void *ctx, const struct tar_operations *ops, struct tar_entry *te, for (long_read = te->size; long_read > 0; long_read -= TARBLKSZ) { int copysize; - status = ops->read(ctx, buf, TARBLKSZ); + status = tar->ops->read(tar, buf, TARBLKSZ); if (status == TARBLKSZ) status = 0; else { /* Read partial header record? */ if (status > 0) { errno = 0; - status = -1; + status = dpkg_put_error(&tar->err, + _("partially read tar header")); } /* If we didn't get TARBLKSZ bytes read, punt. */ @@ -393,6 +408,8 @@ tar_entry_destroy(struct tar_entry *te) free(te->linkname); free(te->stat.uname); free(te->stat.gname); + + memset(te, 0, sizeof(*te)); } struct tar_symlink_entry { @@ -424,7 +441,7 @@ tar_entry_update_from_system(struct tar_entry *te) } int -tar_extractor(void *ctx, const struct tar_operations *ops) +tar_extractor(struct tar_archive *tar) { int status; char buffer[TARBLKSZ]; @@ -442,17 +459,14 @@ tar_extractor(void *ctx, const struct tar_operations *ops) h.stat.uname = NULL; h.stat.gname = NULL; - while ((status = ops->read(ctx, buffer, TARBLKSZ)) == TARBLKSZ) { + while ((status = tar->ops->read(tar, buffer, TARBLKSZ)) == TARBLKSZ) { int name_len; - if (!tar_header_decode((struct tar_header *)buffer, &h)) { + if (tar_header_decode((struct tar_header *)buffer, &h, &tar->err) < 0) { if (h.name[0] == '\0') { - /* End of tape. */ + /* End Of Tape. */ status = 0; } else { - /* Indicates broken tarfile: - * “Header checksum error”. */ - errno = 0; status = -1; } tar_entry_destroy(&h); @@ -471,9 +485,9 @@ tar_extractor(void *ctx, const struct tar_operations *ops) } if (h.name[0] == '\0') { - /* Indicates broken tarfile: “Bad header data”. */ + status = dpkg_put_error(&tar->err, + _("invalid tar header with empty name field")); errno = 0; - status = -1; tar_entry_destroy(&h); break; } @@ -484,7 +498,7 @@ tar_extractor(void *ctx, const struct tar_operations *ops) case TAR_FILETYPE_FILE: /* Compatibility with pre-ANSI ustar. */ if (h.name[name_len - 1] != '/') { - status = ops->extract_file(ctx, &h); + status = tar->ops->extract_file(tar, &h); break; } /* Else, fall through. */ @@ -492,10 +506,10 @@ tar_extractor(void *ctx, const struct tar_operations *ops) if (h.name[name_len - 1] == '/') { h.name[name_len - 1] = '\0'; } - status = ops->mkdir(ctx, &h); + status = tar->ops->mkdir(tar, &h); break; case TAR_FILETYPE_HARDLINK: - status = ops->link(ctx, &h); + status = tar->ops->link(tar, &h); break; case TAR_FILETYPE_SYMLINK: symlink_node = m_malloc(sizeof(*symlink_node)); @@ -512,18 +526,42 @@ tar_extractor(void *ctx, const struct tar_operations *ops) case TAR_FILETYPE_CHARDEV: case TAR_FILETYPE_BLOCKDEV: case TAR_FILETYPE_FIFO: - status = ops->mknod(ctx, &h); + status = tar->ops->mknod(tar, &h); break; case TAR_FILETYPE_GNU_LONGLINK: - status = tar_gnu_long(ctx, ops, &h, &next_long_link); + status = tar_gnu_long(tar, &h, &next_long_link); break; case TAR_FILETYPE_GNU_LONGNAME: - status = tar_gnu_long(ctx, ops, &h, &next_long_name); + status = tar_gnu_long(tar, &h, &next_long_name); + break; + case TAR_FILETYPE_GNU_VOLUME: + case TAR_FILETYPE_GNU_MULTIVOL: + case TAR_FILETYPE_GNU_SPARSE: + case TAR_FILETYPE_GNU_DUMPDIR: + status = dpkg_put_error(&tar->err, + _("unsupported GNU tar header type '%c'"), + h.type); + errno = 0; + break; + case TAR_FILETYPE_SOLARIS_EXTENDED: + case TAR_FILETYPE_SOLARIS_ACL: + status = dpkg_put_error(&tar->err, + _("unsupported Solaris tar header type '%c'"), + h.type); + errno = 0; + break; + case TAR_FILETYPE_PAX_GLOBAL: + case TAR_FILETYPE_PAX_EXTENDED: + status = dpkg_put_error(&tar->err, + _("unsupported PAX tar header type '%c'"), + h.type); + errno = 0; break; default: - /* Indicates broken tarfile: “Bad header field”. */ + status = dpkg_put_error(&tar->err, + _("unknown tar header type '%c'"), + h.type); errno = 0; - status = -1; } tar_entry_destroy(&h); if (status != 0) @@ -534,7 +572,7 @@ tar_extractor(void *ctx, const struct tar_operations *ops) while (symlink_head) { symlink_node = symlink_head->next; if (status == 0) - status = ops->symlink(ctx, &symlink_head->h); + status = tar->ops->symlink(tar, &symlink_head->h); tar_entry_destroy(&symlink_head->h); free(symlink_head); symlink_head = symlink_node; @@ -545,11 +583,11 @@ tar_extractor(void *ctx, const struct tar_operations *ops) free(next_long_link); if (status > 0) { - /* Indicates broken tarfile: “Read partial header record”. */ + status = dpkg_put_error(&tar->err, + _("partially read tar header")); errno = 0; - return -1; - } else { - /* Return whatever I/O function returned. */ - return status; } + + /* Return whatever I/O function returned. */ + return status; } diff --git a/lib/dpkg/tarfn.h b/lib/dpkg/tarfn.h index 37269de02..38ab9a9fe 100644 --- a/lib/dpkg/tarfn.h +++ b/lib/dpkg/tarfn.h @@ -26,6 +26,7 @@ #include <stdint.h> +#include <dpkg/error.h> #include <dpkg/file.h> /** @@ -37,6 +38,7 @@ #define TARBLKSZ 512 enum tar_format { + TAR_FORMAT_UNKNOWN, TAR_FORMAT_OLD, TAR_FORMAT_GNU, TAR_FORMAT_USTAR, @@ -53,12 +55,21 @@ enum tar_filetype { TAR_FILETYPE_BLOCKDEV = '4', TAR_FILETYPE_DIR = '5', TAR_FILETYPE_FIFO = '6', + TAR_FILETYPE_CONTIG = '7', TAR_FILETYPE_GNU_LONGLINK = 'K', TAR_FILETYPE_GNU_LONGNAME = 'L', + TAR_FILETYPE_GNU_VOLUME = 'V', + TAR_FILETYPE_GNU_MULTIVOL = 'M', + TAR_FILETYPE_GNU_DUMPDIR = 'D', + TAR_FILETYPE_GNU_SPARSE = 'S', + TAR_FILETYPE_PAX_GLOBAL = 'g', + TAR_FILETYPE_PAX_EXTENDED = 'x', + TAR_FILETYPE_SOLARIS_EXTENDED = 'X', + TAR_FILETYPE_SOLARIS_ACL = 'A', }; struct tar_entry { - /** Tar archive format. */ + /** Tar entry format. */ enum tar_format format; /** File type. */ enum tar_filetype type; @@ -76,8 +87,10 @@ struct tar_entry { struct file_stat stat; }; -typedef int tar_read_func(void *ctx, char *buffer, int length); -typedef int tar_make_func(void *ctx, struct tar_entry *h); +struct tar_archive; + +typedef int tar_read_func(struct tar_archive *tar, char *buffer, int length); +typedef int tar_make_func(struct tar_archive *tar, struct tar_entry *h); struct tar_operations { tar_read_func *read; @@ -89,6 +102,18 @@ struct tar_operations { tar_make_func *mknod; }; +struct tar_archive { + /* Global tar archive error. */ + struct dpkg_error err; + + /** Tar archive format. */ + enum tar_format format; + + /* Operation functions and context. */ + const struct tar_operations *ops; + void *ctx; +}; + uintmax_t tar_atoul(const char *s, size_t size, uintmax_t max); intmax_t @@ -97,7 +122,8 @@ tar_atosl(const char *s, size_t size, intmax_t min, intmax_t max); void tar_entry_update_from_system(struct tar_entry *te); -int tar_extractor(void *ctx, const struct tar_operations *ops); +int +tar_extractor(struct tar_archive *tar); /** @} */ diff --git a/lib/dpkg/test.h b/lib/dpkg/test.h index 39ecb967b..a0e5ffdae 100644 --- a/lib/dpkg/test.h +++ b/lib/dpkg/test.h @@ -59,6 +59,18 @@ test_alloc(void *ptr, const char *reason) #define test_alloc(ptr) \ test_alloc((ptr), "cannot allocate memory for " #ptr " in " __FILE__ ":" test_stringify(__LINE__)) +static inline const char * +test_get_envdir(const char *envvar) +{ + const char *envdir = getenv(envvar); + return envdir ? envdir : "."; +} + +#define test_get_srcdir() \ + test_get_envdir("srcdir") +#define test_get_builddir() \ + test_get_envdir("builddir") + static int test_id = 1; static int test_skip_code; static const char *test_skip_prefix; diff --git a/lib/dpkg/treewalk.c b/lib/dpkg/treewalk.c index 3a669e6b0..dee072f2d 100644 --- a/lib/dpkg/treewalk.c +++ b/lib/dpkg/treewalk.c @@ -206,7 +206,7 @@ treenode_resize_down(struct treenode *node) else node->down_size = 8; - new_size = node->down_size * sizeof(struct treenode *); + new_size = node->down_size * sizeof(*node); node->down = m_realloc(node->down, new_size); } @@ -367,7 +367,7 @@ treewalk_open(const char *rootdir, enum treewalk_options options, struct treeroot *tree; struct treenode *root; - tree = m_malloc(sizeof(struct treeroot)); + tree = m_malloc(sizeof(*tree)); tree->options = options; if (func) diff --git a/lib/dpkg/trigdeferred.c b/lib/dpkg/trigdeferred.c index e5bb2c347..b9b028f73 100644 --- a/lib/dpkg/trigdeferred.c +++ b/lib/dpkg/trigdeferred.c @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * trigdeferred.c - parsing of triggers/Deferred + * trigdeferred.c - parsing of triggers/Unincorp (was …/Deferred) * * Copyright © 2007 Canonical Ltd * written by Ian Jackson <ijackson@chiark.greenend.org.uk> @@ -83,13 +83,14 @@ trigdef_update_start(enum trigdef_update_flags uf) if (lock_fd == -1) { if (!(errno == ENOENT && (uf & TDUF_NO_LOCK_OK))) ohshite(_("unable to open/create " - "triggers lockfile '%.250s'"), + "triggers lock file '%.250s'"), fn.buf); return TDUS_ERROR_NO_DIR; } } - file_lock(&lock_fd, FILE_LOCK_WAIT, fn.buf, _("triggers area")); + file_lock(&lock_fd, FILE_LOCK_WAIT, fn.buf, + _("triggers database lock")); } constructfn(&fn, triggersdir, TRIGGERSDEFERREDFILE); diff --git a/lib/dpkg/trigdeferred.h b/lib/dpkg/trigdeferred.h index 5b7b87cd3..c353ca150 100644 --- a/lib/dpkg/trigdeferred.h +++ b/lib/dpkg/trigdeferred.h @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * trigdeferred.h - parsing of triggers/Deferred + * trigdeferred.h - parsing of triggers/Unincorp (was …/Deferred) * * Copyright © 2007 Canonical, Ltd. * written by Ian Jackson <ijackson@chiark.greenend.org.uk> diff --git a/lib/dpkg/triglib.c b/lib/dpkg/triglib.c index 57bd2a66b..ab9060f66 100644 --- a/lib/dpkg/triglib.c +++ b/lib/dpkg/triglib.c @@ -26,7 +26,6 @@ #include <sys/types.h> #include <sys/stat.h> -#include <assert.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> @@ -104,7 +103,9 @@ trig_clear_awaiters(struct pkginfo *notpend) struct trigaw *ta; struct pkginfo *aw; - assert(!notpend->trigpend_head); + if (notpend->trigpend_head) + internerr("package %s has pending triggers", + pkg_name(notpend, pnaw_always)); ta = notpend->othertrigaw_head; notpend->othertrigaw_head = NULL; @@ -395,12 +396,15 @@ trk_file_interest_change(const char *trig, struct pkginfo *pkg, struct pkgbin *pkgbin, int signum, enum trig_options opts) { - struct filenamenode *fnn; + struct fsys_namenode *fnn; struct trigfileint **search, *tfi; fnn = trigh.namenode_find(trig, signum <= 0); if (!fnn) { - assert(signum < 0); + if (signum >= 0) + internerr("lost filename node '%s' for package %s " + "triggered to add", trig, + pkgbin_name(pkg, pkgbin, pnaw_always)); return; } @@ -503,7 +507,7 @@ trig_file_interests_ensure(void) triggersfilefile); } - push_cleanup(cu_closestream, ~0, NULL, 0, 1, f); + push_cleanup(cu_closestream, ~0, 1, f); while (fgets_checked(linebuf, sizeof(linebuf), f, triggersfilefile) >= 0) { struct dpkg_error err; char *slash; @@ -541,14 +545,14 @@ ok: void trig_file_activate_byname(const char *trig, struct pkginfo *aw) { - struct filenamenode *fnn = trigh.namenode_find(trig, 1); + struct fsys_namenode *fnn = trigh.namenode_find(trig, 1); if (fnn) trig_file_activate(fnn, aw); } void -trig_file_activate(struct filenamenode *trig, struct pkginfo *aw) +trig_file_activate(struct fsys_namenode *trig, struct pkginfo *aw) { struct trigfileint *tfi; @@ -575,7 +579,7 @@ trig_file_activate_parents(const char *trig, struct pkginfo *aw) } void -trig_path_activate(struct filenamenode *trig, struct pkginfo *aw) +trig_path_activate(struct fsys_namenode *trig, struct pkginfo *aw) { trig_file_activate(trig, aw); trig_file_activate_parents(trigh.namenode_name(trig), aw); @@ -584,7 +588,7 @@ trig_path_activate(struct filenamenode *trig, struct pkginfo *aw) static void trig_path_activate_byname(const char *trig, struct pkginfo *aw) { - struct filenamenode *fnn = trigh.namenode_find(trig, 1); + struct fsys_namenode *fnn = trigh.namenode_find(trig, 1); if (fnn) trig_file_activate(fnn, aw); @@ -627,7 +631,10 @@ trig_cicb_interest_change(const char *trig, struct pkginfo *pkg, { const struct trigkindinfo *tki = trig_classify_byname(trig); - assert(filetriggers_edited >= 0); + if (filetriggers_edited < 0) + internerr("trigger control file for package %s not read", + pkgbin_name(pkg, pkgbin, pnaw_always)); + tki->interest_change(trig, pkg, pkgbin, signum, opts); } @@ -687,7 +694,7 @@ trig_parse_ci(const char *file, trig_parse_cicb *interest, return; /* No file is just like an empty one. */ ohshite(_("unable to open triggers ci file '%.250s'"), file); } - push_cleanup(cu_closestream, ~0, NULL, 0, 1, f); + push_cleanup(cu_closestream, ~0, 1, f); while ((l = fgets_checked(linebuf, sizeof(linebuf), f, file)) >= 0) { for (cmd = linebuf; c_iswhite(*cmd); cmd++) ; @@ -814,42 +821,12 @@ trig_incorporate(enum modstatdb_rw cstatus) /*---------- Default hooks. ----------*/ -struct filenamenode { - struct filenamenode *next; - const char *name; - struct trigfileint *trig_interested; -}; - -static struct filenamenode *trigger_files; - -static struct filenamenode * -th_simple_nn_find(const char *name, bool nonew) -{ - struct filenamenode *search; - - for (search = trigger_files; search; search = search->next) - if (strcmp(search->name, name) == 0) - return search; - - /* Not found. */ - if (nonew) - return NULL; - - search = nfmalloc(sizeof(*search)); - search->name = nfstrsave(name); - search->trig_interested = NULL; - search->next = trigger_files; - trigger_files = search; - - return search; -} - TRIGHOOKS_DEFINE_NAMENODE_ACCESSORS static struct trig_hooks trigh = { .enqueue_deferred = NULL, .transitional_activate = NULL, - .namenode_find = th_simple_nn_find, + .namenode_find = th_nn_find, .namenode_interested = th_nn_interested, .namenode_name = th_nn_name, }; diff --git a/lib/dpkg/triglib.h b/lib/dpkg/triglib.h index 3e63f5cbc..b4c772516 100644 --- a/lib/dpkg/triglib.h +++ b/lib/dpkg/triglib.h @@ -25,6 +25,7 @@ #include <dpkg/macros.h> #include <dpkg/dpkg-db.h> +#include <dpkg/fsys.h> DPKG_BEGIN_DECLS @@ -53,7 +54,7 @@ enum trig_options { struct trigfileint { struct pkginfo *pkg; struct pkgbin *pkgbin; - struct filenamenode *fnn; + struct fsys_namenode *fnn; enum trig_options options; struct trigfileint *samefile_next; struct { @@ -70,24 +71,26 @@ struct trig_hooks { void (*enqueue_deferred)(struct pkginfo *pend); void (*transitional_activate)(enum modstatdb_rw cstatus); - struct filenamenode *(*namenode_find)(const char *filename, bool nonew); - struct trigfileint **(*namenode_interested)(struct filenamenode *fnn); + struct fsys_namenode *(*namenode_find)(const char *filename, bool nonew); + struct trigfileint **(*namenode_interested)(struct fsys_namenode *fnn); /** Returns a pointer from nfmalloc. */ - const char *(*namenode_name)(struct filenamenode *fnn); + const char *(*namenode_name)(struct fsys_namenode *fnn); }; #define TRIGHOOKS_DEFINE_NAMENODE_ACCESSORS \ - static struct trigfileint **th_nn_interested(struct filenamenode *fnn) \ + static struct fsys_namenode *th_nn_find(const char *name, bool nonew) \ + { return fsys_hash_find_node(name, nonew ? FHFF_NONE : 0); } \ + static struct trigfileint **th_nn_interested(struct fsys_namenode *fnn) \ { return &fnn->trig_interested; } \ - static const char *th_nn_name(struct filenamenode *fnn) \ + static const char *th_nn_name(struct fsys_namenode *fnn) \ { return fnn->name; } void trig_override_hooks(const struct trig_hooks *hooks); void trig_file_activate_byname(const char *trig, struct pkginfo *aw); -void trig_file_activate(struct filenamenode *trig, struct pkginfo *aw); -void trig_path_activate(struct filenamenode *trig, struct pkginfo *aw); +void trig_file_activate(struct fsys_namenode *trig, struct pkginfo *aw); +void trig_path_activate(struct fsys_namenode *trig, struct pkginfo *aw); bool trig_note_pend_core(struct pkginfo *pend, const char *trig /*not copied!*/); bool trig_note_pend(struct pkginfo *pend, const char *trig /*not copied!*/); diff --git a/lib/dpkg/trignote.c b/lib/dpkg/trignote.c index 1029a030e..957e80859 100644 --- a/lib/dpkg/trignote.c +++ b/lib/dpkg/trignote.c @@ -76,7 +76,7 @@ trig_note_pend(struct pkginfo *pend, const char *trig) /* * Note: This is called also from fields.c where *aw is a temporary - * but pend is from pkg_db_find()! + * but pend is from pkg_hash_find()! * * @retval true For done. * @retval false For already noted. diff --git a/lib/dpkg/varbuf.c b/lib/dpkg/varbuf.c index 9a62f92e9..ee757bcc7 100644 --- a/lib/dpkg/varbuf.c +++ b/lib/dpkg/varbuf.c @@ -115,6 +115,17 @@ varbuf_get_str(struct varbuf *v) return v->buf; } +struct varbuf * +varbuf_new(size_t size) +{ + struct varbuf *v; + + v = m_malloc(sizeof(*v)); + varbuf_init(v, size); + + return v; +} + void varbuf_init(struct varbuf *v, size_t size) { @@ -186,3 +197,10 @@ varbuf_destroy(struct varbuf *v) { free(v->buf); v->buf=NULL; v->size=0; v->used=0; } + +void +varbuf_free(struct varbuf *v) +{ + free(v->buf); + free(v); +} diff --git a/lib/dpkg/varbuf.h b/lib/dpkg/varbuf.h index 9b9a30f95..06f09f5e8 100644 --- a/lib/dpkg/varbuf.h +++ b/lib/dpkg/varbuf.h @@ -72,12 +72,14 @@ struct varbuf { #define VARBUF_INIT { 0, 0, NULL } +struct varbuf *varbuf_new(size_t size); void varbuf_init(struct varbuf *v, size_t size); void varbuf_grow(struct varbuf *v, size_t need_size); void varbuf_trunc(struct varbuf *v, size_t used_size); char *varbuf_detach(struct varbuf *v); void varbuf_reset(struct varbuf *v); void varbuf_destroy(struct varbuf *v); +void varbuf_free(struct varbuf *v); void varbuf_add_char(struct varbuf *v, int c); void varbuf_dup_char(struct varbuf *v, int c, size_t n); |