Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add kernel function to fully resolve paths #42

Merged
merged 4 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libc/inc/limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@

/// Maximum pid number.
#define PID_MAX_LIMIT 32768

/// Maximum number of links to follow during resolving a path
#define SYMLOOP_MAX 8
19 changes: 19 additions & 0 deletions mentos/inc/fs/namei.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// @file namei.h
/// @brief Headers for path resolution
/// @copyright (c) 2024 This file is distributed under the MIT License.
/// See LICENSE.md for details.

#pragma once

#include "stddef.h"

#define REMOVE_TRAILING_SLASH 1 << 0
#define FOLLOW_LINKS 1 << 1

/// @brief Resolve the path by following all symbolic links.
/// @param path The path to resolve.
/// @param resolved_path The buffer where the resolved path is stored.
/// @param size The size of the provided resolved_path buffer.
/// @param flags The flags controlling how the path is resolved.
/// @return -errno on fail, 1 on success.
int resolve_path(const char *path, char *resolved_path, size_t size, int flags);
9 changes: 8 additions & 1 deletion mentos/inc/fs/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ int vfs_register_filesystem(file_system_type *fs);
/// @return The outcome of the operation, 0 if fails.
int vfs_unregister_filesystem(file_system_type *fs);

/// @brief Given a pathname for a file, kopen() returns a file struct, used to access the file.
/// @brief Given an absolute path to a file, vfs_open_abspath() returns a file struct, used to access the file.
/// @param abspath An absolute path to a file.
/// @param flags Used to set the file status flags and file access modes of the open file description.
/// @param mode Specifies the file mode bits be applied when a new file is created.
/// @return Returns a file struct, or NULL.
vfs_file_t *vfs_open_abspath(const char *pathname, int flags, mode_t mode);

