Compare commits

8 Commits

5 changed files with 509 additions and 79 deletions

View File

@@ -14,9 +14,9 @@
/* CLI config section */
#define CLI_MAX_LINE_LENGTH 256
#define CLI_MAX_LINE_LENGTH 1024
#define CLI_MAX_ACCEPTED_ARGS 4
#define CLI_MAX_TOKEN_LENGTH 64
#define CLI_MAX_TOKEN_LENGTH 256
/* FS config section */
@@ -26,7 +26,8 @@
#define FS_MAX_PATH_LEN 512
#define FS_MAX_OPEN_FD 32
#define FS_MAX_FNAME_LEN 11
#define FS_MAX_DIRECTORY_DEPTH 512
#define FS_MAX_DIRECTORY_DEPTH 2048
#define FS_MAX_SYMLINK_FOLLOWS 1024
#endif

View File

@@ -4,7 +4,8 @@
enum fs_filetype {
REGULAR,
DIRECTORY
DIRECTORY,
SYMLINK
};
__attribute__((packed))
@@ -73,6 +74,7 @@ int fs_close(void *d);
int fs_mkdir(void *d);
int fs_rmdir(void *d);
int fs_cd(void *d);
int fs_symlink(void *d);
int fs_truncate(void *d);
int fs_allow_write(void *d);
int fs_prohibit_write(void *d);

View File

