inode.c 10.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5
/* -*- c -*- --------------------------------------------------------------- *
 *
 * linux/fs/autofs/inode.c
 *
 *  Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
6
 *  Copyright 2005-2006 Ian Kent <raven@themaw.net>
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11 12 13 14 15 16
 *
 * This file is part of the Linux kernel and is made available under
 * the terms of the GNU General Public License, version 2, or at your
 * option, any later version, incorporated herein by reference.
 *
 * ------------------------------------------------------------------------- */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/file.h>
17
#include <linux/seq_file.h>
Linus Torvalds's avatar
Linus Torvalds committed
18 19 20
#include <linux/pagemap.h>
#include <linux/parser.h>
#include <linux/bitops.h>
21
#include <linux/magic.h>
Linus Torvalds's avatar
Linus Torvalds committed
22 23 24 25 26
#include "autofs_i.h"
#include <linux/module.h>

static void ino_lnkfree(struct autofs_info *ino)
{
27 28 29 30
	if (ino->u.symlink) {
		kfree(ino->u.symlink);
		ino->u.symlink = NULL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
}

struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
				     struct autofs_sb_info *sbi, mode_t mode)
{
	int reinit = 1;

	if (ino == NULL) {
		reinit = 0;
		ino = kmalloc(sizeof(*ino), GFP_KERNEL);
	}

	if (ino == NULL)
		return NULL;

46 47 48 49 50 51 52 53 54
	if (!reinit) {
		ino->flags = 0;
		ino->inode = NULL;
		ino->dentry = NULL;
		ino->size = 0;
		INIT_LIST_HEAD(&ino->active);
		INIT_LIST_HEAD(&ino->expiring);
		atomic_set(&ino->count, 0);
	}
55

56
	ino->mode = mode;
Linus Torvalds's avatar
Linus Torvalds committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
	ino->last_used = jiffies;

	ino->sbi = sbi;

	if (reinit && ino->free)
		(ino->free)(ino);

	memset(&ino->u, 0, sizeof(ino->u));

	ino->free = NULL;

	if (S_ISLNK(mode))
		ino->free = ino_lnkfree;

	return ino;
}

void autofs4_free_ino(struct autofs_info *ino)
{
76 77
	struct autofs_info *p_ino;

Linus Torvalds's avatar
Linus Torvalds committed
78 79
	if (ino->dentry) {
		ino->dentry->d_fsdata = NULL;
80 81 82 83 84 85 86
		if (ino->dentry->d_inode) {
			struct dentry *parent = ino->dentry->d_parent;
			if (atomic_dec_and_test(&ino->count)) {
				p_ino = autofs4_dentry_ino(parent);
				if (p_ino && parent != ino->dentry)
					atomic_dec(&p_ino->count);
			}
Linus Torvalds's avatar
Linus Torvalds committed
87
			dput(ino->dentry);
88
		}
Linus Torvalds's avatar
Linus Torvalds committed
89 90 91 92 93 94 95
		ino->dentry = NULL;
	}
	if (ino->free)
		(ino->free)(ino);
	kfree(ino);
}

96 97 98 99 100 101 102 103
/*
 * Deal with the infamous "Busy inodes after umount ..." message.
 *
 * Clean up the dentry tree. This happens with autofs if the user
 * space program goes away due to a SIGKILL, SIGSEGV etc.
 */
static void autofs4_force_release(struct autofs_sb_info *sbi)
{
104
	struct dentry *this_parent = sbi->sb->s_root;
105 106
	struct list_head *next;

107 108 109
	if (!sbi->sb->s_root)
		return;

110 111 112 113 114
	spin_lock(&dcache_lock);
repeat:
	next = this_parent->d_subdirs.next;
resume:
	while (next != &this_parent->d_subdirs) {
115
		struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137

		/* Negative dentry - don`t care */
		if (!simple_positive(dentry)) {
			next = next->next;
			continue;
		}

		if (!list_empty(&dentry->d_subdirs)) {
			this_parent = dentry;
			goto repeat;
		}

		next = next->next;
		spin_unlock(&dcache_lock);

		DPRINTK("dentry %p %.*s",
			dentry, (int)dentry->d_name.len, dentry->d_name.name);

		dput(dentry);
		spin_lock(&dcache_lock);
	}

138
	if (this_parent != sbi->sb->s_root) {
139 140
		struct dentry *dentry = this_parent;

141
		next = this_parent->d_u.d_child.next;
142 143 144 145 146 147 148 149 150 151 152
		this_parent = this_parent->d_parent;
		spin_unlock(&dcache_lock);
		DPRINTK("parent dentry %p %.*s",
			dentry, (int)dentry->d_name.len, dentry->d_name.name);
		dput(dentry);
		spin_lock(&dcache_lock);
		goto resume;
	}
	spin_unlock(&dcache_lock);
}

