export.c 5.11 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11
/*
 * fs/isofs/export.c
 *
 *  (C) 2004  Paul Serice - The new inode scheme requires switching
 *                          from iget() to iget5_locked() which means
 *                          the NFS export operations have to be hand
 *                          coded because the default routines rely on
 *                          iget().
 *
 * The following files are helpful:
 *
12
 *     Documentation/filesystems/nfs/Exporting
Linus Torvalds's avatar
Linus Torvalds committed
13 14 15
 *     fs/exportfs/expfs.c.
 */

16
#include "isofs.h"
Linus Torvalds's avatar
Linus Torvalds committed
17 18 19 20 21 22 23 24

static struct dentry *
isofs_export_iget(struct super_block *sb,
		  unsigned long block,
		  unsigned long offset,
		  __u32 generation)
{
	struct inode *inode;
25

Linus Torvalds's avatar
Linus Torvalds committed
26 27 28
	if (block == 0)
		return ERR_PTR(-ESTALE);
	inode = isofs_iget(sb, block, offset);
29 30 31
	if (IS_ERR(inode))
		return ERR_CAST(inode);
	if (generation && inode->i_generation != generation) {
Linus Torvalds's avatar
Linus Torvalds committed
32 33 34
		iput(inode);
		return ERR_PTR(-ESTALE);
	}
35
	return d_obtain_alias(inode);
Linus Torvalds's avatar
Linus Torvalds committed
36 37 38 39 40 41 42 43 44 45 46
}

/* This function is surprisingly simple.  The trick is understanding
 * that "child" is always a directory. So, to find its parent, you
 * simply need to find its ".." entry, normalize its block and offset,
 * and return the underlying inode.  See the comments for
 * isofs_normalize_block_and_offset(). */
static struct dentry *isofs_export_get_parent(struct dentry *child)
{
	unsigned long parent_block = 0;
	unsigned long parent_offset = 0;
47
	struct inode *child_inode = d_inode(child);
Linus Torvalds's avatar
Linus Torvalds committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
	struct iso_inode_info *e_child_inode = ISOFS_I(child_inode);
	struct iso_directory_record *de = NULL;
	struct buffer_head * bh = NULL;
	struct dentry *rv = NULL;

	/* "child" must always be a directory. */
	if (!S_ISDIR(child_inode->i_mode)) {
		printk(KERN_ERR "isofs: isofs_export_get_parent(): "
		       "child is not a directory!\n");
		rv = ERR_PTR(-EACCES);
		goto out;
	}

	/* It is an invariant that the directory offset is zero.  If
	 * it is not zero, it means the directory failed to be
	 * normalized for some reason. */
	if (e_child_inode->i_iget5_offset != 0) {
		printk(KERN_ERR "isofs: isofs_export_get_parent(): "
		       "child directory not normalized!\n");
		rv = ERR_PTR(-EACCES);
		goto out;
	}

	/* The child inode has been normalized such that its
	 * i_iget5_block value points to the "." entry.  Fortunately,
	 * the ".." entry is located in the same block. */
	parent_block = e_child_inode->i_iget5_block;

	/* Get the block in question. */
	bh = sb_bread(child_inode->i_sb, parent_block);
	if (bh == NULL) {
		rv = ERR_PTR(-EACCES);
		goto out;
	}

	/* This is the "." entry. */
	de = (struct iso_directory_record*)bh->b_data;

	/* The ".." entry is always the second entry. */
	parent_offset = (unsigned long)isonum_711(de->length);
	de = (struct iso_directory_record*)(bh->b_data + parent_offset);

	/* Verify it is in fact the ".." entry. */
	if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) {
		printk(KERN_ERR "isofs: Unable to find the \"..\" "
		       "directory for NFS.\n");
		rv = ERR_PTR(-EACCES);
		goto out;
	}

	/* Normalize */
	isofs_normalize_block_and_offset(de, &parent_block, &parent_offset);

101 102
	rv = d_obtain_alias(isofs_iget(child_inode->i_sb, parent_block,
				     parent_offset));
Linus Torvalds's avatar
Linus Torvalds committed
103
 out:
