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

breakage and major changes.
cute-signatures
Timo Teras 2008-04-17 14:09:13 +00:00
parent 45d2c702fc
commit d6c7435242
30 changed files with 3873 additions and 0 deletions

2
AUTHORS Normal file
View File

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

52
Makefile Normal file
View File

@ -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:

0
NEWS Normal file
View File

0
README Normal file
View File

42
TODO Normal file
View File

@ -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)

39
src/Makefile Normal file
View File

@ -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)

42
src/add.c Normal file
View File

@ -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);

81
src/apk.c Normal file
View File

@ -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();
}

27
src/apk_applet.h Normal file
View File

@ -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

42
src/apk_archive.h Normal file
View File

@ -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

37
src/apk_blob.h Normal file
View File

@ -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

97
src/apk_database.h Normal file
View File

@ -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

147
src/apk_defines.h Normal file
View File

@ -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

55
src/apk_hash.h Normal file
View File

@ -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

84
src/apk_package.h Normal file
View File

@ -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

50
src/apk_state.h Normal file
View File

@ -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

26
src/apk_version.h Normal file
View File

@ -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

349
src/archive.c Normal file
View File

@ -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;
}

125
src/blob.c Normal file
View File

@ -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;
}

802
src/database.c Normal file
View File

@ -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)
dir = NULL;
}
if (dir == NULL) {
dir = apk_db_dir_get(db, bdir);
if (ctx != NULL) {
ctx->dircache = dir;
ctx->file_dir_node = &dir->files.first;
}
}
hlist_for_each_entry(file, cur, &dir->files, dir_files_list) {
if (strncmp(file->filename, bfile.ptr, bfile.len) == 0 &&
file->filename[bfile.len] == 0)
return file;
}
file = apk_db_file_new(dir, bfile, ctx->file_dir_node);
ctx->file_dir_node = &file->dir_files_list.next;
return file;
}
static int apk_db_read_fdb(struct apk_database *db, int fd)
{
struct apk_package *pkg = NULL;
struct apk_db_dir *dir = NULL;
struct apk_db_file *file = NULL;
struct hlist_node **pkg_node = &db->installed.packages.first;
struct hlist_node **file_dir_node = NULL;
struct hlist_node **file_pkg_node = NULL;
char buf[1024];
apk_blob_t l, r;
csum_t csum;
int n;
r = APK_BLOB_PTR_LEN(buf, 0);
while (1) {
n = read(fd, &r.ptr[r.len], sizeof(buf) - r.len);
if (n <= 0)
break;
r.len += n;
while (apk_blob_splitstr(r, "\n", &l, &r)) {
n = l.ptr[0];
l.ptr++;
l.len--;
switch (n) {
case 'P':
if (apk_hexdump_parse(APK_BLOB_BUF(csum), l)) {
apk_error("Not a valid checksum");
return -1;
}
pkg = apk_db_get_pkg(db, csum);
if (pkg == NULL) {
apk_error("Package '%.*s' is installed, but not in any repository",
l.len, l.ptr);
return -1;
}
if (!hlist_hashed(&pkg->installed_pkgs_list)) {
db->installed.stats.packages++;
hlist_add_after(&pkg->installed_pkgs_list, pkg_node);
pkg_node = &pkg->installed_pkgs_list.next;
}
dir = NULL;
file_dir_node = NULL;
file_pkg_node = &pkg->owned_files.first;
break;
case 'D':
if (pkg == NULL) {
apk_error("FDB directory entry before package entry");
return -1;
}
dir = apk_db_dir_get(db, l);
file_dir_node = &dir->files.first;
break;
case 'F':
if (dir == NULL) {
apk_error("FDB file entry before directory entry");
return -1;
}
file = apk_db_file_new(dir, l, file_dir_node);
apk_db_file_set_owner(db, file, pkg, FALSE, file_pkg_node);
file_dir_node = &file->dir_files_list.next;
file_pkg_node = &file->pkg_files_list.next;
break;
default:
apk_error("FDB entry '%c' unsupported", n);
return -1;
}
}
memcpy(&buf[0], r.ptr, r.len);
r = APK_BLOB_PTR_LEN(buf, r.len);
}
return 0;
}
static int apk_db_write_fdb(struct apk_database *db, int fd)
{
struct apk_package *pkg;
struct apk_db_dir *dir;
struct apk_db_file *file;
struct hlist_node *c1, *c2;
char buf[1024];
int n;
hlist_for_each_entry(pkg, c1, &db->installed.packages, installed_pkgs_list) {
n = 0;
buf[n++] = 'P';
n += apk_hexdump_format(sizeof(buf)-n, &buf[n],
APK_BLOB_BUF(pkg->csum));
buf[n++] = '\n';
dir = NULL;
hlist_for_each_entry(file, c2, &pkg->owned_files, pkg_files_list) {
if (file->owner == NULL)
continue;
if (dir != file->dir) {
n += snprintf(&buf[n], sizeof(buf)-n,
"D%s\n",
file->dir->dirname);
dir = file->dir;
}
n += snprintf(&buf[n], sizeof(buf)-n,
"F%s\n",
file->filename);
write(fd, buf, n);
n = 0;
}
}
return 0;
}
struct apk_script_header {
csum_t csum;
unsigned int type;
unsigned int size;
};
static int apk_db_write_scriptdb(struct apk_database *db, int fd)
{
struct apk_package *pkg;
struct apk_script *script;
struct apk_script_header hdr;
struct hlist_node *c1, *c2;
hlist_for_each_entry(pkg, c1, &db->installed.packages,
installed_pkgs_list) {
hlist_for_each_entry(script, c2, &pkg->scripts, script_list) {
memcpy(hdr.csum, pkg->csum, sizeof(csum_t));
hdr.type = script->type;
hdr.size = script->size;
write(fd, &hdr, sizeof(hdr));
write(fd, script->script, script->size);
}
}
return 0;
}
static int apk_db_read_scriptdb(struct apk_database *db, int fd)
{
struct apk_package *pkg;
struct apk_script_header hdr;
while (read(fd, &hdr, sizeof(hdr)) == sizeof(hdr)) {
pkg = apk_db_get_pkg(db, hdr.csum);
apk_pkg_add_script(pkg, fd, hdr.type, hdr.size);
}
return 0;
}
static const char *get_db_path(struct apk_database *db, const char *f)
{
static char fn[1024];
snprintf(fn, sizeof(fn), "%s%s", db->root, f);
return fn;
}
void apk_db_init(struct apk_database *db, const char *root)
{
memset(db, 0, sizeof(*db));
apk_hash_init(&db->available.names, &pkg_name_hash_ops, 1000);
apk_hash_init(&db->available.packages, &pkg_info_hash_ops, 4000);
apk_hash_init(&db->installed.dirs, &dir_hash_ops, 1000);
if (root != NULL) {
db->root = strdup(root);
apk_db_add_repository(db, "/home/fabled/foo/");
mkdir(get_db_path(db, "tmp"), 01777);
mkdir(get_db_path(db, "dev"), 0755);
mknod(get_db_path(db, "dev/null"), 0666, makedev(1, 3));
}
}
int apk_db_read_config(struct apk_database *db)
{
struct stat st;
char *buf;
int fd;
if (db->root == NULL)
return -1;
/* Read:
* 1. installed repository
* 2. source repositories
* 3. master dependencies
* 4. package statuses
* 5. files db
* 6. script db
*/
fd = open(get_db_path(db, "var/lib/apk/world"), O_RDONLY);
if (fd >= 0) {
fstat(fd, &st);
buf = malloc(st.st_size);
read(fd, buf, st.st_size);
apk_deps_parse(db, &db->world,
APK_BLOB_PTR_LEN(buf, st.st_size));
close(fd);
} else {
apk_deps_parse(db, &db->world,
APK_BLOB_STR("busybox, alpine-baselayout, "
"apk-tools, alpine-conf"));
}
fd = open(get_db_path(db, "var/lib/apk/files"), O_RDONLY);
if (fd >= 0) {
apk_db_read_fdb(db, fd);
close(fd);
}
fd = open(get_db_path(db, "var/lib/apk/scripts"), O_RDONLY);
if (fd >= 0) {
apk_db_read_scriptdb(db, fd);
close(fd);
}
return 0;
}
struct write_ctx {
struct apk_database *db;
int fd;
};
int apk_db_write_config(struct apk_database *db)
{
char buf[1024];
int n, fd;
if (db->root == NULL)
return -1;
mkdir(get_db_path(db, "var"), 0755);
mkdir(get_db_path(db, "var/lib"), 0755);
mkdir(get_db_path(db, "var/lib/apk"), 0755);
fd = creat(get_db_path(db, "var/lib/apk/world"), 0600);
if (fd < 0)
return -1;
n = apk_deps_format(buf, sizeof(buf), db->world);
write(fd, buf, n);
close(fd);
fd = creat(get_db_path(db, "var/lib/apk/files"), 0600);
if (fd < 0)
return -1;
apk_db_write_fdb(db, fd);
close(fd);
fd = creat(get_db_path(db, "var/lib/apk/scripts"), 0600);
if (fd < 0)
return -1;
apk_db_write_scriptdb(db, fd);
close(fd);
return 0;
}
void apk_db_free(struct apk_database *db)
{
apk_hash_free(&db->available.names);
apk_hash_free(&db->available.packages);
apk_hash_free(&db->installed.dirs);
if (db->root != NULL)
free(db->root);
}
static void apk_db_pkg_add(struct apk_database *db, struct apk_package *pkg)
{
struct apk_package *idb;
idb = apk_hash_get(&db->available.packages, pkg->csum);
if (idb == NULL) {
pkg->id = db->pkg_id++;
apk_hash_insert(&db->available.packages, pkg);
*apk_package_array_add(&pkg->name->pkgs) = pkg;
} else {
idb->repos |= pkg->repos;
apk_pkg_free(pkg);
}
}
struct apk_package *apk_db_get_pkg(struct apk_database *db, csum_t sum)
{
return apk_hash_get(&db->available.packages, sum);
}
int apk_db_pkg_add_file(struct apk_database *db, const char *file)
{
struct apk_package *info;
info = apk_pkg_read(db, file);
if (info == NULL)
return FALSE;
apk_db_pkg_add(db, info);
return TRUE;
}
int apk_db_index_read(struct apk_database *db, int fd, int repo)
{
struct apk_package *pkg;
char buf[1024];
int n;
apk_blob_t l, r;
r = APK_BLOB_PTR_LEN(buf, 0);
while (1) {
n = read(fd, &r.ptr[r.len], sizeof(buf) - r.len);
if (n <= 0)
break;
r.len += n;
while (apk_blob_splitstr(r, "\n\n", &l, &r)) {
pkg = apk_pkg_parse_index_entry(db, l);
if (pkg != NULL) {
pkg->repos |= BIT(repo);
apk_db_pkg_add(db, pkg);
}
}
memcpy(&buf[0], r.ptr, r.len);
r = APK_BLOB_PTR_LEN(buf, r.len);
}
return 0;
}
static int write_index_entry(apk_hash_item item, void *ctx)
{
int fd = (int) ctx;
char buf[1024];
apk_blob_t blob;
blob = apk_pkg_format_index_entry(item, sizeof(buf), buf);
if (blob.ptr)
write(fd, blob.ptr, blob.len);
return 0;
}
void apk_db_index_write(struct apk_database *db, int fd)
{
apk_hash_foreach(&db->available.packages, write_index_entry, (void *) fd);
}
int apk_db_add_repository(struct apk_database *db, const char *repo)
{
char tmp[256];
int fd, r;
if (db->num_repos >= APK_MAX_REPOS)
return -1;
r = db->num_repos++;
db->repos[r] = (struct apk_repository){
.url = strdup(repo)
};
snprintf(tmp, sizeof(tmp), "%sAPK_INDEX", repo);
fd = open(tmp, O_RDONLY);
if (fd < 0) {
apk_error("Failed to open index file %s", tmp);
return -1;
}
apk_db_index_read(db, fd, r);
close(fd);
return 0;
}
int apk_db_recalculate_and_commit(struct apk_database *db)
{
struct apk_state *state;
int r;
state = apk_state_new(db);
r = apk_state_satisfy_deps(state, db->world);
if (r == 0) {
r = apk_state_commit(state, db);
if (r != 0) {
apk_error("Failed to commit changes");
return r;
}
apk_db_write_config(db);
apk_message("OK: %d packages, %d dirs, %d files",
db->installed.stats.packages,
db->installed.stats.dirs,
db->installed.stats.files);
} else {
apk_error("Failed to build installation graph");
}
apk_state_unref(state);
return r;
}
static int apk_db_install_archive_entry(struct apk_archive_entry *ae,
struct install_ctx *ctx)
{
struct apk_database *db = ctx->db;
struct apk_package *pkg = ctx->pkg;
apk_blob_t name = APK_BLOB_STR(ae->name);
struct apk_db_file *file;
const char *p;
int r = 0, type;
if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
p = &ae->name[11];
if (strncmp(p, pkg->name->name, strlen(pkg->name->name)) != 0)
return 0;
p += strlen(pkg->name->name) + 1;
if (strncmp(p, pkg->version, strlen(pkg->version)) != 0)
return 0;
p += strlen(pkg->version) + 1;
type = apk_script_type(p);
if (type < 0)
return 0;
ae->size -= apk_pkg_add_script(pkg, ae->read_fd,
type, ae->size);
if (type == ctx->script) {
r = apk_pkg_run_script(pkg, db->root, type);
if (r != 0)
apk_error("%s-%s: Failed to execute pre-install/upgrade script",
pkg->name->name, pkg->version);
}
return r;
}
if (ctx->file_pkg_node == NULL)
ctx->file_pkg_node = &pkg->owned_files.first;
if (!S_ISDIR(ae->mode)) {
file = apk_db_file_get(db, name, ctx);
if (file == NULL)
return -1;
if (file->owner != NULL &&
file->owner->name != pkg->name) {
apk_error("%s: Trying to overwrite %s owned by %s.\n",
pkg->name->name, ae->name,
file->owner->name->name);
return -1;
}
apk_db_file_set_owner(db, file, pkg, TRUE, ctx->file_pkg_node);
ctx->file_pkg_node = &file->pkg_files_list.next;
if (strncmp(file->filename, ".keep_", 6) == 0)
return 0;
r = apk_archive_entry_extract(ae, NULL);
} else {
if (name.ptr[name.len-1] == '/')
name.len--;
apk_db_dir_get(db, name)->mode = 0777 & ae->mode;
}
return r;
}
static void apk_db_purge_pkg(struct apk_database *db,
struct apk_package *pkg)
{
struct apk_db_file *file;
struct hlist_node *c, *n;
char fn[1024];
hlist_for_each_entry_safe(file, c, n, &pkg->owned_files, pkg_files_list) {
file->owner = NULL;
snprintf(fn, sizeof(fn), "%s/%s",
file->dir->dirname,
file->filename);
unlink(fn);
apk_db_dir_unref(db, file->dir);
hlist_del(c, &pkg->owned_files.first);
db->installed.stats.files--;
}
db->installed.stats.packages--;
}
int apk_db_install_pkg(struct apk_database *db,
struct apk_package *oldpkg,
struct apk_package *newpkg)
{
struct install_ctx ctx;
csum_t csum;
char file[256];
pthread_t tid = 0;
int fd, r;
if (chdir(db->root) < 0)
return errno;
/* Purge the old package if there */
if (oldpkg != NULL) {
if (newpkg == NULL) {
r = apk_pkg_run_script(oldpkg, db->root,
APK_SCRIPT_PRE_DEINSTALL);
if (r != 0)
return r;
}
apk_db_purge_pkg(db, oldpkg);
if (newpkg == NULL) {
apk_pkg_run_script(oldpkg, db->root,
APK_SCRIPT_POST_DEINSTALL);
return 0;
}
}
/* Install the new stuff */
snprintf(file, sizeof(file),
"%s%s-%s.apk",
db->repos[0].url, newpkg->name->name, newpkg->version);
fd = open(file, O_RDONLY);
if (fd < 0)
return errno;
fcntl(fd, F_SETFD, FD_CLOEXEC);
tid = apk_checksum_and_tee(&fd, csum);
if (tid < 0)
goto err_close;
ctx = (struct install_ctx) {
.db = db,
.pkg = newpkg,
.script = (oldpkg == NULL) ?
APK_SCRIPT_PRE_INSTALL : APK_SCRIPT_PRE_UPGRADE,
};
if (apk_parse_tar_gz(fd, (apk_archive_entry_parser)
apk_db_install_archive_entry, &ctx) != 0)
goto err_close;
pthread_join(tid, NULL);
close(fd);
db->installed.stats.packages++;
hlist_add_head(&newpkg->installed_pkgs_list, &db->installed.packages);
if (memcmp(csum, newpkg->csum, sizeof(csum)) != 0)
apk_warning("%s-%s: checksum does not match",
newpkg->name->name, newpkg->version);
r = apk_pkg_run_script(newpkg, db->root,
(oldpkg == NULL) ?
APK_SCRIPT_POST_INSTALL : APK_SCRIPT_POST_UPGRADE);
if (r != 0)
apk_error("%s-%s: Failed to execute post-install/upgrade script",
newpkg->name->name, newpkg->version);
return r;
err_close:
close(fd);
if (tid != 0)
pthread_join(tid, NULL);
return -1;
}

