mirror of
https://github.com/versity/scoutfs.git
synced 2026-06-03 18:26:21 +00:00
scoutfs: open by handle
This is implemented by filling in our export ops functions. When we get those right, the VFS handles most of the details for us. Internally, scoutfs handles are two u64's (ino and parent ino) and a type which indicates whether the handle contains the parent ino or not. Surpisingly enough, no existing type matches this pattern so we use our own types to identify the handle. Most of the export ops are self explanatory scoutfs_encode_fh() takes an inode and an optional parent and encodes those into the smallest handle that would fit. scoutfs_fh_to_[dentry|parent] turn an existing file handle into a dentry. scoutfs_get_parent() is a bit different and would be called on directory inodes to connect a disconnected dentry path. For scoutfs_get_parent(), we can export add_next_linkref() and use the backref mechanism to quickly find a parent directory. scoutfs_get_name() is almost identical to scoutfs_get_parent(). Here we're linking an inode to a name which exists in the parent directory. We can also use add_next_linkref, and simply copy the name from the backref. As a result of this patch we can also now export scoutfs file systems via NFS, however testing NFS thoroughly is outside the scope of this work so export support should be considered experimental at best. Signed-off-by: Mark Fasheh <mfasheh@versity.com> [zab edited <= NAME_MAX]
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
168
kmod/src/export.c
Normal file
168
kmod/src/export.c
Normal file
@@ -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 <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/exportfs.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
8
kmod/src/export.h
Normal file
8
kmod/src/export.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef _SCOUTFS_EXPORT_H_
|
||||
#define _SCOUTFS_EXPORT_H_
|
||||
|
||||
#include <linux/exportfs.h>
|
||||
|
||||
extern const struct export_operations scoutfs_export_ops;
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#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);
|
||||
|
||||
Reference in New Issue
Block a user