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
8enum 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
60enum 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