diff --git a/kmod/src/Makefile b/kmod/src/Makefile index d8eecdf5..e44808c5 100644 --- a/kmod/src/Makefile +++ b/kmod/src/Makefile @@ -6,8 +6,8 @@ CFLAGS_super.o = -DSCOUTFS_GIT_DESCRIBE=\"$(SCOUTFS_GIT_DESCRIBE)\" \ CFLAGS_scoutfs_trace.o = -I$(src) # define_trace.h double include scoutfs-y += alloc.o bio.o btree.o client.o compact.o counters.o data.o dir.o \ - dlmglue.o file.o kvec.o inode.o ioctl.o item.o key.o lock.o \ - manifest.o msg.o options.o per_task.o seg.o server.o \ + dlmglue.o export.o file.o kvec.o inode.o ioctl.o item.o key.o \ + lock.o manifest.o msg.o options.o per_task.o seg.o server.o \ scoutfs_trace.o sock.o sort_priv.o stackglue.o super.o sysfs.o \ trans.o triggers.o xattr.o diff --git a/kmod/src/dir.c b/kmod/src/dir.c index 12a4d90f..07f3c062 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -1089,9 +1089,9 @@ int scoutfs_symlink_drop(struct super_block *sb, u64 ino, * Callers are comfortable with the race inherent to incrementally * building up a path with individual locked backref item lookups. */ -static int add_next_linkref(struct super_block *sb, u64 ino, - u64 dir_ino, char *name, unsigned int name_len, - struct list_head *list) +int scoutfs_dir_add_next_linkref(struct super_block *sb, u64 ino, + u64 dir_ino, char *name, unsigned int name_len, + struct list_head *list) { struct scoutfs_link_backref_key last_lbkey; struct scoutfs_link_backref_entry *ent; @@ -1225,7 +1225,8 @@ retry: } /* get the next link name to the given inode */ - ret = add_next_linkref(sb, ino, dir_ino, name, name_len, list); + ret = scoutfs_dir_add_next_linkref(sb, ino, dir_ino, name, name_len, + list); if (ret < 0) goto out; @@ -1233,7 +1234,8 @@ retry: par_ino = first_backref_dir_ino(list); while (par_ino != SCOUTFS_ROOT_INO) { - ret = add_next_linkref(sb, par_ino, 0, NULL, 0, list); + ret = scoutfs_dir_add_next_linkref(sb, par_ino, 0, NULL, 0, + list); if (ret < 0) { if (ret == -ENOENT) { /* restart if there was no parent component */ diff --git a/kmod/src/dir.h b/kmod/src/dir.h index 1a17fd70..79aaaa2a 100644 --- a/kmod/src/dir.h +++ b/kmod/src/dir.h @@ -20,6 +20,10 @@ int scoutfs_dir_get_backref_path(struct super_block *sb, u64 target_ino, void scoutfs_dir_free_backref_path(struct super_block *sb, struct list_head *list); +int scoutfs_dir_add_next_linkref(struct super_block *sb, u64 ino, + u64 dir_ino, char *name, unsigned int name_len, + struct list_head *list); + int scoutfs_symlink_drop(struct super_block *sb, u64 ino, struct scoutfs_lock *lock, u64 i_size); diff --git a/kmod/src/export.c b/kmod/src/export.c new file mode 100644 index 00000000..90c14cb9 --- /dev/null +++ b/kmod/src/export.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2018 Versity Software, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "export.h" +#include "inode.h" +#include "dir.h" +#include "format.h" +#include "scoutfs_trace.h" + +/* describe the length of the fileid type in terms of number of u32's used. */ +static int scoutfs_fileid_len(int fh_type) +{ + switch (fh_type) { + case FILEID_SCOUTFS: + return 2; + case FILEID_SCOUTFS_WITH_PARENT: + return 4; + } + return FILEID_INVALID; +} + +static bool scoutfs_valid_fileid(int fh_type) +{ + return scoutfs_fileid_len(fh_type) != FILEID_INVALID; +} + +static int scoutfs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, + struct inode *parent) +{ + struct scoutfs_fid *fid = (struct scoutfs_fid *)fh; + int fh_type = FILEID_SCOUTFS; + int len; + + if (parent) + fh_type = FILEID_SCOUTFS_WITH_PARENT; + + len = scoutfs_fileid_len(fh_type); + + if (*max_len < len) { + *max_len = len; + return FILEID_INVALID; + } + *max_len = len; + + fid->ino = cpu_to_le64(scoutfs_ino(inode)); + if (parent) + fid->parent_ino = cpu_to_le64(scoutfs_ino(parent)); + + trace_scoutfs_encode_fh(inode->i_sb, fh_type, fid); + + return fh_type; +} + +static struct dentry *scoutfs_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, + int fh_type) +{ + struct scoutfs_fid *sfid = (struct scoutfs_fid *)fid; + struct inode *inode = NULL; + + if (fh_len < scoutfs_fileid_len(fh_type)) + return NULL; + + trace_scoutfs_fh_to_dentry(sb, fh_type, sfid); + + if (scoutfs_valid_fileid(fh_type)) + inode = scoutfs_iget(sb, le64_to_cpu(sfid->ino)); + + return d_obtain_alias(inode); +} + +static struct dentry *scoutfs_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, + int fh_type) +{ + struct scoutfs_fid *sfid = (struct scoutfs_fid *)fid; + struct inode *inode = NULL; + + if (fh_len < scoutfs_fileid_len(fh_type)) + return NULL; + + trace_scoutfs_fh_to_parent(sb, fh_type, sfid); + + if (scoutfs_valid_fileid(fh_type) && + fh_type == FILEID_SCOUTFS_WITH_PARENT) + inode = scoutfs_iget(sb, le64_to_cpu(sfid->parent_ino)); + + return d_obtain_alias(inode); +} + +static struct dentry *scoutfs_get_parent(struct dentry *child) +{ + struct inode *inode = child->d_inode; + struct super_block *sb = inode->i_sb; + struct scoutfs_link_backref_entry *ent; + LIST_HEAD(list); + int ret; + u64 ino; + + ret = scoutfs_dir_add_next_linkref(sb, scoutfs_ino(inode), 0, NULL, 0, + &list); + if (ret) + return ERR_PTR(ret); + + ent = list_first_entry(&list, struct scoutfs_link_backref_entry, head); + ino = be64_to_cpu(ent->lbkey.dir_ino); + scoutfs_dir_free_backref_path(sb, &list); + trace_scoutfs_get_parent(sb, inode, ino); + + inode = scoutfs_iget(sb, ino); + + return d_obtain_alias(inode); +} + +static int scoutfs_get_name(struct dentry *parent, char *name, + struct dentry *child) +{ + u64 dir_ino = scoutfs_ino(parent->d_inode); + struct scoutfs_link_backref_entry *ent; + struct inode *inode = child->d_inode; + struct super_block *sb = inode->i_sb; + LIST_HEAD(list); + int ret; + + ret = scoutfs_dir_add_next_linkref(sb, scoutfs_ino(inode), dir_ino, + NULL, 0, &list); + if (ret) + return ret; + + ret = -ENOENT; + ent = list_first_entry(&list, struct scoutfs_link_backref_entry, head); + if (be64_to_cpu(ent->lbkey.ino) == scoutfs_ino(inode) && + be64_to_cpu(ent->lbkey.dir_ino) == dir_ino && + ent->name_len <= NAME_MAX) { + memcpy(name, ent->lbkey.name, ent->name_len); + name[ent->name_len] = '\0'; + ret = 0; + trace_scoutfs_get_name(sb, parent->d_inode, inode, name); + } + scoutfs_dir_free_backref_path(sb, &list); + + return ret; +} + +const struct export_operations scoutfs_export_ops = { + .encode_fh = scoutfs_encode_fh, + .fh_to_dentry = scoutfs_fh_to_dentry, + .fh_to_parent = scoutfs_fh_to_parent, + .get_parent = scoutfs_get_parent, + .get_name = scoutfs_get_name, +}; diff --git a/kmod/src/export.h b/kmod/src/export.h new file mode 100644 index 00000000..7ed9771a --- /dev/null +++ b/kmod/src/export.h @@ -0,0 +1,8 @@ +#ifndef _SCOUTFS_EXPORT_H_ +#define _SCOUTFS_EXPORT_H_ + +#include + +extern const struct export_operations scoutfs_export_ops; + +#endif diff --git a/kmod/src/format.h b/kmod/src/format.h index fc15a08a..03e49d7c 100644 --- a/kmod/src/format.h +++ b/kmod/src/format.h @@ -642,4 +642,16 @@ enum { SCOUTFS_NET_STATUS_UNKNOWN, }; +/* + * Scoutfs file handle structure - this can be copied out to userspace + * via open by handle or put on the wire from NFS. + */ +struct scoutfs_fid { + __le64 ino; + __le64 parent_ino; +} __packed; + +#define FILEID_SCOUTFS 0x81 +#define FILEID_SCOUTFS_WITH_PARENT 0x82 + #endif diff --git a/kmod/src/scoutfs_trace.h b/kmod/src/scoutfs_trace.h index a70a9ebe..def4daf7 100644 --- a/kmod/src/scoutfs_trace.h +++ b/kmod/src/scoutfs_trace.h @@ -37,6 +37,7 @@ #include "bio.h" #include "dlmglue.h" #include "stackglue.h" +#include "export.h" struct lock_info; @@ -2076,6 +2077,89 @@ DEFINE_EVENT(ocfs2_lock_res_class, ocfs2_unblock_lock, TP_PROTO(struct ocfs2_super *osb, struct ocfs2_lock_res *lockres), TP_ARGS(osb, lockres) ); + +DECLARE_EVENT_CLASS(scoutfs_fileid_class, + TP_PROTO(struct super_block *sb, int fh_type, struct scoutfs_fid *fid), + TP_ARGS(sb, fh_type, fid), + TP_STRUCT__entry( + __field(__u64, fsid) + __field(int, fh_type) + __field(u64, ino) + __field(u64, parent_ino) + ), + TP_fast_assign( + __entry->fsid = SCOUTFS_SB(sb) ? FSID_ARG(sb) : 0; + __entry->fh_type = fh_type; + __entry->ino = le64_to_cpu(fid->ino); + __entry->parent_ino = fh_type == FILEID_SCOUTFS_WITH_PARENT ? + le64_to_cpu(fid->parent_ino) : 0ULL; + ), + TP_printk("fsid "FSID_FMT" type %d ino %llu parent %llu", + __entry->fsid, __entry->fh_type, __entry->ino, + __entry->parent_ino) +); + +DEFINE_EVENT(scoutfs_fileid_class, scoutfs_encode_fh, + TP_PROTO(struct super_block *sb, int fh_type, struct scoutfs_fid *fid), + TP_ARGS(sb, fh_type, fid) +); + +DEFINE_EVENT(scoutfs_fileid_class, scoutfs_fh_to_dentry, + TP_PROTO(struct super_block *sb, int fh_type, struct scoutfs_fid *fid), + TP_ARGS(sb, fh_type, fid) +); + +DEFINE_EVENT(scoutfs_fileid_class, scoutfs_fh_to_parent, + TP_PROTO(struct super_block *sb, int fh_type, struct scoutfs_fid *fid), + TP_ARGS(sb, fh_type, fid) +); + +TRACE_EVENT(scoutfs_get_parent, + TP_PROTO(struct super_block *sb, struct inode *inode, u64 parent), + + TP_ARGS(sb, inode, parent), + + TP_STRUCT__entry( + __field(__u64, fsid) + __field(__u64, ino) + __field(__u64, parent) + ), + + TP_fast_assign( + __entry->fsid = SCOUTFS_SB(sb) ? FSID_ARG(sb) : 0; + __entry->ino = scoutfs_ino(inode); + __entry->parent = parent; + ), + + TP_printk("fsid "FSID_FMT" child %llu parent %llu", + __entry->fsid, __entry->ino, __entry->parent) +); + +TRACE_EVENT(scoutfs_get_name, + TP_PROTO(struct super_block *sb, struct inode *parent, + struct inode *child, char *name), + + TP_ARGS(sb, parent, child, name), + + TP_STRUCT__entry( + __field(__u64, fsid) + __field(__u64, parent_ino) + __field(__u64, child_ino) + __string(name, name) + ), + + TP_fast_assign( + __entry->fsid = SCOUTFS_SB(sb) ? FSID_ARG(sb) : 0; + __entry->parent_ino = scoutfs_ino(parent); + __entry->child_ino = scoutfs_ino(child); + __assign_str(name, name); + ), + + TP_printk("fsid "FSID_FMT" parent %llu child %llu name: %s", + __entry->fsid, __entry->parent_ino, __entry->child_ino, + __get_str(name)) +); + #endif /* _TRACE_SCOUTFS_H */ /* This part must be outside protection */ diff --git a/kmod/src/super.c b/kmod/src/super.c index b0c5f6af..00667954 100644 --- a/kmod/src/super.c +++ b/kmod/src/super.c @@ -22,6 +22,7 @@ #include #include "super.h" +#include "export.h" #include "format.h" #include "inode.h" #include "dir.h" @@ -297,6 +298,7 @@ static int scoutfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_magic = SCOUTFS_SUPER_MAGIC; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_op = &scoutfs_super_ops; + sb->s_export_op = &scoutfs_export_ops; /* btree blocks use long lived bh->b_data refs */ mapping_set_gfp_mask(sb->s_bdev->bd_inode->i_mapping, GFP_NOFS);