153
void autofs4_kill_sb(struct super_block *sb)
Linus Torvalds's avatar
Linus Torvalds committed
154 155 156
{
	struct autofs_sb_info *sbi = autofs4_sbi(sb);

157 158 159
	/*
	 * In the event of a failure in get_sb_nodev the superblock
	 * info is not present so nothing else has been setup, so
160 161
	 * just call kill_anon_super when we are called from
	 * deactivate_super.
162 163
	 */
	if (!sbi)
164
		goto out_kill_sb;
165

Ian Kent's avatar
Ian Kent committed
166 167
	/* Free wait queues, close pipe */
	autofs4_catatonic_mode(sbi);
Linus Torvalds's avatar
Linus Torvalds committed
168

169
	/* Clean up and release dangling references */
170
	autofs4_force_release(sbi);
171

172
	sb->s_fs_info = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
173 174
	kfree(sbi);

175
out_kill_sb:
Linus Torvalds's avatar
Linus Torvalds committed
176
	DPRINTK("shutting down");
177
	kill_anon_super(sb);
Linus Torvalds's avatar
Linus Torvalds committed
178 179
}

180 181 182
static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt)
{
	struct autofs_sb_info *sbi = autofs4_sbi(mnt->mnt_sb);
183
	struct inode *root_inode = mnt->mnt_sb->s_root->d_inode;
184 185 186 187 188

	if (!sbi)
		return 0;

	seq_printf(m, ",fd=%d", sbi->pipefd);
189 190 191 192
	if (root_inode->i_uid != 0)
		seq_printf(m, ",uid=%u", root_inode->i_uid);
	if (root_inode->i_gid != 0)
		seq_printf(m, ",gid=%u", root_inode->i_gid);
193 194 195 196 197
	seq_printf(m, ",pgrp=%d", sbi->oz_pgrp);
	seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ);
	seq_printf(m, ",minproto=%d", sbi->min_proto);
	seq_printf(m, ",maxproto=%d", sbi->max_proto);

198
	if (sbi->type & AUTOFS_TYPE_OFFSET)
199
		seq_printf(m, ",offset");
200
	else if (sbi->type & AUTOFS_TYPE_DIRECT)
201 202 203 204
		seq_printf(m, ",direct");
	else
		seq_printf(m, ",indirect");

205 206 207
	return 0;
}

208
static const struct super_operations autofs4_sops = {
Linus Torvalds's avatar
Linus Torvalds committed
209
	.statfs		= simple_statfs,
210
	.show_options	= autofs4_show_options,
Linus Torvalds's avatar
Linus Torvalds committed
211 212
};

213 214
enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto,
	Opt_indirect, Opt_direct, Opt_offset};
Linus Torvalds's avatar
Linus Torvalds committed
215

216
static const match_table_t tokens = {
Linus Torvalds's avatar
Linus Torvalds committed
217 218 219 220 221 222
	{Opt_fd, "fd=%u"},
	{Opt_uid, "uid=%u"},
	{Opt_gid, "gid=%u"},
	{Opt_pgrp, "pgrp=%u"},
	{Opt_minproto, "minproto=%u"},
	{Opt_maxproto, "maxproto=%u"},
223 224 225
	{Opt_indirect, "indirect"},
	{Opt_direct, "direct"},
	{Opt_offset, "offset"},
Linus Torvalds's avatar
Linus Torvalds committed
226 227 228 229
	{Opt_err, NULL}
};

static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
230
		pid_t *pgrp, unsigned int *type, int *minproto, int *maxproto)