@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static const struct CliCommandEntry cmd[] = {
@@ -29,6 +30,7 @@ static const struct CliCommandEntry cmd[] = {
{"mkdir", 1, (enum CliArgType[]) {STR}, fs_mkdir},
{"rmdir", 1, (enum CliArgType[]) {STR}, fs_rmdir},
{"cd", 1, (enum CliArgType[]) {STR}, fs_cd},
{"symlink", 2, (enum CliArgType[]) {STR, STR}, fs_symlink},
// custom commands
{"use", 1, (enum CliArgType[]) {STR}, fs_use},
@@ -207,11 +209,11 @@ abandon_struct:
unsigned int cli_poll_process_next(void)
{
for ( ;; ) {
fputs(fs_get_cwd(), stdout);
int echo_commands = !isatty(STDIN_FILENO);
for ( ;; ) {
if (fs_get_cwd()[0]) {
printf(" $ ");
printf("%s $ ", fs_get_cwd());
} else {
printf("$ ");
}
@@ -220,8 +222,13 @@ unsigned int cli_poll_process_next(void)
char *line = NULL;
ssize_t result = getline(&line, &buf_size, stdin);
if (result == -1)
if (result == -1) {
printf("\n");
return 0x1;
}
if (echo_commands)
printf(line);
char **tokenized_line = tokenize_line(line, result);
free(line);

447
src/fs.c
View File

@@ -24,6 +24,7 @@ static unsigned int fs_cwd_inode_ptr;
static struct fs_file_description fs_file_descriptions[FS_MAX_OPEN_FD];
static int extract_basename(char *path, char *name);
static int read_symlink(int symlink_inode_ptr, char *path);
static int read_block(unsigned int block_no, void *data)
@@ -190,6 +191,16 @@ static unsigned int find_free_block(void)
return 0;
}
static unsigned int claim_free_block(void)
{
int free_block = find_free_block();
if (free_block)
mark_used(free_block);
return free_block;
}
static unsigned int find_free_inode_ptr(void)
{
unsigned int i = 1; // inode0 always points to root dir, so can't be free
@@ -897,18 +908,21 @@ fs_remove_fname_from_directory_finish:
return 0;
}
static void resolve_path(struct resolved_path *rp, char *path, unsigned int params)
static void resolve_path(struct resolved_path * const rp, const char * const original_path, const unsigned int params)
{
rp->parent_inode_ptr = -1;
rp->target_inode_ptr = -1;
int path_len = strlen(path);
int path_len = strlen(original_path);
if (!path_len) {
pr_err("no path specified\n");
return;
}
char path[FS_MAX_PATH_LEN+1];
strcpy(path, original_path);
if (!strcmp(path, "/")) {
rp->parent_inode_ptr = 0;
rp->target_inode_ptr = 0;
@@ -921,6 +935,8 @@ static void resolve_path(struct resolved_path *rp, char *path, unsigned int para
int current_inode_ptr;
int i;
unsigned int symlinks_followed = 0;
if (path[0] == '/') {
current_inode_ptr = 0;
rp->parent_inode_ptr = 0;
@@ -940,65 +956,156 @@ static void resolve_path(struct resolved_path *rp, char *path, unsigned int para
if (path[i] != '/') {
next_fname[next_fname_ptr] = path[i];
next_fname_ptr++;
continue;
}
// path[i] == '/', trying to change current directory
if ((path[i] != '/') && (i+1 < path_len))
continue;
if (!next_fname_ptr)
continue;
if (i+1 == path_len)
continue;
{
int res = find_fname_in_directory(current_inode_ptr, next_fname);
if (res < 0) {
pr_warn("directory '%s' does not exist\n", next_fname);
if (i+1 < path_len) {
pr_warn("parent '%s' does not exist\n", next_fname);
rp->parent_inode_ptr = -1;
}
rp->target_inode_ptr = -1;
break;
}
{
struct fs_inode d;
read_block(read_inode_ptr(res), &d);
if (d.ftype == REGULAR) {
pr_warn("'%s' is a regular file\n", next_fname);
if (d.ftype == SYMLINK) {
if (i+1 < path_len) {
symlinks_followed++;
if (symlinks_followed > FS_MAX_SYMLINK_FOLLOWS) {
pr_err("too many symlink follows\n");
rp->parent_inode_ptr = -1;
rp->target_inode_ptr = -1;
break;
}
char symlink_path[FS_MAX_PATH_LEN+1];
int symlink_path_len = read_symlink(res, symlink_path);
if (symlink_path_len < 0) {
pr_warn("broken symlink\n");
rp->parent_inode_ptr = -1;
rp->target_inode_ptr = -1;
break;
}
pr("Found directory '%s'\n", next_fname);
current_inode_ptr = res;
rp->parent_inode_ptr = res;
}
pr("Found symlink '%s', following (-> '%s')\n", next_fname, symlink_path);
if (symlink_path[0] == '/') {
memmove(&(path[symlink_path_len]), &(path[i+1]), path_len-i-1);
strcpy(path, &(symlink_path[1]));
path[symlink_path_len-1] = '/';
path_len = symlink_path_len + path_len - i - 1;
i = -1;
next_fname_ptr = 0;
strcpy(current_fname, "/");
memset(next_fname, 0, FS_MAX_FNAME_LEN);
} else {
memmove(&(path[symlink_path_len+1]), &(path[i+1]), path_len-i-1);
strcpy(path, symlink_path);
path[symlink_path_len] = '/';
path_len = symlink_path_len + path_len - i;
i = -1;
next_fname_ptr = 0;
strcpy(current_fname, next_fname);
memset(next_fname, 0, FS_MAX_FNAME_LEN);
}
if (rp->parent_inode_ptr < 0) {
rp->target_inode_ptr = -1;
return;
} else {
if (!(params & FOLLOW_LAST_SYMLINK)) {
pr("Stopping at symlink '%s', not following\n", next_fname);
rp->target_inode_ptr = res;
strcpy(rp->parent_fname, current_fname);
strcpy(rp->target_fname, next_fname);
break;
}
strcpy(rp->parent_fname, current_fname);
{
int res = find_fname_in_directory(current_inode_ptr, next_fname);
if (res < 0) {
pr_warn("target fname '%s' does not exist\n", next_fname);
symlinks_followed++;
if (symlinks_followed > FS_MAX_SYMLINK_FOLLOWS) {
pr_err("too many symlink follows\n");
rp->parent_inode_ptr = -1;
rp->target_inode_ptr = -1;
break;
}
char symlink_path[FS_MAX_PATH_LEN+1];
int symlink_path_len = read_symlink(res, symlink_path);
if (symlink_path_len < 0) {
pr_warn("broken symlink\n");
rp->parent_inode_ptr = -1;
rp->target_inode_ptr = -1;
break;
}
pr("Found symlink '%s' at last position, following (-> '%s')\n", next_fname, symlink_path);
if (!strcmp(symlink_path, "/")) {
rp->parent_inode_ptr = 0;
rp->target_inode_ptr = 0;
strcpy(rp->parent_fname, "/");
strcpy(rp->target_fname, "/");
break;
}
if (symlink_path[0] == '/') {
strcpy(path, &(symlink_path[1]));
path_len = symlink_path_len - 1;
i = -1;
next_fname_ptr = 0;
strcpy(current_fname, "/");
memset(next_fname, 0, FS_MAX_FNAME_LEN);
} else {
strcpy(path, symlink_path);
path_len = symlink_path_len;
i = -1;
next_fname_ptr = 0;
strcpy(current_fname, next_fname);
memset(next_fname, 0, FS_MAX_FNAME_LEN);
}
}
} else if (d.ftype == REGULAR) {
if (i+1 < path_len) {
pr_warn("'%s': regular file not in the end of a path\n", next_fname);
rp->parent_inode_ptr = -1;
rp->target_inode_ptr = -1;
break;
} else {
pr("Stopping at regular file '%s'\n", next_fname);
rp->target_inode_ptr = res;
strcpy(rp->parent_fname, current_fname);
strcpy(rp->target_fname, next_fname);
}
} else if (d.ftype == DIRECTORY) {
if (i+1 < path_len) {
pr("Found directory '%s'\n", next_fname);
current_inode_ptr = res;
rp->parent_inode_ptr = res;
next_fname_ptr = 0;
strcpy(current_fname, next_fname);
memset(next_fname, 0, FS_MAX_FNAME_LEN);
} else {
pr("Stopping at directory '%s'\n", next_fname);
rp->target_inode_ptr = res;
strcpy(rp->parent_fname, current_fname);
strcpy(rp->target_fname, next_fname);
}
}
return;
}
}
@@ -1584,6 +1691,12 @@ int fs_ln(void *d)
}
}
/*
* TODO: add support for directory as target, e. g.:
* $ create file1
* $ mkdir dir2
* $ ln file1 dir2 -> creates hardlink 'dir2/file1'
*/
resolve_path(&new_rp, new_path, 1);
if (new_rp.parent_inode_ptr < 0) {
pr_err("failed to create hard link: no such directory: '%s'\n", new_path);
@@ -1615,6 +1728,223 @@ int fs_ln(void *d)
return 0;
}
static int read_symlink(int symlink_inode_ptr, char *path)
{
struct fs_inode i;
read_block(read_inode_ptr(symlink_inode_ptr), &i);
path[0] = '\0';
int bytes_read = 0;
for (int j = 0; j < BLOCK_ADDRESSES_PER_INODE; j++) {
if (!i.blocks[j]) {
pr_err("inode size (%d) indicates existing block but it's address is absent in inode, symlink at inode_ptr=%d might be broken\n",
i.size, symlink_inode_ptr);
return -1;
}
char data[FS_BLOCK_SIZE];
read_block(i.blocks[j], data);
if (i.size - bytes_read < FS_BLOCK_SIZE) {
memcpy(&(path[bytes_read]), data, i.size - bytes_read);
bytes_read = i.size;
path[bytes_read] = '\0';
} else {
memcpy(&(path[bytes_read]), data, FS_BLOCK_SIZE);
bytes_read += FS_BLOCK_SIZE;
path[bytes_read] = '\0';
}
if (i.size == bytes_read)
goto finish_reading;
}
for (unsigned int next_ext = i.next_extension; ; ) {
struct fs_inode_extension ext;
read_block(next_ext, &ext);
for (int j = 0; j < BLOCK_ADDRESSES_PER_INODE_EXTENSION; j++) {
if (!ext.blocks[j]) {
pr_err("inode size (%d) indicates existing block but it's address is absent in inode extension, symlink at inode_ptr=%d might be broken\n",
i.size, symlink_inode_ptr);
return -1;
}
char data[FS_BLOCK_SIZE];
read_block(ext.blocks[j], data);
if (i.size - bytes_read < FS_BLOCK_SIZE) {
memcpy(&(path[bytes_read]), data, i.size - bytes_read);
bytes_read = i.size;
path[bytes_read] = '\0';
} else {
memcpy(&(path[bytes_read]), data, FS_BLOCK_SIZE);
bytes_read += FS_BLOCK_SIZE;
path[bytes_read] = '\0';
}
if (i.size == bytes_read)
goto finish_reading;
}
next_ext = ext.next_extension;
}
finish_reading:
return bytes_read;
}
int fs_symlink(void *d)
{
char *target_path = ((char **) d)[0];
char *link_path = ((char **)d)[1];
int target_path_len = strlen(target_path);
struct resolved_path rp;
resolve_path(&rp, link_path, FOLLOW_LAST_SYMLINK);
if (rp.parent_inode_ptr < 0) {
pr_err("failed to create symlink: no such directory: '%s'\n", link_path);
return 0;
}
char link_fname[FS_MAX_FNAME_LEN+1];
unsigned int dir_inode_ptr;
if (rp.target_inode_ptr >= 0) {
{
struct fs_inode i;
read_block(read_inode_ptr(rp.target_inode_ptr), &i);
if (i.ftype == REGULAR) {
pr_err("file already exists: '%s'\n", rp.target_fname);
return 0;
} else {
int link_fname_len = extract_basename(target_path, link_fname);
if (link_fname_len > FS_MAX_FNAME_LEN) {
pr_err("new filename too long (%d > %d)\n", link_fname_len, FS_MAX_FNAME_LEN);
return 0;
}
dir_inode_ptr = rp.target_inode_ptr;
}
}
} else {
int link_fname_len = extract_basename(link_path, link_fname);
if (link_fname_len > FS_MAX_FNAME_LEN) {
pr_err("new filename too long (%d > %d)\n", link_fname_len, FS_MAX_FNAME_LEN);
return 0;
}
dir_inode_ptr = rp.parent_inode_ptr;
}
int inode_ptr = find_free_inode_ptr();
if (!inode_ptr) {
pr_err("no free inode_ptr found\n");
return 0;
}
int inode_addr = claim_free_block();
if (!inode_addr) {
pr_err("no space left on device\n");
return 0;
}
write_inode_ptr(inode_ptr, inode_addr);
int required_block_amount = target_path_len % FS_BLOCK_SIZE
? target_path_len / FS_BLOCK_SIZE + 1
: target_path_len / FS_BLOCK_SIZE;
int inode_blocks[required_block_amount];
for (int i = 0; i < required_block_amount; i++) {
inode_blocks[i] = claim_free_block();
if (!inode_blocks[i]) {
pr_err("no space left on device\n");
return 0;
}
}
int bytes_written = 0;
int blocks_written = 0;
struct fs_inode i;
memset(&i, 0, sizeof(i));
i.ftype = SYMLINK;
i.ref_count = 1;
i.size = target_path_len;
for (int j = 0; j < BLOCK_ADDRESSES_PER_INODE; j++, blocks_written++) {
i.blocks[j] = inode_blocks[blocks_written];
write_block(inode_addr, &i);
if (target_path_len - bytes_written < FS_BLOCK_SIZE) {
char data_block[FS_BLOCK_SIZE];
memset(data_block, 0, sizeof(data_block));
memcpy(data_block, &(target_path[bytes_written]), target_path_len - bytes_written);
write_block(inode_blocks[blocks_written], data_block);
bytes_written = target_path_len;
} else {
write_block(inode_blocks[blocks_written], &(target_path[bytes_written]));
bytes_written += FS_BLOCK_SIZE;
}
if (target_path_len == bytes_written)
goto finish_writing;
}
i.next_extension = claim_free_block();
if (!i.next_extension) {
pr_err("no space left on device\n");
return 0;
}
write_block(inode_addr, &i);
for (unsigned int next_ext = i.next_extension; ; ) {
struct fs_inode_extension ext;
memset(&ext, 0, sizeof(ext));
for (int j = 0; j < BLOCK_ADDRESSES_PER_INODE_EXTENSION; j++, blocks_written++) {
ext.blocks[j] = inode_blocks[blocks_written];
write_block(next_ext, &ext);
if (target_path_len - bytes_written < FS_BLOCK_SIZE) {
char data_block[FS_BLOCK_SIZE];
memset(data_block, 0, sizeof(data_block));
memcpy(data_block, &(target_path[bytes_written]), target_path_len - bytes_written);
write_block(inode_blocks[blocks_written], data_block);
bytes_written = target_path_len;
} else {
write_block(inode_blocks[blocks_written], &(target_path[bytes_written]));
bytes_written += FS_BLOCK_SIZE;
}
if (target_path_len == bytes_written)
goto finish_writing;
}
ext.next_extension = claim_free_block();
if (!ext.next_extension) {
pr_err("no space left on device\n");
return 0;
}
write_block(next_ext, &ext);
next_ext = ext.next_extension;
}
finish_writing:
pr("Symlink '%s' written successfully (%d/%d bytes)\n", link_fname, bytes_written, target_path_len);
fs_add_fname_to_directory(dir_inode_ptr, inode_ptr, link_fname);
return 0;
}
static int last_significant_slash_position(char *path, int path_len)
{
@@ -1637,12 +1967,9 @@ static int extract_basename(char *path, char *name)
if (fname_len > FS_MAX_FNAME_LEN) {
pr_err("filename too long (%d > %d)",
fname_len, FS_MAX_FNAME_LEN);
return -1;
}
// allow the usage of NULL ptr to discard basename string
if (name)
} else if (name) {
strcpy(name, &(path[lsp+1]));
}
return fname_len;
}
@@ -2099,13 +2426,27 @@ int fs_la(void *d)
struct fs_inode f_inode;
read_block(read_inode_ptr(recs[k].inode_no), (void *) &f_inode);
if (f_inode.ftype == DIRECTORY) {
if (f_inode.ftype == REGULAR) {
pr_stdout("%s (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=reg)\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size);
} else if (f_inode.ftype == DIRECTORY) {
pr_stdout(COLOR_BLUE "%s" COLOR_RESET
" (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=dir)\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size);
} else {
pr_stdout("%s (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=reg)\n",
} else if (f_inode.ftype == SYMLINK) {
char l_path[FS_MAX_PATH_LEN+1];
int l_path_len = read_symlink(recs[k].inode_no, l_path);
if (l_path_len > 0)
pr_stdout(COLOR_CYAN "%s" COLOR_RESET
" (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=lnk, -> '%s')\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size, l_path);
else
pr_stdout(COLOR_RED "%s" COLOR_RESET
" (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=lnk)\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size);
}
@@ -2134,13 +2475,27 @@ int fs_la(void *d)
struct fs_inode f_inode;
read_block(read_inode_ptr(recs[k].inode_no), (void *) &f_inode);
if (f_inode.ftype == DIRECTORY) {
if (f_inode.ftype == REGULAR) {
pr_stdout("%s (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=reg)\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size);
} else if (f_inode.ftype == DIRECTORY) {
pr_stdout(COLOR_BLUE "%s" COLOR_RESET
" (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=dir)\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size);
} else {
pr_stdout("%s (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=reg)\n",
} else if (f_inode.ftype == SYMLINK) {
char l_path[FS_MAX_PATH_LEN+1];
int l_path_len = read_symlink(recs[k].inode_no, l_path);
if (l_path_len > 0)
pr_stdout(COLOR_CYAN "%s" COLOR_RESET
" (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=lnk, -> '%s')\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size, l_path);
else
pr_stdout(COLOR_RED "%s" COLOR_RESET
" (inode_ptr=%d -> inode=%d, ref_count=%d, size=%d, type=lnk)\n",
recs[k].fname, recs[k].inode_no, read_inode_ptr(recs[k].inode_no),
f_inode.ref_count, f_inode.size);
}
@@ -2166,13 +2521,23 @@ int fs_stat(void *d)
struct fs_inode i;
read_block(read_inode_ptr(rp.target_inode_ptr), &i);
if (i.ftype == DIRECTORY) {
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=dir\n",
if (i.ftype == REGULAR) {
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=regular\n",
rp.target_inode_ptr, read_inode_ptr(rp.target_inode_ptr), i.ref_count, i.size);
} else if (i.ftype == DIRECTORY) {
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=directory\n",
rp.target_inode_ptr, read_inode_ptr(rp.target_inode_ptr), i.ref_count, i.size);
} else if (i.ftype == SYMLINK) {
char l_path[FS_MAX_PATH_LEN+1];
int l_path_len = read_symlink(rp.target_inode_ptr, l_path);
if (l_path_len > 0) {
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=symlink\npoints to: %s\n",
rp.target_inode_ptr, read_inode_ptr(rp.target_inode_ptr), i.ref_count, i.size, l_path);
} else {
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=reg\n",
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=symlink\n<broken>\n",
rp.target_inode_ptr, read_inode_ptr(rp.target_inode_ptr), i.ref_count, i.size);
}
}
return 0;
}

55
testing_script.t Normal file
View File

@@ -0,0 +1,55 @@
use disk1
mkfs 32
mkdir test1
mkdir test2
la
cd test1
la
create hello1
mkdir inside
symlink ../../test2/ inside/hello3
stat inside/hello3
cd ../test2
la
symlink ../test1/inside hello2
stat hello2
cd /
cd test1/../test2/hello2/../../test2/.././././test1/inside/hello3/./hello2/.
cd ..
create inside/././hello3/.././test1/f7
symlink /test1/f7 ../f1
la
cd ..
la
cd test1
open ../test2/hello2/../../f1
write 0 data
la
close 0
cd /
rm test1
rmdir test1
rm test1/inside/hello3
rmdir test1/inside
rm test1/hello1
rm test1/f7
rmdir test1
cd test2
la
cd hello2
symlink garbage/ttttt ..
cd ..
la
cd ttttt
rm ttttt