Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e656fd5 authored by Ryusuke Konishi's avatar Ryusuke Konishi
Browse files

nilfs2: make snapshots in checkpoint tree exportable



The previous export operations cannot handle multiple versions of
a filesystem if they belong to the same sb instance.

This adds a new type of file handle and extends export operations so
that they can get the inode specified by a checkpoint number as well
as an inode number and a generation number.

Signed-off-by: default avatarRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
parent 4d8d9293
Loading
Loading
Loading
Loading

fs/nilfs2/export.h

0 → 100644
+17 −0
Original line number Diff line number Diff line
#ifndef NILFS_EXPORT_H
#define NILFS_EXPORT_H

#include <linux/exportfs.h>

extern const struct export_operations nilfs_export_ops;

struct nilfs_fid {
	u64 cno;
	u64 ino;
	u32 gen;

	u32 parent_gen;
	u64 parent_ino;
} __attribute__ ((packed));

#endif
+120 −17
Original line number Diff line number Diff line
@@ -40,7 +40,11 @@

#include <linux/pagemap.h>
#include "nilfs.h"
#include "export.h"

#define NILFS_FID_SIZE_NON_CONNECTABLE \
	(offsetof(struct nilfs_fid, parent_gen) / 4)
#define NILFS_FID_SIZE_CONNECTABLE	(sizeof(struct nilfs_fid) / 4)

static inline int nilfs_add_nondir(struct dentry *dentry, struct inode *inode)
{
@@ -77,23 +81,6 @@ nilfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
	return d_splice_alias(inode, dentry);
}

struct dentry *nilfs_get_parent(struct dentry *child)
{
	unsigned long ino;
	struct inode *inode;
	struct qstr dotdot = {.name = "..", .len = 2};

	ino = nilfs_inode_by_name(child->d_inode, &dotdot);
	if (!ino)
		return ERR_PTR(-ENOENT);

	inode = nilfs_iget(child->d_inode->i_sb,
			   NILFS_I(child->d_inode)->i_root, ino);
	if (IS_ERR(inode))
		return ERR_CAST(inode);
	return d_obtain_alias(inode);
}

/*
 * By the time this is called, we already have created
 * the directory cache entry for the new file, but it
@@ -469,6 +456,115 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	return err;
}

/*
 * Export operations
 */
static struct dentry *nilfs_get_parent(struct dentry *child)
{
	unsigned long ino;
	struct inode *inode;
	struct qstr dotdot = {.name = "..", .len = 2};
	struct nilfs_root *root;

	ino = nilfs_inode_by_name(child->d_inode, &dotdot);
	if (!ino)
		return ERR_PTR(-ENOENT);

	root = NILFS_I(child->d_inode)->i_root;

	inode = nilfs_iget(child->d_inode->i_sb, root, ino);
	if (IS_ERR(inode))
		return ERR_CAST(inode);

	return d_obtain_alias(inode);
}

static struct dentry *nilfs_get_dentry(struct super_block *sb, u64 cno,
				       u64 ino, u32 gen)
{
	struct nilfs_root *root;
	struct inode *inode;

	if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO)
		return ERR_PTR(-ESTALE);

	root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs, cno);
	if (!root)
		return ERR_PTR(-ESTALE);

	inode = nilfs_iget(sb, root, ino);
	nilfs_put_root(root);

	if (IS_ERR(inode))
		return ERR_CAST(inode);
	if (gen && inode->i_generation != gen) {
		iput(inode);
		return ERR_PTR(-ESTALE);
	}
	return d_obtain_alias(inode);
}

static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
					 int fh_len, int fh_type)
{
	struct nilfs_fid *fid = (struct nilfs_fid *)fh;

	if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE &&
	     fh_len != NILFS_FID_SIZE_CONNECTABLE) ||
	    (fh_type != FILEID_NILFS_WITH_PARENT &&
	     fh_type != FILEID_NILFS_WITHOUT_PARENT))
		return NULL;

	return nilfs_get_dentry(sb, fid->cno, fid->ino, fid->gen);
}

static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh,
					 int fh_len, int fh_type)
{
	struct nilfs_fid *fid = (struct nilfs_fid *)fh;

	if (fh_len != NILFS_FID_SIZE_CONNECTABLE ||
	    fh_type != FILEID_NILFS_WITH_PARENT)
		return NULL;

	return nilfs_get_dentry(sb, fid->cno, fid->parent_ino, fid->parent_gen);
}