53
src/del.c Normal file
View File

@ -0,0 +1,53 @@
/* del.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 del_main(int argc, char **argv)
{
struct apk_database db;
int i, j;
apk_db_init(&db, "/home/fabled/tmproot/");
apk_db_read_config(&db);
if (db.world == NULL)
goto out;
for (i = 0; i < argc; i++) {
for (j = 0; j < db.world->num; j++) {
if (strcmp(db.world->item[j].name->name,
argv[i]) == 0) {
db.world->item[j] =
db.world->item[db.world->num-1];
db.world =
apk_dependency_array_resize(db.world, db.world->num-1);
}
}
}
apk_db_recalculate_and_commit(&db);
out:
apk_db_free(&db);
return 0;
}
static struct apk_applet apk_del = {
.name = "del",
.usage = "apkname...",
.main = del_main,
};
APK_DEFINE_APPLET(apk_del);

97
src/hash.c Normal file
View File

@ -0,0 +1,97 @@
/* hash.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 "apk_defines.h"
#include "apk_hash.h"
unsigned long apk_hash_string(const char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++) != 0)
hash = hash * 33 + c;
return hash;
}
unsigned long apk_hash_csum(const void *ptr)
{
return *(const unsigned long *) ptr;
}
void apk_hash_init(struct apk_hash *h, const struct apk_hash_ops *ops,
int num_buckets)
{
h->ops = ops;
h->buckets = apk_hash_array_resize(NULL, num_buckets);
h->num_items = 0;
}
void apk_hash_free(struct apk_hash *h)
{
apk_hash_foreach(h, (apk_hash_enumerator_f) h->ops->delete_item, NULL);
free(h->buckets);
}
int apk_hash_foreach(struct apk_hash *h, apk_hash_enumerator_f e, void *ctx)
{
apk_hash_node *pos, *n;
ptrdiff_t offset = h->ops->node_offset;
int i, r;
for (i = 0; i < h->buckets->num; i++) {
hlist_for_each_safe(pos, n, &h->buckets->item[i]) {
r = e(((void *) pos) - offset, ctx);
if (r != 0)
return r;
}
}
return 0;
}
apk_hash_item apk_hash_get(struct apk_hash *h, apk_hash_key key)
{
ptrdiff_t offset = h->ops->node_offset;
unsigned long hash;
apk_hash_node *pos;
apk_hash_item item;
apk_hash_key itemkey;
hash = h->ops->hash_key(key) % h->buckets->num;
hlist_for_each(pos, &h->buckets->item[hash]) {
item = ((void *) pos) - offset;
itemkey = h->ops->get_key(item);
if (h->ops->compare(key, itemkey) == 0)
return item;
}
return NULL;
}
void apk_hash_insert(struct apk_hash *h, apk_hash_item item)
{
apk_hash_key key;
unsigned long hash;
apk_hash_node *node;
key = h->ops->get_key(item);
hash = h->ops->hash_key(key) % h->buckets->num;
node = (apk_hash_node *) (item + h->ops->node_offset);
hlist_add_head(node, &h->buckets->item[hash]);
h->num_items++;
}
void apk_hash_delete(struct apk_hash *h, apk_hash_key key)
{
}

70
src/index.c Normal file
View File

@ -0,0 +1,70 @@
/* index.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 <unistd.h>
#include "apk_applet.h"
#include "apk_database.h"
struct counts {
int total;
int unsatisfied;
};
static int warn_if_no_providers(apk_hash_item item, void *ctx)
{
struct counts *counts = (struct counts *) ctx;
struct apk_name *name = (struct apk_name *) item;
if (name->pkgs == NULL) {
if (++counts->unsatisfied < 10)
apk_warning("No provider for dependency '%s'",
name->name);
else if (counts->unsatisfied == 10)
apk_warning("Too many unsatisfiable dependencies, "
"not reporting the rest.");
}
counts->total++;
return 0;
}
static int index_main(int argc, char **argv)
{
struct apk_database db;
struct counts counts = {0,0};
int i;
apk_db_init(&db, NULL);
for (i = 0; i < argc; i++)
apk_db_pkg_add_file(&db, argv[i]);
apk_db_index_write(&db, STDOUT_FILENO);
apk_hash_foreach(&db.available.names, warn_if_no_providers, &counts);
apk_db_free(&db);
if (counts.unsatisfied != 0)
apk_warning("Total of %d unsatisfiable package "
"names. Your repository maybe broken.",
counts.unsatisfied);
apk_message("Index has %d packages", counts.total);
return 0;
}
static struct apk_applet apk_index = {
.name = "index",
.usage = "apkname...",
.main = index_main,
};
APK_DEFINE_APPLET(apk_index);

488
src/md5.c Normal file
View File

@ -0,0 +1,488 @@
/* md5.c - Compute MD5 checksum of files or strings according to the
* definition of MD5 in RFC 1321 from April 1992.
* Copyright (C) 1995-1999 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu> */
/* Hacked to work with BusyBox by Alfred M. Szmidt <ams@trillian.itslinux.org> */
/* Sucked directly into ipkg since the md5sum functions aren't in libbb
Dropped a few functions since ipkg only needs md5_stream.
Got rid of evil, twisted defines of FALSE=1 and TRUE=0
6 March 2002 Carl Worth <cworth@east.isi.edu>
*/
/*
* June 29, 2001 Manuel Novoa III
*
* Added MD5SUM_SIZE_VS_SPEED configuration option.
*
* Current valid values, with data from my system for comparison, are:
* (using uClibc and running on linux-2.4.4.tar.bz2)
* user times (sec) text size (386)
* 0 (fastest) 1.1 6144
* 1 1.4 5392
* 2 3.0 5088
* 3 (smallest) 5.1 4912
*/
#define MD5SUM_SIZE_VS_SPEED 0
/**********************************************************************/
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <endian.h>
#include <sys/types.h>
#if defined HAVE_LIMITS_H
# include <limits.h>
#endif
#include "md5.h"
/* Handle endian-ness */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define SWAP(n) (n)
#else
#define SWAP(n) ((n << 24) | ((n&65280)<<8) | ((n&16711680)>>8) | (n>>24))
#endif
#if MD5SUM_SIZE_VS_SPEED == 0
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. (RFC 1321, 3.1: Step 1) */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
#endif
/* These are the four functions used in the four steps of the MD5 algorithm
and defined in the RFC 1321. The first function is a little bit optimized
(as found in Colin Plumbs public domain implementation). */
/* #define FF(b, c, d) ((b & c) | (~b & d)) */
#define FF(b, c, d) (d ^ (b & (c ^ d)))
#define FG(b, c, d) FF (d, b, c)
#define FH(b, c, d) (b ^ c ^ d)
#define FI(b, c, d) (c ^ (b | ~d))
#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
/* Process LEN bytes of BUFFER, accumulating context into CTX.
It is assumed that LEN % 64 == 0. */
static void md5_process_block(struct md5_ctx *ctx,
const void *buffer, size_t len)
{
md5_uint32 correct_words[16];
const md5_uint32 *words = buffer;
size_t nwords = len / sizeof(md5_uint32);
const md5_uint32 *endp = words + nwords;
#if MD5SUM_SIZE_VS_SPEED > 0
static const md5_uint32 C_array[] = {
/* round 1 */
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
/* round 2 */
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
/* round 3 */
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
/* round 4 */
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
static const char P_array[] = {
#if MD5SUM_SIZE_VS_SPEED > 1
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */
#endif
1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */
5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */
0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */
};
#if MD5SUM_SIZE_VS_SPEED > 1
static const char S_array[] = {
7, 12, 17, 22,
5, 9, 14, 20,
4, 11, 16, 23,
6, 10, 15, 21
};
#endif
#endif
md5_uint32 A = ctx->A;
md5_uint32 B = ctx->B;
md5_uint32 C = ctx->C;
md5_uint32 D = ctx->D;
/* First increment the byte count. RFC 1321 specifies the possible
length of the file up to 2^64 bits. Here we only compute the
number of bytes. Do a double word increment. */
ctx->total[0] += len;
if (ctx->total[0] < len)
++ctx->total[1];
/* Process all bytes in the buffer with 64 bytes in each round of
the loop. */
while (words < endp) {
md5_uint32 *cwp = correct_words;
md5_uint32 A_save = A;
md5_uint32 B_save = B;
md5_uint32 C_save = C;
md5_uint32 D_save = D;
#if MD5SUM_SIZE_VS_SPEED > 1
const md5_uint32 *pc;
const char *pp;
const char *ps;
int i;
md5_uint32 temp;
for ( i=0 ; i < 16 ; i++ ) {
cwp[i] = SWAP(words[i]);
}
words += 16;
#if MD5SUM_SIZE_VS_SPEED > 2
pc = C_array; pp = P_array; ps = S_array - 4;
for ( i = 0 ; i < 64 ; i++ ) {
if ((i&0x0f) == 0) ps += 4;
temp = A;
switch (i>>4) {
case 0:
temp += FF(B,C,D);
break;
case 1:
temp += FG(B,C,D);
break;
case 2:
temp += FH(B,C,D);
break;
case 3:
temp += FI(B,C,D);
}
temp += cwp[(int)(*pp++)] + *pc++;
temp = CYCLIC(temp, ps[i&3]);
temp += B;
A = D; D = C; C = B; B = temp;
}
#else
pc = C_array; pp = P_array; ps = S_array;
for ( i = 0 ; i < 16 ; i++ ) {
temp = A + FF(B,C,D) + cwp[(int)(*pp++)] + *pc++;
temp = CYCLIC (temp, ps[i&3]);
temp += B;
A = D; D = C; C = B; B = temp;
}
ps += 4;
for ( i = 0 ; i < 16 ; i++ ) {
temp = A + FG(B,C,D) + cwp[(int)(*pp++)] + *pc++;
temp = CYCLIC (temp, ps[i&3]);
temp += B;
A = D; D = C; C = B; B = temp;
}
ps += 4;
for ( i = 0 ; i < 16 ; i++ ) {
temp = A + FH(B,C,D) + cwp[(int)(*pp++)] + *pc++;
temp = CYCLIC (temp, ps[i&3]);
temp += B;
A = D; D = C; C = B; B = temp;
}
ps += 4;
for ( i = 0 ; i < 16 ; i++ ) {
temp = A + FI(B,C,D) + cwp[(int)(*pp++)] + *pc++;
temp = CYCLIC (temp, ps[i&3]);
temp += B;
A = D; D = C; C = B; B = temp;
}
#endif
#else
/* First round: using the given function, the context and a constant
the next context is computed. Because the algorithms processing
unit is a 32-bit word and it is determined to work on words in
little endian byte order we perhaps have to change the byte order
before the computation. To reduce the work for the next steps
we store the swapped words in the array CORRECT_WORDS. */
#define OP(a, b, c, d, s, T) \
do \
{ \
a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
++words; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* Before we start, one word to the strange constants.
They are defined in RFC 1321 as
T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
*/
#if MD5SUM_SIZE_VS_SPEED == 1
const md5_uint32 *pc;
const char *pp;
int i;
#endif
/* Round 1. */
#if MD5SUM_SIZE_VS_SPEED == 1
pc = C_array;
for ( i=0 ; i < 4 ; i++ ) {
OP(A, B, C, D, 7, *pc++);
OP(D, A, B, C, 12, *pc++);
OP(C, D, A, B, 17, *pc++);
OP(B, C, D, A, 22, *pc++);
}
#else
OP(A, B, C, D, 7, 0xd76aa478);
OP(D, A, B, C, 12, 0xe8c7b756);
OP(C, D, A, B, 17, 0x242070db);
OP(B, C, D, A, 22, 0xc1bdceee);
OP(A, B, C, D, 7, 0xf57c0faf);
OP(D, A, B, C, 12, 0x4787c62a);
OP(C, D, A, B, 17, 0xa8304613);
OP(B, C, D, A, 22, 0xfd469501);
OP(A, B, C, D, 7, 0x698098d8);
OP(D, A, B, C, 12, 0x8b44f7af);
OP(C, D, A, B, 17, 0xffff5bb1);
OP(B, C, D, A, 22, 0x895cd7be);
OP(A, B, C, D, 7, 0x6b901122);
OP(D, A, B, C, 12, 0xfd987193);
OP(C, D, A, B, 17, 0xa679438e);
OP(B, C, D, A, 22, 0x49b40821);
#endif
/* For the second to fourth round we have the possibly swapped words
in CORRECT_WORDS. Redefine the macro to take an additional first
argument specifying the function to use. */
#undef OP
#define OP(f, a, b, c, d, k, s, T) \
do \
{ \
a += f (b, c, d) + correct_words[k] + T; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* Round 2. */
#if MD5SUM_SIZE_VS_SPEED == 1
pp = P_array;
for ( i=0 ; i < 4 ; i++ ) {
OP(FG, A, B, C, D, (int)(*pp++), 5, *pc++);
OP(FG, D, A, B, C, (int)(*pp++), 9, *pc++);
OP(FG, C, D, A, B, (int)(*pp++), 14, *pc++);
OP(FG, B, C, D, A, (int)(*pp++), 20, *pc++);
}
#else
OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
OP(FG, D, A, B, C, 6, 9, 0xc040b340);
OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
OP(FG, D, A, B, C, 10, 9, 0x02441453);
OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
#endif
/* Round 3. */
#if MD5SUM_SIZE_VS_SPEED == 1
for ( i=0 ; i < 4 ; i++ ) {
OP(FH, A, B, C, D, (int)(*pp++), 4, *pc++);
OP(FH, D, A, B, C, (int)(*pp++), 11, *pc++);
OP(FH, C, D, A, B, (int)(*pp++), 16, *pc++);
OP(FH, B, C, D, A, (int)(*pp++), 23, *pc++);
}
#else
OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
OP(FH, D, A, B, C, 8, 11, 0x8771f681);
OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
OP(FH, B, C, D, A, 6, 23, 0x04881d05);
OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
#endif
/* Round 4. */
#if MD5SUM_SIZE_VS_SPEED == 1
for ( i=0 ; i < 4 ; i++ ) {
OP(FI, A, B, C, D, (int)(*pp++), 6, *pc++);
OP(FI, D, A, B, C, (int)(*pp++), 10, *pc++);
OP(FI, C, D, A, B, (int)(*pp++), 15, *pc++);
OP(FI, B, C, D, A, (int)(*pp++), 21, *pc++);
}
#else
OP(FI, A, B, C, D, 0, 6, 0xf4292244);
OP(FI, D, A, B, C, 7, 10, 0x432aff97);
OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
OP(FI, C, D, A, B, 6, 15, 0xa3014314);
OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
#endif
#endif
/* Add the starting values of the context. */
A += A_save;
B += B_save;
C += C_save;
D += D_save;
}
/* Put checksum in context given as argument. */
ctx->A = A;
ctx->B = B;
ctx->C = C;
ctx->D = D;
}
/* Initialize structure containing state of computation.
(RFC 1321, 3.3: Step 3) */
void md5_init(struct md5_ctx *ctx)
{
ctx->A = 0x67452301;
ctx->B = 0xefcdab89;
ctx->C = 0x98badcfe;
ctx->D = 0x10325476;
ctx->total[0] = ctx->total[1] = 0;
ctx->buflen = 0;
}
void md5_process(struct md5_ctx *ctx, const void *buffer, size_t len)
{
/* When we already have some bits in our internal buffer concatenate
both inputs first. */
if (ctx->buflen != 0) {
size_t left_over = ctx->buflen;
size_t add = 128 - left_over > len ? len : 128 - left_over;
memcpy(&ctx->buffer[left_over], buffer, add);
ctx->buflen += add;
if (left_over + add > 64) {
md5_process_block(ctx, ctx->buffer, (left_over + add) & ~63);
/* The regions in the following copy operation cannot overlap. */
memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
(left_over + add) & 63);
ctx->buflen = (left_over + add) & 63;
}
buffer = (const char *) buffer + add;
len -= add;
}
/* Process available complete blocks. */
if (len > 64) {
md5_process_block(ctx, buffer, len & ~63);
buffer = (const char *) buffer + (len & ~63);
len &= 63;
}
/* Move remaining bytes in internal buffer. */
if (len > 0) {
memcpy(ctx->buffer, buffer, len);
ctx->buflen = len;
}
}
/* Process the remaining bytes in the internal buffer and the usual
prolog according to the standard and write the result to RESBUF.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
void md5_finish(struct md5_ctx *ctx, md5sum_t resbuf)
{
/* Take yet unprocessed bytes into account. */
md5_uint32 bytes = ctx->buflen;
size_t pad;
/* Now count remaining bytes. */
ctx->total[0] += bytes;
if (ctx->total[0] < bytes)
++ctx->total[1];
pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
#if MD5SUM_SIZE_VS_SPEED > 0
memset(&ctx->buffer[bytes], 0, pad);
ctx->buffer[bytes] = 0x80;
#else
memcpy(&ctx->buffer[bytes], fillbuf, pad);
#endif
/* Put the 64-bit file length in *bits* at the end of the buffer. */
*(md5_uint32 *) & ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3);
*(md5_uint32 *) & ctx->buffer[bytes + pad + 4] =
SWAP( ((ctx->total[1] << 3) | (ctx->total[0] >> 29)) );
/* Process last bytes. */
md5_process_block(ctx, ctx->buffer, bytes + pad + 8);
/* Put result from CTX in first 16 bytes following RESBUF. The result is
always in little endian byte order, so that a byte-wise output yields
to the wanted ASCII representation of the message digest.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
((md5_uint32 *) resbuf)[0] = SWAP(ctx->A);
((md5_uint32 *) resbuf)[1] = SWAP(ctx->B);
((md5_uint32 *) resbuf)[2] = SWAP(ctx->C);
((md5_uint32 *) resbuf)[3] = SWAP(ctx->D);
}

60
src/md5.h Normal file
View File

@ -0,0 +1,60 @@
/* md5.h - Compute MD5 checksum of files or strings according to the
* definition of MD5 in RFC 1321 from April 1992.
* Copyright (C) 1995-1999 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef MD5_H
#define MD5_H
#include <sys/types.h>
typedef unsigned char md5sum_t[16];
typedef u_int32_t md5_uint32;
struct md5_ctx
{
md5_uint32 A;
md5_uint32 B;
md5_uint32 C;
md5_uint32 D;
md5_uint32 total[2];
md5_uint32 buflen;
char buffer[128];
};
/* Initialize structure containing state of computation.
(RFC 1321, 3.3: Step 3) */
void md5_init(struct md5_ctx *ctx);
/* Starting with the result of former calls of this function (or the
initialization function update the context for the next LEN bytes
starting at BUFFER.
It is NOT required that LEN is a multiple of 64. */
void md5_process(struct md5_ctx *ctx, const void *buffer, size_t len);
/* Process the remaining bytes in the buffer and put result from CTX
in first 16 bytes following RESBUF. The result is always in little
endian byte order, so that a byte-wise output yields to the wanted
ASCII representation of the message digest.
IMPORTANT: On some systems it is required that RESBUF is correctly
aligned for a 32 bits value. */
void md5_finish(struct md5_ctx *ctx, md5sum_t resbuf);
#endif

483
src/package.c Normal file
View File

@ -0,0 +1,483 @@
/* package.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 <fcntl.h>
#include <ctype.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
#include "apk_defines.h"
#include "apk_archive.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"
int apk_pkg_parse_name(apk_blob_t apkname,
apk_blob_t *name,
apk_blob_t *version)
{
int i, dash = 0;
for (i = apkname.len - 2; i >= 0; i--) {
if (apkname.ptr[i] != '-')
continue;
if (isdigit(apkname.ptr[i+1]))
break;
if (++dash >= 2)
return -1;
}
if (name != NULL)
*name = APK_BLOB_PTR_LEN(apkname.ptr, i);
if (version != NULL)
*version = APK_BLOB_PTR_PTR(&apkname.ptr[i+1],
&apkname.ptr[apkname.len-1]);
return 0;
}
static char *trim(apk_blob_t str)
{
if (str.ptr == NULL || str.len < 1)
return NULL;
if (str.ptr[str.len-2] == '\n')
str.ptr[str.len-2] = 0;
return str.ptr;
}
static void parse_depend(struct apk_database *db,
struct apk_dependency_array **depends,
apk_blob_t blob)
{
struct apk_dependency *dep;
struct apk_name *name;
char *cname;
while (blob.len && blob.ptr[0] == ' ')
blob.ptr++, blob.len--;
while (blob.len && (blob.ptr[blob.len-1] == ' ' ||
blob.ptr[blob.len-1] == 0))
blob.len--;
if (blob.len == 0)
return;
cname = apk_blob_cstr(blob);
name = apk_db_get_name(db, cname);
free(cname);
dep = apk_dependency_array_add(depends);
*dep = (struct apk_dependency){
.prefer_upgrade = 0,
.version_mask = 0,
.name = name,
.version = NULL,
};
}
int apk_deps_add(struct apk_dependency_array **depends,
struct apk_dependency *dep)
{
struct apk_dependency_array *deps = *depends;
int i;
if (deps != NULL) {
for (i = 0; i < deps->num; i++) {
if (deps->item[i].name == dep->name)
return 0;
}
}
*apk_dependency_array_add(depends) = *dep;
return 0;
}
void apk_deps_parse(struct apk_database *db,
struct apk_dependency_array **depends,
apk_blob_t blob)
{
char *start;
int i;
start = blob.ptr;
for (i = 0; i < blob.len; i++) {
if (blob.ptr[i] != ',' && blob.ptr[i] != '\n')
continue;
parse_depend(db, depends,
APK_BLOB_PTR_PTR(start, &blob.ptr[i-1]));
start = &blob.ptr[i+1];
}
parse_depend(db, depends,
APK_BLOB_PTR_PTR(start, &blob.ptr[i-1]));
}
int apk_deps_format(char *buf, int size,
struct apk_dependency_array *depends)
{
int i, n = 0;
if (depends == NULL)
return 0;
for (i = 0; i < depends->num - 1; i++)
n += snprintf(&buf[n], size-n,
"%s, ",
depends->item[i].name->name);
n += snprintf(&buf[n], size-n,
"%s\n",
depends->item[i].name->name);
return n;
}
static const char *script_types[] = {
[APK_SCRIPT_PRE_INSTALL] = "pre-install",
[APK_SCRIPT_POST_INSTALL] = "post-install",
[APK_SCRIPT_PRE_DEINSTALL] = "pre-deinstall",
[APK_SCRIPT_POST_DEINSTALL] = "post-deinstall",
[APK_SCRIPT_PRE_UPGRADE] = "pre-upgrade",
[APK_SCRIPT_POST_UPGRADE] = "post-upgrade",
};
int apk_script_type(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(script_types); i++)
if (script_types[i] &&
strcmp(script_types[i], name) == 0)
return i;
return -1;
}
struct read_info_ctx {
struct apk_database *db;
struct apk_package *pkg;
int has_install;
};
static int read_info_entry(struct apk_archive_entry *ae, void *ctx)
{
struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
struct apk_database *db = ri->db;
struct apk_package *pkg = ri->pkg;
const int bsize = 4 * 1024;
apk_blob_t name, version;
char *slash, *str;
if (strncmp(ae->name, "var/db/apk/", 11) != 0) {
pkg->installed_size += (ae->size + bsize - 1) & ~(bsize - 1);
return 0;
}
if (!S_ISREG(ae->mode))
return 0;
slash = strchr(&ae->name[11], '/');
if (slash == NULL)
return 0;
if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
&name, &version) < 0)
return -1;
if (pkg->name == NULL) {
str = apk_blob_cstr(name);
pkg->name = apk_db_get_name(db, str);
free(str);
}
if (pkg->version == NULL)
pkg->version = apk_blob_cstr(version);
if (strcmp(slash, "/DEPEND") == 0) {
apk_blob_t blob = apk_archive_entry_read(ae);
if (blob.ptr) {
apk_deps_parse(db, &pkg->depends, blob);
free(blob.ptr);
}
} else if (strcmp(slash, "/DESC") == 0) {
pkg->description = trim(apk_archive_entry_read(ae));
} else if (strcmp(slash, "/WWW") == 0) {
pkg->url = trim(apk_archive_entry_read(ae));
} else if (strcmp(slash, "/LICENSE") == 0) {
pkg->license = trim(apk_archive_entry_read(ae));
} else if (apk_script_type(slash+1) == APK_SCRIPT_POST_INSTALL ||
apk_script_type(slash+1) == APK_SCRIPT_PRE_INSTALL)
ri->has_install = 1;
return 0;
}
struct apk_package *apk_pkg_read(struct apk_database *db, const char *file)
{
struct read_info_ctx ctx;
struct stat st;
pthread_t tid;
int fd;
ctx.pkg = calloc(1, sizeof(struct apk_package));
if (ctx.pkg == NULL)
return NULL;
fd = open(file, O_RDONLY);
if (fd < 0)
goto err;
fstat(fd, &st);
fcntl(fd, F_SETFD, FD_CLOEXEC);
tid = apk_checksum_and_tee(&fd, ctx.pkg->csum);
if (fd < 0)
goto err;
ctx.db = db;
ctx.pkg->size = st.st_size;
ctx.has_install = 0;
if (apk_parse_tar_gz(fd, read_info_entry, &ctx) != 0) {
pthread_join(tid, NULL);
goto err;
}
pthread_join(tid, NULL);
if (ctx.pkg->name == NULL)
goto err;
close(fd);
/* Add implicit busybox dependency if there is scripts */
if (ctx.has_install) {
struct apk_dependency dep = {
.name = apk_db_get_name(db, "busybox"),
};
apk_deps_add(&ctx.pkg->depends, &dep);
}
return ctx.pkg;
err:
apk_pkg_free(ctx.pkg);
return NULL;
}
void apk_pkg_free(struct apk_package *pkg)
{
struct apk_script *script;
struct hlist_node *c, *n;
if (pkg == NULL)
return;
hlist_for_each_entry_safe(script, c, n, &pkg->scripts, script_list)
free(script);
if (pkg->version)
free(pkg->version);
if (pkg->url)
free(pkg->url);
if (pkg->description)
free(pkg->description);
if (pkg->license)
free(pkg->license);
free(pkg);
}
int apk_pkg_get_state(struct apk_package *pkg)
{
if (hlist_hashed(&pkg->installed_pkgs_list))
return APK_STATE_INSTALL;
return APK_STATE_NO_INSTALL;
}
int apk_pkg_add_script(struct apk_package *pkg, int fd,
unsigned int type, unsigned int size)
{
struct apk_script *script;
int r;
script = malloc(sizeof(struct apk_script) + size);
script->type = type;
script->size = size;
r = read(fd, script->script, size);
if (r < 0) {
free(script);
return r;
}
hlist_add_head(&script->script_list, &pkg->scripts);
return r;
}
int apk_pkg_run_script(struct apk_package *pkg, const char *root,
unsigned int type)
{
struct apk_script *script;
struct hlist_node *c;
int fd, status;
pid_t pid;
char fn[1024];
hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
if (script->type != type)
continue;
snprintf(fn, sizeof(fn),
"tmp/%s-%s.%s",
pkg->name->name, pkg->version,
script_types[script->type]);
fd = creat(fn, 0777);
if (fd < 0)
return fd;
write(fd, script->script, script->size);
close(fd);
apk_message("Executing %s", &fn[4]);
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
chroot(root);
fn[2] = '.';
execl(&fn[2], script_types[script->type],
pkg->version, "", NULL);
exit(1);
}
waitpid(pid, &status, 0);
unlink(fn);
if (WIFEXITED(status))
return WEXITSTATUS(status);
return -1;
}
/* FIXME: Remove this ugly kludge */
if (strcmp(pkg->name->name, "busybox") == 0 &&
type == APK_SCRIPT_POST_INSTALL) {
apk_message("Create busybox links");
pid = fork();
if (pid == -1)
return -1;
if (pid == 0) {
chroot(root);
execl("/bin/busybox", "busybox", "--install", "-s", NULL);
exit(1);
}
waitpid(pid, &status, 0);
if (WIFEXITED(status))
return WEXITSTATUS(status);
return -1;
}
return 0;
}
static int parse_index_line(struct apk_database *db, struct apk_package *pkg,
apk_blob_t blob)
{
apk_blob_t d;
char *str;
if (blob.len < 2 || blob.ptr[1] != ':')
return -1;
d = APK_BLOB_PTR_LEN(blob.ptr+2, blob.len-2);
switch (blob.ptr[0]) {
case 'P':
str = apk_blob_cstr(d);
pkg->name = apk_db_get_name(db, str);
free(str);
break;
case 'V':
pkg->version = apk_blob_cstr(d);
break;
case 'T':
pkg->description = apk_blob_cstr(d);
break;
case 'U':
pkg->url = apk_blob_cstr(d);
break;
case 'L':
pkg->license = apk_blob_cstr(d);
break;
case 'D':
apk_deps_parse(db, &pkg->depends, d);
break;
case 'C':
apk_hexdump_parse(APK_BLOB_BUF(pkg->csum), d);
break;
case 'S':
pkg->size = apk_blob_uint(d, 10);
break;
case 'I':
pkg->installed_size = apk_blob_uint(d, 10);
break;
}
return 0;
}
struct apk_package *apk_pkg_parse_index_entry(struct apk_database *db, apk_blob_t blob)
{
struct apk_package *pkg;
apk_blob_t l, r;
pkg = calloc(1, sizeof(struct apk_package));
if (pkg == NULL)
return NULL;
r = blob;
while (apk_blob_splitstr(r, "\n", &l, &r))
parse_index_line(db, pkg, l);
parse_index_line(db, pkg, r);
if (pkg->name == NULL) {
apk_pkg_free(pkg);
printf("%.*s\n", blob.len, blob.ptr);
pkg = NULL;
}
return pkg;
}
apk_blob_t apk_pkg_format_index_entry(struct apk_package *info, int size,
char *buf)
{
int n = 0;
n += snprintf(&buf[n], size-n,
"P:%s\n"
"V:%s\n"
"S:%u\n"
"I:%u\n"
"T:%s\n"
"U:%s\n"
"L:%s\n",
info->name->name, info->version,
info->size, info->installed_size,
info->description, info->url, info->license);
if (info->depends != NULL) {
n += snprintf(&buf[n], size-n, "D:");
n += apk_deps_format(&buf[n], size-n, info->depends);
}
n += snprintf(&buf[n], size-n, "C:");
n += apk_hexdump_format(size-n, &buf[n],
APK_BLOB_BUF(info->csum));
n += snprintf(&buf[n], size-n,
"\n\n");
return APK_BLOB_PTR_LEN(buf, n);
}

