add symlink command
This commit is contained in:
parent
72a02047d5
commit
552e574cde
1
config.h
1
config.h
|
@ -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
|
||||
|
|
4
inc/fs.h
4
inc/fs.h
|
@ -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);
|
||||
|
|
|
@ -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
173
src/fs.c
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue