vfs_super.c 7.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 *  linux/fs/9p/vfs_super.c
 *
 * This file contians superblock ops for 9P2000. It is intended that
 * you mount this file system on directories.
 *
 *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
 *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
 *
 *  This program is free software; you can redistribute it and/or modify
11 12
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to:
 *  Free Software Foundation
 *  51 Franklin Street, Fifth Floor
 *  Boston, MA  02111-1301  USA
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/inet.h>
#include <linux/pagemap.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include <linux/idr.h>
A
Alexey Dobriyan 已提交
39
#include <linux/sched.h>
40
#include <linux/slab.h>
41
#include <linux/statfs.h>
M
M. Mohan Kumar 已提交
42
#include <linux/magic.h>
43 44
#include <net/9p/9p.h>
#include <net/9p/client.h>
45 46 47 48

#include "v9fs.h"
#include "v9fs_vfs.h"
#include "fid.h"
49
#include "xattr.h"
50
#include "acl.h"
51

52
static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl;
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

/**
 * v9fs_set_super - set the superblock
 * @s: super block
 * @data: file system specific data
 *
 */

static int v9fs_set_super(struct super_block *s, void *data)
{
	s->s_fs_info = data;
	return set_anon_super(s, data);
}

/**
 * v9fs_fill_super - populate superblock with info
 * @sb: superblock
 * @v9ses: session information
A
Al Viro 已提交
71
 * @flags: flags propagated from v9fs_mount()
72 73 74 75 76
 *
 */

static void
v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
77
		int flags, void *data)
78 79 80 81 82
{
	sb->s_maxbytes = MAX_LFS_FILESIZE;
	sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
	sb->s_blocksize = 1 << sb->s_blocksize_bits;
	sb->s_magic = V9FS_MAGIC;
83
	if (v9fs_proto_dotl(v9ses)) {
84
		sb->s_op = &v9fs_super_ops_dotl;
85 86
		sb->s_xattr = v9fs_xattr_handlers;
	} else
87
		sb->s_op = &v9fs_super_ops;
88
	sb->s_bdi = &v9ses->bdi;
89 90
	if (v9ses->cache)
		sb->s_bdi->ra_pages = (VM_MAX_READAHEAD * 1024)/PAGE_CACHE_SIZE;
91

92 93 94
	sb->s_flags = flags | MS_ACTIVE | MS_DIRSYNC | MS_NOATIME;
	if (!v9ses->cache)
		sb->s_flags |= MS_SYNCHRONOUS;
95

96
#ifdef CONFIG_9P_FS_POSIX_ACL
97
	if ((v9ses->flags & V9FS_ACL_MASK) == V9FS_POSIX_ACL)
98
		sb->s_flags |= MS_POSIXACL;
99 100
#endif

101
	save_mount_options(sb, data);
102 103 104
}

/**
A
Al Viro 已提交
105
 * v9fs_mount - mount a superblock
106 107 108 109 110 111 112
 * @fs_type: file system type
 * @flags: mount flags
 * @dev_name: device name that was mounted
 * @data: mount options
 *
 */

A
Al Viro 已提交
113 114
static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
		       const char *dev_name, void *data)
115 116 117 118 119 120
{
	struct super_block *sb = NULL;
	struct inode *inode = NULL;
	struct dentry *root = NULL;
	struct v9fs_session_info *v9ses = NULL;
	int mode = S_IRWXUGO | S_ISVTX;
121
	struct p9_fid *fid;
122 123
	int retval = 0;

124
	P9_DPRINTK(P9_DEBUG_VFS, " \n");
125

126
	v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL);
127
	if (!v9ses)
A
Al Viro 已提交
128
		return ERR_PTR(-ENOMEM);
129

130 131 132
	fid = v9fs_session_init(v9ses, dev_name, data);
	if (IS_ERR(fid)) {
		retval = PTR_ERR(fid);
133 134 135 136
		/*
		 * we need to call session_close to tear down some
		 * of the data structure setup by session_init
		 */
137
		goto close_session;
138 139
	}

140
	sb = sget(fs_type, NULL, v9fs_set_super, v9ses);
141 142
	if (IS_ERR(sb)) {
		retval = PTR_ERR(sb);
143
		goto clunk_fid;
144
	}
145
	v9fs_fill_super(sb, v9ses, flags, data);
146

A
Al Viro 已提交
147 148 149 150 151
	if (v9ses->cache)
		sb->s_d_op = &v9fs_cached_dentry_operations;
	else
		sb->s_d_op = &v9fs_dentry_operations;

152 153 154
	inode = v9fs_get_inode(sb, S_IFDIR | mode);
	if (IS_ERR(inode)) {
		retval = PTR_ERR(inode);
155
		goto release_sb;
156 157 158
	}
	root = d_alloc_root(inode);
	if (!root) {
159
		iput(inode);
160
		retval = -ENOMEM;
161
		goto release_sb;
162 163
	}
	sb->s_root = root;
164 165 166 167 168
	if (v9fs_proto_dotl(v9ses)) {
		struct p9_stat_dotl *st = NULL;
		st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
		if (IS_ERR(st)) {
			retval = PTR_ERR(st);
169
			goto release_sb;
170 171 172 173 174 175 176 177 178
		}

		v9fs_stat2inode_dotl(st, root->d_inode);
		kfree(st);
	} else {
		struct p9_wstat *st = NULL;
		st = p9_client_stat(fid);
		if (IS_ERR(st)) {
			retval = PTR_ERR(st);
179
			goto release_sb;
180 181 182 183 184 185 186 187
		}

		root->d_inode->i_ino = v9fs_qid2ino(&st->qid);
		v9fs_stat2inode(st, root->d_inode, sb);

		p9stat_free(st);
		kfree(st);
	}
