Files
scoutfs/kmod/src/export.c
Zach Brown df6a8af71f scoutfs: remove name from dirent keys
Directory entries were the last items that had large variable length
keys because they stored the entry name in the key.  We'd like to have
small fixed size keys so let's store dirents with small keys.

Entries for lookup are stored at the hash of the name instead of the
full name.  The key also contains the unique readdir pos so that we
don't have to deal with collision on creation.  The lookup procedure now
does need to iterate over all the readdir positions for the hash value
and compare the names.

Entries for link backref walking are stored with the entry's position in
the parent dir instead of the entry's name.  The name is then stored in
the value.  Inode to path conversion can still walk the backref items
without having to lookup dirent items.

These changes mean that all directory entry items are now stored at a
small key with some u64s (hash, pos, parent dir, etc) and have a value
with the dirent struct and full entry name.  This lets us use the same
key and value format for the three entry key types.  We no longer have
to allocate keys, we can store them on the stack.

We store the entry's hash and pos in the dirent struct in the item value
so that any item has all the fields to reference all the other item
keys.  We store the same values in the dentry_info so that deletion
(unlink and rename) can find all the entries.

The ino_path ioctl can now much more clearly iterate over parent
directories and entry positions instead of oh so cleverly iterating over
null terminated names in the parent directories.  The ioctl interface
structs and implementation become simpler.

Signed-off-by: Zach Brown <zab@versity.com>
2018-04-04 09:15:27 -05:00

168 lines
4.2 KiB
C

/*
* 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, 0, &list);
if (ret)
return ERR_PTR(ret);
ent = list_first_entry(&list, struct scoutfs_link_backref_entry, head);
ino = ent->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,
0, &list);
if (ret)
return ret;
ret = -ENOENT;
ent = list_first_entry(&list, struct scoutfs_link_backref_entry, head);
if (le64_to_cpu(ent->dent.ino) == scoutfs_ino(inode) &&
ent->dir_ino == dir_ino &&
ent->name_len <= NAME_MAX) {
memcpy(name, ent->dent.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,
};