summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2019-11-26 14:00:30 +0300
committerIgor Pashev <pashev.igor@gmail.com>2019-11-26 14:00:30 +0300
commit414ea1706306e061fc44a8b5ce3042d4f0728489 (patch)
treeef0b2c4eac79e479ed686a5d88d7b3b954717824 /lib
parented2b463626bd721942143baa6207f2ccac67a616 (diff)
parent89afa9af7cd589eb8384ed96b6d86dd59d56bdf5 (diff)
downloaddpkg-414ea1706306e061fc44a8b5ce3042d4f0728489.tar.gz
Merge https://salsa.debian.org/dpkg-team/dpkg
Diffstat (limited to 'lib')
-rw-r--r--lib/compat/Makefile.am6
-rw-r--r--lib/compat/compat.h6
-rw-r--r--lib/compat/getopt.c2
-rw-r--r--lib/compat/md5.c20
-rw-r--r--lib/compat/md5.h14
-rw-r--r--lib/compat/scandir.c6
-rw-r--r--lib/compat/strchrnul.c37
-rw-r--r--lib/dpkg/Makefile.am20
-rw-r--r--lib/dpkg/ar.h2
-rw-r--r--lib/dpkg/arch.c13
-rw-r--r--lib/dpkg/atomic-file.c2
-rw-r--r--lib/dpkg/buffer.c4
-rw-r--r--lib/dpkg/command.c25
-rw-r--r--lib/dpkg/command.h1
-rw-r--r--lib/dpkg/db-ctrl-access.c120
-rw-r--r--lib/dpkg/db-ctrl-format.c158
-rw-r--r--lib/dpkg/db-ctrl-upgrade.c252
-rw-r--r--lib/dpkg/db-ctrl.h52
-rw-r--r--lib/dpkg/db-fsys-digest.c151
-rw-r--r--lib/dpkg/db-fsys-divert.c132
-rw-r--r--lib/dpkg/db-fsys-files.c324
-rw-r--r--lib/dpkg/db-fsys-override.c268
-rw-r--r--lib/dpkg/db-fsys.h75
-rw-r--r--lib/dpkg/dbmodify.c76
-rw-r--r--lib/dpkg/dpkg-db.h89
-rw-r--r--lib/dpkg/dpkg.h2
-rw-r--r--lib/dpkg/dump.c138
-rw-r--r--lib/dpkg/ehandle.c40
-rw-r--r--lib/dpkg/ehandle.h6
-rw-r--r--lib/dpkg/error.c29
-rw-r--r--lib/dpkg/error.h10
-rw-r--r--lib/dpkg/fields.c128
-rw-r--r--lib/dpkg/file.c109
-rw-r--r--lib/dpkg/file.h9
-rw-r--r--lib/dpkg/fsys-dir.c93
-rw-r--r--lib/dpkg/fsys-hash.c210
-rw-r--r--lib/dpkg/fsys-iter.c126
-rw-r--r--lib/dpkg/fsys.h204
-rw-r--r--lib/dpkg/i18n.c42
-rw-r--r--lib/dpkg/i18n.h8
-rw-r--r--lib/dpkg/libdpkg.map101
-rw-r--r--lib/dpkg/log.c2
-rw-r--r--lib/dpkg/mlib.c16
-rw-r--r--lib/dpkg/options-parsers.c2
-rw-r--r--lib/dpkg/pager.c145
-rw-r--r--lib/dpkg/pager.h54
-rw-r--r--lib/dpkg/parse.c125
-rw-r--r--lib/dpkg/parsedump.h33
-rw-r--r--lib/dpkg/parsehelp.c69
-rw-r--r--lib/dpkg/path-remove.c6
-rw-r--r--lib/dpkg/perf.h71
-rw-r--r--lib/dpkg/pkg-array.c17
-rw-r--r--lib/dpkg/pkg-array.h2
-rw-r--r--lib/dpkg/pkg-files.c95
-rw-r--r--lib/dpkg/pkg-files.h46
-rw-r--r--lib/dpkg/pkg-format.c76
-rw-r--r--lib/dpkg/pkg-format.h2
-rw-r--r--lib/dpkg/pkg-hash.c (renamed from lib/dpkg/pkg-db.c)89
-rw-r--r--lib/dpkg/pkg-show.c128
-rw-r--r--lib/dpkg/pkg-show.h6
-rw-r--r--lib/dpkg/pkg-spec.c14
-rw-r--r--lib/dpkg/pkg-spec.h2
-rw-r--r--lib/dpkg/pkg.c11
-rw-r--r--lib/dpkg/report.c22
-rw-r--r--lib/dpkg/report.h5
-rw-r--r--lib/dpkg/string.c19
-rw-r--r--lib/dpkg/string.h1
-rw-r--r--lib/dpkg/subproc.c2
-rw-r--r--lib/dpkg/t/.gitignore13
-rw-r--r--lib/dpkg/t/Makefile.am17
-rw-r--r--lib/dpkg/t/b-fsys-hash.c80
-rw-r--r--lib/dpkg/t/b-pkg-hash.c65
-rw-r--r--lib/dpkg/t/c-tarextract.c28
-rw-r--r--lib/dpkg/t/t-ar.c6
-rw-r--r--lib/dpkg/t/t-command.c38
-rw-r--r--lib/dpkg/t/t-ehandle.c2
-rw-r--r--lib/dpkg/t/t-error.c10
-rw-r--r--lib/dpkg/t/t-file.c96
-rw-r--r--lib/dpkg/t/t-fsys-dir.c65
-rw-r--r--lib/dpkg/t/t-fsys-hash.c108
-rw-r--r--lib/dpkg/t/t-headers-cpp.cc82
-rw-r--r--lib/dpkg/t/t-mod-db.c2
-rw-r--r--lib/dpkg/t/t-namevalue.c48
-rw-r--r--lib/dpkg/t/t-pager.c75
-rw-r--r--lib/dpkg/t/t-pkg-hash.c178
-rw-r--r--lib/dpkg/t/t-pkg-show.c61
-rw-r--r--lib/dpkg/t/t-string.c36
-rw-r--r--lib/dpkg/t/t-tar.c8
-rwxr-xr-xlib/dpkg/t/t-tarextract.t2
-rw-r--r--lib/dpkg/t/t-test.c7
-rwxr-xr-xlib/dpkg/t/t-treewalk.t2
-rw-r--r--lib/dpkg/t/t-varbuf.c25
-rw-r--r--lib/dpkg/tarfn.c110
-rw-r--r--lib/dpkg/tarfn.h34
-rw-r--r--lib/dpkg/test.h12
-rw-r--r--lib/dpkg/treewalk.c4
-rw-r--r--lib/dpkg/trigdeferred.c7
-rw-r--r--lib/dpkg/trigdeferred.h2
-rw-r--r--lib/dpkg/triglib.c61
-rw-r--r--lib/dpkg/triglib.h19
-rw-r--r--lib/dpkg/trignote.c2
-rw-r--r--lib/dpkg/varbuf.c18
-rw-r--r--lib/dpkg/varbuf.h2
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 = &current->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);