+static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
+{
+ struct ext4_block b;
+ struct ext4_inode_ref ref;
+ uint32_t sblock, fblock;
+ uint32_t block_size;
+ int r;
+
+ ext4_assert(f && f->mp);
+
+ if (!size)
+ return EOK;
+
+ r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+ if (r != EOK) {
+ EXT4_MP_UNLOCK(f->mp);
+ return r;
+ }
+
+ /*Sync file size*/
+ block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
+ if (size > block_size) {
+ r = EINVAL;
+ goto Finish;
+ }
+ r = ext4_ftruncate_no_lock(f, 0);
+ if (r != EOK)
+ goto Finish;
+
+ /*Start write back cache mode.*/
+ r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
+ if (r != EOK)
+ goto Finish;
+
+ /*If the size of symlink is smaller than 60 bytes*/
+ if (size < sizeof(ref.inode->blocks)) {
+ char *content = (char *)ref.inode->blocks;
+ memset(content, 0, sizeof(ref.inode->blocks));
+ memcpy(content, buf, size);
+ ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
+ } else {
+ ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
+ r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
+ if (r != EOK)
+ goto Finish;
+
+ r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
+ if (r != EOK)
+ goto Finish;
+
+ memcpy(b.data, buf, size);
+ b.dirty = true;
+ r = ext4_block_set(f->mp->fs.bdev, &b);
+ if (r != EOK)
+ goto Finish;
+ }
+
+ /*Stop write back cache mode*/
+ ext4_block_cache_write_back(f->mp->fs.bdev, 0);
+
+ if (r != EOK)
+ goto Finish;
+
+ ext4_inode_set_size(ref.inode, size);
+ ref.dirty = true;
+
+ f->fsize = size;
+ if (f->fpos > size)
+ f->fpos = size;
+
+Finish:
+ ext4_fs_put_inode_ref(&ref);
+ return r;
+}
+
+int ext4_fsymlink(const char *target, const char *path)
+{
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ int r;
+ ext4_file f;
+ int filetype;
+
+ if (!mp)
+ return ENOENT;
+
+ filetype = EXT4_DIRENTRY_SYMLINK;
+
+ EXT4_MP_LOCK(mp);
+ ext4_block_cache_write_back(mp->fs.bdev, 1);
+ r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, 0, 0);
+ if (r == EOK)
+ r = ext4_fsymlink_set(&f, target, strlen(target));
+ else
+ goto Finish;
+
+ ext4_fclose(&f);
+
+Finish:
+ ext4_block_cache_write_back(mp->fs.bdev, 0);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+
+int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
+{
+ struct ext4_mountpoint *mp = ext4_get_mount(path);
+ int r;
+ ext4_file f;
+ int filetype;
+
+ if (!mp)
+ return ENOENT;
+
+ if (!buf)
+ return EINVAL;
+
+ memset(buf, 0, sizeof(bufsize));
+
+ filetype = EXT4_DIRENTRY_SYMLINK;
+
+ EXT4_MP_LOCK(mp);
+ ext4_block_cache_write_back(mp->fs.bdev, 1);
+ r = ext4_generic_open2(&f, path, O_RDONLY, filetype, 0, 0);
+ if (r == EOK)
+ r = ext4_fread(&f, buf, bufsize, rcnt);
+ else
+ goto Finish;
+
+ ext4_fclose(&f);
+
+Finish:
+ ext4_block_cache_write_back(mp->fs.bdev, 0);
+ EXT4_MP_UNLOCK(mp);
+ return r;
+}
+