diff --git a/src/apk_adb.c b/src/apk_adb.c index 02e6cda..8a887cf 100644 --- a/src/apk_adb.c +++ b/src/apk_adb.c @@ -448,7 +448,7 @@ const struct adb_object_schema schema_file = { ADB_FIELD(ADBI_FI_SIZE, "size", scalar_int), ADB_FIELD(ADBI_FI_MTIME, "mtime", scalar_int), ADB_FIELD(ADBI_FI_HASHES, "hash", scalar_hexblob), - ADB_FIELD(ADBI_FI_TARGET, "target", scalar_string), + ADB_FIELD(ADBI_FI_TARGET, "target", scalar_hexblob), }, }; diff --git a/src/app_extract.c b/src/app_extract.c index a8ada87..f9e54b9 100644 --- a/src/app_extract.c +++ b/src/app_extract.c @@ -169,25 +169,62 @@ static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istrea struct adb_obj acl; struct apk_digest_ctx dctx; struct apk_digest d; + apk_blob_t target; int r; - apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES)); - if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA; apk_extract_acl(&fi, adb_ro_obj(&ctx->file, ADBI_FI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac)); - fi.mode |= S_IFREG; - apk_digest_ctx_init(&dctx, fi.digest.alg); - if (ctx->is_uvol) { - r = apk_extract_volume(ac, &fi, is, &dctx); - } else { - r = apk_archive_entry_extract( - ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx, + target = adb_ro_blob(&ctx->file, ADBI_FI_TARGET); + if (!APK_BLOB_IS_NULL(target)) { + char *target_path; + uint16_t mode; + + if (target.len < 2) return -APKE_ADB_SCHEMA; + mode = *(uint16_t*)target.ptr; + target.ptr += 2; + target.len -= 2; + switch (mode) { + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + if (target.len != sizeof(uint64_t)) return -APKE_ADB_SCHEMA; + struct unaligned64 { + uint64_t value; + } __attribute__((packed)); + fi.device = ((struct unaligned64 *)target.ptr)->value; + break; + case S_IFLNK: + target_path = alloca(target.len + 1); + memcpy(target_path, target.ptr, target.len); + target_path[target.len] = 0; + fi.link_target = target_path; + break; + default: + return -APKE_ADB_SCHEMA; + } + fi.mode |= mode; + return apk_archive_entry_extract( + ctx->root_fd, &fi, 0, 0, is, 0, 0, 0, ctx->extract_flags, out); + } else { + apk_digest_from_blob(&fi.digest, adb_ro_blob(&ctx->file, ADBI_FI_HASHES)); + if (fi.digest.alg == APK_DIGEST_NONE) return -APKE_ADB_SCHEMA; + + fi.mode |= S_IFREG; + apk_digest_ctx_init(&dctx, fi.digest.alg); + if (ctx->is_uvol) { + r = apk_extract_volume(ac, &fi, is, &dctx); + } else { + r = apk_archive_entry_extract( + ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx, + ctx->extract_flags, out); + } + apk_digest_ctx_final(&dctx, &d); + apk_digest_ctx_free(&dctx); + if (r != 0) return r; + if (apk_digest_cmp(&fi.digest, &d) != 0) return -APKE_FILE_INTEGRITY; } - apk_digest_ctx_final(&dctx, &d); - apk_digest_ctx_free(&dctx); - if (r != 0) return r; - if (apk_digest_cmp(&fi.digest, &d) != 0) return -APKE_FILE_INTEGRITY; + return 0; } diff --git a/src/app_mkpkg.c b/src/app_mkpkg.c index 633b11d..3191639 100644 --- a/src/app_mkpkg.c +++ b/src/app_mkpkg.c @@ -119,40 +119,71 @@ static int mkpkg_process_dirent(void *pctx, int dirfd, const char *entry) struct apk_id_cache *idc = apk_ctx_get_id_cache(ac); struct apk_file_info fi; struct adb_obj fio, acl; + apk_blob_t target = APK_BLOB_NULL; + union { + uint16_t mode; + struct { + uint16_t mode; + uint64_t dev; + } __attribute__((packed)) dev; + struct { + uint16_t mode; + char target[1022]; + } symlink; + } ft; int r; r = apk_fileinfo_get(dirfd, entry, APK_FI_NOFOLLOW | APK_FI_DIGEST(APK_DIGEST_SHA256), &fi, NULL); if (r) return r; switch (fi.mode & S_IFMT) { + case S_IFREG: + ctx->installed_size += (fi.size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE-1); + break; + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + ft.dev.mode = fi.mode & S_IFMT; + ft.dev.dev = fi.device; + target = APK_BLOB_STRUCT(ft.dev); + break; + case S_IFLNK: + ft.symlink.mode = fi.mode & S_IFMT; + r = readlinkat(dirfd, entry, ft.symlink.target, sizeof ft.symlink.target); + if (r < 0) return r; + target = APK_BLOB_PTR_LEN((void*)&ft.symlink, sizeof(ft.symlink.mode) + r); + r = 0; + break; case S_IFDIR: apk_pathbuilder_push(&ctx->pb, entry); r = mkpkg_process_directory(ctx, openat(dirfd, entry, O_RDONLY), &fi); apk_pathbuilder_pop(&ctx->pb); - break; - case S_IFREG: - adb_wo_alloca(&fio, &schema_file, &ctx->db); - adb_wo_alloca(&acl, &schema_acl, &ctx->db); - adb_wo_blob(&fio, ADBI_FI_NAME, APK_BLOB_STR(entry)); - adb_wo_blob(&fio, ADBI_FI_HASHES, APK_DIGEST_BLOB(fi.digest)); - adb_wo_int(&fio, ADBI_FI_MTIME, fi.mtime); - adb_wo_int(&fio, ADBI_FI_SIZE, fi.size); - ctx->installed_size += (fi.size + BLOCK_SIZE - 1) & ~(BLOCK_SIZE-1); - - adb_wo_int(&acl, ADBI_ACL_MODE, fi.mode & 07777); - adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, fi.uid)); - adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, fi.gid)); - adb_wo_obj(&fio, ADBI_FI_ACL, &acl); - - adb_wa_append_obj(ctx->files, &fio); - break; + return r; default: apk_pathbuilder_push(&ctx->pb, entry); apk_out(out, "%s: special file ignored", apk_pathbuilder_cstr(&ctx->pb), entry); apk_pathbuilder_pop(&ctx->pb); - break; + return 0; } + + adb_wo_alloca(&fio, &schema_file, &ctx->db); + adb_wo_alloca(&acl, &schema_acl, &ctx->db); + adb_wo_blob(&fio, ADBI_FI_NAME, APK_BLOB_STR(entry)); + if (APK_BLOB_IS_NULL(target)) + adb_wo_blob(&fio, ADBI_FI_HASHES, APK_DIGEST_BLOB(fi.digest)); + else + adb_wo_blob(&fio, ADBI_FI_TARGET, target); + adb_wo_int(&fio, ADBI_FI_MTIME, fi.mtime); + adb_wo_int(&fio, ADBI_FI_SIZE, fi.size); + + adb_wo_int(&acl, ADBI_ACL_MODE, fi.mode & 07777); + adb_wo_blob(&acl, ADBI_ACL_USER, apk_id_cache_resolve_user(idc, fi.uid)); + adb_wo_blob(&acl, ADBI_ACL_GROUP, apk_id_cache_resolve_group(idc, fi.gid)); + adb_wo_obj(&fio, ADBI_FI_ACL, &acl); + + adb_wa_append_obj(ctx->files, &fio); + return r; } diff --git a/src/io.c b/src/io.c index ece34e4..5a426cf 100644 --- a/src/io.c +++ b/src/io.c @@ -724,7 +724,7 @@ int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags, .gid = st.st_gid, .mode = st.st_mode, .mtime = st.st_mtime, - .device = st.st_dev, + .device = st.st_rdev, }; if (xattr_hash_alg != APK_DIGEST_NONE) {