Linus Torvalds's avatar
Linus Torvalds committed
231 232 233 234 235 236 237
{
	char *p;
	substring_t args[MAX_OPT_ARGS];
	int option;

	*uid = current->uid;
	*gid = current->gid;
238
	*pgrp = task_pgrp_nr(current);
Linus Torvalds's avatar
Linus Torvalds committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283

	*minproto = AUTOFS_MIN_PROTO_VERSION;
	*maxproto = AUTOFS_MAX_PROTO_VERSION;

	*pipefd = -1;

	if (!options)
		return 1;

	while ((p = strsep(&options, ",")) != NULL) {
		int token;
		if (!*p)
			continue;

		token = match_token(p, tokens, args);
		switch (token) {
		case Opt_fd:
			if (match_int(args, pipefd))
				return 1;
			break;
		case Opt_uid:
			if (match_int(args, &option))
				return 1;
			*uid = option;
			break;
		case Opt_gid:
			if (match_int(args, &option))
				return 1;
			*gid = option;
			break;
		case Opt_pgrp:
			if (match_int(args, &option))
				return 1;
			*pgrp = option;
			break;
		case Opt_minproto:
			if (match_int(args, &option))
				return 1;
			*minproto = option;
			break;
		case Opt_maxproto:
			if (match_int(args, &option))
				return 1;
			*maxproto = option;
			break;
284
		case Opt_indirect:
285
			*type = AUTOFS_TYPE_INDIRECT;
286 287
			break;
		case Opt_direct:
288
			*type = AUTOFS_TYPE_DIRECT;
289 290
			break;
		case Opt_offset:
291
			*type = AUTOFS_TYPE_DIRECT | AUTOFS_TYPE_OFFSET;
292
			break;
Linus Torvalds's avatar
Linus Torvalds committed
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
		default:
			return 1;
		}
	}
	return (*pipefd < 0);
}

static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
{
	struct autofs_info *ino;

	ino = autofs4_init_ino(NULL, sbi, S_IFDIR | 0755);
	if (!ino)
		return NULL;

	return ino;
}

311 312 313 314
static struct dentry_operations autofs4_sb_dentry_operations = {
	.d_release      = autofs4_dentry_release,
};

Linus Torvalds's avatar
Linus Torvalds committed
315 316 317 318 319 320 321 322 323
int autofs4_fill_super(struct super_block *s, void *data, int silent)
{
	struct inode * root_inode;
	struct dentry * root;
	struct file * pipe;
	int pipefd;
	struct autofs_sb_info *sbi;
	struct autofs_info *ino;

324
	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
325
	if (!sbi)
Linus Torvalds's avatar
Linus Torvalds committed
326 327 328 329 330
		goto fail_unlock;
	DPRINTK("starting up, sbi = %p",sbi);

	s->s_fs_info = sbi;
	sbi->magic = AUTOFS_SBI_MAGIC;
331
	sbi->pipefd = -1;
332 333
	sbi->pipe = NULL;
	sbi->catatonic = 1;
Linus Torvalds's avatar
Linus Torvalds committed
334
	sbi->exp_timeout = 0;
335
	sbi->oz_pgrp = task_pgrp_nr(current);
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338
	sbi->sb = s;
	sbi->version = 0;
	sbi->sub_version = 0;
339
	sbi->type = 0;
340 341
	sbi->min_proto = 0;
	sbi->max_proto = 0;
342
	mutex_init(&sbi->wq_mutex);
343
	spin_lock_init(&sbi->fs_lock);
Linus Torvalds's avatar
Linus Torvalds committed
344
	sbi->queues = NULL;
345
	spin_lock_init(&sbi->lookup_lock);
346
	INIT_LIST_HEAD(&sbi->active_list);
347
	INIT_LIST_HEAD(&sbi->expiring_list);
Linus Torvalds's avatar
Linus Torvalds committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361
	s->s_blocksize = 1024;
	s->s_blocksize_bits = 10;
	s->s_magic = AUTOFS_SUPER_MAGIC;
	s->s_op = &autofs4_sops;
	s->s_time_gran = 1;

	/*
	 * Get the root inode and dentry, but defer checking for errors.
	 */
	ino = autofs4_mkroot(sbi);
	if (!ino)
		goto fail_free;
	root_inode = autofs4_get_inode(s, ino);
	if (!root_inode)
362
		goto fail_ino;
Linus Torvalds's avatar
Linus Torvalds committed
363 364 365 366

	root = d_alloc_root(root_inode);
	if (!root)
		goto fail_iput;
367 368 369 370
	pipe = NULL;

	root->d_op = &autofs4_sb_dentry_operations;
	root->d_fsdata = ino;
Linus Torvalds's avatar
Linus Torvalds committed
371 372

	/* Can this call block? */
373 374 375
	if (parse_options(data, &pipefd, &root_inode->i_uid, &root_inode->i_gid,
				&sbi->oz_pgrp, &sbi->type, &sbi->min_proto,
				&sbi->max_proto)) {
Linus Torvalds's avatar
Linus Torvalds committed
376 377 378 379
		printk("autofs: called with bogus options\n");
		goto fail_dput;
	}

380
	root_inode->i_fop = &autofs4_root_operations;
381
	root_inode->i_op = sbi->type & AUTOFS_TYPE_DIRECT ?
382 383 384
			&autofs4_direct_root_inode_operations :
			&autofs4_indirect_root_inode_operations;

Linus Torvalds's avatar
Linus Torvalds committed
385
	/* Couldn't this be tested earlier? */
386 387
	if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
	    sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) {
Linus Torvalds's avatar
Linus Torvalds committed
388 389
		printk("autofs: kernel does not match daemon version "
		       "daemon (%d, %d) kernel (%d, %d)\n",
390
			sbi->min_proto, sbi->max_proto,
Linus Torvalds's avatar
Linus Torvalds committed
391 392 393 394
			AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
		goto fail_dput;
	}

395 396 397 398 399
	/* Establish highest kernel protocol version */
	if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION)
		sbi->version = AUTOFS_MAX_PROTO_VERSION;
	else
		sbi->version = sbi->max_proto;
