Compare commits

10 Commits

5 changed files with 613 additions and 216 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);

748
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,95 +956,198 @@ 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);
rp->parent_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);
rp->parent_inode_ptr = -1;
break;
}
}
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);
}
if (rp->parent_inode_ptr < 0) {
rp->target_inode_ptr = -1;
return;
}
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);
rp->target_inode_ptr = -1;
} else {
rp->target_inode_ptr = res;
strcpy(rp->target_fname, next_fname);
}
}
if (i+1 < path_len) {
pr_warn("parent '%s' does not exist\n", next_fname);
rp->parent_inode_ptr = -1;
}
return;
rp->target_inode_ptr = -1;
break;
}
struct fs_inode d;
read_block(read_inode_ptr(res), &d);
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 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);
}
} 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;
}
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);
}
}
}
}
static int find_free_fd(void)
{
for (int i = 0; i < FS_MAX_OPEN_FD; i++)
if (!fs_file_descriptions[i].inode)
return i;
return -1;
}
int fs_open(void *d)
{
char *fname = *((char **) d);
char *path = *((char **) d);
// find file inode
struct fs_file_description fd;
{
int *r = find_filename_in_directory(fs_cwd_inode_ptr, fname);
if (!r) {
pr_err("no such file: '%s'\n", fname);
return 0;
}
struct resolved_path rp;
resolve_path(&rp, path, FOLLOW_LAST_SYMLINK);
fd.inode = read_inode_ptr(r[2]);
free(r);
if (rp.target_inode_ptr < 0) {
pr_err("no such file: '%s'\n", path);
return 0;
}
fd.rw_offset = 0;
// find free file descriptor
int free_fd;
for (free_fd = 0; (free_fd < FS_MAX_OPEN_FD) && (fs_file_descriptions[free_fd].inode); free_fd++);
if (free_fd == FS_MAX_OPEN_FD) {
int free_fd = find_free_fd();
if (free_fd < 0) {
pr_err("no free file descriptor found\n");
return 0;
}
memcpy(&(fs_file_descriptions[free_fd]), &fd, sizeof(struct fs_file_description));
{
struct fs_inode i;
read_block(read_inode_ptr(rp.target_inode_ptr), &i);
if (i.ftype == DIRECTORY) {
pr_err("can't open '%s': file is a directory\n", rp.target_fname);
return 0;
}
}
fs_file_descriptions[free_fd].inode = read_inode_ptr(rp.target_inode_ptr);
fs_file_descriptions[free_fd].rw_offset = 0;
pr_stdout("%d\n", free_fd);
@@ -1332,7 +1451,7 @@ int fs_write(void *d)
int fs_truncate(void *d)
{
char *fname = *((char **) d);
char *path = *((char **) d);
int size = *((int *) ((char **) d+1));
if (size < 0) {
@@ -1340,52 +1459,48 @@ int fs_truncate(void *d)
return 0;
}
int file_inode_ptr;
{
int *r = find_filename_in_directory(fs_cwd_inode_ptr, fname);
if (r == NULL) {
pr_err("no such file: '%s'\n", fname);
return 0;
}
struct resolved_path rp;
resolve_path(&rp, path, FOLLOW_LAST_SYMLINK);
file_inode_ptr = r[2];
free(r);
if (rp.target_inode_ptr < 0) {
pr_err("no such file: '%s'\n", path);
return 0;
}
struct fs_inode f;
read_block(read_inode_ptr(file_inode_ptr), (void *) &f);
struct fs_inode inode;
read_block(read_inode_ptr(rp.target_inode_ptr), &inode);
if (size > f.size) {
pr("Increasing file size of '%s': %d -> %d\n", fname, f.size, size);
f.size = size;
write_block(read_inode_ptr(file_inode_ptr), (void *) &f);
if (size > inode.size) {
pr("Increasing file size of '%s': %d -> %d\n", rp.target_fname, inode.size, size);
inode.size = size;
write_block(read_inode_ptr(rp.target_inode_ptr), &inode);
} else {
pr("Decreasing file size of '%s': %d -> %d\n", fname, f.size, size);
f.size = size;
pr("Decreasing file size of '%s': %d -> %d\n", rp.target_fname, inode.size, size);
inode.size = size;
// cleanup
int new_block_amount = f.size / FS_BLOCK_SIZE;
if (f.size % FS_BLOCK_SIZE)
int new_block_amount = inode.size / FS_BLOCK_SIZE;
if (inode.size % FS_BLOCK_SIZE)
new_block_amount++;
int blocks_seen = 0;
// look through base inode blocks
for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE; i++, blocks_seen++) {
if ((blocks_seen >= new_block_amount) && (f.blocks[i])) {
mark_free(f.blocks[i]);
f.blocks[i] = 0;
if ((blocks_seen >= new_block_amount) && (inode.blocks[i])) {
mark_free(inode.blocks[i]);
inode.blocks[i] = 0;
}
}
write_block(read_inode_ptr(file_inode_ptr), (void *) &f);
write_block(read_inode_ptr(rp.target_inode_ptr), &inode);
// look through inode extension blocks
struct fs_inode_extension ext;
unsigned int next_ext = f.next_extension;
unsigned int next_ext = inode.next_extension;
unsigned int curr_ext;
while (next_ext) {
read_block(next_ext, (void *) &ext);
read_block(next_ext, &ext);
for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE_EXTENSION; i++, blocks_seen++) {
if ((blocks_seen >= new_block_amount) && (ext.blocks[i])) {
@@ -1394,25 +1509,25 @@ int fs_truncate(void *d)
}
}
write_block(next_ext, (void *) &ext);
write_block(next_ext, &ext);
next_ext = ext.next_extension;
}
// look through inode extensions themselves
int required_blocks_after_base_inode = (int) f.size - (int) BLOCK_ADDRESSES_PER_INODE;
int required_blocks_after_base_inode = (int) inode.size - (int) BLOCK_ADDRESSES_PER_INODE;
int required_extensions = required_blocks_after_base_inode >= 0
? required_blocks_after_base_inode / BLOCK_ADDRESSES_PER_INODE_EXTENSION
: 0;
if ((f.size - BLOCK_ADDRESSES_PER_INODE) % BLOCK_ADDRESSES_PER_INODE_EXTENSION)
if ((inode.size - BLOCK_ADDRESSES_PER_INODE) % BLOCK_ADDRESSES_PER_INODE_EXTENSION)
required_extensions++;
next_ext = f.next_extension;
next_ext = inode.next_extension;
if (!required_extensions) {
// zero base inode next_extension ptr
f.next_extension = 0;
write_block(read_inode_ptr(file_inode_ptr), (void *) &f);
inode.next_extension = 0;
write_block(read_inode_ptr(rp.target_inode_ptr), &inode);
} else {
// seek to last required extension
for (int i = 0; i < required_extensions; i++) {
@@ -1420,19 +1535,19 @@ int fs_truncate(void *d)
return 0;
curr_ext = next_ext;
read_block(curr_ext, (void *) &ext);
read_block(curr_ext, &ext);
next_ext = ext.next_extension;
}
// remove next_extension ptr
ext.next_extension = 0;
write_block(curr_ext, (void *) &ext);
write_block(curr_ext, &ext);
}
// erase all remaining extensions
while (next_ext) {
mark_free(next_ext);
read_block(next_ext, (void *) &ext);
read_block(next_ext, &ext);
next_ext = ext.next_extension;
}
}
@@ -1546,112 +1661,290 @@ int fs_create(void *d)
int fs_ln(void *d)
{
if (used_file_fd <= 0) {
pr_err("no storage device\n");
return 0;
}
char *existing_fname = ((char **) d)[0];
char *new_fname = ((char **)d)[1];
int new_fname_len = strlen(new_fname);
char *prev_path = ((char **) d)[0];
char *new_path = ((char **)d)[1];
char new_fname[FS_MAX_FNAME_LEN+1];
{
// check if duplicate filename exists in current directory
int *r = find_filename_in_directory(fs_cwd_inode_ptr, new_fname);
if (r) {
free(r);
pr_err("filename '%s' already exists\n", new_fname);
int new_fname_len = extract_basename(new_path, new_fname);
if (new_fname_len > FS_MAX_FNAME_LEN) {
pr_err("new filename too long (%d > %d)\n", new_fname_len, FS_MAX_FNAME_LEN);
return 0;
}
}
struct resolved_path prev_rp, new_rp;
if (new_fname_len > FS_MAX_FNAME_LEN) {
pr_err("new filename too long (%d > %d)\n", new_fname_len, FS_MAX_FNAME_LEN);
resolve_path(&prev_rp, prev_path, 0);
if (prev_rp.target_inode_ptr < 0) {
pr_err("no such file: '%s'\n", prev_path);
return 0;
}
pr("Making hard link '%s' -> '%s'\n", new_fname, existing_fname);
{
struct fs_inode prev_i;
read_block(read_inode_ptr(prev_rp.target_inode_ptr), &prev_i);
// find original file name
unsigned int original_inode_ptr = 0;
int dir_inode = read_inode_ptr(fs_cwd_inode_ptr);
struct fs_inode dir;
read_block(dir_inode, (void *) &dir);
// list entries from base inode
for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE; i++) {
struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK];
if (dir.blocks[i]) {
read_block(dir.blocks[i], (void *) &recs);
for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) {
if (!recs[k].inode_no)
continue;
if (!strcmp(existing_fname, recs[k].fname)) {
original_inode_ptr = recs[k].inode_no;
goto original_inode_ptr_found;
}
}
if (prev_i.ftype == DIRECTORY) {
pr_err("refusing to create hard link for directory\n");
return 0;
}
}
// list entries from inode extensions
struct fs_inode_extension ext;
unsigned int next_ext = dir.next_extension;
while (next_ext) {
read_block(next_ext, (void *) &ext);
next_ext = ext.next_extension;
for (int i = 0; i < BLOCK_ADDRESSES_PER_INODE_EXTENSION; i++) {
struct fs_directory_record recs[DIRECTORY_RECORDS_PER_BLOCK];
if (dir.blocks[i]) {
read_block(ext.blocks[i], (void *) &recs);
for (int k = 0; k < DIRECTORY_RECORDS_PER_BLOCK; k++) {
if (!recs[k].inode_no)
continue;
if (!strcmp(existing_fname, recs[k].fname)) {
original_inode_ptr = recs[k].inode_no;
goto original_inode_ptr_found;
}
}
}
}
/*
* 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);
return 0;
} else if (new_rp.target_inode_ptr >= 0) {
pr_err("file already exists: '%s'\n", new_rp.target_fname);
return 0;
}
pr_err("no such file '%s'\n", existing_fname);
return 0;
pr("Making hard link '%s' -> '%s'\n", new_path, prev_path);
original_inode_ptr_found:
pr("Original inode_ptr found (%d)\n", original_inode_ptr);
// register new filename with the same inode_ptr
if (fs_add_fname_to_directory(fs_cwd_inode_ptr, original_inode_ptr, new_fname) < 0) {
pr_err("failed to register filename in directory '%s'\n", fs_cwd);
if (fs_add_fname_to_directory(new_rp.parent_inode_ptr, prev_rp.target_inode_ptr, new_fname) < 0) {
pr_err("failed to add filename to directory '%s' (inode_ptr=%d)\n",
new_rp.parent_fname, new_rp.parent_inode_ptr);
return 0;
} else {
pr("Registered new filename in directory '%s'\n", fs_cwd);
pr("Added new filename to directory '%s' (inode_ptr=%d)\n",
new_rp.parent_fname, new_rp.parent_inode_ptr);
}
// update ref_count in file inode
struct fs_inode f;
read_block(read_inode_ptr(original_inode_ptr), (void *) &f);
f.ref_count++;
write_block(read_inode_ptr(original_inode_ptr), (void *) &f);
struct fs_inode i;
read_block(read_inode_ptr(prev_rp.target_inode_ptr), &i);
i.ref_count++;
write_block(read_inode_ptr(prev_rp.target_inode_ptr), &i);
pr("Updated inode ref_count (%d -> %d)\n", f.ref_count-1, f.ref_count);
pr("Updated inode ref_count (%d -> %d)\n", i.ref_count-1, i.ref_count);
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)
{
@@ -1674,11 +1967,10 @@ 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;
} else if (name) {
strcpy(name, &(path[lsp+1]));
}
strcpy(name, &(path[lsp+1]));
return fname_len;
}
@@ -2134,15 +2426,29 @@ 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",
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 == 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);
}
}
}
@@ -2169,15 +2475,29 @@ 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",
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 == 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);
}
}
}
@@ -2188,23 +2508,35 @@ int fs_la(void *d)
int fs_stat(void *d)
{
char *fname = *((char **) d);
char *path = *((char **) d);
int file_inode_ptr = find_fname_in_directory(fs_cwd_inode_ptr, fname);
if (file_inode_ptr < 0) {
pr_err("no such file: '%s'\n", fname);
struct resolved_path rp;
resolve_path(&rp, path, 0);
if (rp.target_inode_ptr < 0) {
pr_err("no such file: '%s'\n", path);
return 0;
}
struct fs_inode f;
read_block(read_inode_ptr(file_inode_ptr), (void *) &f);
struct fs_inode i;
read_block(read_inode_ptr(rp.target_inode_ptr), &i);
if (f.ftype == DIRECTORY) {
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=dir\n",
file_inode_ptr, read_inode_ptr(file_inode_ptr), f.ref_count, f.size);
} else {
pr_stdout("inode_ptr=%d -> inode=%d\nref_count=%d\nsize=%d\ntype=reg\n",
file_inode_ptr, read_inode_ptr(file_inode_ptr), f.ref_count, f.size);
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=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