|
|
|
@@ -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;
|
|
|
|
|
}
|
|
|
|
|