ops_export.c 6.51 KB
Newer Older
David Teigland's avatar
David Teigland committed
1 2
/*
 * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
3
 * Copyright (C) 2004-2006 Red Hat, Inc.  All rights reserved.
David Teigland's avatar
David Teigland committed
4 5 6
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
7
 * of the GNU General Public License version 2.
David Teigland's avatar
David Teigland committed
8 9 10 11 12 13 14
 */

#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
15
#include <linux/gfs2_ondisk.h>
16
#include <linux/crc32.h>
17
#include <linux/lm_interface.h>
David Teigland's avatar
David Teigland committed
18 19

#include "gfs2.h"
20
#include "incore.h"
David Teigland's avatar
David Teigland committed
21 22 23 24
#include "dir.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
25
#include "ops_dentry.h"
David Teigland's avatar
David Teigland committed
26 27
#include "ops_export.h"
#include "rgrp.h"
28
#include "util.h"
David Teigland's avatar
David Teigland committed
29 30

static struct dentry *gfs2_decode_fh(struct super_block *sb,
31
				     __u32 *p,
David Teigland's avatar
David Teigland committed
32 33 34 35 36 37
				     int fh_len,
				     int fh_type,
				     int (*acceptable)(void *context,
						       struct dentry *dentry),
				     void *context)
{
38
	__be32 *fh = (__force __be32 *)p;
Wendy Cheng's avatar
Wendy Cheng committed
39
	struct gfs2_fh_obj fh_obj;
Al Viro's avatar
Al Viro committed
40
	struct gfs2_inum_host *this, parent;
David Teigland's avatar
David Teigland committed
41 42 43 44

	if (fh_type != fh_len)
		return NULL;

Wendy Cheng's avatar
Wendy Cheng committed
45 46
	this 		= &fh_obj.this;
	fh_obj.imode 	= DT_UNKNOWN;
David Teigland's avatar
David Teigland committed
47 48 49
	memset(&parent, 0, sizeof(struct gfs2_inum));

	switch (fh_type) {
50
	case GFS2_LARGE_FH_SIZE:
51
		parent.no_formal_ino = ((u64)be32_to_cpu(fh[4])) << 32;
David Teigland's avatar
David Teigland committed
52
		parent.no_formal_ino |= be32_to_cpu(fh[5]);
53
		parent.no_addr = ((u64)be32_to_cpu(fh[6])) << 32;
David Teigland's avatar
David Teigland committed
54
		parent.no_addr |= be32_to_cpu(fh[7]);
Wendy Cheng's avatar
Wendy Cheng committed
55
		fh_obj.imode = be32_to_cpu(fh[8]);
56
	case GFS2_SMALL_FH_SIZE:
57
		this->no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32;
Wendy Cheng's avatar
Wendy Cheng committed
58
		this->no_formal_ino |= be32_to_cpu(fh[1]);
59
		this->no_addr = ((u64)be32_to_cpu(fh[2])) << 32;
Wendy Cheng's avatar
Wendy Cheng committed
60
		this->no_addr |= be32_to_cpu(fh[3]);
David Teigland's avatar
David Teigland committed
61 62 63 64 65
		break;
	default:
		return NULL;
	}

Wendy Cheng's avatar
Wendy Cheng committed
66
	return gfs2_export_ops.find_exported_dentry(sb, &fh_obj, &parent,
David Teigland's avatar
David Teigland committed
67 68 69
						    acceptable, context);
}

