summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/kernel/syscalls.c4
-rw-r--r--src/shared/include/camellia/path.h11
-rw-r--r--src/shared/path.c30
-rw-r--r--src/user/app/tests/kernel/path.c2
-rw-r--r--src/user/lib/include/unistd.h3
-rw-r--r--src/user/lib/unistd.c2
6 files changed, 28 insertions, 24 deletions
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index d7ac5b2..7b5d294 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -96,7 +96,7 @@ handle_t _syscall_open(const char __user *path, long len, int flags) {
if (!virt_cpy_from(process_current->pages, path_buf, path, len)) goto fail;
len = path_simplify(path_buf, path_buf, len);
- if (len < 0) goto fail;
+ if (len == 0) goto fail;
mount = vfs_mount_resolve(process_current->mount, path_buf, len);
if (!mount) goto fail;
@@ -138,7 +138,7 @@ long _syscall_mount(handle_t hid, const char __user *path, long len) {
if (!virt_cpy_from(process_current->pages, path_buf, path, len)) goto fail;
len = path_simplify(path_buf, path_buf, len);
- if (len < 0) goto fail;
+ if (len == 0) goto fail;
// remove trailing slash
// mounting something under `/this` and `/this/` should be equalivent
diff --git a/src/shared/include/camellia/path.h b/src/shared/include/camellia/path.h
index 8efa0d4..b268595 100644
--- a/src/shared/include/camellia/path.h
+++ b/src/shared/include/camellia/path.h
@@ -4,7 +4,14 @@
#define PATH_MAX 512
/** Reduce a path to its simplest form.
+ * Kinds of invalid paths:
+ * - relative - "" "a" "./a"
+ * - going behind the root directory - "/../"
*
- * @return length of the string in *out, always less than len. Negative if the path was invalid.
+ * @return On success, length of the string in *out, <= len. 0 if the path was invalid.
+ *
+ * returns an unsigned type because:
+ * 1. valid paths always return at least 1, for the initial slash
+ * 2. it makes it easier to assign the result to an unsigned variable and check for error
*/
-int path_simplify(const char *in, char *out, size_t len);
+size_t path_simplify(const char *in, char *out, size_t len);
diff --git a/src/shared/path.c b/src/shared/path.c
index 175796f..4e3077b 100644
--- a/src/shared/path.c
+++ b/src/shared/path.c
@@ -3,13 +3,13 @@
#include <shared/mem.h>
#include <stdbool.h>
-int path_simplify(const char *in, char *out, size_t len) {
- if (len == 0) return -1; // empty paths are invalid
- if (in[0] != '/') return -1; // so are relative paths
+size_t path_simplify(const char *in, char *out, size_t len) {
+ if (len == 0) return 0; /* empty paths are invalid */
+ if (in[0] != '/') return 0; /* so are relative paths */
- int seg_len; // the length of the current path segment
+ int seg_len;
int out_pos = 0;
- bool directory = 0;
+ bool directory = false;
for (size_t i = 0; i < len; i += seg_len + 1) {
assert(in[i] == '/');
@@ -24,14 +24,11 @@ int path_simplify(const char *in, char *out, size_t len) {
seg_len++;
}
- /* example iteration, illustrated with terrible ASCII art
- *
- * |i=5 |next i = i + seg_len + 1 = 10
- * v v
- * /some/path/asdf
- * |--|
- * seg_len = 4
- * (segment starts at i+1) */
+ /* |i=5 |next i = i + seg_len + 1 = 10
+ * v v
+ * /some/path/asdf
+ * |--|
+ * seg_len = 4, segment starts at i+1 */
if (seg_len == 0 || (seg_len == 1 && in[i + 1] == '.')) {
/* // or /./ */
@@ -39,21 +36,18 @@ int path_simplify(const char *in, char *out, size_t len) {
} else if (seg_len == 2 && in[i + 1] == '.' && in[i + 2] == '.') {
/* /../ */
directory = true;
-
/* try to backtrack to the last slash */
while (--out_pos >= 0 && out[out_pos] != '/');
- if (out_pos < 0) return -1;
+ if (out_pos < 0) return 0;
} else {
/* a normal segment, e.g. /asdf/ */
out[out_pos] = '/';
memcpy(&out[out_pos + 1], &in[i + 1], seg_len);
out_pos += seg_len + 1;
}
-
}
- /* paths to directories should have a trailing slash */
if (directory) out[out_pos++] = '/';
-
+ assert(0 < out_pos && (size_t)out_pos <= len);
return out_pos;
}
diff --git a/src/user/app/tests/kernel/path.c b/src/user/app/tests/kernel/path.c
index 5fb3123..ddaf692 100644
--- a/src/user/app/tests/kernel/path.c
+++ b/src/user/app/tests/kernel/path.c
@@ -55,7 +55,7 @@ static void test_path_simplify(void) {
const char *expected = testcases[i][1];
int len = path_simplify(input, buf, strlen(input));
if (expected == NULL) {
- test(len < 0);
+ test(len == 0);
} else {
// TODO an argument for printing info on test failure
test(len == (int)strlen(expected) && !memcmp(expected, buf, len));
diff --git a/src/user/lib/include/unistd.h b/src/user/lib/include/unistd.h
index 1f0c002..59af24b 100644
--- a/src/user/lib/include/unistd.h
+++ b/src/user/lib/include/unistd.h
@@ -19,6 +19,9 @@ char *getcwd(char *buf, size_t size);
* except for the root dir. Includes the null byte.
* If size isn't enough to fit the path, returns the amount of bytes needed to fit
* it, including the null byte.
+ *
+ * Note that some errors are only detected if *out != NULL, so you must check the return
+ * value twice.
* @return 0 on failure, length of the path otherwise */
size_t absolutepath(char *out, const char *in, size_t size);
diff --git a/src/user/lib/unistd.c b/src/user/lib/unistd.c
index 1a50c3e..ce43551 100644
--- a/src/user/lib/unistd.c
+++ b/src/user/lib/unistd.c
@@ -163,7 +163,7 @@ size_t absolutepath(char *out, const char *in, size_t size) {
if (pos <= size) {
pos = path_simplify(out, out, pos);
- if (pos > 0) out[pos] = '\0';
+ if (pos == 0) return 0;
}
if (pos + 1 <= size) out[pos] = '\0';