267
src/state.c Normal file
View File

@ -0,0 +1,267 @@
/* state.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 <malloc.h>
#include "apk_state.h"
#include "apk_database.h"
static int apk_state_commit_deps(struct apk_state *state,
struct apk_database *db,
struct apk_dependency_array *deps);
struct apk_state *apk_state_new(struct apk_database *db)
{
struct apk_state *state;
int num_bytes;
num_bytes = sizeof(struct apk_state) + (db->pkg_id * 2 + 7) / 8;
state = (struct apk_state*) calloc(1, num_bytes);
state->refs = 1;
return state;
}
struct apk_state *apk_state_dup(struct apk_state *state)
{
state->refs++;
return state;
}
void apk_state_unref(struct apk_state *state)
{
if (--state->refs > 0)
return;
free(state);
}
static void apk_state_set(struct apk_state *state, int pos, int val)
{
int byte = pos / 4, offs = pos % 4;
state->bitarray[byte] &= ~(0x3 << (offs * 2));
state->bitarray[byte] |= (val & 0x3) << (offs * 2);
}
static int apk_state_get(struct apk_state *state, int pos)
{
int byte = pos / 4, offs = pos % 4;
if (state == NULL)
return APK_STATE_NOT_CONSIDERED;
return (state->bitarray[byte] >> (offs * 2)) & 0x3;
}
static int apk_state_commit_pkg(struct apk_state *state,
struct apk_database *db,
struct apk_name *name,
struct apk_package *oldpkg,
struct apk_package *newpkg)
{
const char *msg = NULL;
int r, upgrade = 0;
if (newpkg != NULL) {
r = apk_state_commit_deps(state, db, newpkg->depends);
if (r != 0)
return r;
}
if (oldpkg == NULL) {
apk_message("Installing %s (%s)",
name->name, newpkg->version);
} else if (newpkg == NULL) {
apk_message("Purging %s (%s)",
name->name, oldpkg->version);
} else {
r = apk_version_compare(APK_BLOB_STR(newpkg->version),
APK_BLOB_STR(oldpkg->version));
switch (r) {
case APK_VERSION_LESS:
msg = "Downgrading";
upgrade = 1;
break;
case APK_VERSION_EQUAL:
msg = "Re-installing";
break;
case APK_VERSION_GREATER:
msg = "Upgrading";
upgrade = 1;
break;
}
apk_message("%s %s (%s -> %s)",
msg, name->name, oldpkg->version, newpkg->version);
}
return apk_db_install_pkg(db, oldpkg, newpkg);
}
static int apk_state_commit_name(struct apk_state *state,
struct apk_database *db,
struct apk_name *name)
{
struct apk_package *oldpkg = NULL, *newpkg = NULL;
int i;
for (i = 0; i < name->pkgs->num; i++) {
if (apk_pkg_get_state(name->pkgs->item[i]) == APK_STATE_INSTALL)
oldpkg = name->pkgs->item[i];
if (apk_state_get(state, name->pkgs->item[i]->id) == APK_STATE_INSTALL)
newpkg = name->pkgs->item[i];
}
if (oldpkg == NULL && newpkg == NULL)
return 0;
/* No reinstallations for now */
if (newpkg == oldpkg)
return 0;
return apk_state_commit_pkg(state, db, name, oldpkg, newpkg);
}
static int apk_state_commit_deps(struct apk_state *state,
struct apk_database *db,
struct apk_dependency_array *deps)
{
int r, i;
if (deps == NULL)
return 0;
for (i = 0; i < deps->num; i++) {
r = apk_state_commit_name(state, db, deps->item[i].name);
if (r != 0)
return r;
}
return 0;
}
int apk_state_commit(struct apk_state *state,
struct apk_database *db)
{
struct apk_package *pkg;
struct hlist_node *c, *n;
int r;
/* Check all dependencies */
r = apk_state_commit_deps(state, db, db->world);
if (r != 0)
return r;
/* And purge all installed packages that were not considered */
hlist_for_each_entry_safe(pkg, c, n, &db->installed.packages, installed_pkgs_list)
apk_state_commit_name(state, db, pkg->name);
return 0;
}
int apk_state_satisfy_name(struct apk_state *state,
struct apk_name *name)
{
struct apk_package *preferred = NULL;
int i;
int upgrading = 1;
/* Is something already installed? Or figure out the preferred
* package. */
for (i = 0; i < name->pkgs->num; i++) {
if (apk_state_get(state, name->pkgs->item[i]->id) ==
APK_STATE_INSTALL)
return 0;
if (preferred == NULL) {
preferred = name->pkgs->item[i];
continue;
}
if (upgrading) {
if (apk_version_compare(APK_BLOB_STR(name->pkgs->item[i]->version),
APK_BLOB_STR(preferred->version)) ==
APK_VERSION_GREATER) {
preferred = name->pkgs->item[i];
continue;
}
} else {
if (apk_pkg_get_state(name->pkgs->item[i]) ==
APK_STATE_INSTALL) {
preferred = name->pkgs->item[i];
continue;
}
}
}
/* Mark conflicting names as no install */
for (i = 0; i < name->pkgs->num; i++) {
if (name->pkgs->item[i] != preferred)
apk_state_set(state, name->pkgs->item[i]->id,
APK_STATE_NO_INSTALL);
}
return apk_state_pkg_install(state, preferred);
}
int apk_state_satisfy_deps(struct apk_state *state,
struct apk_dependency_array *deps)
{
struct apk_name *name;
int r, i;
if (deps == NULL)
return 0;
for (i = 0; i < deps->num; i++) {
name = deps->item[i].name;
if (name->pkgs == NULL) {
apk_error("No providers for '%s'", name->name);
return -1;
}
r = apk_state_satisfy_name(state, name);
if (r != 0)
return r;
}
return 0;
}
void apk_state_pkg_set(struct apk_state *state,
struct apk_package *pkg)
{
apk_state_set(state, pkg->id, APK_STATE_INSTALL);
}
int apk_state_pkg_install(struct apk_state *state,
struct apk_package *pkg)
{
switch (apk_state_get(state, pkg->id)) {
case APK_STATE_INSTALL:
return 0;
case APK_STATE_NO_INSTALL:
return -1;
}
apk_state_set(state, pkg->id, APK_STATE_INSTALL);
if (pkg->depends == NULL)
return 0;
return apk_state_satisfy_deps(state, pkg->depends);
}
int apk_state_pkg_is_installed(struct apk_state *state,
struct apk_package *pkg)
{
return apk_state_get(state, pkg->id);
}