104
	if (bh)
Linus Torvalds's avatar
Linus Torvalds committed
105 106 107 108 109
		brelse(bh);
	return rv;
}

static int
Al Viro's avatar
Al Viro committed
110
isofs_export_encode_fh(struct inode *inode,
Linus Torvalds's avatar
Linus Torvalds committed
111 112
		       __u32 *fh32,
		       int *max_len,
Al Viro's avatar
Al Viro committed
113
		       struct inode *parent)
Linus Torvalds's avatar
Linus Torvalds committed
114 115 116 117 118 119 120 121 122 123 124 125
{
	struct iso_inode_info * ei = ISOFS_I(inode);
	int len = *max_len;
	int type = 1;
	__u16 *fh16 = (__u16*)fh32;

	/*
	 * WARNING: max_len is 5 for NFSv2.  Because of this
	 * limitation, we use the lower 16 bits of fh32[1] to hold the
	 * offset of the inode and the upper 16 bits of fh32[1] to
	 * hold the offset of the parent.
	 */
Al Viro's avatar
Al Viro committed
126
	if (parent && (len < 5)) {
127
		*max_len = 5;
128
		return FILEID_INVALID;
129 130
	} else if (len < 3) {
		*max_len = 3;
131
		return FILEID_INVALID;
132
	}
Linus Torvalds's avatar
Linus Torvalds committed
133 134 135 136

	len = 3;
	fh32[0] = ei->i_iget5_block;
 	fh16[2] = (__u16)ei->i_iget5_offset;  /* fh16 [sic] */
137
	fh16[3] = 0;  /* avoid leaking uninitialized data */
Linus Torvalds's avatar
Linus Torvalds committed
138
	fh32[2] = inode->i_generation;
Al Viro's avatar
Al Viro committed
139
	if (parent) {
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142 143 144 145 146 147 148 149 150 151
		struct iso_inode_info *eparent;
		eparent = ISOFS_I(parent);
		fh32[3] = eparent->i_iget5_block;
		fh16[3] = (__u16)eparent->i_iget5_offset;  /* fh16 [sic] */
		fh32[4] = parent->i_generation;
		len = 5;
		type = 2;
	}
	*max_len = len;
	return type;
}

152 153 154 155 156 157 158 159
struct isofs_fid {
	u32 block;
	u16 offset;
	u16 parent_offset;
	u32 generation;
	u32 parent_block;
	u32 parent_generation;
};
Linus Torvalds's avatar
Linus Torvalds committed
160

161 162
static struct dentry *isofs_fh_to_dentry(struct super_block *sb,
	struct fid *fid, int fh_len, int fh_type)
Linus Torvalds's avatar
Linus Torvalds committed
163
{
164
	struct isofs_fid *ifid = (struct isofs_fid *)fid;
Linus Torvalds's avatar
Linus Torvalds committed
165

166
	if (fh_len < 3 || fh_type > 2)
Linus Torvalds's avatar
Linus Torvalds committed
167 168
		return NULL;

169 170
	return isofs_export_iget(sb, ifid->block, ifid->offset,
			ifid->generation);
Linus Torvalds's avatar
Linus Torvalds committed
171 172
}

173 174 175 176 177
static struct dentry *isofs_fh_to_parent(struct super_block *sb,
		struct fid *fid, int fh_len, int fh_type)
{
	struct isofs_fid *ifid = (struct isofs_fid *)fid;

178
	if (fh_len < 2 || fh_type != 2)
179 180 181 182 183 184 185
		return NULL;

	return isofs_export_iget(sb,
			fh_len > 2 ? ifid->parent_block : 0,
			ifid->parent_offset,
			fh_len > 4 ? ifid->parent_generation : 0);
}
Linus Torvalds's avatar
Linus Torvalds committed
186

187
const struct export_operations isofs_export_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
188
	.encode_fh	= isofs_export_encode_fh,
189 190
	.fh_to_dentry	= isofs_fh_to_dentry,
	.fh_to_parent	= isofs_fh_to_parent,
Linus Torvalds's avatar
Linus Torvalds committed
191 192
	.get_parent     = isofs_export_get_parent,
};