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: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
This commit is contained in:
Ryusuke Konishi 2010-08-27 00:23:02 +09:00
parent 4d8d9293dc
commit 8e656fd518
5 changed files with 151 additions and 71 deletions

17
fs/nilfs2/export.h Normal file
View File

@ -0,0 +1,17 @@
#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

View File

@ -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,
};

View File

@ -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 *);

View File

@ -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,

View File

@ -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 {