70
static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len,
David Teigland's avatar
David Teigland committed
71 72
			  int connectable)
{
73
	__be32 *fh = (__force __be32 *)p;
David Teigland's avatar
David Teigland committed
74
	struct inode *inode = dentry->d_inode;
Steven Whitehouse's avatar
Steven Whitehouse committed
75
	struct super_block *sb = inode->i_sb;
76
	struct gfs2_inode *ip = GFS2_I(inode);
David Teigland's avatar
David Teigland committed
77

78 79
	if (*len < GFS2_SMALL_FH_SIZE ||
	    (connectable && *len < GFS2_LARGE_FH_SIZE))
David Teigland's avatar
David Teigland committed
80 81
		return 255;

82 83 84 85
	fh[0] = cpu_to_be32(ip->i_num.no_formal_ino >> 32);
	fh[1] = cpu_to_be32(ip->i_num.no_formal_ino & 0xFFFFFFFF);
	fh[2] = cpu_to_be32(ip->i_num.no_addr >> 32);
	fh[3] = cpu_to_be32(ip->i_num.no_addr & 0xFFFFFFFF);
86
	*len = GFS2_SMALL_FH_SIZE;
David Teigland's avatar
David Teigland committed
87

Steven Whitehouse's avatar
Steven Whitehouse committed
88
	if (!connectable || inode == sb->s_root->d_inode)
David Teigland's avatar
David Teigland committed
89 90 91 92
		return *len;

	spin_lock(&dentry->d_lock);
	inode = dentry->d_parent->d_inode;
93 94
	ip = GFS2_I(inode);
	igrab(inode);
David Teigland's avatar
David Teigland committed
95 96
	spin_unlock(&dentry->d_lock);

97 98 99 100
	fh[4] = cpu_to_be32(ip->i_num.no_formal_ino >> 32);
	fh[5] = cpu_to_be32(ip->i_num.no_formal_ino & 0xFFFFFFFF);
	fh[6] = cpu_to_be32(ip->i_num.no_addr >> 32);
	fh[7] = cpu_to_be32(ip->i_num.no_addr & 0xFFFFFFFF);
Wendy Cheng's avatar
Wendy Cheng committed
101 102 103

	fh[8]  = cpu_to_be32(inode->i_mode);
	fh[9]  = 0;	/* pad to double word */
104
	*len = GFS2_LARGE_FH_SIZE;
David Teigland's avatar
David Teigland committed
105

106
	iput(inode);
David Teigland's avatar
David Teigland committed
107 108 109 110 111

	return *len;
}

struct get_name_filldir {
Al Viro's avatar
Al Viro committed
112
	struct gfs2_inum_host inum;
David Teigland's avatar
David Teigland committed
113 114 115
	char *name;
};

116 117
static int get_name_filldir(void *opaque, const char *name, int length,
			    loff_t offset, u64 inum, unsigned int type)
David Teigland's avatar
David Teigland committed
118
{
119
	struct get_name_filldir *gnfd = opaque;
David Teigland's avatar
David Teigland committed
120

121
	if (inum != gnfd->inum.no_addr)
David Teigland's avatar
David Teigland committed
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
		return 0;

	memcpy(gnfd->name, name, length);
	gnfd->name[length] = 0;

	return 1;
}

static int gfs2_get_name(struct dentry *parent, char *name,
			 struct dentry *child)
{
	struct inode *dir = parent->d_inode;
	struct inode *inode = child->d_inode;
	struct gfs2_inode *dip, *ip;
	struct get_name_filldir gnfd;
	struct gfs2_holder gh;
138
	u64 offset = 0;
David Teigland's avatar
David Teigland committed
139 140 141 142 143 144 145 146
	int error;

	if (!dir)
		return -EINVAL;

	if (!S_ISDIR(dir->i_mode) || !inode)
		return -EINVAL;

147 148
	dip = GFS2_I(dir);
	ip = GFS2_I(inode);
David Teigland's avatar
David Teigland committed
149 150 151 152 153 154 155 156 157

	*name = 0;
	gnfd.inum = ip->i_num;
	gnfd.name = name;

	error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &gh);
	if (error)
		return error;

158
	error = gfs2_dir_read(dir, &offset, &gnfd, get_name_filldir);
David Teigland's avatar
David Teigland committed
159 160 161 162 163 164 165 166 167 168 169

	gfs2_glock_dq_uninit(&gh);

	if (!error && !*name)
		error = -ENOENT;

	return error;
}

static struct dentry *gfs2_get_parent(struct dentry *child)
{
170
	struct qstr dotdot;
David Teigland's avatar
David Teigland committed
171 172 173
	struct inode *inode;
	struct dentry *dentry;

174
	gfs2_str2qstr(&dotdot, "..");
175 176 177 178
	inode = gfs2_lookupi(child->d_inode, &dotdot, 1, NULL);

	if (!inode)
		return ERR_PTR(-ENOENT);
179 180 181 182
	/*
	 * In case of an error, @inode carries the error value, and we
	 * have to return that as a(n invalid) pointer to dentry.
	 */
183 184
	if (IS_ERR(inode))
		return ERR_PTR(PTR_ERR(inode));
David Teigland's avatar
David Teigland committed
185 186 187 188 189 190 191

