diff options
-rw-r--r-- | include/conf.h | 1 | ||||
-rw-r--r-- | include/error_handler.h | 34 | ||||
-rw-r--r-- | include/install.h | 2 | ||||
-rw-r--r-- | include/spm.h | 1 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/config_global.c | 6 | ||||
-rw-r--r-- | lib/error_handler.c | 27 | ||||
-rw-r--r-- | lib/fs.c | 2 | ||||
-rw-r--r-- | lib/install.c | 54 | ||||
-rw-r--r-- | lib/purge.c | 6 | ||||
-rw-r--r-- | src/spm.c | 12 | ||||
-rw-r--r-- | tests/test_error_handler_spm_strerror.c | 27 |
12 files changed, 156 insertions, 17 deletions
diff --git a/include/conf.h b/include/conf.h index a5a4d36..7952eed 100644 --- a/include/conf.h +++ b/include/conf.h @@ -16,6 +16,7 @@ typedef struct { } ConfigItem; typedef struct { + char *rootrec; char *rootdir; char *bindir; char *includedir; diff --git a/include/error_handler.h b/include/error_handler.h new file mode 100644 index 0000000..a731d28 --- /dev/null +++ b/include/error_handler.h @@ -0,0 +1,34 @@ +#ifndef SPM_ERROR_HANDLER_H +#define SPM_ERROR_HANDLER_H + +#define _SPM_ERR_BASE 0x8000 // SPM errors begin at 32768 +#define _SPM_ERR_MASK 0x7FFF // Support up to 32768 error strings (zero index) +#define _SPM_ERR(X) _SPM_ERR_BASE + X // Create an error code +#define SPM_ERR_CONFIRM(X) (X >= 0x8000) // Is X a SPM error code? (no=0, yes=!0) +#define SPM_ERR_INDEX(X) (_SPM_ERR_MASK & X) // get index of error string + +#define SPM_ERR_SUCCESS _SPM_ERR(0) // no error +#define SPM_ERR_ROOT_NO_RECORD _SPM_ERR(1) // "root" has no root record +#define SPM_ERR_ROOT_UNSAFE _SPM_ERR(2) // "root" at root, "/" +#define SPM_ERR_PKG_NOT_FOUND _SPM_ERR(3) // package not found +#define SPM_ERR_PKG_INVALID _SPM_ERR(4) // invalid package (wrong structure, missing data, etc) +#define SPM_ERR_PKG_CHECKSUM _SPM_ERR(5) // bad checksum +#define SPM_ERR_PKG_FETCH _SPM_ERR(6) // failed to download package + +extern int spmerrno; + +static const char *SPM_ERR_STRING[] = { + "Success", + "No root record", + "Dangerous root path", + "Package not found", + "Invalid package", + "Bad package checksum", + "Failed to fetch package", + NULL, +}; + +char *spm_strerror(int code); +void spm_perror(const char *msg); + +#endif //SPM_ERROR_HANDLER_H diff --git a/include/install.h b/include/install.h index fb08661..bc1fe19 100644 --- a/include/install.h +++ b/include/install.h @@ -4,6 +4,8 @@ #ifndef SPM_INSTALL_H #define SPM_INSTALL_H +int spm_hierarchy_is_root(SPM_Hierarchy *fs); +int spm_hierarchy_make_root(SPM_Hierarchy *fs); void spm_install_show_package(ManifestPackage *package); int spm_install(SPM_Hierarchy *fs, const char *tmpdir, const char *_package); int spm_install_package_record(SPM_Hierarchy *fs, char *tmpdir, char *package_name); diff --git a/include/spm.h b/include/spm.h index 5931a94..b96554a 100644 --- a/include/spm.h +++ b/include/spm.h @@ -28,6 +28,7 @@ #endif #include "compat.h" +#include "error_handler.h" #include "package.h" #include "str.h" #include "strlist.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fd5c905..2185ef4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -30,6 +30,7 @@ set(libspm_src user_input.c metadata.c purge.c + error_handler.c ) add_library(libspm_obj OBJECT ${libspm_src}) diff --git a/lib/config_global.c b/lib/config_global.c index f63566b..e97cddb 100644 --- a/lib/config_global.c +++ b/lib/config_global.c @@ -153,7 +153,7 @@ void check_runtime_environment(void) { } /** - * + * Define the structure of a SPM root hierarchy * @param basepath * @return */ @@ -170,12 +170,13 @@ SPM_Hierarchy *spm_hierarchy_init(char *basepath) { fs->tmpdir = join((char *[]) {fs->rootdir, "tmp", NULL}, DIRSEPS); fs->dbdir = join((char *[]) {fs->localstatedir, "db", NULL}, DIRSEPS); fs->dbrecdir = join((char *[]) {fs->dbdir, "records", NULL}, DIRSEPS); + fs->rootrec = join((char *[]) {".spm_root", NULL}, DIRSEPS); return fs; } /** - * + * Free SPM_Hierarchy structure * @param fs */ void spm_hierarchy_free(SPM_Hierarchy *fs) { @@ -190,6 +191,7 @@ void spm_hierarchy_free(SPM_Hierarchy *fs) { free(fs->tmpdir); free(fs->dbdir); free(fs->dbrecdir); + free(fs->rootrec); free(fs); } diff --git a/lib/error_handler.c b/lib/error_handler.c new file mode 100644 index 0000000..baf56b4 --- /dev/null +++ b/lib/error_handler.c @@ -0,0 +1,27 @@ +#include "spm.h" + +int spmerrno = 0; +static char spmerrbuf[255]; + +/** + * + * @param code + * @return + */ +char *spm_strerror(int code) { + char *buf = spmerrbuf; + int is_spm_error = SPM_ERR_CONFIRM(code); + + memset(buf, '\0', sizeof(spmerrbuf)); + if (is_spm_error == 0) { + strcpy(buf, strerror(code)); + } else { + strcpy(buf, SPM_ERR_STRING[SPM_ERR_INDEX(code)]); + } + return buf; +} + +void spm_perror(const char *msg) { + fprintf(stderr, "%s: %s\n", msg ? msg : "", spm_strerror(spmerrno)); +} + @@ -624,7 +624,6 @@ char *spm_mkdtemp(const char *base, const char *name, const char *extended_path) path_stat = stat(path, &st); if (path_stat < 0) { if ((fp = fopen(path, "w+")) == NULL) { - perror(path); return -1; } fclose(fp); @@ -632,7 +631,6 @@ char *spm_mkdtemp(const char *base, const char *name, const char *extended_path) if ((S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) > 0) { if (utime(path, NULL) < 0) { // failed to set file time - perror(path); return -1; } // updated file time diff --git a/lib/install.c b/lib/install.c index c6e12ca..c5c2c4c 100644 --- a/lib/install.c +++ b/lib/install.c @@ -4,6 +4,37 @@ #include <url.h> #include "spm.h" +/** + * Check for the existence of `$ROOT/var/.spm_root` + * @param path + * @return yes=1, no=0 + */ +int spm_hierarchy_is_root(SPM_Hierarchy *fs) { + if (exists(fs->rootrec) != 0) { + return 0; + } + return 1; +} + +/** + * Initialize (not populate) a spm root directory. + * A "root record" is dropped into $ROOT/var + * @param fs `SPM_Hierarchy` structure + * @return success=0, error=-1 + */ +int spm_hierarchy_make_root(SPM_Hierarchy *fs) { + // Create the root directory if it does not exist + if (mkdirs(fs->rootdir, 0755) != 0) { + return -1; + } + + if (touch(fs->rootrec) < 0) { + return -1; + } + + return 0; +} + void spm_install_show_package(ManifestPackage *package) { if (package == NULL) { fprintf(stderr, "ERROR: package was NULL\n"); @@ -27,17 +58,6 @@ int spm_install(SPM_Hierarchy *fs, const char *tmpdir, const char *_package) { return -1; } - if (exists(fs->rootdir) != 0) { - if (SPM_GLOBAL.verbose) { - printf("Creating destination root: %s\n", fs->rootdir); - } - if (mkdirs(fs->rootdir, 0755) != 0) { - fprintf(SYSERROR); - free(package); - return -2; - } - } - if (SPM_GLOBAL.verbose) { printf("Extracting archive: %s\n", package); } @@ -192,6 +212,11 @@ int spm_do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) { printf("Installation root: %s\n", fs->rootdir); } + if (spm_hierarchy_make_root(fs) < 0) { + spmerrno = SPM_ERR_ROOT_NO_RECORD; + return -1; + } + // Produce a dependency tree from requested package(s) for (size_t i = 0; i < strlist_count(packages); i++) { char *item = strlist_item(packages, i); @@ -272,6 +297,12 @@ int spm_do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) { manifest_free(tmp_manifest); } + if (spm_hierarchy_is_root(fs) == 0) { + if (SPM_GLOBAL.verbose) { + printf("Creating destination root: %s\n", fs->rootdir); + } + } + printf("Installing package(s):\n"); size_t num_installed = 0; for (size_t i = 0; requirements != NULL && requirements[i] != NULL; i++) { @@ -288,7 +319,6 @@ int spm_do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) { // Relocate installation root relocate_root(fs->rootdir, tmpdir); - spm_install_package_record(fs, tmpdir, requirements[i]->name); num_installed++; free(package_path); diff --git a/lib/purge.c b/lib/purge.c index 997df51..da3e5e6 100644 --- a/lib/purge.c +++ b/lib/purge.c @@ -14,6 +14,7 @@ int spm_purge(SPM_Hierarchy *fs, const char *_package_name) { char *package_topdir = join((char *[]) {fs->dbrecdir, package_name, NULL}, DIRSEPS); char *descriptor = join((char *[]) {package_topdir, SPM_META_DESCRIPTOR, NULL}, DIRSEPS); char *filelist = join((char *[]) {package_topdir, SPM_META_FILELIST, NULL}, DIRSEPS); + char *rootrec = join((char *[]){path, "var", ".spm_root"}, DIRSEPS); if (spm_check_installed(fs, package_name) == 0) { // package is not installed in this root @@ -67,6 +68,11 @@ int spm_purge(SPM_Hierarchy *fs, const char *_package_name) { int spm_do_purge(SPM_Hierarchy *fs, StrList *packages) { int status_remove = 0; + if (spm_hierarchy_is_root(fs) < 0) { + spmerrno = SPM_ERR_ROOT_NO_RECORD; + return -1; + } + printf("Removing package(s):\n"); for (size_t i = 0; i < strlist_count(packages); i++) { char *package = strlist_item(packages, i); @@ -128,6 +128,13 @@ int main(int argc, char *argv[], char *arge[]) { usage(program_name); exit(1); } + + // Installing into the system's root is forbidden for obvious reasons + if (strcmp(arg_next, DIRSEPS) == 0) { + fprintf(stderr, "FATAL: refusing to operate on the system root directory\n"); + exit(1); + } + strcpy(rootdir, arg_next); i++; } @@ -236,7 +243,10 @@ int main(int argc, char *argv[], char *arge[]) { if (RUNTIME_INSTALL) { int status_install = 0; if ((status_install = spm_do_install(rootfs, mf, packages)) == -1) { - // failed to create temporary destination root + // general failure + if (spmerrno) { + spm_perror("Failed with reason"); + } exit(1); } else if (status_install == -2) { // user said no when asked to proceed diff --git a/tests/test_error_handler_spm_strerror.c b/tests/test_error_handler_spm_strerror.c new file mode 100644 index 0000000..f373a59 --- /dev/null +++ b/tests/test_error_handler_spm_strerror.c @@ -0,0 +1,27 @@ +#include "spm.h" +#include "framework.h" + +const char *testFmt = "translated error code '%d': returned '%s', expected '%s'\n"; +struct TestCase testCase[] = { + {.caseValue.signed_integer = 0, .truthValue.sptr = "Success", .arg[0].signed_integer = 0}, + {.caseValue.signed_integer = SPM_ERR_ROOT_NO_RECORD, .truthValue.sptr = "No root record", .arg[0].signed_integer = 0}, + {.caseValue.signed_integer = SPM_ERR_ROOT_UNSAFE, .truthValue.sptr = "Dangerous root path", .arg[0].signed_integer = 0}, + {.caseValue.signed_integer = ENOENT, .truthValue.sptr = "No such file or directory", .arg[0].signed_integer = ENOENT}, + {.caseValue.signed_integer = EPIPE, .truthValue.sptr = "Broken pipe", .arg[0].signed_integer = EPIPE}, + {.caseValue.signed_integer = -1, .truthValue.sptr = "Unknown error -1", .arg[0].signed_integer = 0}, +}; +size_t numCases = sizeof(testCase) / sizeof(struct TestCase); + +int main(int argc, char *argv[]) { + for (size_t i = 0; i < numCases; i++) { + // Mock global errno value to the value stored in the test case + errno = testCase[i].arg[0].signed_integer; + + // Get SPM error (or system error) + char *estr = spm_strerror(testCase[i].caseValue.signed_integer); + + // Assert error string matches error produced + myassert(strcmp(estr, testCase[i].truthValue.sptr) == 0, testFmt, testCase[i].caseValue.signed_integer, estr, testCase[i].truthValue.sptr); + } + return 0; +}
\ No newline at end of file |