| 1 | #include <errno.h> |
| 2 | #include <fs/ext2.h> |
| 3 | #include <stdbool.h> |
| 4 | #include <stdint.h> |
| 5 | #include <string.h> |
| 6 | #include <time/time.h> |
| 7 | |
| 8 | enum errno ext2_symlink_file(struct ext2_fs *fs, |
| 9 | struct ext2_full_inode *dir_inode, |
| 10 | const char *name, const char *target) { |
| 11 | inode_t inode_num = ext2_alloc_inode(fs); |
| 12 | if (inode_num == 0) |
| 13 | return ERR_FS_NO_INODE; |
| 14 | |
| 15 | struct ext2_inode new_inode = {0}; |
| 16 | new_inode.ctime = time_get_unix(); |
| 17 | new_inode.mtime = time_get_unix(); |
| 18 | new_inode.atime = time_get_unix(); |
| 19 | new_inode.mode = EXT2_S_IFLNK; |
| 20 | new_inode.links_count = 1; |
| 21 | new_inode.size = strlen(str: target); |
| 22 | new_inode.blocks = 0; |
| 23 | |
| 24 | if (strlen(str: target) <= sizeof(new_inode.block)) { |
| 25 | memcpy(new_inode.block, target, strlen(target)); |
| 26 | new_inode.block[strlen(str: target)] = '\0'; |
| 27 | } else { |
| 28 | uint32_t block = ext2_alloc_block(fs); |
| 29 | if (block == 0) |
| 30 | return ERR_FS_NO_INODE; |
| 31 | |
| 32 | struct bcache_entry *ent; |
| 33 | uint8_t *buffer = ext2_create_bcache_ent(fs, block, out: &ent); |
| 34 | if (!buffer) |
| 35 | return ERR_IO; |
| 36 | |
| 37 | bcache_ent_acquire(ent); |
| 38 | memcpy(buffer, target, strlen(target) + 1); |
| 39 | bcache_ent_release(ent); |
| 40 | |
| 41 | ext2_block_write(fs, ent, EXT2_PRIO_DIRENT); |
| 42 | |
| 43 | new_inode.block[0] = block; |
| 44 | new_inode.blocks = fs->block_size / fs->drive->sector_size; |
| 45 | } |
| 46 | |
| 47 | if (!ext2_inode_write(fs, inode_num, inode: &new_inode)) |
| 48 | return ERR_FS_INTERNAL; |
| 49 | |
| 50 | struct ext2_full_inode wrapped_inode = { |
| 51 | .inode_num = inode_num, |
| 52 | .node = new_inode, |
| 53 | }; |
| 54 | |
| 55 | enum errno ret = ext2_link_file(fs, dir_inode, inode: &wrapped_inode, |
| 56 | name: (char *) name, EXT2_FT_SYMLINK, false); |
| 57 | return ret; |
| 58 | } |
| 59 | |
| 60 | enum errno ext2_readlink(struct ext2_fs *fs, struct ext2_full_inode *node, |
| 61 | char *buf, uint64_t size) { |
| 62 | if (!fs || !node || !buf) |
| 63 | return ERR_INVAL; |
| 64 | |
| 65 | uint64_t link_size = node->node.size; |
| 66 | |
| 67 | if (link_size > size) |
| 68 | link_size = size; |
| 69 | |
| 70 | /* inline data stored in i_block[] */ |
| 71 | if (link_size <= 60) { |
| 72 | memcpy(buf, node->node.block, link_size); |
| 73 | return 0; |
| 74 | } |
| 75 | |
| 76 | /* target is stored in data blocks */ |
| 77 | uint32_t first_block = node->node.block[0]; |
| 78 | |
| 79 | if (first_block == 0) |
| 80 | return ERR_IO; |
| 81 | |
| 82 | struct bcache_entry *ent; |
| 83 | uint8_t *block = ext2_block_read(fs, block_num: first_block, out: &ent); |
| 84 | if (!block) |
| 85 | return ERR_IO; |
| 86 | |
| 87 | memcpy(buf, block, link_size > fs->block_size ? fs->block_size : link_size); |
| 88 | bcache_ent_release(ent); |
| 89 | |
| 90 | return 0; |
| 91 | } |
| 92 | |