Initial commit of some stuff written so far. Still in state of flux. Expect

breakage and major changes.
cute-signatures
Timo Teras 16 years ago
parent 45d2c702fc
commit d6c7435242

@ -0,0 +1,2 @@
Natanael Copa <n@tanael.org>
Timo Teräs <timo.teras@iki.fi>

@ -0,0 +1,52 @@
# Makefile - one file to rule them all, one file to bind them
#
# Copyright (C) 2007 Timo Teräs <timo.teras@iki.fi>
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3 as published
# by the Free Software Foundation. See http://www.gnu.org/ for details.
VERSION := 2.0-pre0
SVN_REV := $(shell svn info 2> /dev/null | grep ^Revision | cut -d ' ' -f 2)
ifneq ($(SVN_REV),)
FULL_VERSION := $(VERSION)-r$(SVN_REV)
else
FULL_VERSION := $(VERSION)
endif
CC=gcc
INSTALL=install
INSTALLDIR=$(INSTALL) -d
CFLAGS=-O2 -g -D_GNU_SOURCE -Werror -Wall -Wstrict-prototypes -std=gnu99 \
-DAPK_VERSION=\"$(FULL_VERSION)\"
LDFLAGS=-g -lpthread
DESTDIR=
SBINDIR=/usr/sbin
CONFDIR=/etc/apk
MANDIR=/usr/share/man
DOCDIR=/usr/share/doc/apk
SUBDIRS=src
.PHONY: compile install clean all
all: compile
compile install clean::
@for i in $(SUBDIRS); do $(MAKE) $(MFLAGS) -C $$i $(MAKECMDGOALS); done
install::
$(INSTALLDIR) $(DESTDIR)$(DOCDIR)
$(INSTALL) README $(DESTDIR)$(DOCDIR)
dist:
svn-clean
(TOP=`pwd` && cd .. && ln -s $$TOP apk-tools-$(VERSION) && \
tar --exclude '*/.svn*' -cjvf apk-tools-$(VERSION).tar.bz2 apk-tools-$(VERSION)/* && \
rm apk-tools-$(VERSION))
.EXPORT_ALL_VARIABLES:

42
TODO

@ -0,0 +1,42 @@
- Command line parsing
- Get repositories/root from command line
- Repository index/package fetching from URLs
- Installation of local files
- Implement lbu stuff
- Error handling and rollback
- Dependency manipulation API: deletion, overwrite, check compatibility
- File ownership chowning
- New user/group creation
- Non-trivial solution finder
- Versioned dependencies
- Conflicts
- Provides
- Order removal of packages to honour dependencies
- Create reverse dependencies for installed pkgs
- Remember counts for hash table creation
- Possibly create a token hash for package names, versions and licenses, etc.
- Calculate changeset installed-size change
- Compress databases
- Option to not read fs entry cache
- Essentials(?)
- Oldies:
add, delete: read (pkgs+fs), modify DEPs, recalc+commit+write (pkgs+fs)
fetch: read (pkgs), download remote packages
fetch -u: read (pkgs), download indexes, write (pkgs)
glob: read (pkgs), operate on package db
info: read (pkgs+fs), mostly on package db, might need .apks
version: read (pkgs), compare all installed pkg versions
- New:
deps: show master dependencies
index: new TARGET, scan packages, write INDEX (pkgs)
upgrade: read TARGET, mark upgrade flags, recalculate, commit (pkgs+fs)

@ -0,0 +1,39 @@
# Makefile - one file to rule them all, one file to bind them
#
# Copyright (C) 2007 Timo Teräs <timo.teras@iki.fi>
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3 as published
# by the Free Software Foundation. See http://www.gnu.org/ for details.
TARGETS = apk
apk_OBJS = \
state.o \
database.o \
package.o \
archive.o \
version.o \
blob.o \
hash.o \
md5.o \
add.o \
del.o \
ver.o \
index.o \
apk.o
ALL_OBJS = $(apk_OBJS)
all: $(TARGETS)
apk: $(apk_OBJS)
clean::
@rm -f $(TARGETS) $(ALL_OBJS)
install::
$(INSTALLDIR) $(DESTDIR)$(SBINDIR)
$(INSTALL) $(TARGETS) $(DESTDIR)$(SBINDIR)

@ -0,0 +1,42 @@
/* add.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#include <stdio.h>
#include "apk_applet.h"
#include "apk_database.h"
static int add_main(int argc, char **argv)
{
struct apk_database db;
int i;
apk_db_init(&db, "/home/fabled/tmproot/");
apk_db_read_config(&db);
for (i = 0; i < argc; i++) {
struct apk_dependency dep = {
.name = apk_db_get_name(&db, argv[i]),
};
apk_deps_add(&db.world, &dep);
}
apk_db_recalculate_and_commit(&db);
apk_db_free(&db);
return 0;
}
static struct apk_applet apk_add = {
.name = "add",
.usage = "apkname...",
.main = add_main,
};
APK_DEFINE_APPLET(apk_add);

@ -0,0 +1,81 @@
/* apk.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "apk_defines.h"
#include "apk_applet.h"
void apk_log(const char *prefix, const char *format, ...)
{
va_list va;
if (prefix != NULL)
fprintf(stderr, prefix);
va_start(va, format);
vfprintf(stderr, format, va);
va_end(va);
fprintf(stderr, "\n");
}
int usage(void)
{
struct apk_applet **a, *applet;
printf("apk-tools " APK_VERSION "\n"
"\n"
"Usage:\n");
for (a = &__start_apkapplets; a < &__stop_apkapplets; a++) {
applet = *a;
printf(" apk %s %s\n",
applet->name, applet->usage);
}
printf("\n");
return 1;
}
int main(int argc, char **argv)
{
struct apk_applet **a, *applet;
char *prog;
prog = strrchr(argv[0], '/');
if (prog == NULL)
prog = argv[0];
else
prog++;
if (strcmp(prog, "apk") == 0) {
if (argc < 2)
return usage();
prog = argv[1];
argv++;
argc--;
} else if (strncmp(prog, "apk_", 4) == 0) {
prog += 4;
} else
return usage();
for (a = &__start_apkapplets; a < &__stop_apkapplets; a++) {
applet = *a;
if (strcmp(prog, applet->name) == 0) {
argv++;
argc--;
return applet->main(argc, argv);
}
}
return usage();
}

@ -0,0 +1,27 @@
/* apk_applet.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_APPLET_H
#define APK_APPLET_H
struct apk_applet {
const char *name;
const char *usage;
int (*main)(int argc, char **argv);
};
extern struct apk_applet *__start_apkapplets, *__stop_apkapplets;
#define APK_DEFINE_APPLET(x) \
static struct apk_applet *__applet_##x \
__attribute__((__section__("apkapplets") used)) = &x;
#endif

@ -0,0 +1,42 @@
/* apk_archive.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_ARCHIVE
#define APK_ARCHIVE
#include <sys/types.h>
#include <pthread.h>
#include "apk_blob.h"
struct apk_archive_entry {
char *name;
char *link_target;
char *uname;
char *gname;
off_t size;
uid_t uid;
gid_t gid;
mode_t mode;
time_t mtime;
dev_t device;
int read_fd;
};
typedef int (*apk_archive_entry_parser)(struct apk_archive_entry *entry, void *ctx);
pid_t apk_open_gz(int *fd);
int apk_parse_tar(int fd, apk_archive_entry_parser parser, void *ctx);
int apk_parse_tar_gz(int fd, apk_archive_entry_parser parser, void *ctx);
apk_blob_t apk_archive_entry_read(struct apk_archive_entry *ae);
int apk_archive_entry_extract(struct apk_archive_entry *ae, const char *to);
pthread_t apk_checksum_and_tee(int *fd, void *ptr);
#endif

@ -0,0 +1,37 @@
/* apk_blob.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_BLOB_H
#define APK_BLOB_H
#include <string.h>
struct apk_blob {
unsigned int len;
char *ptr;
};
typedef struct apk_blob apk_blob_t;
#define APK_BLOB_NULL ((apk_blob_t){0, NULL})
#define APK_BLOB_STR(str) ((apk_blob_t){strlen(str), (str)})
#define APK_BLOB_BUF(buf) ((apk_blob_t){sizeof(buf), (char *)(buf)})
#define APK_BLOB_PTR_LEN(beg,len) ((apk_blob_t){(len), (beg)})
#define APK_BLOB_PTR_PTR(beg,end) APK_BLOB_PTR_LEN((beg),(end)-(beg)+1)
char *apk_blob_cstr(apk_blob_t str);
int apk_blob_splitstr(apk_blob_t blob, char *split, apk_blob_t *l, apk_blob_t *r);
int apk_blob_rsplit(apk_blob_t blob, char split, apk_blob_t *l, apk_blob_t *r);
unsigned apk_blob_uint(apk_blob_t blob, int base);
int apk_hexdump_parse(apk_blob_t to, apk_blob_t from);
int apk_hexdump_format(int tolen, char *to, apk_blob_t from);
#endif

@ -0,0 +1,97 @@
/* apk_database.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_PKGDB_H
#define APK_PKGDB_H
#include "apk_version.h"
#include "apk_hash.h"
#include "apk_archive.h"
#include "apk_package.h"
#define APK_MAX_REPOS 32
struct apk_db_file {
struct hlist_node dir_files_list;
struct hlist_node pkg_files_list;
struct apk_db_dir *dir;
struct apk_package *owner;
char filename[];
};
struct apk_db_dir {
apk_hash_node hash_node;
struct hlist_head files;
struct apk_db_dir *parent;
unsigned refs;
mode_t mode;
char dirname[];
};
struct apk_name {
apk_hash_node hash_node;
char *name;
struct apk_package_array *pkgs;
};
struct apk_repository {
char *url;
};
struct apk_database {
char *root;
unsigned pkg_id, num_repos;
struct apk_dependency_array *world;
struct apk_repository repos[APK_MAX_REPOS];
struct {
struct apk_hash names;
struct apk_hash packages;
} available;
struct {
struct hlist_head packages;
struct apk_hash dirs;
struct {
unsigned files;
unsigned dirs;
unsigned packages;
} stats;
} installed;
};
struct apk_name *apk_db_get_name(struct apk_database *db, const char *name);
void apk_name_free(struct apk_name *pkgname);
void apk_db_init(struct apk_database *db, const char *root);
void apk_db_free(struct apk_database *db);
int apk_db_read_config(struct apk_database *db);
int apk_db_write_config(struct apk_database *db);
int apk_db_pkg_add_file(struct apk_database *db, const char *file);
struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum);
int apk_db_index_read(struct apk_database *db, int fd, int repo);
void apk_db_index_write(struct apk_database *db, int fd);
int apk_db_add_repository(struct apk_database *db, const char *repo);
int apk_db_recalculate_and_commit(struct apk_database *db);
int apk_db_install_pkg(struct apk_database *db,
struct apk_package *oldpkg,
struct apk_package *newpkg);
#endif

@ -0,0 +1,147 @@
/* apk_defines.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_DEFINES_H
#define APK_DEFINES_H
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define BIT(x) (1 << (x))
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0L
#endif
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#endif
#if 1
#include "md5.h"
typedef md5sum_t csum_t;
typedef struct md5_ctx csum_ctx_t;
#define csum_init(ctx) md5_init(ctx)
#define csum_process(ctx, buf, len) md5_process(ctx, buf, len)
#define csum_finish(ctx, buf) md5_finish(ctx, buf)
#endif
#define apk_error(args...) apk_log("ERROR: ", args)
#define apk_warning(args...) apk_log("WARNING: ", args)
#define apk_message(args...) apk_log(NULL, args)
void apk_log(const char *prefix, const char *format, ...);
#define APK_ARRAY(array_type_name, elem_type_name) \
struct array_type_name { \
int num; \
elem_type_name item[]; \
}; \
static inline struct array_type_name * \
array_type_name##_resize(struct array_type_name *a, int size) \
{ \
struct array_type_name *tmp; \
tmp = (struct array_type_name *) \
realloc(a, sizeof(struct array_type_name) + \
size * sizeof(elem_type_name)); \
tmp->num = size; \
return tmp; \
} \
static inline elem_type_name * \
array_type_name##_add(struct array_type_name **a) \
{ \
int size = 1; \
if (*a != NULL) size += (*a)->num; \
*a = array_type_name##_resize(*a, size); \
return &(*a)->item[size-1]; \
}
#define LIST_END (void *) 0xe01
#define LIST_POISON1 (void *) 0xdeadbeef
#define LIST_POISON2 (void *) 0xabbaabba
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next;
};
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline int hlist_hashed(const struct hlist_node *n)
{
return n->next != NULL;
}
static inline void __hlist_del(struct hlist_node *n, struct hlist_node **pprev)
{
*pprev = n->next;
}
static inline void hlist_del(struct hlist_node *n, struct hlist_node **pprev)
{
__hlist_del(n, pprev);
n->next = LIST_POISON1;
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first ? first : LIST_END;
h->first = n;
}
static inline void hlist_add_after(struct hlist_node *n, struct hlist_node **prev)
{
n->next = *prev ? *prev : LIST_END;
*prev = n;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && pos != LIST_END; \
pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && pos != LIST_END && \
({ n = pos->next; 1; }); \
pos = n)
#define hlist_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->first; \
pos && pos != LIST_END && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
pos && pos != LIST_END && ({ n = pos->next; 1; }) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
#endif

@ -0,0 +1,55 @@
/* apk_hash.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_HASH_H
#define APK_HASH_H
#include <malloc.h>
#include "apk_defines.h"
typedef void *apk_hash_item;
typedef const void *apk_hash_key;
typedef unsigned long (*apk_hash_f)(apk_hash_key);
typedef int (*apk_hash_compare_f)(apk_hash_key, apk_hash_key);
typedef void (*apk_hash_delete_f)(apk_hash_item);
typedef int (*apk_hash_enumerator_f)(apk_hash_item, void *ctx);
struct apk_hash_ops {
ptrdiff_t node_offset;
apk_hash_key (*get_key)(apk_hash_item item);
unsigned long (*hash_key)(apk_hash_key key);
int (*compare)(apk_hash_key key, apk_hash_key item);
void (*delete_item)(apk_hash_item item);
};
typedef struct hlist_node apk_hash_node;
APK_ARRAY(apk_hash_array, struct hlist_head);
struct apk_hash {
const struct apk_hash_ops *ops;
struct apk_hash_array *buckets;
int num_items;
};
unsigned long apk_hash_string(const char *string);
unsigned long apk_hash_csum(const void *);
void apk_hash_init(struct apk_hash *h, const struct apk_hash_ops *ops,
int num_buckets);
void apk_hash_free(struct apk_hash *h);
int apk_hash_foreach(struct apk_hash *h, apk_hash_enumerator_f e, void *ctx);
apk_hash_item apk_hash_get(struct apk_hash *h, apk_hash_key key);
void apk_hash_insert(struct apk_hash *h, apk_hash_item item);
void apk_hash_delete(struct apk_hash *h, apk_hash_key key);
#endif

@ -0,0 +1,84 @@
/* apk_database.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_PKG_H
#define APK_PKG_H
#include "apk_version.h"
#include "apk_hash.h"
struct apk_database;
struct apk_name;
#define APK_SCRIPT_PRE_INSTALL 0
#define APK_SCRIPT_POST_INSTALL 1
#define APK_SCRIPT_PRE_DEINSTALL 2
#define APK_SCRIPT_POST_DEINSTALL 3
#define APK_SCRIPT_PRE_UPGRADE 4
#define APK_SCRIPT_POST_UPGRADE 5
struct apk_script {
struct hlist_node script_list;
unsigned int type;
unsigned int size;
char script[];
};
struct apk_dependency {
unsigned conflict : 1;
unsigned prefer_upgrade : 1;
unsigned version_mask : 3;
struct apk_name *name;
char *version;
};
APK_ARRAY(apk_dependency_array, struct apk_dependency);
struct apk_package {
apk_hash_node hash_node;
csum_t csum;
unsigned id, repos;
struct apk_name *name;
char *version;
char *url, *description, *license;
struct apk_dependency_array *depends;
unsigned int installed_size, size;
/* for installed packages only */
struct hlist_node installed_pkgs_list;
struct hlist_head owned_files;
struct hlist_head scripts;
};
APK_ARRAY(apk_package_array, struct apk_package *);
int apk_deps_add(struct apk_dependency_array **depends,
struct apk_dependency *dep);
void apk_deps_parse(struct apk_database *db,
struct apk_dependency_array **depends,
apk_blob_t blob);
int apk_deps_format(char *buf, int size,
struct apk_dependency_array *depends);
int apk_script_type(const char *name);
struct apk_package *apk_pkg_read(struct apk_database *db, const char *name);
void apk_pkg_free(struct apk_package *pkg);
int apk_pkg_get_state(struct apk_package *pkg);
int apk_pkg_add_script(struct apk_package *pkg, int fd,
unsigned int type, unsigned int size);
int apk_pkg_run_script(struct apk_package *pkg, const char *root,
unsigned int type);
struct apk_package *apk_pkg_parse_index_entry(struct apk_database *db, apk_blob_t entry);
apk_blob_t apk_pkg_format_index_entry(struct apk_package *pkg, int size, char *buf);
#endif