Linus Torvalds's avatar
Linus Torvalds committed
400 401 402 403 404
	sbi->sub_version = AUTOFS_PROTO_SUBVERSION;

	DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp);
	pipe = fget(pipefd);
	
405
	if (!pipe) {
Linus Torvalds's avatar
Linus Torvalds committed
406 407 408
		printk("autofs: could not open pipe file descriptor\n");
		goto fail_dput;
	}
409
	if (!pipe->f_op || !pipe->f_op->write)
Linus Torvalds's avatar
Linus Torvalds committed
410 411
		goto fail_fput;
	sbi->pipe = pipe;
412
	sbi->pipefd = pipefd;
413
	sbi->catatonic = 0;
Linus Torvalds's avatar
Linus Torvalds committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433

	/*
	 * Success! Install the root dentry now to indicate completion.
	 */
	s->s_root = root;
	return 0;
	
	/*
	 * Failure ... clean up.
	 */
fail_fput:
	printk("autofs: pipe file descriptor does not contain proper ops\n");
	fput(pipe);
	/* fall through */
fail_dput:
	dput(root);
	goto fail_free;
fail_iput:
	printk("autofs: get root dentry failed\n");
	iput(root_inode);
434 435
fail_ino:
	kfree(ino);
Linus Torvalds's avatar
Linus Torvalds committed
436 437
fail_free:
	kfree(sbi);
438
	s->s_fs_info = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
fail_unlock:
	return -EINVAL;
}

struct inode *autofs4_get_inode(struct super_block *sb,
				struct autofs_info *inf)
{
	struct inode *inode = new_inode(sb);

	if (inode == NULL)
		return NULL;

	inf->inode = inode;
	inode->i_mode = inf->mode;
	if (sb->s_root) {
		inode->i_uid = sb->s_root->d_inode->i_uid;
		inode->i_gid = sb->s_root->d_inode->i_gid;
	} else {
		inode->i_uid = 0;
		inode->i_gid = 0;
	}
	inode->i_blocks = 0;
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;

	if (S_ISDIR(inf->mode)) {
		inode->i_nlink = 2;
		inode->i_op = &autofs4_dir_inode_operations;
		inode->i_fop = &autofs4_dir_operations;
	} else if (S_ISLNK(inf->mode)) {
		inode->i_size = inf->size;
		inode->i_op = &autofs4_symlink_inode_operations;
	}

	return inode;
}