@@ -18,5 +18,5 @@ zuf-y += zuf-core.o zuf-root.o
# Main FS
zuf-y += rw.o
-zuf-y += super.o inode.o directory.o namei.o file.o
+zuf-y += super.o inode.o directory.o namei.o file.o symlink.o
zuf-y += module.o
@@ -42,6 +42,10 @@ void zuf_set_inode_flags(struct inode *inode, struct zus_inode *zi);
bool zuf_dir_emit(struct super_block *sb, struct dir_context *ctx,
ulong ino, const char *name, int length);
+/* symlink.c */
+uint zuf_prepare_symname(struct zufs_ioc_new_inode *ioc_new_inode,
+ const char *symname, ulong len, struct page *pages[2]);
+
/* rw.c */
int zuf_trim_edge(struct inode *inode, ulong filepos, uint len);
@@ -105,4 +109,7 @@ void zuf_zii_sync(struct inode *inode, bool sync_nlink);
extern const struct inode_operations zuf_dir_inode_operations;
extern const struct inode_operations zuf_special_inode_operations;
+/* symlink.c */
+extern const struct inode_operations zuf_symlink_inode_operations;
+
#endif /*ndef __ZUF_EXTERN_H__*/
@@ -82,6 +82,9 @@ static void _set_inode_from_zi(struct inode *inode, struct zus_inode *zi)
inode->i_op = &zuf_dir_inode_operations;
inode->i_fop = &zuf_dir_operations;
break;
+ case S_IFLNK:
+ inode->i_op = &zuf_symlink_inode_operations;
+ break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
@@ -357,6 +360,10 @@ struct inode *zuf_new_inode(struct inode *dir, umode_t mode,
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
init_special_inode(inode, mode, rdev_or_isize);
+ } else if (symname) {
+ inode->i_size = rdev_or_isize;
+ nump = zuf_prepare_symname(&ioc_new_inode, symname,
+ rdev_or_isize, pages);
}
err = _set_zi_from_inode(dir, &ioc_new_inode.zi, inode);
@@ -164,6 +164,32 @@ static int zuf_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
return 0;
}
+static int zuf_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ struct inode *inode;
+ ulong len;
+
+ zuf_dbg_vfs("[%ld] de->name=%s symname=%s\n",
+ dir->i_ino, dentry->d_name.name, symname);
+
+ len = strlen(symname);
+ if (len + 1 > ZUFS_MAX_SYMLINK)
+ return -ENAMETOOLONG;
+
+ inode = zuf_new_inode(dir, S_IFLNK|S_IRWXUGO, &dentry->d_name,
+ symname, len, false);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ inode->i_op = &zuf_symlink_inode_operations;
+ inode->i_mapping->a_ops = &zuf_aops;
+
+ _instantiate_unlock(dentry, inode);
+
+ return 0;
+}
+
static int zuf_link(struct dentry *dest_dentry, struct inode *dir,
struct dentry *dentry)
{
@@ -385,6 +411,7 @@ const struct inode_operations zuf_dir_inode_operations = {
.lookup = zuf_lookup,
.link = zuf_link,
.unlink = zuf_unlink,
+ .symlink = zuf_symlink,
.mkdir = zuf_mkdir,
.rmdir = zuf_rmdir,
.mknod = zuf_mknod,
new file mode 100644
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BRIEF DESCRIPTION
+ *
+ * Symlink operations
+ *
+ * Copyright (c) 2018 NetApp Inc. All rights reserved.
+ *
+ * ZUFS-License: GPL-2.0. See module.c for LICENSE details.
+ *
+ * Authors:
+ * Boaz Harrosh <boazh@netapp.com>
+ * Sagi Manole <sagim@netapp.com>"
+ */
+
+#include "zuf.h"
+
+/* Can never fail all checks already made before.
+ * Returns: The number of pages stored @pages
+ */
+uint zuf_prepare_symname(struct zufs_ioc_new_inode *ioc_new_inode,
+ const char *symname, ulong len,
+ struct page *pages[2])
+{
+ uint nump;
+
+ ioc_new_inode->zi.i_size = cpu_to_le64(len);
+ if (len < sizeof(ioc_new_inode->zi.i_symlink)) {
+ memcpy(&ioc_new_inode->zi.i_symlink, symname, len);
+ return 0;
+ }
+
+ pages[0] = virt_to_page(symname);
+ nump = 1;
+
+ ioc_new_inode->hdr.len = len;
+ ioc_new_inode->hdr.offset = (ulong)symname & (PAGE_SIZE - 1);
+
+ if (PAGE_SIZE < ioc_new_inode->hdr.offset + len) {
+ pages[1] = virt_to_page(symname + PAGE_SIZE);
+ ++nump;
+ }
+
+ return nump;
+}
+
+/*
+ * In case of short symlink, we serve it directly from zi; otherwise, read
+ * symlink value directly from pmem using dpp mapping.
+ */
+static const char *zuf_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *notused)
+{
+ const char *link;
+ struct zuf_inode_info *zii = ZUII(inode);
+
+ if (inode->i_size < sizeof(zii->zi->i_symlink))
+ return zii->zi->i_symlink;
+
+ link = zuf_dpp_t_addr(inode->i_sb, le64_to_cpu(zii->zi->i_sym_dpp));
+ if (!link) {
+ zuf_err("bad symlink: i_sym_dpp=0x%llx\n", zii->zi->i_sym_dpp);
+ return ERR_PTR(-EIO);
+ }
+ return link;
+}
+
+const struct inode_operations zuf_symlink_inode_operations = {
+ .get_link = zuf_get_link,
+ .update_time = zuf_update_time,
+ .setattr = zuf_setattr,
+ .getattr = zuf_getattr,
+};
@@ -774,6 +774,7 @@ const char *zuf_op_name(enum e_zufs_operation op)
CASE_ENUM_NAME(ZUFS_OP_REMOVE_DENTRY );
CASE_ENUM_NAME(ZUFS_OP_RENAME );
CASE_ENUM_NAME(ZUFS_OP_READDIR );
+ CASE_ENUM_NAME(ZUFS_OP_GET_SYMLINK );
CASE_ENUM_NAME(ZUFS_OP_SETATTR );
CASE_ENUM_NAME(ZUFS_OP_BREAK );
default:
@@ -338,6 +338,7 @@ enum e_zufs_operation {
ZUFS_OP_RENAME,
ZUFS_OP_READDIR,
+ ZUFS_OP_GET_SYMLINK,
ZUFS_OP_SETATTR,
ZUFS_OP_BREAK, /* Kernel telling Server to exit */