@ -0,0 +1,50 @@
/* apk_state.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_STATE_H
#define APK_STATE_H
#include "apk_database.h"
#define APK_STATE_NOT_CONSIDERED 0
#define APK_STATE_PREFER_UPGRADE 1
#define APK_STATE_INSTALL 2
#define APK_STATE_NO_INSTALL 3
struct apk_state {
int refs;
unsigned char bitarray[];
};
struct apk_deferred_state {
unsigned int preference;
struct apk_package *deferred_install;
/* struct apk_pkg_name_queue *install_queue; */
struct apk_state *state;
};
struct apk_state *apk_state_new(struct apk_database *db);
struct apk_state *apk_state_dup(struct apk_state *state);
void apk_state_unref(struct apk_state *state);
int apk_state_commit(struct apk_state *state, struct apk_database *db);
int apk_state_satisfy_deps(struct apk_state *state,
struct apk_dependency_array *deps);
void apk_state_pkg_set(struct apk_state *state,
struct apk_package *pkg);
int apk_state_pkg_install(struct apk_state *state,
struct apk_package *pkg);
int apk_state_pkg_is_installed(struct apk_state *state,
struct apk_package *pkg);
#endif

@ -0,0 +1,26 @@
/* apk_version.h - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#ifndef APK_VERSION_H
#define APK_VERSION_H
#include "apk_blob.h"
#define APK_VERSION_LESS -1
#define APK_VERSION_EQUAL 0
#define APK_VERSION_GREATER 1
#define APK_VERSION_RESULT_MASK(r) (1 << ((r)+1))
int apk_version_validate(apk_blob_t ver);
int apk_version_compare(apk_blob_t a, apk_blob_t b);
#endif

@ -0,0 +1,349 @@
/* archive.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#include <stdio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
#include <sys/wait.h>
#include "apk_defines.h"
#include "apk_archive.h"
#ifndef GUNZIP_BINARY
#define GUNZIP_BINARY "/bin/gunzip"
#endif
struct tar_header {
/* ustar header, Posix 1003.1 */
char name[100]; /* 0-99 */
char mode[8]; /* 100-107 */
char uid[8]; /* 108-115 */
char gid[8]; /* 116-123 */
char size[12]; /* 124-135 */
char mtime[12]; /* 136-147 */
char chksum[8]; /* 148-155 */
char typeflag; /* 156-156 */
char linkname[100]; /* 157-256 */
char magic[8]; /* 257-264 */
char uname[32]; /* 265-296 */
char gname[32]; /* 297-328 */
char devmajor[8]; /* 329-336 */
char devminor[8]; /* 337-344 */
char prefix[155]; /* 345-499 */
char padding[12]; /* 500-512 */
};
static int get_dev_null(void)
{
static int fd_null = 0;
if (fd_null == 0) {
fd_null = open("/dev/null", O_WRONLY);
if (fd_null < 0)
err(EX_OSFILE, "/dev/null");
}
return fd_null;
}
pid_t apk_open_gz(int *fd)
{
int pipe_fd[2];
pid_t child_pid;
if (pipe(pipe_fd) < 0)
err(EX_OSERR, "pipe");
child_pid = fork();
if (child_pid < 0)
err(EX_OSERR, "fork");
if (child_pid == 0) {
close(pipe_fd[0]);
dup2(pipe_fd[1], STDOUT_FILENO);
dup2(*fd, STDIN_FILENO);
dup2(get_dev_null(), STDERR_FILENO);
close(pipe_fd[1]);
execl(GUNZIP_BINARY, "gunzip", "-c", NULL);
err(EX_UNAVAILABLE, GUNZIP_BINARY);
}
close(pipe_fd[1]);
*fd = pipe_fd[0];
return child_pid;
}
#define GET_OCTAL(s) apk_blob_uint(APK_BLOB_PTR_LEN(s, sizeof(s)), 8)
static int do_splice(int from_fd, int to_fd, int len)
{
int i = 0, r;
while (i != len) {
r = splice(from_fd, NULL, to_fd, NULL, len - i, SPLICE_F_MOVE);
if (r == -1)
return i;
i += r;
}
return i;
}
int apk_parse_tar(int fd, apk_archive_entry_parser parser, void *ctx)
{
struct apk_archive_entry entry;
struct tar_header buf;
unsigned long offset = 0;
int end = 0, r;
memset(&entry, 0, sizeof(entry));
while (read(fd, &buf, 512) == 512) {
offset += 512;
if (buf.name[0] == '\0') {
if (end)
break;
end++;
continue;
}
entry = (struct apk_archive_entry){
.size = GET_OCTAL(buf.size),
.uid = GET_OCTAL(buf.uid),
.gid = GET_OCTAL(buf.gid),
.mode = GET_OCTAL(buf.mode) & 0777,
.mtime = GET_OCTAL(buf.mtime),
.name = entry.name,
.uname = buf.uname,
.gname = buf.gname,
.device = makedev(GET_OCTAL(buf.devmajor),
GET_OCTAL(buf.devminor)),
.read_fd = fd,
};
switch (buf.typeflag) {
case 'L':
if (entry.name != NULL)
free(entry.name);
entry.name = malloc(entry.size+1);
read(fd, entry.name, entry.size);
offset += entry.size;
entry.size = 0;
break;
case '0':
case '7': /* regular file */
entry.mode |= S_IFREG;
break;
case '1': /* hard link */
entry.mode |= S_IFREG;
entry.link_target = buf.linkname;
break;
case '2': /* symbolic link */
entry.mode |= S_IFLNK;
entry.link_target = buf.linkname;
break;
case '3': /* char device */
entry.mode |= S_IFCHR;
break;
case '4': /* block devicek */
entry.mode |= S_IFBLK;
break;
case '5': /* directory */
entry.mode |= S_IFDIR;
break;
default:
break;
}
if (entry.mode & S_IFMT) {
if (entry.name == NULL)
entry.name = strdup(buf.name);
/* callback parser function */
offset += entry.size;
r = parser(&entry, ctx);
if (r != 0)
return r;
offset -= entry.size;
free(entry.name);
entry.name = NULL;
}
if (entry.size)
offset += do_splice(fd, get_dev_null(), entry.size);
/* align to next 512 block */
if (offset & 511)
offset += do_splice(fd, get_dev_null(),
512 - (offset & 511));
}
return 0;
}
int apk_parse_tar_gz(int fd, apk_archive_entry_parser parser, void *ctx)
{
pid_t pid;
int r, status;
pid = apk_open_gz(&fd);
if (pid < 0)
return pid;
r = apk_parse_tar(fd, parser, ctx);
close(fd);
waitpid(pid, &status, 0);
return r;
}
apk_blob_t apk_archive_entry_read(struct apk_archive_entry *ae)
{
char *str;
int pos = 0;
ssize_t r;
str = malloc(ae->size + 1);
pos = 0;
while (ae->size) {
r = read(ae->read_fd, &str[pos], ae->size);
if (r < 0) {
free(str);
return APK_BLOB_NULL;
}
pos += r;
ae->size -= r;
}
str[pos] = 0;
return APK_BLOB_PTR_LEN(str, pos+1);
}
int apk_archive_entry_extract(struct apk_archive_entry *ae, const char *fn)
{
int r = -1;
if (fn == NULL)
fn = ae->name;
/* BIG HONKING FIXME */
unlink(fn);
switch (ae->mode & S_IFMT) {
case S_IFDIR:
r = mkdir(fn, ae->mode & 0777);
if (r < 0 && errno == EEXIST)
r = 0;
break;
case S_IFREG:
if (ae->link_target == NULL) {
r = open(fn, O_WRONLY | O_CREAT, ae->mode & 0777);
if (r < 0)
break;
ae->size -= do_splice(ae->read_fd, r, ae->size);
close(r);
r = ae->size ? -1 : 0;
} else {
r = link(ae->link_target, fn);
}
break;
case S_IFLNK:
r = symlink(ae->link_target, fn);
break;
case S_IFSOCK:
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
r = mknod(fn, ae->mode, ae->device);
break;
}
if (r != 0)
apk_error("Failed to extract %s\n", ae->name);
return r;
}
struct checksum_and_tee {
int in_fd, tee_fd;
void *ptr;
};
static void *__apk_checksum_and_tee(void *arg)
{
struct checksum_and_tee *args = (struct checksum_and_tee *) arg;
char buf[2*1024];
int r, w, wt;
__off64_t offset;
csum_ctx_t ctx;
int dosplice = 1;
offset = lseek(args->in_fd, 0, SEEK_CUR);
csum_init(&ctx);
do {
r = read(args->in_fd, buf, sizeof(buf));
if (r <= 0)
break;
wt = 0;
do {
if (dosplice) {
w = splice(args->in_fd, &offset, args->tee_fd, NULL,
r - wt, SPLICE_F_MOVE);
if (w < 0) {
dosplice = 0;
continue;
}
} else {
w = write(args->tee_fd, &buf[wt], r - wt);
if (w < 0)
break;
}
wt += w;
} while (wt != r);
csum_process(&ctx, buf, r);
} while (r == sizeof(buf));
csum_finish(&ctx, args->ptr);
close(args->tee_fd);
close(args->in_fd);
free(args);
return NULL;
}
pthread_t apk_checksum_and_tee(int *fd, void *ptr)
{
struct checksum_and_tee *args;
int fds[2];
pthread_t tid;
if (pipe(fds) < 0)
return -1;
fcntl(fds[0], F_SETFD, FD_CLOEXEC);
fcntl(fds[1], F_SETFD, FD_CLOEXEC);
args = malloc(sizeof(*args));
*args = (struct checksum_and_tee){ *fd, fds[1], ptr };
if (pthread_create(&tid, NULL, __apk_checksum_and_tee, args) < 0)
return -1;
*fd = fds[0];
return tid;
}

