-
Notifications
You must be signed in to change notification settings - Fork 763
system/nxpkg: Make the storage root configurable and crash-safe. #3642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,30 +27,93 @@ | |
| * Included Files | ||
| ****************************************************************************/ | ||
|
|
||
| #include <nuttx/config.h> | ||
|
|
||
| #include <limits.h> | ||
| #include <stdbool.h> | ||
| #include <stddef.h> | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
|
|
||
| /**************************************************************************** | ||
| * Pre-processor Definitions | ||
| ****************************************************************************/ | ||
|
|
||
| #define PKG_REPO_DIR "/etc/nxpkg" | ||
| #define PKG_REPO_INDEX "/etc/nxpkg/index.json" | ||
| #define PKG_REPO_INSTALLED "/var/lib/nxpkg/installed.json" | ||
| #define PKG_STORE_DIR "/var/lib/nxpkg/pkgs" | ||
| #define PKG_TMP_DIR "/var/cache/nxpkg" | ||
| #define PKG_TMP_PKG_DIR "/var/cache/nxpkg/pkg" | ||
| #define PKG_ROOT_DIR CONFIG_SYSTEM_NXPKG_ROOT | ||
| #define PKG_REPO_DIR PKG_ROOT_DIR | ||
| #define PKG_REPO_INDEX PKG_ROOT_DIR "/index.jsn" | ||
| #define PKG_REPO_SOURCE PKG_ROOT_DIR "/repo.url" | ||
| #define PKG_REPO_INSTALLED PKG_ROOT_DIR "/instpkg.jsn" | ||
| #define PKG_STORE_DIR PKG_ROOT_DIR "/pkgs" | ||
| #define PKG_TMP_DIR PKG_ROOT_DIR "/tmp" | ||
| #define PKG_TMP_PKG_DIR PKG_ROOT_DIR "/tmp/pkg" | ||
|
|
||
| #define PKG_NAME_MAX 63 | ||
| #define PKG_VERSION_MAX 31 | ||
| #define PKG_ARCH_MAX 31 | ||
| #define PKG_COMPAT_MAX 63 | ||
| #define PKG_DESCRIPTION_MAX 127 | ||
| #define PKG_CATEGORY_MAX 31 | ||
| #define PKG_HASH_HEX_LEN 64 | ||
| #define PKG_INDEX_MAX 32 | ||
| /* Each manifest slot is ~1.7KB (dominated by PKG_LAUNCH_ARGS_MAX slots). | ||
| * Keep the catalog bounded so repository-provided metadata cannot cause | ||
| * unbounded memory use. Callers should allocate struct pkg_index_s from | ||
| * the application heap rather than placing it on a small task stack. | ||
| */ | ||
|
|
||
| #define PKG_INDEX_MAX 16 | ||
| #define PKG_INSTALLED_MAX 16 | ||
| #define PKG_INSTALLED_VERSIONS_MAX 8 | ||
| #define PKG_LAUNCH_ARGS_MAX 8 | ||
| #define PKG_LAUNCH_ARG_MAX 127 | ||
|
|
||
| /* Caps against a malicious/compromised HTTP server: without these, an | ||
| * oversized response can exhaust SD-card space (downloads) or force an | ||
| * unbounded single heap allocation sized directly off attacker-controlled | ||
| * content (pkg_store_read_text). Text/metadata files (index.jsn, | ||
| * instpkg.jsn) are always small; artifact downloads cover the largest | ||
| * real payloads seen in practice (a multi-MB WAD, a ~1MB game ELF) with | ||
| * generous headroom. | ||
| */ | ||
|
|
||
| #define PKG_TEXT_MAX_SIZE (256 * 1024) | ||
| #define PKG_DOWNLOAD_MAX_SIZE (32 * 1024 * 1024) | ||
|
|
||
| /* nxpkg is a one-shot CLI, not a daemon, so a lock file older than this | ||
| * cannot belong to a still-running install under normal use (even a full | ||
| * multi-MB artifact over a slow link finishes well within this window) - | ||
| * it can only be left over from a process that was killed or a device | ||
| * that lost power mid-install. Reclaiming it is what makes install/ | ||
| * update/rollback usable again after the crash/power-loss scenarios this | ||
| * target is prone to, instead of failing with EBUSY forever. | ||
| */ | ||
|
|
||
| #define PKG_LOCK_STALE_SECONDS (600) | ||
|
|
||
| static inline void *pkg_malloc(size_t size) | ||
| { | ||
| return malloc(size); | ||
| } | ||
|
|
||
| static inline void *pkg_zalloc(size_t size) | ||
| { | ||
| return calloc(1, size); | ||
| } | ||
|
|
||
| static inline void *pkg_realloc(void *ptr, size_t size) | ||
| { | ||
| return realloc(ptr, size); | ||
| } | ||
|
|
||
| static inline void pkg_free(void *ptr) | ||
| { | ||
| free(ptr); | ||
| } | ||
|
|
||
| static inline FAR char *pkg_path_alloc(void) | ||
| { | ||
| return pkg_malloc(PATH_MAX); | ||
| } | ||
|
Comment on lines
+93
to
+116
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why |
||
|
|
||
| /**************************************************************************** | ||
| * Public Types | ||
|
|
@@ -83,7 +146,12 @@ struct pkg_manifest_s | |
| char compat[PKG_COMPAT_MAX + 1]; | ||
| char artifact[PATH_MAX]; | ||
| char sha256[PKG_HASH_HEX_LEN + 1]; | ||
| char launch_args[PKG_LAUNCH_ARGS_MAX][PKG_LAUNCH_ARG_MAX + 1]; | ||
| char description[PKG_DESCRIPTION_MAX + 1]; | ||
| char category[PKG_CATEGORY_MAX + 1]; | ||
| char icon[PATH_MAX]; | ||
| enum pkg_payload_type_e type; | ||
| size_t launch_argc; | ||
| }; | ||
|
|
||
| struct pkg_index_s | ||
|
|
@@ -116,6 +184,7 @@ struct pkg_installed_db_s | |
|
|
||
| const char *pkg_manifest_type_str(enum pkg_payload_type_e type); | ||
| int pkg_manifest_validate(FAR const struct pkg_manifest_s *manifest); | ||
| bool pkg_validate_path_component(FAR const char *value); | ||
| int pkg_manifest_parse_type(FAR const char *value, | ||
| FAR enum pkg_payload_type_e *type); | ||
|
|
||
|
|
@@ -124,6 +193,7 @@ int pkg_store_ensure_package_root(FAR const char *name); | |
| int pkg_store_ensure_version_dir(FAR const char *name, | ||
| FAR const char *version); | ||
| int pkg_store_format_index_path(FAR char *buffer, size_t size); | ||
| int pkg_store_format_repo_source_path(FAR char *buffer, size_t size); | ||
| int pkg_store_format_installed_path(FAR char *buffer, size_t size); | ||
| int pkg_store_format_package_root(FAR char *buffer, size_t size, | ||
| FAR const char *name); | ||
|
|
@@ -152,6 +222,8 @@ int pkg_store_read_text(FAR const char *path, FAR char **buffer); | |
| int pkg_store_write_text_atomic(FAR const char *path, FAR const char *text); | ||
| int pkg_store_copy_file(FAR const char *src, FAR const char *dest); | ||
| int pkg_store_remove_file(FAR const char *path); | ||
| int pkg_store_remove_version_dir(FAR const char *name, | ||
| FAR const char *version); | ||
|
|
||
| const char *pkg_runtime_arch(void); | ||
| const char *pkg_runtime_compat(void); | ||
|
|
@@ -160,7 +232,11 @@ int pkg_compat_check(FAR const struct pkg_manifest_s *manifest); | |
| int pkg_hash_file_sha256(FAR const char *path, | ||
| FAR char digest[PKG_HASH_HEX_LEN + 1]); | ||
|
|
||
| int pkg_metadata_load_index_path(FAR const char *path, | ||
| FAR struct pkg_index_s *index); | ||
| int pkg_metadata_load_index(FAR struct pkg_index_s *index); | ||
| int pkg_metadata_load_manifest_path(FAR const char *path, | ||
| FAR struct pkg_manifest_s *manifest); | ||
| FAR const struct pkg_manifest_s * | ||
| pkg_metadata_find_latest(FAR const struct pkg_index_s *index, | ||
| FAR const char *name); | ||
|
|
@@ -178,7 +254,20 @@ const char *pkg_txn_state_str(enum pkg_txn_state_e state); | |
| int pkg_txn_write_state(FAR const char *name, enum pkg_txn_state_e state); | ||
| int pkg_txn_clear_state(FAR const char *name); | ||
|
|
||
| bool pkg_source_is_url(FAR const char *source); | ||
| int pkg_resolve_artifact_source(FAR char *buffer, size_t size, | ||
| FAR const struct pkg_manifest_s *manifest); | ||
| int pkg_resolve_icon_source(FAR char *buffer, size_t size, | ||
| FAR const struct pkg_manifest_s *manifest); | ||
| int pkg_acquire_source(FAR const char *source, FAR const char *dest, | ||
| FAR const char *renew_lock_path); | ||
| int pkg_lock_create(FAR const char *path); | ||
| void pkg_reclaim_stale_lock(FAR const char *path); | ||
| int pkg_sync(FAR const char *source); | ||
| int pkg_install(FAR const char *name); | ||
| int pkg_uninstall(FAR const char *name); | ||
| int pkg_rollback(FAR const char *name); | ||
| int pkg_available(FAR FILE *stream); | ||
| int pkg_list(FAR FILE *stream); | ||
|
|
||
| void pkg_error(FAR const char *fmt, ...); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,16 @@ const char *pkg_runtime_arch(void) | |
|
|
||
| const char *pkg_runtime_compat(void) | ||
| { | ||
| #ifdef CONFIG_ARCH_BOARD | ||
| return CONFIG_ARCH_BOARD; | ||
| #elif defined(CONFIG_ARCH_BOARD_CUSTOM_NAME) | ||
| if (CONFIG_ARCH_BOARD_CUSTOM_NAME[0] != '\0') | ||
| { | ||
| return CONFIG_ARCH_BOARD_CUSTOM_NAME; | ||
| } | ||
| #endif | ||
|
Comment on lines
+45
to
+50
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why check this if you'll just return the empty string anyways? |
||
|
|
||
| return ""; | ||
| } | ||
|
|
||
| int pkg_compat_check(FAR const struct pkg_manifest_s *manifest) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,20 +26,28 @@ | |
|
|
||
| #include <stdarg.h> | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include <syslog.h> | ||
|
|
||
| #include "pkg.h" | ||
|
|
||
| /**************************************************************************** | ||
| * Private Functions | ||
| ****************************************************************************/ | ||
|
|
||
| static void pkg_vlog(FAR FILE *stream, FAR const char *level, | ||
| FAR const char *fmt, va_list ap) | ||
| static void pkg_vlog(FAR const char *level, FAR const char *fmt, va_list ap) | ||
| { | ||
| fprintf(stream, "nxpkg: %s: ", level); | ||
| vfprintf(stream, fmt, ap); | ||
| fputc('\n', stream); | ||
| fflush(stream); | ||
| char message[256]; | ||
| int ret; | ||
|
|
||
| ret = vsnprintf(message, sizeof(message), fmt, ap); | ||
| if (ret < 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| syslog(strcmp(level, "error") == 0 ? LOG_ERR : LOG_INFO, | ||
| "nxpkg: %s: %s", level, message); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't add level to the message. There is already a syslog option that allows level to be logged. I would suggest a macro that just compile time prefixes the message with |
||
| } | ||
|
|
||
| /**************************************************************************** | ||
|
|
@@ -51,7 +59,7 @@ void pkg_error(FAR const char *fmt, ...) | |
| va_list ap; | ||
|
|
||
| va_start(ap, fmt); | ||
| pkg_vlog(stderr, "error", fmt, ap); | ||
| pkg_vlog("error", fmt, ap); | ||
| va_end(ap); | ||
| } | ||
|
|
||
|
|
@@ -60,6 +68,6 @@ void pkg_info(FAR const char *fmt, ...) | |
| va_list ap; | ||
|
|
||
| va_start(ap, fmt); | ||
| pkg_vlog(stdout, "info", fmt, ap); | ||
| pkg_vlog("info", fmt, ap); | ||
| va_end(ap); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How slow of a link? Did you test this on something or is this hypothesizing. 10 minutes seems pretty reasonable for now, but I would remove this claim.