mirror of
https://github.com/ish-app/ish.git
synced 2026-01-18 13:57:29 +00:00
130 lines
3.8 KiB
C
130 lines
3.8 KiB
C
#include <string.h>
|
|
#include "kernel/calls.h"
|
|
#include "fs/path.h"
|
|
|
|
static int __path_normalize(const char *at_path, const char *path, char *out, bool follow_links, int levels) {
|
|
const char *p = path;
|
|
char *o = out;
|
|
*o = '\0';
|
|
int n = MAX_PATH - 1;
|
|
|
|
if (strcmp(path, "") == 0)
|
|
return _ENOENT;
|
|
|
|
if (at_path != NULL && strcmp(at_path, "/") != 0) {
|
|
strcpy(o, at_path);
|
|
n -= strlen(at_path);
|
|
o += strlen(at_path);
|
|
}
|
|
|
|
while (*p == '/')
|
|
p++;
|
|
|
|
while (*p != '\0') {
|
|
if (p[0] == '.') {
|
|
if (p[1] == '\0' || p[1] == '/') {
|
|
// single dot path component, ignore
|
|
p++;
|
|
while (*p == '/')
|
|
p++;
|
|
continue;
|
|
} else if (p[1] == '.' && (p[2] == '\0' || p[2] == '/')) {
|
|
// double dot path component, delete the last component
|
|
if (o != out) {
|
|
do {
|
|
o--;
|
|
n++;
|
|
} while (*o != '/');
|
|
}
|
|
p += 2;
|
|
while (*p == '/')
|
|
p++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// output a slash
|
|
*o++ = '/'; n--;
|
|
char *c = o;
|
|
// copy up to a slash or null
|
|
while (*p != '/' && *p != '\0' && --n > 0)
|
|
*o++ = *p++;
|
|
// eat any slashes
|
|
while (*p == '/')
|
|
p++;
|
|
|
|
if (n == 0)
|
|
return _ENAMETOOLONG;
|
|
|
|
if (follow_links || *p != '\0') {
|
|
// this buffer is used to store the path that we're readlinking, then
|
|
// if it turns out to point to a symlink it's reused as the buffer
|
|
// passed to the next path_normalize call
|
|
char possible_symlink[MAX_PATH];
|
|
*o = '\0';
|
|
strcpy(possible_symlink, out);
|
|
struct mount *mount = find_mount_and_trim_path(possible_symlink);
|
|
assert(path_is_normalized(possible_symlink));
|
|
int res = _EINVAL;
|
|
if (mount->fs->readlink)
|
|
res = mount->fs->readlink(mount, possible_symlink, c, MAX_PATH - (c - out));
|
|
mount_release(mount);
|
|
if (res >= 0) {
|
|
if (levels >= 5)
|
|
return _ELOOP;
|
|
// readlink does not null terminate
|
|
c[res] = '\0';
|
|
// if we should restart from the root, copy down
|
|
if (*c == '/')
|
|
memmove(out, c, strlen(c) + 1);
|
|
char *expanded_path = possible_symlink;
|
|
strcpy(expanded_path, out);
|
|
strcat(expanded_path, "/");
|
|
strcat(expanded_path, p);
|
|
return __path_normalize(NULL, expanded_path, out, follow_links, levels + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
*o = '\0';
|
|
assert(path_is_normalized(out));
|
|
return 0;
|
|
}
|
|
|
|
int path_normalize(struct fd *at, const char *path, char *out, bool follow_links) {
|
|
assert(at != NULL);
|
|
if (strcmp(path, "") == 0)
|
|
return _ENOENT;
|
|
|
|
// start with root or cwd, depending on whether it starts with a slash
|
|
lock(¤t->fs->lock);
|
|
if (path[0] == '/')
|
|
at = current->fs->root;
|
|
else if (at == AT_PWD)
|
|
at = current->fs->pwd;
|
|
unlock(¤t->fs->lock);
|
|
char at_path[MAX_PATH];
|
|
if (at != NULL) {
|
|
int err = generic_getpath(at, at_path);
|
|
if (err < 0)
|
|
return err;
|
|
assert(path_is_normalized(at_path));
|
|
}
|
|
|
|
return __path_normalize(at != NULL ? at_path : NULL, path, out, follow_links, 0);
|
|
}
|
|
|
|
|
|
bool path_is_normalized(const char *path) {
|
|
while (*path != '\0') {
|
|
if (*path != '/')
|
|
return false;
|
|
path++;
|
|
if (*path == '/')
|
|
return false;
|
|
while (*path != '/' && *path != '\0')
|
|
path++;
|
|
}
|
|
return true;
|
|
}
|