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_OPEN_FD 32
|
||||||
#define FS_MAX_FNAME_LEN 11
|
#define FS_MAX_FNAME_LEN 11
|
||||||
#define FS_MAX_DIRECTORY_DEPTH 512
|
#define FS_MAX_DIRECTORY_DEPTH 512
|
||||||
|
#define FS_MAX_SYMLINK_FOLLOWING_DEPTH 1024
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
4
inc/fs.h
4
inc/fs.h
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
enum fs_filetype {
|
enum fs_filetype {
|
||||||
REGULAR,
|
REGULAR,
|
||||||
DIRECTORY
|
DIRECTORY,
|
||||||
|
SYMLINK
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((packed))
|
__attribute__((packed))
|
||||||
|
@ -73,6 +74,7 @@ int fs_close(void *d);
|
||||||
int fs_mkdir(void *d);
|
int fs_mkdir(void *d);
|
||||||
int fs_rmdir(void *d);
|
int fs_rmdir(void *d);
|
||||||
int fs_cd(void *d);
|
int fs_cd(void *d);
|
||||||
|
int fs_symlink(void *d);
|
||||||
int fs_truncate(void *d);
|
int fs_truncate(void *d);
|
||||||
int fs_allow_write(void *d);
|
int fs_allow_write(void *d);
|
||||||
int fs_prohibit_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},
|
{"mkdir", 1, (enum CliArgType[]) {STR}, fs_mkdir},
|
||||||
{"rmdir", 1, (enum CliArgType[]) {STR}, fs_rmdir},
|
{"rmdir", 1, (enum CliArgType[]) {STR}, fs_rmdir},
|
||||||
{"cd", 1, (enum CliArgType[]) {STR}, fs_cd},
|
{"cd", 1, (enum CliArgType[]) {STR}, fs_cd},
|
||||||
|
{"symlink", 2, (enum CliArgType[]) {STR, STR}, fs_symlink},
|
||||||
|
|
||||||
// custom commands
|
// custom commands
|
||||||
{"use", 1, (enum CliArgType[]) {STR}, fs_use},
|
{"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;
|
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)
|
static unsigned int find_free_inode_ptr(void)
|
||||||
{
|
{
|
||||||
unsigned int i = 1; // inode0 always points to root dir, so can't be free
|
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);
|
resolve_path(&new_rp, new_path, 1);
|
||||||
if (new_rp.parent_inode_ptr < 0) {
|
if (new_rp.parent_inode_ptr < 0) {
|
||||||
pr_err("failed to create hard link: no such directory: '%s'\n", new_path);
|
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;
|
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)
|
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) {
|
if (fname_len > FS_MAX_FNAME_LEN) {
|
||||||
pr_err("filename too long (%d > %d)",
|
pr_err("filename too long (%d > %d)",
|
||||||
fname_len, FS_MAX_FNAME_LEN);
|
fname_len, FS_MAX_FNAME_LEN);
|
||||||
return -1;
|
} else if (name) {
|
||||||
}
|
|
||||||
|
|
||||||
// allow the usage of NULL ptr to discard basename string
|
|
||||||
if (name)
|
|
||||||
strcpy(name, &(path[lsp+1]));
|
strcpy(name, &(path[lsp+1]));
|
||||||
|
}
|
||||||
|
|
||||||
return fname_len;
|
return fname_len;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue