diff --git a/TODO b/TODO index f31352e..ee62c66 100644 --- a/TODO +++ b/TODO @@ -8,9 +8,6 @@ - cache .apks on USB stick when using network repo for reboot - Installation of .APK files not in any repository -- Configfiles list in .PKGINFO -- Implement lbu stuff - - Error handling and rollback - Dependency manipulation API: deletion, overwrite, check compatibility diff --git a/src/apk_archive.h b/src/apk_archive.h index b51aee5..ac2387f 100644 --- a/src/apk_archive.h +++ b/src/apk_archive.h @@ -16,29 +16,17 @@ #include "apk_blob.h" #include "apk_io.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; -}; - typedef int (*apk_archive_entry_parser)(void *ctx, - const struct apk_archive_entry *ae, + const struct apk_file_info *ae, struct apk_istream *istream); +int apk_file_get_info(const char *filename, struct apk_file_info *fi); struct apk_istream *apk_gunzip_bstream(struct apk_bstream *); int apk_parse_tar(struct apk_istream *, apk_archive_entry_parser parser, void *ctx); int apk_parse_tar_gz(struct apk_bstream *, apk_archive_entry_parser parser, void *ctx); -int apk_archive_entry_extract(const struct apk_archive_entry *ae, +int apk_archive_entry_extract(const struct apk_file_info *ae, struct apk_istream *is, const char *to); diff --git a/src/apk_database.h b/src/apk_database.h index d687bce..5cf2928 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -25,9 +25,12 @@ struct apk_db_file { struct apk_db_dir *dir; struct apk_package *owner; + csum_t csum; char filename[]; }; +#define APK_DBDIRF_PROTECTED 0x0001 + struct apk_db_dir { apk_hash_node hash_node; @@ -38,6 +41,7 @@ struct apk_db_dir { mode_t mode; uid_t uid; gid_t gid; + unsigned flags; char dirname[]; }; @@ -58,6 +62,7 @@ struct apk_database { unsigned pkg_id, num_repos; struct apk_dependency_array *world; + struct apk_string_array *protected_paths; struct apk_repository repos[APK_MAX_REPOS]; struct { diff --git a/src/apk_defines.h b/src/apk_defines.h index d6557a2..64abe72 100644 --- a/src/apk_defines.h +++ b/src/apk_defines.h @@ -12,6 +12,8 @@ #ifndef APK_DEFINES_H #define APK_DEFINES_H +#include + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define BIT(x) (1 << (x)) @@ -36,12 +38,15 @@ #if 1 #include "md5.h" +typedef unsigned char *csum_p; typedef md5sum_t csum_t; typedef struct md5_ctx csum_ctx_t; +extern csum_t bad_checksum; #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) +#define csum_valid(buf) memcmp(buf, bad_checksum, sizeof(csum_t)) #endif extern int apk_cwd_fd, apk_quiet; @@ -76,6 +81,8 @@ void apk_log(const char *prefix, const char *format, ...); return &(*a)->item[size-1]; \ } +APK_ARRAY(apk_string_array, char *); + #define LIST_END (void *) 0xe01 #define LIST_POISON1 (void *) 0xdeadbeef #define LIST_POISON2 (void *) 0xabbaabba diff --git a/src/apk_io.h b/src/apk_io.h index a0ceac2..0eadec0 100644 --- a/src/apk_io.h +++ b/src/apk_io.h @@ -14,15 +14,28 @@ #include "apk_defines.h" #include "apk_blob.h" +struct apk_file_info { + 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; + csum_t csum; +}; + struct apk_istream { size_t (*read)(void *stream, void *ptr, size_t size); - size_t (*splice)(void *stream, int fd, size_t size); void (*close)(void *stream); }; struct apk_bstream { size_t (*read)(void *stream, void **ptr); - void (*close)(void *stream, csum_t csum); + void (*close)(void *stream, csum_p csum); }; struct apk_istream *apk_istream_from_fd(int fd); diff --git a/src/archive.c b/src/archive.c index 33e3428..6562297 100644 --- a/src/archive.c +++ b/src/archive.c @@ -53,6 +53,8 @@ struct apk_tar_entry_istream { struct apk_istream is; struct apk_istream *tar_is; size_t bytes_left; + csum_ctx_t csum_ctx; + csum_p csum; }; static size_t tar_entry_read(void *stream, void *ptr, size_t size) @@ -63,33 +65,24 @@ static size_t tar_entry_read(void *stream, void *ptr, size_t size) if (size > teis->bytes_left) size = teis->bytes_left; size = teis->tar_is->read(teis->tar_is, ptr, size); - if (size >= 0) - teis->bytes_left -= size; - return size; -} - -static size_t tar_entry_splice(void *stream, int fd, size_t size) -{ - struct apk_tar_entry_istream *teis = - container_of(stream, struct apk_tar_entry_istream, is); - - if (size > teis->bytes_left) - size = teis->bytes_left; - size = teis->tar_is->splice(teis->tar_is, fd, size); - if (size >= 0) + if (size > 0) { teis->bytes_left -= size; + csum_process(&teis->csum_ctx, ptr, size); + if (teis->bytes_left == 0) + csum_finish(&teis->csum_ctx, teis->csum); + } return size; } int apk_parse_tar(struct apk_istream *is, apk_archive_entry_parser parser, void *ctx) { + struct apk_file_info entry; struct apk_tar_entry_istream teis = { .is.read = tar_entry_read, - .is.splice = tar_entry_splice, .tar_is = is, + .csum = entry.csum, }; - struct apk_archive_entry entry; struct tar_header buf; unsigned long offset = 0; int end = 0, r; @@ -107,7 +100,7 @@ int apk_parse_tar(struct apk_istream *is, apk_archive_entry_parser parser, continue; } - entry = (struct apk_archive_entry){ + entry = (struct apk_file_info){ .size = GET_OCTAL(buf.size), .uid = GET_OCTAL(buf.uid), .gid = GET_OCTAL(buf.gid), @@ -160,6 +153,7 @@ int apk_parse_tar(struct apk_istream *is, apk_archive_entry_parser parser, entry.name = strdup(buf.name); /* callback parser function */ + csum_init(&teis.csum_ctx); r = parser(ctx, &entry, &teis.is); if (r != 0) return r; @@ -191,7 +185,7 @@ int apk_parse_tar_gz(struct apk_bstream *bs, apk_archive_entry_parser parser, return apk_parse_tar(apk_gunzip_bstream(bs), parser, ctx); } -int apk_archive_entry_extract(const struct apk_archive_entry *ae, +int apk_archive_entry_extract(const struct apk_file_info *ae, struct apk_istream *is, const char *fn) { @@ -216,7 +210,7 @@ int apk_archive_entry_extract(const struct apk_archive_entry *ae, r = -1; break; } - if (is->splice(is, fd, ae->size) == ae->size) + if (apk_istream_splice(is, fd, ae->size) == ae->size) r = 0; close(fd); } else { diff --git a/src/database.c b/src/database.c index 15757ff..4ed3ece 100644 --- a/src/database.c +++ b/src/database.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -137,6 +138,7 @@ static struct apk_db_dir *apk_db_dir_get(struct apk_database *db, struct apk_db_dir *dir; apk_blob_t bparent; char *cstr; + int i; if (name.len && name.ptr[name.len-1] == '/') name.len--; @@ -154,11 +156,22 @@ static struct apk_db_dir *apk_db_dir_get(struct apk_database *db, if (name.len == 0) dir->parent = NULL; - else if (apk_blob_rsplit(name, '/', &bparent, NULL)) + else if (apk_blob_rsplit(name, '/', &bparent, NULL)) dir->parent = apk_db_dir_get(db, bparent); else dir->parent = apk_db_dir_get(db, APK_BLOB_NULL); + if (dir->parent != NULL) + dir->flags = dir->parent->flags; + + for (i = 0; i < db->protected_paths->num; i++) { + if (db->protected_paths->item[i][0] == '-' && + strcmp(&db->protected_paths->item[i][1], dir->dirname) == 0) + dir->flags &= ~APK_DBDIRF_PROTECTED; + else if (strcmp(db->protected_paths->item[i], dir->dirname) == 0) + dir->flags |= APK_DBDIRF_PROTECTED; + } + return dir; } @@ -295,6 +308,16 @@ static int apk_db_read_fdb(struct apk_database *db, int fd) file_dir_node = &file->dir_files_list.next; file_pkg_node = &file->pkg_files_list.next; break; + case 'C': + if (file == NULL) { + apk_error("FDB checksum entry before file entry"); + return -1; + } + if (apk_hexdump_parse(APK_BLOB_BUF(file->csum), l)) { + apk_error("Not a valid checksum"); + return -1; + } + break; default: apk_error("FDB entry '%c' unsupported", n); return -1; @@ -342,6 +365,12 @@ static int apk_db_write_fdb(struct apk_database *db, int fd) n += snprintf(&buf[n], sizeof(buf)-n, "F%s\n", file->filename); + if (csum_valid(file->csum)) { + n += snprintf(&buf[n], sizeof(buf)-n, "C"); + n += apk_hexdump_format(sizeof(buf)-n, &buf[n], + APK_BLOB_BUF(file->csum)); + n += snprintf(&buf[n], sizeof(buf)-n, "\n"); + } if (write(fd, buf, n) != n) return -1; @@ -418,7 +447,7 @@ int apk_db_create(const char *root) return 0; } -static int apk_db_read_config(struct apk_database *db) +static int apk_db_read_state(struct apk_database *db) { struct apk_istream *is; struct stat st; @@ -466,8 +495,18 @@ static int apk_db_read_config(struct apk_database *db) return 0; } +static int add_protected_path(void *ctx, apk_blob_t blob) +{ + struct apk_database *db = (struct apk_database *) ctx; + + *apk_string_array_add(&db->protected_paths) = apk_blob_cstr(blob); + return 0; +} + int apk_db_open(struct apk_database *db, const char *root) { + apk_blob_t dirs; + 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); @@ -485,7 +524,10 @@ int apk_db_open(struct apk_database *db, const char *root) if (apk_repository != NULL) apk_db_add_repository(db, apk_repository); - return apk_db_read_config(db); + dirs = APK_BLOB_STR("etc:-etc/init.d"); + apk_blob_for_each_segment(dirs, ":", add_protected_path, db); + + return apk_db_read_state(db); } struct write_ctx { @@ -668,7 +710,7 @@ int apk_db_recalculate_and_commit(struct apk_database *db) } static int apk_db_install_archive_entry(void *_ctx, - const struct apk_archive_entry *ae, + const struct apk_file_info *ae, struct apk_istream *is) { struct install_ctx *ctx = (struct install_ctx *) _ctx; @@ -677,6 +719,8 @@ static int apk_db_install_archive_entry(void *_ctx, apk_blob_t name = APK_BLOB_STR(ae->name); struct apk_db_dir *dir; struct apk_db_file *file; + struct apk_file_info fi; + char alt_name[PATH_MAX]; const char *p; int r = 0, type = APK_SCRIPT_INVALID; @@ -740,7 +784,20 @@ static int apk_db_install_archive_entry(void *_ctx, if (strncmp(file->filename, ".keep_", 6) == 0) return 0; - r = apk_archive_entry_extract(ae, is, NULL); + if ((file->dir->flags & APK_DBDIRF_PROTECTED) && + csum_valid(file->csum) && + apk_file_get_info(ae->name, &fi) == 0 && + memcmp(file->csum, fi.csum, sizeof(csum_t)) != 0) { + /* Protected file, which is modified locally. + * Extract to separate place */ + snprintf(alt_name, sizeof(alt_name), + "%s/%s.apk-new", + dir->dirname, file->filename); + r = apk_archive_entry_extract(ae, is, alt_name); + } else { + r = apk_archive_entry_extract(ae, is, NULL); + } + memcpy(file->csum, ae->csum, sizeof(csum_t)); } else { if (name.ptr[name.len-1] == '/') name.len--; diff --git a/src/gunzip.c b/src/gunzip.c index f488fa4..cfee860 100644 --- a/src/gunzip.c +++ b/src/gunzip.c @@ -77,7 +77,6 @@ struct apk_istream *apk_gunzip_bstream(struct apk_bstream *bs) *gis = (struct apk_gzip_istream) { .is.read = gz_read, - .is.splice = apk_istream_splice, .is.close = gz_close, .bs = bs, .z_err = 0, diff --git a/src/io.c b/src/io.c index 418c7f8..2bb8afc 100644 --- a/src/io.c +++ b/src/io.c @@ -47,22 +47,6 @@ static size_t fd_read(void *stream, void *ptr, size_t size) return i; } -static size_t fd_splice(void *stream, int fd, size_t size) -{ - struct apk_fd_istream *fis = - container_of(stream, struct apk_fd_istream, is); - size_t i = 0, r; - - while (i != size) { - r = splice(fis->fd, NULL, fd, NULL, size - i, SPLICE_F_MOVE); - if (r == -1) - return i; - i += r; - } - - return i; -} - static void fd_close(void *stream) { struct apk_fd_istream *fis = @@ -82,7 +66,6 @@ struct apk_istream *apk_istream_from_fd(int fd) *fis = (struct apk_fd_istream) { .is.read = fd_read, - .is.splice = fd_splice, .is.close = fd_close, .fd = fd, }; @@ -322,3 +305,31 @@ apk_blob_t apk_blob_from_istream(struct apk_istream *is, size_t size) return APK_BLOB_PTR_LEN(ptr, rsize); } +int apk_file_get_info(const char *filename, struct apk_file_info *fi) +{ + struct stat st; + struct apk_bstream *bs; + int fd; + + if (stat(filename, &st) != 0) + return -1; + + *fi = (struct apk_file_info) { + .size = st.st_size, + .uid = st.st_uid, + .gid = st.st_gid, + .mode = st.st_mode, + .mtime = st.st_mtime, + .device = st.st_dev, + }; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + + bs = apk_bstream_from_fd(fd); + if (bs != NULL) + bs->close(bs, fi->csum); + + return 0; +} diff --git a/src/md5.c b/src/md5.c index e165724..82e670b 100644 --- a/src/md5.c +++ b/src/md5.c @@ -58,6 +58,8 @@ #include "md5.h" +md5sum_t bad_checksum = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + /* Handle endian-ness */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define SWAP(n) (n) diff --git a/src/package.c b/src/package.c index 165656d..3430d19 100644 --- a/src/package.c +++ b/src/package.c @@ -255,7 +255,7 @@ static int read_info_line(void *ctx, apk_blob_t line) return 0; } -static int read_info_entry(void *ctx, const struct apk_archive_entry *ae, +static int read_info_entry(void *ctx, const struct apk_file_info *ae, struct apk_istream *is) { static struct {