15
src/template.c Normal file
View File

@ -0,0 +1,15 @@
/* template.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 "apk_database.h"

76
src/ver.c Normal file
View File

@ -0,0 +1,76 @@
/* ver.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_defines.h"
#include "apk_applet.h"
#include "apk_database.h"
#include "apk_version.h"
static int res2char(int res)
{
switch (res) {
case APK_VERSION_LESS:
return '<';
case APK_VERSION_GREATER:
return '>';
case APK_VERSION_EQUAL:
return '=';
default:
return '?';
}
}
static int ver_main(int argc, char **argv)
{
struct apk_database db;
struct apk_name *name;
struct apk_package *pkg, *upg, *tmp;
struct hlist_node *c;
int i, r;
if (argc == 2) {
r = apk_version_compare(APK_BLOB_STR(argv[0]),
APK_BLOB_STR(argv[1]));
printf("%c\n", res2char(r));
return 0;
}
apk_db_init(&db, "/home/fabled/tmproot/");
apk_db_read_config(&db);
hlist_for_each_entry(pkg, c, &db.installed.packages, installed_pkgs_list) {
name = pkg->name;
upg = pkg;
for (i = 0; i < name->pkgs->num; i++) {
tmp = name->pkgs->item[i];
if (tmp->name != name)
continue;
r = apk_version_compare(APK_BLOB_STR(tmp->version),
APK_BLOB_STR(upg->version));
if (r == APK_VERSION_GREATER)
upg = tmp;
}
printf("%-40s%c\n", name->name, pkg != upg ? '<' : '=');
}
apk_db_free(&db);
return 0;
}
static struct apk_applet apk_ver = {
.name = "version",
.usage = "[version1 version2]",
.main = ver_main,
};
APK_DEFINE_APPLET(apk_ver);

165
src/version.c Normal file
View File

@ -0,0 +1,165 @@
/* version.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 <ctype.h>
#include "apk_defines.h"
#include "apk_version.h"
/* Gentoo version: {digit}{.digit}...{letter}{_suf{#}}...{-r#} */
enum PARTS {
TOKEN_INVALID = -1,
TOKEN_DIGIT_OR_ZERO,
TOKEN_DIGIT,
TOKEN_LETTER,
TOKEN_SUFFIX,
TOKEN_SUFFIX_NO,
TOKEN_REVISION_NO,
TOKEN_END,
};
static void next_token(int *type, apk_blob_t *blob)
{
int n = TOKEN_INVALID;
if (blob->len == 0 || blob->ptr[0] == 0) {
n = TOKEN_END;
} else if (islower(blob->ptr[0])) {
n = TOKEN_LETTER;
} else if (*type == TOKEN_SUFFIX && isdigit(blob->ptr[0])) {
n = TOKEN_SUFFIX_NO;
} else {
switch (blob->ptr[0]) {
case '.':
n = TOKEN_DIGIT_OR_ZERO;
break;
case '_':
n = TOKEN_SUFFIX;
break;
case '-':
if (blob->len > 1 && blob->ptr[1] == 'r') {
n = TOKEN_REVISION_NO;
blob->ptr++;
blob->len--;
} else
n = TOKEN_INVALID;
break;
}
blob->ptr++;
blob->len--;
}
if (n < *type) {
if (! ((n == TOKEN_DIGIT_OR_ZERO && *type == TOKEN_DIGIT) ||
(n == TOKEN_SUFFIX && *type == TOKEN_SUFFIX_NO)))
n = TOKEN_INVALID;
}
*type = n;
}
static int get_token(int *type, apk_blob_t *blob)
{
static const char *pre_suffixes[] = { "alpha", "beta", "pre", "rc" };
int v = 0, i = 0, nt = TOKEN_INVALID;
switch (*type) {
case TOKEN_DIGIT_OR_ZERO:
/* Leading zero digits get a special treatment */
if (blob->ptr[i] == '0') {
while (blob->ptr[i] == '0' && i < blob->len)
i++;
nt = TOKEN_DIGIT;
v = -i;
break;
}
case TOKEN_DIGIT:
case TOKEN_SUFFIX_NO:
case TOKEN_REVISION_NO:
while (isdigit(blob->ptr[i]) && i < blob->len) {
v *= 10;
v += blob->ptr[i++] - '0';
}
break;
case TOKEN_LETTER:
v = blob->ptr[i++];
break;
case TOKEN_SUFFIX:
for (v = 0; v < ARRAY_SIZE(pre_suffixes); v++) {
i = strlen(pre_suffixes[v]);
if (i < blob->len &&
strncmp(pre_suffixes[v], blob->ptr, i) == 0)
break;
}
if (v < ARRAY_SIZE(pre_suffixes)) {
nt = TOKEN_SUFFIX_NO;
v = v - ARRAY_SIZE(pre_suffixes);
break;
}
if (strncmp("p", blob->ptr, 1) == 0) {
nt = TOKEN_SUFFIX_NO;
v = 1;
break;
}
/* fallthrough: invalid suffix */
default:
*type = TOKEN_INVALID;
return -1;
}
blob->ptr += i;
blob->len -= i;
if (nt != TOKEN_INVALID)
*type = nt;
else
next_token(type, blob);
return v;
}
int apk_version_validate(apk_blob_t ver)
{
int t = TOKEN_DIGIT;
while (t != TOKEN_END && t != TOKEN_INVALID)
get_token(&t, &ver);
return t == TOKEN_END;
}
int apk_version_compare(apk_blob_t a, apk_blob_t b)
{
int at = TOKEN_DIGIT, bt = TOKEN_DIGIT;
int av = 0, bv = 0;
while (at == bt && at != TOKEN_END && av == bv) {
av = get_token(&at, &a);
bv = get_token(&bt, &b);
#if 0
fprintf(stderr,
"av=%d, at=%d, a.len=%d\n"
"bv=%d, bt=%d, b.len=%d\n",
av, at, a.len, bv, bt, b.len);
#endif
}
/* value of this token differs? */
if (av < bv)
return APK_VERSION_LESS;
if (av > bv)
return APK_VERSION_GREATER;
if (at < bt)
return get_token(&at, &a) < 0 ?
APK_VERSION_LESS : APK_VERSION_GREATER;
if (bt < at)
return get_token(&bt, &b) > 0 ?
APK_VERSION_LESS : APK_VERSION_GREATER;
return APK_VERSION_EQUAL;
}