188
	v9fs_fid_add(root, fid);
189 190 191
	retval = v9fs_get_acl(inode, fid);
	if (retval)
		goto release_sb;
192 193 194 195 196 197 198 199 200 201 202
	/*
	 * Add the root fid to session info. This is used
	 * for file system sync. We want a cloned fid here
	 * so that we can do a sync_filesystem after a
	 * shrink_dcache_for_umount
	 */
	v9ses->root_fid = v9fs_fid_clone(root);
	if (IS_ERR(v9ses->root_fid)) {
		retval = PTR_ERR(v9ses->root_fid);
		goto release_sb;
	}
203

204
	P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n");
A
Al Viro 已提交
205
	return dget(sb->s_root);
206

207 208 209 210 211
clunk_fid:
	p9_client_clunk(fid);
close_session:
	v9fs_session_close(v9ses);
	kfree(v9ses);
A
Al Viro 已提交
212
	return ERR_PTR(retval);
213
release_sb:
214
	/*
215 216
	 * we will do the session_close and root dentry
	 * release in the below call.
217
	 */
218
	deactivate_locked_super(sb);
A
Al Viro 已提交
219
	return ERR_PTR(retval);
220 221 222 223 224 225 226 227 228 229 230 231
}

/**
 * v9fs_kill_super - Kill Superblock
 * @s: superblock
 *
 */

static void v9fs_kill_super(struct super_block *s)
{
	struct v9fs_session_info *v9ses = s->s_fs_info;

232
	P9_DPRINTK(P9_DEBUG_VFS, " %p\n", s);
233 234

	kill_anon_super(s);
235
	p9_client_clunk(v9ses->root_fid);
236
	v9fs_session_cancel(v9ses);
237 238
	v9fs_session_close(v9ses);
	kfree(v9ses);
239
	s->s_fs_info = NULL;
240
	P9_DPRINTK(P9_DEBUG_VFS, "exiting kill_super\n");
241 242
}

243
static void
244
v9fs_umount_begin(struct super_block *sb)
245
{
246
	struct v9fs_session_info *v9ses;
247

248
	v9ses = sb->s_fs_info;
249
	v9fs_session_begin_cancel(v9ses);
250 251
}

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_rstatfs rs;
	int res;

	fid = v9fs_fid_lookup(dentry);
	if (IS_ERR(fid)) {
		res = PTR_ERR(fid);
		goto done;
	}

	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	if (v9fs_proto_dotl(v9ses)) {
		res = p9_client_statfs(fid, &rs);
		if (res == 0) {
M
M. Mohan Kumar 已提交
269
			buf->f_type = V9FS_MAGIC;
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
			buf->f_bsize = rs.bsize;
			buf->f_blocks = rs.blocks;
			buf->f_bfree = rs.bfree;
			buf->f_bavail = rs.bavail;
			buf->f_files = rs.files;
			buf->f_ffree = rs.ffree;
			buf->f_fsid.val[0] = rs.fsid & 0xFFFFFFFFUL;
			buf->f_fsid.val[1] = (rs.fsid >> 32) & 0xFFFFFFFFUL;
			buf->f_namelen = rs.namelen;
		}
		if (res != -ENOSYS)
			goto done;
	}
	res = simple_statfs(dentry, buf);
done:
	return res;
}

288 289 290 291 292 293 294 295
static int v9fs_sync_fs(struct super_block *sb, int wait)
{
	struct v9fs_session_info *v9ses = sb->s_fs_info;

	P9_DPRINTK(P9_DEBUG_VFS, "v9fs_sync_fs: super_block %p\n", sb);
	return p9_client_sync_fs(v9ses->root_fid);
}

296 297 298 299 300 301 302 303 304 305 306 307 308 309
static int v9fs_drop_inode(struct inode *inode)
{
	struct v9fs_session_info *v9ses;
	v9ses = v9fs_inode2v9ses(inode);
	if (v9ses->cache)
		return generic_drop_inode(inode);
	/*
	 * in case of non cached mode always drop the
	 * the inode because we want the inode attribute
	 * to always match that on the server.
	 */
	return 1;
}

310
static const struct super_operations v9fs_super_ops = {
311 312
	.alloc_inode = v9fs_alloc_inode,
	.destroy_inode = v9fs_destroy_inode,
313
	.statfs = simple_statfs,
314
	.evict_inode = v9fs_evict_inode,
315
	.show_options = generic_show_options,
316
	.umount_begin = v9fs_umount_begin,
317 318
};

319 320 321
static const struct super_operations v9fs_super_ops_dotl = {
	.alloc_inode = v9fs_alloc_inode,
	.destroy_inode = v9fs_destroy_inode,
322
	.sync_fs = v9fs_sync_fs,
323
	.statfs = v9fs_statfs,
324
	.drop_inode = v9fs_drop_inode,
325
	.evict_inode = v9fs_evict_inode,
326 327 328 329
	.show_options = generic_show_options,
	.umount_begin = v9fs_umount_begin,
};

330
struct file_system_type v9fs_fs_type = {
331
	.name = "9p",
A
Al Viro 已提交
332
	.mount = v9fs_mount,
333 334
	.kill_sb = v9fs_kill_super,
	.owner = THIS_MODULE,
335
	.fs_flags = FS_RENAME_DOES_D_MOVE,
336
};