diff --git a/system/nxpkg/pkg.h b/system/nxpkg/pkg.h index 39a4bf0c21d..032c0dda3d2 100644 --- a/system/nxpkg/pkg.h +++ b/system/nxpkg/pkg.h @@ -27,30 +27,66 @@ * Included Files ****************************************************************************/ +#include + #include #include #include #include +#include /**************************************************************************** * 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 +/* Keep repository metadata memory bounded. */ + +#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 + +/* Bound metadata allocations and artifact downloads. */ + +#define PKG_TEXT_MAX_SIZE (256 * 1024) +#define PKG_DOWNLOAD_MAX_SIZE (32 * 1024 * 1024) + +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); +} /**************************************************************************** * Public Types @@ -83,7 +119,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 +157,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 +166,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 +195,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 +205,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 +227,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); +int pkg_lock_create(FAR const char *path); +void pkg_lock_remove(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, ...); diff --git a/system/nxpkg/pkg_compat.c b/system/nxpkg/pkg_compat.c index 301fb07aacf..817a3b4bf42 100644 --- a/system/nxpkg/pkg_compat.c +++ b/system/nxpkg/pkg_compat.c @@ -40,7 +40,13 @@ 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) + return CONFIG_ARCH_BOARD_CUSTOM_NAME; +#else + return ""; +#endif } int pkg_compat_check(FAR const struct pkg_manifest_s *manifest) diff --git a/system/nxpkg/pkg_install.c b/system/nxpkg/pkg_install.c index c4098200191..b9b0955978d 100644 --- a/system/nxpkg/pkg_install.c +++ b/system/nxpkg/pkg_install.c @@ -433,7 +433,7 @@ int pkg_install(FAR const char *name) pkg_txn_clear_state(name); if (lock[0] != '\0') { - pkg_store_remove_file(lock); + pkg_lock_remove(lock); } pkg_info("installed %s version %s", manifest->name, manifest->version); @@ -451,7 +451,7 @@ int pkg_install(FAR const char *name) pkg_txn_clear_state(name); if (lock[0] != '\0') { - pkg_store_remove_file(lock); + pkg_lock_remove(lock); } free(index); diff --git a/system/nxpkg/pkg_log.c b/system/nxpkg/pkg_log.c index bed06b13ffb..375b8880e8b 100644 --- a/system/nxpkg/pkg_log.c +++ b/system/nxpkg/pkg_log.c @@ -26,6 +26,7 @@ #include #include +#include #include "pkg.h" @@ -33,13 +34,18 @@ * Private Functions ****************************************************************************/ -static void pkg_vlog(FAR FILE *stream, FAR const char *level, - FAR const char *fmt, va_list ap) +static void pkg_vlog(int priority, 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(priority, "nxpkg: %s", message); } /**************************************************************************** @@ -51,7 +57,7 @@ void pkg_error(FAR const char *fmt, ...) va_list ap; va_start(ap, fmt); - pkg_vlog(stderr, "error", fmt, ap); + pkg_vlog(LOG_ERR, fmt, ap); va_end(ap); } @@ -60,6 +66,6 @@ void pkg_info(FAR const char *fmt, ...) va_list ap; va_start(ap, fmt); - pkg_vlog(stdout, "info", fmt, ap); + pkg_vlog(LOG_INFO, fmt, ap); va_end(ap); } diff --git a/system/nxpkg/pkg_store.c b/system/nxpkg/pkg_store.c index 0b23b054ca3..79ad7941b9d 100644 --- a/system/nxpkg/pkg_store.c +++ b/system/nxpkg/pkg_store.c @@ -24,8 +24,10 @@ * Included Files ****************************************************************************/ +#include #include #include +#include #include #include #include @@ -35,10 +37,60 @@ #include "pkg.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PKG_LOCK_OWNER "owner" + /**************************************************************************** * Private Functions ****************************************************************************/ +static int pkg_lock_read_owner(FAR const char *path, FAR pid_t *owner) +{ + char owner_path[PATH_MAX]; + char record[32]; + char extra; + long parsed_owner; + ssize_t nread; + int fd; + int ret; + + ret = snprintf(owner_path, sizeof(owner_path), "%s/%s", path, + PKG_LOCK_OWNER); + if (ret < 0 || (size_t)ret >= sizeof(owner_path)) + { + return ret < 0 ? ret : -ENAMETOOLONG; + } + + fd = open(owner_path, O_RDONLY); + if (fd < 0) + { + return -errno; + } + + nread = read(fd, record, sizeof(record) - 1); + if (nread < 0) + { + ret = -errno; + close(fd); + return ret; + } + + close(fd); + record[nread] = '\0'; + if (sscanf(record, "%ld%c", &parsed_owner, &extra) != 1 || + parsed_owner <= 0 || + (long)(pid_t)parsed_owner != parsed_owner) + { + return -EINVAL; + } + + *owner = (pid_t)parsed_owner; + return 0; +} + static int pkg_store_format(FAR char *buffer, size_t size, FAR const char *fmt, FAR const char *name, @@ -145,6 +197,11 @@ static int pkg_store_write_all(int fd, FAR const char *buffer, size_t length) return -errno; } + if (ret == 0) + { + return -EIO; + } + offset += (size_t)ret; } @@ -188,6 +245,122 @@ int pkg_store_prepare_layout(void) return pkg_store_mkdirs(PKG_TMP_PKG_DIR); } +int pkg_lock_create(FAR const char *path) +{ + char owner_path[PATH_MAX]; + char record[32]; + int fd; + int ret; + + if (mkdir(path, 0755) < 0) + { + return -errno; + } + + ret = snprintf(owner_path, sizeof(owner_path), "%s/%s", path, + PKG_LOCK_OWNER); + if (ret < 0 || (size_t)ret >= sizeof(owner_path)) + { + ret = ret < 0 ? ret : -ENAMETOOLONG; + rmdir(path); + return ret; + } + + fd = open(owner_path, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd < 0) + { + ret = -errno; + rmdir(path); + return ret; + } + + ret = snprintf(record, sizeof(record), "%ld", (long)getpid()); + if (ret < 0 || (size_t)ret >= sizeof(record)) + { + ret = ret < 0 ? ret : -EOVERFLOW; + } + else + { + ret = pkg_store_write_all(fd, record, (size_t)ret); + } + + if (ret == 0 && fsync(fd) < 0) + { + ret = -errno; + } + + if (close(fd) < 0 && ret == 0) + { + ret = -errno; + } + + if (ret < 0) + { + unlink(owner_path); + rmdir(path); + } + + return ret; +} + +void pkg_lock_remove(FAR const char *path) +{ + char owner_path[PATH_MAX]; + int ret; + + ret = snprintf(owner_path, sizeof(owner_path), "%s/%s", path, + PKG_LOCK_OWNER); + if (ret >= 0 && (size_t)ret < sizeof(owner_path)) + { + unlink(owner_path); + } + + rmdir(path); +} + +void pkg_reclaim_stale_lock(FAR const char *path) +{ + pid_t owner; + int ret; + + ret = pkg_lock_read_owner(path, &owner); + if (ret == -ENOENT || ret == -EINVAL) + { + /* The creator may not have written the owner marker yet. */ + + usleep(20 * 1000); + ret = pkg_lock_read_owner(path, &owner); + if (ret == -ENOENT || ret == -EINVAL) + { + pkg_lock_remove(path); + return; + } + } + + if (ret < 0) + { + return; + } + + if (owner == getpid()) + { + pkg_lock_remove(path); + return; + } + + if (kill(owner, 0) == 0 || errno == EPERM) + { + return; + } + + if (errno == ESRCH) + { + pkg_error("reclaiming lock from exited task %ld '%s'", + (long)owner, path); + pkg_lock_remove(path); + } +} + int pkg_store_ensure_package_root(FAR const char *name) { char path[PATH_MAX]; @@ -228,6 +401,11 @@ int pkg_store_format_index_path(FAR char *buffer, size_t size) return pkg_store_format(buffer, size, "%s", PKG_REPO_INDEX, ""); } +int pkg_store_format_repo_source_path(FAR char *buffer, size_t size) +{ + return pkg_store_format(buffer, size, "%s", PKG_REPO_SOURCE, ""); +} + int pkg_store_format_installed_path(FAR char *buffer, size_t size) { return pkg_store_format(buffer, size, "%s", PKG_REPO_INSTALLED, ""); @@ -266,21 +444,40 @@ int pkg_store_format_previous_path(FAR char *buffer, size_t size, int pkg_store_format_txn_path(FAR char *buffer, size_t size, FAR const char *name) { - return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/.txn", name, ""); + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/txn.tx", name, + ""); } int pkg_store_format_lock_path(FAR char *buffer, size_t size, FAR const char *name) { - return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/.lock", name, ""); + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/lock.lk", name, + ""); } int pkg_store_format_download_path(FAR char *buffer, size_t size, FAR const char *name, FAR const char *version) { - return pkg_store_format(buffer, size, PKG_TMP_PKG_DIR "/%s-%s.npkg", name, - version); + int ret; + + UNUSED(name); + UNUSED(version); + + /* Use the PID for a unique FAT 8.3-compatible staging name. */ + + ret = snprintf(buffer, size, PKG_TMP_PKG_DIR "/dl%d.pkg", (int)getpid()); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= size) + { + return -ENAMETOOLONG; + } + + return 0; } int pkg_store_format_payload_path(FAR char *buffer, size_t size, @@ -311,16 +508,18 @@ int pkg_store_format_manifest_path(FAR char *buffer, size_t size, FAR const char *name, FAR const char *version) { - return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/%s/manifest.json", + return pkg_store_format(buffer, size, PKG_STORE_DIR "/%s/%s/manifest.jsn", name, version); } int pkg_store_read_text(FAR const char *path, FAR char **buffer) { - FAR FILE *stream; FAR char *data; - long length; + struct stat st; + size_t length; size_t nread; + size_t total; + int fd; if (buffer == NULL) { @@ -329,70 +528,161 @@ int pkg_store_read_text(FAR const char *path, FAR char **buffer) *buffer = NULL; - stream = fopen(path, "rb"); - if (stream == NULL) + fd = open(path, O_RDONLY); + if (fd < 0) { return errno == ENOENT ? -ENOENT : -errno; } - if (fseek(stream, 0, SEEK_END) < 0) + if (fstat(fd, &st) < 0) { - fclose(stream); + close(fd); return -errno; } - length = ftell(stream); - if (length < 0) + if (!S_ISREG(st.st_mode)) { - fclose(stream); - return -errno; + close(fd); + return -EINVAL; } - if (fseek(stream, 0, SEEK_SET) < 0) + /* Validate the size before allocating length plus its terminator. */ + + if (st.st_size < 0 || st.st_size > (off_t)PKG_TEXT_MAX_SIZE) { - fclose(stream); - return -errno; + close(fd); + return -EFBIG; } - data = malloc((size_t)length + 1); + length = (size_t)st.st_size; + data = pkg_malloc((size_t)length + 1); if (data == NULL) { - fclose(stream); + close(fd); return -ENOMEM; } - nread = fread(data, 1, (size_t)length, stream); - if (nread != (size_t)length) + total = 0; + while (total < length) { - int err = ferror(stream); + ssize_t ret; - fclose(stream); - free(data); - return err ? -EIO : -EINVAL; + ret = read(fd, data + total, length - total); + if (ret < 0) + { + if (errno == EINTR) + { + continue; + } + + close(fd); + pkg_free(data); + return -errno; + } + + if (ret == 0) + { + break; + } + + total += (size_t)ret; } - fclose(stream); + nread = total; + close(fd); + + if (nread != length) + { + pkg_free(data); + return -EINVAL; + } data[length] = '\0'; *buffer = data; return 0; } +#ifndef CONFIG_PSEUDOFS_FILE +/**************************************************************************** + * Name: pkg_store_make_tmp_path + * + * Description: + * Build a FAT short-name-compatible staging path. + * + ****************************************************************************/ + +static int pkg_store_make_tmp_path(FAR char *tmp, size_t size, + FAR const char *path) +{ + FAR char *dot; + FAR char *slash; + int ret; + + ret = snprintf(tmp, size, "%s", path); + if (ret < 0) + { + return ret; + } + + if ((size_t)ret >= size) + { + return -ENAMETOOLONG; + } + + slash = strrchr(tmp, '/'); + dot = strrchr(slash != NULL ? slash : tmp, '.'); + if (dot != NULL) + { + *dot = '\0'; + } + + if (strlcat(tmp, ".tm", size) >= size) + { + return -ENAMETOOLONG; + } + + return 0; +} +#endif + int pkg_store_write_text_atomic(FAR const char *path, FAR const char *text) { - char tmp[PATH_MAX]; +#ifdef CONFIG_PSEUDOFS_FILE int fd; int ret; - ret = snprintf(tmp, sizeof(tmp), "%s.tmp", path); + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + return -errno; + } + + ret = pkg_store_write_all(fd, text, strlen(text)); + if (ret < 0) + { + close(fd); + unlink(path); + return ret; + } + + ret = close(fd); if (ret < 0) { + ret = -errno; + unlink(path); return ret; } - if ((size_t)ret >= sizeof(tmp)) + return 0; +#else + char tmp[PATH_MAX]; + int fd; + int ret; + + ret = pkg_store_make_tmp_path(tmp, sizeof(tmp), path); + if (ret < 0) { - return -ENAMETOOLONG; + return ret; } fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644); @@ -409,19 +699,35 @@ int pkg_store_write_text_atomic(FAR const char *path, FAR const char *text) return ret; } - if (close(fd) < 0) + /* Commit the temporary file before the atomic rename. */ + + ret = fsync(fd); + if (ret < 0) + { + ret = -errno; + close(fd); + unlink(tmp); + return ret; + } + + ret = close(fd); + if (ret < 0) { + ret = -errno; unlink(tmp); - return -errno; + return ret; } - if (rename(tmp, path) < 0) + ret = rename(tmp, path); + if (ret < 0) { + ret = -errno; unlink(tmp); - return -errno; + return ret; } return 0; +#endif } int pkg_store_copy_file(FAR const char *src, FAR const char *dest) @@ -430,6 +736,20 @@ int pkg_store_copy_file(FAR const char *src, FAR const char *dest) int outfd; int ret; char buffer[512]; +#ifndef CONFIG_PSEUDOFS_FILE + char tmp[PATH_MAX]; + FAR const char *outpath; + + ret = pkg_store_make_tmp_path(tmp, sizeof(tmp), dest); + if (ret < 0) + { + return ret; + } + + outpath = tmp; +#else + FAR const char *outpath = dest; +#endif infd = open(src, O_RDONLY); if (infd < 0) @@ -437,7 +757,7 @@ int pkg_store_copy_file(FAR const char *src, FAR const char *dest) return -errno; } - outfd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0644); + outfd = open(outpath, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (outfd < 0) { ret = -errno; @@ -475,18 +795,41 @@ int pkg_store_copy_file(FAR const char *src, FAR const char *dest) close(infd); - if (close(outfd) < 0) +#ifndef CONFIG_PSEUDOFS_FILE + /* Commit the payload before the atomic rename. */ + + if (fsync(outfd) < 0) { - unlink(dest); - return -errno; + ret = -errno; + close(outfd); + unlink(outpath); + return ret; + } +#endif + + ret = close(outfd); + if (ret < 0) + { + ret = -errno; + unlink(outpath); + return ret; + } + +#ifndef CONFIG_PSEUDOFS_FILE + if (rename(outpath, dest) < 0) + { + ret = -errno; + unlink(outpath); + return ret; } +#endif return 0; errout: close(infd); close(outfd); - unlink(dest); + unlink(outpath); return ret; } @@ -499,3 +842,51 @@ int pkg_store_remove_file(FAR const char *path) return 0; } + +int pkg_store_remove_version_dir(FAR const char *name, + FAR const char *version) +{ + char path[PATH_MAX]; + char entry_path[PATH_MAX]; + FAR DIR *dir; + FAR struct dirent *ent; + int ret; + + /* Remove every file in a staged or installed version directory. */ + + ret = pkg_store_format_version_path(path, sizeof(path), name, version); + if (ret < 0) + { + return ret; + } + + dir = opendir(path); + if (dir == NULL) + { + return errno == ENOENT ? 0 : -errno; + } + + while ((ent = readdir(dir)) != NULL) + { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) + { + continue; + } + + ret = snprintf(entry_path, sizeof(entry_path), "%s/%s", path, + ent->d_name); + if (ret > 0 && (size_t)ret < sizeof(entry_path)) + { + unlink(entry_path); + } + } + + closedir(dir); + + if (rmdir(path) < 0) + { + return errno == ENOENT ? 0 : -errno; + } + + return 0; +}