@@ -17,5 +17,5 @@ zuf-y += md.o t1.o t2.o
zuf-y += zuf-core.o zuf-root.o
# Main FS
-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
@@ -90,6 +90,10 @@ void zuf_set_inode_flags(struct inode *inode, struct zus_inode *zi);
int zuf_add_dentry(struct inode *dir, struct qstr *str, struct inode *inode);
int zuf_remove_dentry(struct inode *dir, struct qstr *str, struct inode *inode);
+/* symlink.c */
+uint zuf_prepare_symname(struct zufs_ioc_new_inode *ioc_new_inode,
+ const char *symname, ulong len, struct page *pages[2]);
+
/* t1.c */
int zuf_pmem_mmap(struct file *file, struct vm_area_struct *vma);
@@ -111,4 +115,7 @@ extern const struct inode_operations zuf_special_inode_operations;
/* dir.c */
extern const struct file_operations zuf_dir_operations;
+/* symlink.c */
+extern const struct inode_operations zuf_symlink_inode_operations;
+
#endif /*ndef __ZUF_EXTERN_H__*/
@@ -85,6 +85,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:
@@ -350,6 +353,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,
+};
The symlink support is all hidden within the creation/open of the inode. As part of ZUFS_OP_NEW_INODE we also send the requested content of the symlink for storage. On an open of an existing symlink the link information is returned within the zufs_inode structure via a zufs_dpp_t pointer. (See Documentation about zufs_dpp_t pointers) Signed-off-by: Boaz Harrosh <boazh@netapp.com> --- fs/zuf/Makefile | 2 +- fs/zuf/_extern.h | 7 +++++ fs/zuf/inode.c | 7 +++++ fs/zuf/namei.c | 27 ++++++++++++++++++ fs/zuf/symlink.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 fs/zuf/symlink.c