	dentry = d_alloc_anon(inode);
	if (!dentry) {
		iput(inode);
		return ERR_PTR(-ENOMEM);
	}

192
	dentry->d_op = &gfs2_dops;
David Teigland's avatar
David Teigland committed
193 194 195
	return dentry;
}

Wendy Cheng's avatar
Wendy Cheng committed
196
static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj)
David Teigland's avatar
David Teigland committed
197
{
198
	struct gfs2_sbd *sdp = sb->s_fs_info;
Wendy Cheng's avatar
Wendy Cheng committed
199
	struct gfs2_fh_obj *fh_obj = (struct gfs2_fh_obj *)inum_obj;
Al Viro's avatar
Al Viro committed
200
	struct gfs2_inum_host *inum = &fh_obj->this;
David Teigland's avatar
David Teigland committed
201 202 203 204 205 206 207 208
	struct gfs2_holder i_gh, ri_gh, rgd_gh;
	struct gfs2_rgrpd *rgd;
	struct inode *inode;
	struct dentry *dentry;
	int error;

	/* System files? */

209
	inode = gfs2_ilookup(sb, inum);
David Teigland's avatar
David Teigland committed
210
	if (inode) {
211
		if (GFS2_I(inode)->i_num.no_formal_ino != inum->no_formal_ino) {
David Teigland's avatar
David Teigland committed
212 213 214 215 216 217
			iput(inode);
			return ERR_PTR(-ESTALE);
		}
		goto out_inode;
	}

218
	error = gfs2_glock_nq_num(sdp, inum->no_addr, &gfs2_inode_glops,
219
				  LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
David Teigland's avatar
David Teigland committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	if (error)
		return ERR_PTR(error);

	error = gfs2_rindex_hold(sdp, &ri_gh);
	if (error)
		goto fail;

	error = -EINVAL;
	rgd = gfs2_blk2rgrpd(sdp, inum->no_addr);
	if (!rgd)
		goto fail_rindex;

	error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh);
	if (error)
		goto fail_rindex;

	error = -ESTALE;
	if (gfs2_get_block_type(rgd, inum->no_addr) != GFS2_BLKST_DINODE)
		goto fail_rgd;

	gfs2_glock_dq_uninit(&rgd_gh);
	gfs2_glock_dq_uninit(&ri_gh);

Wendy Cheng's avatar
Wendy Cheng committed
243
	inode = gfs2_inode_lookup(sb, inum, fh_obj->imode);
244 245 246 247
	if (!inode)
		goto fail;
	if (IS_ERR(inode)) {
		error = PTR_ERR(inode);
David Teigland's avatar
David Teigland committed
248
		goto fail;
249
	}
David Teigland's avatar
David Teigland committed
250

251
	error = gfs2_inode_refresh(GFS2_I(inode));
David Teigland's avatar
David Teigland committed
252
	if (error) {
253
		iput(inode);
David Teigland's avatar
David Teigland committed
254 255 256 257
		goto fail;
	}

	error = -EIO;
258 259
	if (GFS2_I(inode)->i_di.di_flags & GFS2_DIF_SYSTEM) {
		iput(inode);
David Teigland's avatar
David Teigland committed
260 261 262 263 264
		goto fail;
	}

	gfs2_glock_dq_uninit(&i_gh);

265
out_inode:
David Teigland's avatar
David Teigland committed
266 267 268 269 270 271
	dentry = d_alloc_anon(inode);
	if (!dentry) {
		iput(inode);
		return ERR_PTR(-ENOMEM);
	}

272
	dentry->d_op = &gfs2_dops;
David Teigland's avatar
David Teigland committed
273 274
	return dentry;

275
fail_rgd:
David Teigland's avatar
David Teigland committed
276 277
	gfs2_glock_dq_uninit(&rgd_gh);

278
fail_rindex:
David Teigland's avatar
David Teigland committed
279 280
	gfs2_glock_dq_uninit(&ri_gh);

281
fail:
David Teigland's avatar
David Teigland committed
282 283 284 285 286 287 288 289 290 291 292 293
	gfs2_glock_dq_uninit(&i_gh);
	return ERR_PTR(error);
}

struct export_operations gfs2_export_ops = {
	.decode_fh = gfs2_decode_fh,
	.encode_fh = gfs2_encode_fh,
	.get_name = gfs2_get_name,
	.get_parent = gfs2_get_parent,
	.get_dentry = gfs2_get_dentry,
};