static int nilfs_encode_fh(struct dentry *dentry, __u32 *fh, int *lenp,
			   int connectable)
{
	struct nilfs_fid *fid = (struct nilfs_fid *)fh;
	struct inode *inode = dentry->d_inode;
	struct nilfs_root *root = NILFS_I(inode)->i_root;
	int type;

	if (*lenp < NILFS_FID_SIZE_NON_CONNECTABLE ||
	    (connectable && *lenp < NILFS_FID_SIZE_CONNECTABLE))
		return 255;

	fid->cno = root->cno;
	fid->ino = inode->i_ino;
	fid->gen = inode->i_generation;

	if (connectable && !S_ISDIR(inode->i_mode)) {
		struct inode *parent;

		spin_lock(&dentry->d_lock);
		parent = dentry->d_parent->d_inode;
		fid->parent_ino = parent->i_ino;
		fid->parent_gen = parent->i_generation;
		spin_unlock(&dentry->d_lock);

		type = FILEID_NILFS_WITH_PARENT;
		*lenp = NILFS_FID_SIZE_CONNECTABLE;
	} else {
		type = FILEID_NILFS_WITHOUT_PARENT;
		*lenp = NILFS_FID_SIZE_NON_CONNECTABLE;
	}

	return type;
}

const struct inode_operations nilfs_dir_inode_operations = {
	.create		= nilfs_create,
	.lookup		= nilfs_lookup,
@@ -493,3 +589,10 @@ const struct inode_operations nilfs_symlink_inode_operations = {
	.follow_link	= page_follow_link_light,
	.put_link	= page_put_link,
};

const struct export_operations nilfs_export_ops = {
	.encode_fh = nilfs_encode_fh,
	.fh_to_dentry = nilfs_fh_to_dentry,
	.fh_to_parent = nilfs_fh_to_parent,
	.get_parent = nilfs_get_parent,
};
+0 −3
Original line number Diff line number Diff line
@@ -264,9 +264,6 @@ extern int nilfs_set_file_dirty(struct nilfs_sb_info *, struct inode *,
extern int nilfs_mark_inode_dirty(struct inode *);
extern void nilfs_dirty_inode(struct inode *);

/* namei.c */
extern struct dentry *nilfs_get_parent(struct dentry *);

/* super.c */
extern struct inode *nilfs_alloc_inode_common(struct the_nilfs *);
extern struct inode *nilfs_alloc_inode(struct super_block *);
+1 −51
Original line number Diff line number Diff line
@@ -48,10 +48,10 @@
#include <linux/vfs.h>
#include <linux/writeback.h>
#include <linux/kobject.h>
#include <linux/exportfs.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include "nilfs.h"
#include "export.h"
#include "mdt.h"
#include "alloc.h"
#include "btree.h"
@@ -556,56 +556,6 @@ static const struct super_operations nilfs_sops = {
	.show_options = nilfs_show_options
};

static struct inode *
nilfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation)
{
	struct inode *inode;
	struct nilfs_root *root;

	if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO &&
	    ino != NILFS_SKETCH_INO)
		return ERR_PTR(-ESTALE);

	root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs,
				 NILFS_CPTREE_CURRENT_CNO);
	if (!root)
		return ERR_PTR(-ESTALE);

	/* new file handle type is required to export snapshots */
	inode = nilfs_iget(sb, root, ino);
	nilfs_put_root(root);
	if (IS_ERR(inode))
		return ERR_CAST(inode);
	if (generation && inode->i_generation != generation) {
		iput(inode);
		return ERR_PTR(-ESTALE);
	}

	return inode;
}

static struct dentry *
nilfs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
		   int fh_type)
{
	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
				    nilfs_nfs_get_inode);
}

static struct dentry *
nilfs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len,
		   int fh_type)
{
	return generic_fh_to_parent(sb, fid, fh_len, fh_type,
				    nilfs_nfs_get_inode);
}

static const struct export_operations nilfs_export_ops = {
	.fh_to_dentry = nilfs_fh_to_dentry,
	.fh_to_parent = nilfs_fh_to_parent,
	.get_parent = nilfs_get_parent,
};

enum {
	Opt_err_cont, Opt_err_panic, Opt_err_ro,
	Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
+13 −0
Original line number Diff line number Diff line
@@ -67,6 +67,19 @@ enum fid_type {
	 * 32 bit parent block number, 32 bit parent generation number
	 */
	FILEID_UDF_WITH_PARENT = 0x52,

	/*
	 * 64 bit checkpoint number, 64 bit inode number,
	 * 32 bit generation number.
	 */
	FILEID_NILFS_WITHOUT_PARENT = 0x61,

	/*
	 * 64 bit checkpoint number, 64 bit inode number,
	 * 32 bit generation number, 32 bit parent generation.
	 * 64 bit parent inode number.
	 */
	FILEID_NILFS_WITH_PARENT = 0x62,
};

struct fid {