@ -0,0 +1,125 @@
/* blob.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#include <malloc.h>
#include <string.h>
#include "apk_blob.h"
char *apk_blob_cstr(apk_blob_t blob)
{
char *cstr;
if (blob.ptr[blob.len-1] == 0)
return strdup(blob.ptr);
cstr = malloc(blob.len + 1);
memcpy(cstr, blob.ptr, blob.len);
cstr[blob.len] = 0;
return cstr;
}
int apk_blob_rsplit(apk_blob_t blob, char split, apk_blob_t *l, apk_blob_t *r)
{
char *sep;
sep = memrchr(blob.ptr, split, blob.len);
if (sep == NULL)
return 0;
if (l != NULL)
*l = APK_BLOB_PTR_PTR(blob.ptr, sep - 1);
if (r != NULL)
*r = APK_BLOB_PTR_PTR(sep + 1, blob.ptr + blob.len);
return 1;
}
int apk_blob_splitstr(apk_blob_t blob, char *split, apk_blob_t *l, apk_blob_t *r)
{
int splitlen = strlen(split);
char *pos = blob.ptr, *end = blob.ptr + blob.len - splitlen + 1;
if (end < pos)
return 0;
while (1) {
pos = memchr(pos, split[0], end - pos);
if (pos == NULL)
return 0;
if (memcmp(pos, split, splitlen) != 0) {
pos++;
continue;
}
*l = APK_BLOB_PTR_PTR(blob.ptr, pos-1);
*r = APK_BLOB_PTR_PTR(pos+splitlen, blob.ptr+blob.len-1);
return 1;
}
}
static int dx(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 0xa;
if (c >= 'A' && c <= 'F')
return c - 'A' + 0xa;
return -1;
}
unsigned apk_blob_uint(apk_blob_t blob, int base)
{
unsigned val;
int i, ch;
val = 0;
for (i = 0; i < blob.len; i++) {
if (blob.ptr[i] == 0)
break;
ch = dx(blob.ptr[i]);
if (ch < 0 || ch >= base)
return 0;
val *= base;
val += ch;
}
return val;
}
int apk_hexdump_parse(apk_blob_t to, apk_blob_t from)
{
int i;
if (to.len * 2 != from.len)
return -1;
for (i = 0; i < from.len / 2; i++)
to.ptr[i] = (dx(from.ptr[i*2]) << 4) + dx(from.ptr[i*2+1]);
return 0;
}
int apk_hexdump_format(int tolen, char *to, apk_blob_t from)
{
static const char *xd = "0123456789abcdef";
int i;
for (i = 0; i < from.len && i*2+2 < tolen; i++) {
to[i*2+0] = xd[(from.ptr[i] >> 4) & 0xf];
to[i*2+1] = xd[from.ptr[i] & 0xf];
}
to[i*2] = 0;
return i*2;
}

@ -0,0 +1,802 @@
/* database.c - Alpine Package Keeper (APK)
*
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
* Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. See http://www.gnu.org/ for details.
*/
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include "apk_defines.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"
struct install_ctx {
struct apk_database *db;
struct apk_package *pkg;
int script;
struct apk_db_dir *dircache;
struct hlist_node **file_dir_node;
struct hlist_node **file_pkg_node;
};
static apk_hash_key pkg_name_get_key(apk_hash_item item)
{
return ((struct apk_name *) item)->name;
}
static const struct apk_hash_ops pkg_name_hash_ops = {
.node_offset = offsetof(struct apk_name, hash_node),
.get_key = pkg_name_get_key,
.hash_key = (apk_hash_f) apk_hash_string,
.compare = (apk_hash_compare_f) strcmp,
.delete_item = (apk_hash_delete_f) apk_name_free,
};
static apk_hash_key pkg_info_get_key(apk_hash_item item)
{
return ((struct apk_package *) item)->csum;
}
static int cmpcsum(apk_hash_key a, apk_hash_key b)
{
return memcmp(a, b, sizeof(csum_t));
}
static const struct apk_hash_ops pkg_info_hash_ops = {
.node_offset = offsetof(struct apk_package, hash_node),
.get_key = pkg_info_get_key,
.hash_key = (apk_hash_f) apk_hash_csum,
.compare = cmpcsum,
.delete_item = (apk_hash_delete_f) apk_pkg_free,
};
static apk_hash_key apk_db_dir_get_key(apk_hash_item item)
{
return ((struct apk_db_dir *) item)->dirname;
}
static const struct apk_hash_ops dir_hash_ops = {
.node_offset = offsetof(struct apk_db_dir, hash_node),
.get_key = apk_db_dir_get_key,
.hash_key = (apk_hash_f) apk_hash_string,
.compare = (apk_hash_compare_f) strcmp,
.delete_item = (apk_hash_delete_f) free,
};
struct apk_name *apk_db_get_name(struct apk_database *db, const char *name)
{
struct apk_name *pn;
pn = (struct apk_name *) apk_hash_get(&db->available.names, name);
if (pn != NULL)
return pn;
pn = calloc(1, sizeof(struct apk_name));
if (pn == NULL)
return NULL;
pn->name = strdup(name);
apk_hash_insert(&db->available.names, pn);
return pn;
}
void apk_name_free(struct apk_name *name)
{
free(name->name);
free(name);
}
static struct apk_db_dir *apk_db_dir_ref(struct apk_database *db,
struct apk_db_dir *dir,
int create_dir)
{
if (dir->refs == 0) {
if (dir->parent != NULL)
apk_db_dir_ref(db, dir->parent, create_dir);
db->installed.stats.dirs++;
if (create_dir)
mkdir(dir->dirname, dir->mode);
}
dir->refs++;
return dir;
}
static void apk_db_dir_unref(struct apk_database *db, struct apk_db_dir *dir)
{
dir->refs--;
if (dir->refs > 0)
return;
db->installed.stats.dirs--;
rmdir(dir->dirname);
if (dir->parent != NULL)
apk_db_dir_unref(db, dir->parent);
}
static struct apk_db_dir *apk_db_dir_get(struct apk_database *db,
apk_blob_t name)
{
struct apk_db_dir *dir;
apk_blob_t bparent;
char *cstr;
if (name.ptr[name.len-1] == '/')
name.len--;
cstr = apk_blob_cstr(name);
dir = (struct apk_db_dir *) apk_hash_get(&db->installed.dirs, cstr);
free(cstr);
if (dir != NULL)
return dir;
dir = calloc(1, sizeof(*dir) + name.len + 1);
memcpy(dir->dirname, name.ptr, name.len);
dir->dirname[name.len] = 0;
apk_hash_insert(&db->installed.dirs, dir);
if (apk_blob_rsplit(name, '/', &bparent, NULL))
dir->parent = apk_db_dir_get(db, bparent);
return dir;
}
static struct apk_db_file *apk_db_file_new(struct apk_db_dir *dir,
apk_blob_t name,
struct hlist_node **after)
{
struct apk_db_file *file;
file = calloc(1, sizeof(*file) + name.len + 1);
hlist_add_after(&file->dir_files_list, after);
file->dir = dir;
memcpy(file->filename, name.ptr, name.len);
file->filename[name.len] = 0;
return file;
}
static void apk_db_file_set_owner(struct apk_database *db,
struct apk_db_file *file,
struct apk_package *owner,
int create_dir,
struct hlist_node **after)
{
if (file->owner != NULL)
return;
db->installed.stats.files++;
file->dir = apk_db_dir_ref(db, file->dir, create_dir);
file->owner = owner;
hlist_add_after(&file->pkg_files_list, after);
}
static struct apk_db_file *apk_db_file_get(struct apk_database *db,
apk_blob_t name,
struct install_ctx *ctx)
{
struct apk_db_dir *dir;
struct apk_db_file *file;
struct hlist_node *cur;
apk_blob_t bdir, bfile;
if (!apk_blob_rsplit(name, '/', &bdir, &bfile))
return NULL;
dir = NULL;
if (ctx != NULL && ctx->dircache != NULL) {
dir = ctx->dircache;
if (strncmp(dir->dirname, bdir.ptr, bdir.len) != 0 ||
dir->dirname[bdir.len] != 0)