Initial commit of some stuff written so far. Still in state of flux. Expect
breakage and major changes.cute-signatures
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:
|
|
@ -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)
|
||||
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;
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue