aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/conf.h1
-rw-r--r--include/error_handler.h34
-rw-r--r--include/install.h2
-rw-r--r--include/spm.h1
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/config_global.c6
-rw-r--r--lib/error_handler.c27
-rw-r--r--lib/fs.c2
-rw-r--r--lib/install.c54
-rw-r--r--lib/purge.c6
-rw-r--r--src/spm.c12
-rw-r--r--tests/test_error_handler_spm_strerror.c27
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));
+}
+
diff --git a/lib/fs.c b/lib/fs.c
index 3ba01e7..20a5999 100644
--- a/lib/fs.c
+++ b/lib/fs.c
@@ -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);
diff --git a/src/spm.c b/src/spm.c
index e2e7a8f..bb58401 100644
--- a/src/spm.c
+++ b/src/spm.c
@@ -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