add symlink command

This commit is contained in:
ІО-23 Шмуляр Олег 2025-05-17 18:52:52 +03:00
parent 72a02047d5
commit 552e574cde
4 changed files with 173 additions and 6 deletions

View File

@ -27,6 +27,7 @@
#define FS_MAX_OPEN_FD 32
#define FS_MAX_FNAME_LEN 11
#define FS_MAX_DIRECTORY_DEPTH 512
#define FS_MAX_SYMLINK_FOLLOWING_DEPTH 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

@ -29,6 +29,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},

173
src/fs.c
View File

@ -190,6 +190,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
@ -1584,6 +1594,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 +1631,156 @@ int fs_ln(void *d)
return 0;
}
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 +1803,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;
}