/// @brief Given a pathname for a file, vfs_open() returns a file struct, used to access the file.
/// @param pathname A pathname for a file.
/// @param flags Used to set the file status flags and file access modes of the open file description.
/// @param mode Specifies the file mode bits be applied when a new file is created.
Expand Down
10 changes: 5 additions & 5 deletions mentos/src/fs/attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "libgen.h"
#include "process/scheduler.h"
#include "fcntl.h"
#include "fs/namei.h"
#include "fs/vfs.h"
#include "io/debug.h"
#include "limits.h"
Expand All @@ -19,9 +20,10 @@
static int __setattr(const char *path, struct iattr* attr, bool_t follow_links) {
// Allocate a variable for the path.
char absolute_path[PATH_MAX];
if (!realpath(path, absolute_path, sizeof(absolute_path))) {
pr_err("sys_chown(%s): Cannot get the absolute path.", path);
return -ENOENT;
int ret = resolve_path(path, absolute_path, sizeof(absolute_path), follow_links ? FOLLOW_LINKS: 0);
if (ret < 0) {
pr_err("__setattr(%s): Cannot get the absolute path.", path);
return ret;
}

super_block_t *sb = vfs_get_superblock(absolute_path);
Expand All @@ -35,8 +37,6 @@ static int __setattr(const char *path, struct iattr* attr, bool_t follow_links)
return -ENOENT;
}

// TODO: resolve links

// Check if the function is implemented.
if (sb_root->sys_operations->setattr_f == NULL) {
pr_err("setattr(%s): Function not supported in current filesystem.", absolute_path);
Expand Down
184 changes: 184 additions & 0 deletions mentos/src/fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
/// @copyright (c) 2014-2024 This file is distributed under the MIT License.
/// See LICENSE.md for details.

#include "assert.h"
#include "limits.h"
#include "fs/namei.h"
#include "fcntl.h"
#include "fs/vfs.h"
#include "io/debug.h"
#include "process/scheduler.h"
#include "sys/errno.h"
#include "string.h"

int sys_unlink(const char *path)
{
Expand Down Expand Up @@ -67,3 +71,183 @@ int sys_readlink(const char *path, char *buffer, size_t bufsize)
// Return the number of bytes we read.
return nbytes;
}

char *realpath(const char *path, char *buffer, size_t buflen) {
int ret = resolve_path(path, buffer, buflen, REMOVE_TRAILING_SLASH);
if (ret < 0) {
errno = -ret;
return NULL;
}
return buffer;
}

#define APPEND_PATH_SEP_OR_FAIL(b, remaining) \
{ \
strncat(b, "/", remaining); \
remaining--; \
if (remaining < 0) \
return -ENAMETOOLONG; \
}

#define APPEND_PATH_OR_FAIL(b, path, remaining) \
{ \
strncat(b, path, remaining); \
remaining -= strlen(path); \
if (remaining < 0) \
return -ENAMETOOLONG; \
}

int resolve_path(const char *path, char *buffer, size_t buflen, int flags)
{
assert(path && "Provided null path.");
assert(buffer && "Provided null buffer.");

// Buffer used to build up the absolute path
char abspath[buflen];
// Null-terminate our work buffer
memset(abspath,0, buflen);
int remaining = buflen - 1;

// Track the resolved symlinks to ensure we do not end up in a loop
int symlinks = 0;

if (path[0] != '/') {
// Get the working directory of the current task.
sys_getcwd(abspath, remaining);
// Check the absolute path.
assert((strlen(abspath) > 0) && "Current working directory is not set.");
// Check that the current working directory is an absolute path.
assert((abspath[0] == '/') && "Current working directory is not an absolute path.");
// Count the remaining space in the absolute path.
remaining -= strlen(abspath);
APPEND_PATH_SEP_OR_FAIL(abspath, remaining);
}

// Copy the path into the working buffer;
APPEND_PATH_OR_FAIL(abspath, path, remaining);

int absidx, pathidx;
resolve_abspath:
absidx = pathidx = 0;
while (abspath[absidx]) {
// Skip multiple consecutive / characters
if (!strncmp("//", abspath + absidx, 2)) {
absidx++;
}
// Go to previous directory if /../ is found
else if (!strncmp("/../", abspath + absidx, 4)) {
// Go to a valid path character (pathidx points to the next one)
if (pathidx) {
pathidx--;
}
while (pathidx && buffer[pathidx] != '/') {
pathidx--;
}
absidx += 3;
} else if (!strncmp("/./", abspath + absidx, 3)) {
absidx += 2;
} else {
// Resolve a possible symlink
if (flags & FOLLOW_LINKS) {
// Write the next path separator
buffer[pathidx++] = abspath[absidx++];

// Find the next separator after the current path component
char* sep_after_cur = strchr(abspath + absidx, '/');
if (sep_after_cur) {
// Null-terminate work buffer to properly open the current component
*sep_after_cur = 0;
}

vfs_file_t *file;
file = vfs_open_abspath(abspath, O_RDONLY, 0);
if (!file) { return -errno; }

stat_t statbuf;
int ret = vfs_fstat(file, &statbuf);
if (ret < 0) { vfs_close(file); return ret; }

// The path component is no symbolic link
if (!S_ISLNK(statbuf.st_mode)) {
vfs_close(file);
// Restore replaced path separator
*sep_after_cur = '/';
// Just copy the component into the buffer
goto copy_path_component;
}

symlinks++;
if (symlinks > SYMLOOP_MAX) { return -ELOOP; }

char link[PATH_MAX];
ssize_t nbytes = vfs_readlink(file, link, sizeof(link));
vfs_close(file);
if (nbytes == -1) { return -errno; }

// Null-terminate link
link[nbytes] = 0;

// Link is an absolute path, replace everything and continue
if (link[0] == '/') {
// Save the rest of the current abspath into the buffer
if (sep_after_cur) {
strncpy(buffer, sep_after_cur + 1, buflen);
}

// Reset abspath with link
remaining = buflen - 1;
strncpy(abspath, link, remaining);

remaining -= strlen(link);
if (remaining < 0) { return -ENAMETOOLONG; }

// This is not the last component, therefore it must be a directory
if (sep_after_cur) {
APPEND_PATH_SEP_OR_FAIL(abspath, remaining);
// Copy the saved content from buffer back to the abspath
APPEND_PATH_OR_FAIL(abspath, buffer, remaining);
}
goto resolve_abspath;
// Link is relative, add it and continue
} else {
// Recalculate the remaining space in the working buffer
remaining = buflen - absidx - 1;
strncpy(abspath + absidx, link, remaining);

remaining -= strlen(link);
if (remaining < 0) { return -ENAMETOOLONG; }

if (sep_after_cur) {
// Restore replaced path separator
*sep_after_cur = '/';
}

// Continue with to previous path separator
absidx--;
pathidx--;
}

}
// Copy the path component
else {
copy_path_component:
do {
buffer[pathidx++] = abspath[absidx++];
}
while (abspath[absidx] && abspath[absidx] != '/');
}
}
}

// Null-terminate buffer
buffer[pathidx] = 0;

if (flags & REMOVE_TRAILING_SLASH) {
// Ensure the path is not just "/"
if (pathidx > 1 && buffer[pathidx] == '/') {
buffer[pathidx - 1] = '\0';
}
}

return 0;
}
32 changes: 19 additions & 13 deletions mentos/src/fs/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,40 +106,33 @@ super_block_t *vfs_get_superblock(const char *absolute_path)
return last_sb;
}

vfs_file_t *vfs_open(const char *path, int flags, mode_t mode)
vfs_file_t *vfs_open_abspath(const char *absolute_path, int flags, mode_t mode)
{
// Allocate a variable for the path.
char absolute_path[PATH_MAX];
// If the first character is not the '/' then get the absolute path.
if (!realpath(path, absolute_path, sizeof(absolute_path))) {
pr_err("vfs_open(%s): Cannot get the absolute path!\n", path);
errno = ENODEV;
return NULL;
}
super_block_t *sb = vfs_get_superblock(absolute_path);
if (sb == NULL) {
pr_err("vfs_open(%s): Cannot find the superblock!\n", path);
pr_err("vfs_open(%s): Cannot find the superblock!\n", absolute_path);
errno = ENOENT;
return NULL;
}
vfs_file_t *sb_root = sb->root;
if (sb_root == NULL) {
pr_err("vfs_open(%s): Cannot find the superblock root!\n", path);
pr_err("vfs_open(%s): Cannot find the superblock root!\n", absolute_path);
errno = ENOENT;
return NULL;
}
// Rebase the absolute path.
//size_t name_offset = (strcmp(mp->name, "/") == 0) ? 0 : strlen(mp->name);
// Check if the function is implemented.
if (sb_root->fs_operations->open_f == NULL) {
pr_err("vfs_open(%s): Function not supported in current filesystem.", path);
pr_err("vfs_open(%s): Function not supported in current filesystem.", absolute_path);
errno = ENOSYS;
return NULL;
}
// Retrieve the file.
vfs_file_t *file = sb_root->fs_operations->open_f(absolute_path, flags, mode);
if (file == NULL) {
pr_debug("vfs_open(%s): Filesystem open returned NULL file (errno: %d, %s)!\n", path, errno, strerror(errno));
pr_debug("vfs_open(%s): Filesystem open returned NULL file (errno: %d, %s)!\n",
absolute_path, errno, strerror(errno));
return NULL;
}
// Increment file reference counter.
Expand All @@ -148,6 +141,19 @@ vfs_file_t *vfs_open(const char *path, int flags, mode_t mode)
return file;
}

vfs_file_t *vfs_open(const char *path, int flags, mode_t mode)
{
// Allocate a variable for the path.
char absolute_path[PATH_MAX];
// If the first character is not the '/' then get the absolute path.
if (!realpath(path, absolute_path, sizeof(absolute_path))) {
pr_err("vfs_open(%s): Cannot get the absolute path!\n", path);
errno = ENODEV;
return NULL;
}
return vfs_open_abspath(absolute_path, flags, mode);
}

int vfs_close(vfs_file_t *file)
{
pr_debug("vfs_close(ino: %d, file: \"%s\", count: %d)\n", file->ino, file->name, file->count - 1);
Expand Down
Loading
Loading