waitq.c 13.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
/* -*- c -*- --------------------------------------------------------------- *
 *
 * linux/fs/autofs/waitq.c
 *
 *  Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
6
 *  Copyright 2001-2006 Ian Kent <raven@themaw.net>
Linus Torvalds's avatar
Linus Torvalds committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * 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/slab.h>
#include <linux/time.h>
#include <linux/signal.h>
#include <linux/file.h>
#include "autofs_i.h"

/* We make this a static variable rather than a part of the superblock; it
   is better if we don't reassign numbers easily even across filesystems */
static autofs_wqt_t autofs4_next_wait_queue = 1;

/* These are the signals we allow interrupting a pending mount */
#define SHUTDOWN_SIGS	(sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))

void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
{
	struct autofs_wait_queue *wq, *nwq;

Ian Kent's avatar
Ian Kent committed
31
32
33
34
35
36
	mutex_lock(&sbi->wq_mutex);
	if (sbi->catatonic) {
		mutex_unlock(&sbi->wq_mutex);
		return;
	}

Linus Torvalds's avatar
Linus Torvalds committed
37
38
39
40
41
	DPRINTK("entering catatonic mode");

	sbi->catatonic = 1;
	wq = sbi->queues;
	sbi->queues = NULL;	/* Erase all wait queues */
42
	while (wq) {
Linus Torvalds's avatar
Linus Torvalds committed
43
44
		nwq = wq->next;
		wq->status = -ENOENT; /* Magic is gone - report failure */
45
46
47
48
		if (wq->name.name) {
			kfree(wq->name.name);
			wq->name.name = NULL;
		}
Ian Kent's avatar
Ian Kent committed
49
		wq->wait_ctr--;
Linus Torvalds's avatar
Linus Torvalds committed
50
51
52
		wake_up_interruptible(&wq->queue);
		wq = nwq;
	}
53
54
	fput(sbi->pipe);	/* Close the pipe */
	sbi->pipe = NULL;
Ian Kent's avatar
Ian Kent committed
55
56
	sbi->pipefd = -1;
	mutex_unlock(&sbi->wq_mutex);
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
}

static int autofs4_write(struct file *file, const void *addr, int bytes)
{
	unsigned long sigpipe, flags;
	mm_segment_t fs;
	const char *data = (const char *)addr;
	ssize_t wr = 0;

	/** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/

	sigpipe = sigismember(&current->pending.signal, SIGPIPE);

	/* Save pointer to user space and point back to kernel space */
	fs = get_fs();
	set_fs(KERNEL_DS);

	while (bytes &&
	       (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) {
		data += wr;
		bytes -= wr;
	}

	set_fs(fs);

	/* Keep the currently executing process from receiving a
	   SIGPIPE unless it was already supposed to get one */
	if (wr == -EPIPE && !sigpipe) {
		spin_lock_irqsave(&current->sighand->siglock, flags);
		sigdelset(&current->pending.signal, SIGPIPE);
		recalc_sigpending();
		spin_unlock_irqrestore(&current->sighand->siglock, flags);
	}

	return (bytes > 0);
}
	
static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
				 struct autofs_wait_queue *wq,
				 int type)
{
Ian Kent's avatar
Ian Kent committed
98
99
100
101
102
	union {
		struct autofs_packet_hdr hdr;
		union autofs_packet_union v4_pkt;
		union autofs_v5_packet_union v5_pkt;
	} pkt;
103
	struct file *pipe = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
104
105
106
	size_t pktsz;

	DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d",
107
		wq->wait_queue_token, wq->name.len, wq->name.name, type);
Linus Torvalds's avatar
Linus Torvalds committed
108
109
110
111
112

	memset(&pkt,0,sizeof pkt); /* For security reasons */

	pkt.hdr.proto_version = sbi->version;
	pkt.hdr.type = type;
113
114
115
116
	switch (type) {
	/* Kernel protocol v4 missing and expire packets */
	case autofs_ptype_missing:
	{
Ian Kent's avatar
Ian Kent committed
117
		struct autofs_packet_missing *mp = &pkt.v4_pkt.missing;
Linus Torvalds's avatar
Linus Torvalds committed
118
119
120
121

		pktsz = sizeof(*mp);

		mp->wait_queue_token = wq->wait_queue_token;
122
123
124
		mp->len = wq->name.len;
		memcpy(mp->name, wq->name.name, wq->name.len);
		mp->name[wq->name.len] = '\0';
125
126
127
128
		break;
	}
	case autofs_ptype_expire_multi:
	{
Ian Kent's avatar
Ian Kent committed
129
		struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi;
Linus Torvalds's avatar
Linus Torvalds committed
130
131
132
133

		pktsz = sizeof(*ep);

		ep->wait_queue_token = wq->wait_queue_token;
134
135
136
		ep->len = wq->name.len;
		memcpy(ep->name, wq->name.name, wq->name.len);
		ep->name[wq->name.len] = '\0';
137
138
139
140
141
142
143
144
145
146
147
		break;
	}
	/*
	 * Kernel protocol v5 packet for handling indirect and direct
	 * mount missing and expire requests
	 */
	case autofs_ptype_missing_indirect:
	case autofs_ptype_expire_indirect:
	case autofs_ptype_missing_direct:
	case autofs_ptype_expire_direct:
	{
Ian Kent's avatar
Ian Kent committed
148
		struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet;
149
150
151
152

		pktsz = sizeof(*packet);

		packet->wait_queue_token = wq->wait_queue_token;
153
154
155
		packet->len = wq->name.len;
		memcpy(packet->name, wq->name.name, wq->name.len);
		packet->name[wq->name.len] = '\0';
156
157
158
159
160
161
162
163
164
		packet->dev = wq->dev;
		packet->ino = wq->ino;
		packet->uid = wq->uid;
		packet->gid = wq->gid;
		packet->pid = wq->pid;
		packet->tgid = wq->tgid;
		break;
	}
	default:
Linus Torvalds's avatar
Linus Torvalds committed
165
166
167
168
		printk("autofs4_notify_daemon: bad type %d!\n", type);
		return;
	}

169
170
171
172
173
174
175
176
177
178
179
180
181
	/* Check if we have become catatonic */
	mutex_lock(&sbi->wq_mutex);
	if (!sbi->catatonic) {
		pipe = sbi->pipe;
		get_file(pipe);
	}
	mutex_unlock(&sbi->wq_mutex);

	if (pipe) {
		if (autofs4_write(pipe, &pkt, pktsz))
			autofs4_catatonic_mode(sbi);
		fput(pipe);
	}
Linus Torvalds's avatar
Linus Torvalds committed
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
}

static int autofs4_getpath(struct autofs_sb_info *sbi,
			   struct dentry *dentry, char **name)
{
	struct dentry *root = sbi->sb->s_root;
	struct dentry *tmp;
	char *buf = *name;
	char *p;
	int len = 0;

	spin_lock(&dcache_lock);
	for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent)
		len += tmp->d_name.len + 1;

197
	if (!len || --len > NAME_MAX) {
Linus Torvalds's avatar
Linus Torvalds committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
		spin_unlock(&dcache_lock);
		return 0;
	}

	*(buf + len) = '\0';
	p = buf + len - dentry->d_name.len;
	strncpy(p, dentry->d_name.name, dentry->d_name.len);

	for (tmp = dentry->d_parent; tmp != root ; tmp = tmp->d_parent) {
		*(--p) = '/';
		p -= tmp->d_name.len;
		strncpy(p, tmp->d_name.name, tmp->d_name.len);
	}
	spin_unlock(&dcache_lock);

	return len;
}

216
static struct autofs_wait_queue *
217
autofs4_find_wait(struct autofs_sb_info *sbi, struct qstr *qstr)
218
219
220
221
{
	struct autofs_wait_queue *wq;

	for (wq = sbi->queues; wq; wq = wq->next) {
222
223
224
225
		if (wq->name.hash == qstr->hash &&
		    wq->name.len == qstr->len &&
		    wq->name.name &&
			 !memcmp(wq->name.name, qstr->name, qstr->len))
226
227
228
229
230
			break;
	}
	return wq;
}

Ian Kent's avatar
Ian Kent committed
231
232
233
234
235
236
237
238
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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
 * Check if we have a valid request.
 * Returns
 * 1 if the request should continue.
 *   In this case we can return an autofs_wait_queue entry if one is
 *   found or NULL to idicate a new wait needs to be created.
 * 0 or a negative errno if the request shouldn't continue.
 */
static int validate_request(struct autofs_wait_queue **wait,
			    struct autofs_sb_info *sbi,
			    struct qstr *qstr,
			    struct dentry*dentry, enum autofs_notify notify)
{
	struct autofs_wait_queue *wq;
	struct autofs_info *ino;

	/* Wait in progress, continue; */
	wq = autofs4_find_wait(sbi, qstr);
	if (wq) {
		*wait = wq;
		return 1;
	}

	*wait = NULL;

	/* If we don't yet have any info this is a new request */
	ino = autofs4_dentry_ino(dentry);
	if (!ino)
		return 1;

	/*
	 * If we've been asked to wait on an existing expire (NFY_NONE)
	 * but there is no wait in the queue ...
	 */
	if (notify == NFY_NONE) {
		/*
		 * Either we've betean the pending expire to post it's
		 * wait or it finished while we waited on the mutex.
		 * So we need to wait till either, the wait appears
		 * or the expire finishes.
		 */

		while (ino->flags & AUTOFS_INF_EXPIRING) {
			mutex_unlock(&sbi->wq_mutex);
			schedule_timeout_interruptible(HZ/10);
			if (mutex_lock_interruptible(&sbi->wq_mutex))
				return -EINTR;

			wq = autofs4_find_wait(sbi, qstr);
			if (wq) {
				*wait = wq;
				return 1;
			}
		}

		/*
		 * Not ideal but the status has already gone. Of the two
		 * cases where we wait on NFY_NONE neither depend on the
		 * return status of the wait.
		 */
		return 0;
	}

	/*
	 * If we've been asked to trigger a mount and the request
	 * completed while we waited on the mutex ...
	 */
	if (notify == NFY_MOUNT) {
		/*
300
301
302
303
304
305
306
307
		 * If the dentry was successfully mounted while we slept
		 * on the wait queue mutex we can return success. If it
		 * isn't mounted (doesn't have submounts for the case of
		 * a multi-mount with no mount at it's base) we can
		 * continue on and create a new request.
		 */
		if (have_submounts(dentry))
			return 0;
Ian Kent's avatar
Ian Kent committed
308
309
310
311
312
	}

	return 1;
}

Linus Torvalds's avatar
Linus Torvalds committed
313
314
315
316
int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
		enum autofs_notify notify)
{
	struct autofs_wait_queue *wq;
317
	struct qstr qstr;
Linus Torvalds's avatar
Linus Torvalds committed
318
	char *name;
Ian Kent's avatar
Ian Kent committed
319
	int status, ret, type;
Linus Torvalds's avatar
Linus Torvalds committed
320
321

	/* In catatonic mode, we don't wait for nobody */
322
	if (sbi->catatonic)
Linus Torvalds's avatar
Linus Torvalds committed
323
		return -ENOENT;
Ian Kent's avatar
Ian Kent committed
324

325
326
327
328
329
330
331
332
333
	if (!dentry->d_inode) {
		/*
		 * A wait for a negative dentry is invalid for certain
		 * cases. A direct or offset mount "always" has its mount
		 * point directory created and so the request dentry must
		 * be positive or the map key doesn't exist. The situation
		 * is very similar for indirect mounts except only dentrys
		 * in the root of the autofs file system may be negative.
		 */
334
		if (autofs_type_trigger(sbi->type))
335
336
337
338
			return -ENOENT;
		else if (!IS_ROOT(dentry->d_parent))
			return -ENOENT;
	}
339

Linus Torvalds's avatar
Linus Torvalds committed
340
341
342
343
	name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
	if (!name)
		return -ENOMEM;

344
	/* If this is a direct mount request create a dummy name */
345
	if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type))
346
		qstr.len = sprintf(name, "%p", dentry);
347
	else {
348
349
		qstr.len = autofs4_getpath(sbi, dentry, &name);
		if (!qstr.len) {
350
351
352
			kfree(name);
			return -ENOENT;
		}
Linus Torvalds's avatar
Linus Torvalds committed
353
	}
354
355
	qstr.name = name;
	qstr.hash = full_name_hash(name, qstr.len);
Linus Torvalds's avatar
Linus Torvalds committed
356

Ian Kent's avatar
Ian Kent committed
357
358
	if (mutex_lock_interruptible(&sbi->wq_mutex)) {
		kfree(qstr.name);
Linus Torvalds's avatar
Linus Torvalds committed
359
		return -EINTR;
Ian Kent's avatar
Ian Kent committed
360
	}
361

Ian Kent's avatar
Ian Kent committed
362
363
364
	ret = validate_request(&wq, sbi, &qstr, dentry, notify);
	if (ret <= 0) {
		if (ret == 0)
365
			mutex_unlock(&sbi->wq_mutex);
Ian Kent's avatar
Ian Kent committed
366
367
		kfree(qstr.name);
		return ret;
368
	}
369

370
	if (!wq) {
Linus Torvalds's avatar
Linus Torvalds committed
371
372
		/* Create a new wait queue */
		wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
373
		if (!wq) {
374
			kfree(qstr.name);
375
			mutex_unlock(&sbi->wq_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
376
377
378
379
380
381
382
383
384
			return -ENOMEM;
		}

		wq->wait_queue_token = autofs4_next_wait_queue;
		if (++autofs4_next_wait_queue == 0)
			autofs4_next_wait_queue = 1;
		wq->next = sbi->queues;
		sbi->queues = wq;
		init_waitqueue_head(&wq->queue);
385
		memcpy(&wq->name, &qstr, sizeof(struct qstr));
386
387
		wq->dev = autofs4_get_dev(sbi);
		wq->ino = autofs4_get_ino(sbi);
388
389
		wq->uid = current_uid();
		wq->gid = current_gid();
390
391
		wq->pid = current->pid;
		wq->tgid = current->tgid;
Linus Torvalds's avatar
Linus Torvalds committed
392
		wq->status = -EINTR; /* Status return if interrupted */
Ian Kent's avatar
Ian Kent committed
393
		wq->wait_ctr = 2;
394
		mutex_unlock(&sbi->wq_mutex);
395

396
397
398
399
400
401
402
		if (sbi->version < 5) {
			if (notify == NFY_MOUNT)
				type = autofs_ptype_missing;
			else
				type = autofs_ptype_expire_multi;
		} else {
			if (notify == NFY_MOUNT)
403
				type = autofs_type_trigger(sbi->type) ?
404
405
406
					autofs_ptype_missing_direct :
					 autofs_ptype_missing_indirect;
			else
407
				type = autofs_type_trigger(sbi->type) ?
408
409
410
					autofs_ptype_expire_direct :
					autofs_ptype_expire_indirect;
		}
Ian Kent's avatar
Ian Kent committed
411

412
		DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n",
413
414
			(unsigned long) wq->wait_queue_token, wq->name.len,
			wq->name.name, notify);
Ian Kent's avatar
Ian Kent committed
415
416
417

		/* autofs4_notify_daemon() may block */
		autofs4_notify_daemon(sbi, wq, type);
418
	} else {
Ian Kent's avatar
Ian Kent committed
419
		wq->wait_ctr++;
420
		mutex_unlock(&sbi->wq_mutex);
421
		kfree(qstr.name);
422
		DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d",
423
424
			(unsigned long) wq->wait_queue_token, wq->name.len,
			wq->name.name, notify);
Ian Kent's avatar
Ian Kent committed
425
426
	}

Ian Kent's avatar
Ian Kent committed
427
428
429
430
	/*
	 * wq->name.name is NULL iff the lock is already released
	 * or the mount has been made catatonic.
	 */
431
	if (wq->name.name) {
Linus Torvalds's avatar
Linus Torvalds committed
432
433
434
435
436
437
438
439
440
441
		/* Block all but "shutdown" signals while waiting */
		sigset_t oldset;
		unsigned long irqflags;

		spin_lock_irqsave(&current->sighand->siglock, irqflags);
		oldset = current->blocked;
		siginitsetinv(&current->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]);
		recalc_sigpending();
		spin_unlock_irqrestore(&current->sighand->siglock, irqflags);

442
		wait_event_interruptible(wq->queue, wq->name.name == NULL);
Linus Torvalds's avatar
Linus Torvalds committed
443
444
445
446
447
448
449
450
451
452
453

		spin_lock_irqsave(&current->sighand->siglock, irqflags);
		current->blocked = oldset;
		recalc_sigpending();
		spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
	} else {
		DPRINTK("skipped sleeping");
	}

	status = wq->status;

454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
	/*
	 * For direct and offset mounts we need to track the requester's
	 * uid and gid in the dentry info struct. This is so it can be
	 * supplied, on request, by the misc device ioctl interface.
	 * This is needed during daemon resatart when reconnecting
	 * to existing, active, autofs mounts. The uid and gid (and
	 * related string values) may be used for macro substitution
	 * in autofs mount maps.
	 */
	if (!status) {
		struct autofs_info *ino;
		struct dentry *de = NULL;

		/* direct mount or browsable map */
		ino = autofs4_dentry_ino(dentry);
		if (!ino) {
			/* If not lookup actual dentry used */
			de = d_lookup(dentry->d_parent, &dentry->d_name);
			if (de)
				ino = autofs4_dentry_ino(de);
		}

		/* Set mount requester */
		if (ino) {
			spin_lock(&sbi->fs_lock);
			ino->uid = wq->uid;
			ino->gid = wq->gid;
			spin_unlock(&sbi->fs_lock);
		}

		if (de)
			dput(de);
	}

Linus Torvalds's avatar
Linus Torvalds committed
488
	/* Are we the last process to need status? */
Ian Kent's avatar
Ian Kent committed
489
490
	mutex_lock(&sbi->wq_mutex);
	if (!--wq->wait_ctr)
Linus Torvalds's avatar
Linus Torvalds committed
491
		kfree(wq);
Ian Kent's avatar
Ian Kent committed
492
	mutex_unlock(&sbi->wq_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
493
494
495
496
497
498
499
500
501

	return status;
}


int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status)
{
	struct autofs_wait_queue *wq, **wql;

502
	mutex_lock(&sbi->wq_mutex);
503
	for (wql = &sbi->queues; (wq = *wql) != NULL; wql = &wq->next) {
504
		if (wq->wait_queue_token == wait_queue_token)
Linus Torvalds's avatar
Linus Torvalds committed
505
506
507
			break;
	}

508
	if (!wq) {
509
		mutex_unlock(&sbi->wq_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
510
511
512
513
		return -EINVAL;
	}

	*wql = wq->next;	/* Unlink from chain */
514
515
	kfree(wq->name.name);
	wq->name.name = NULL;	/* Do not wait on this queue */
Linus Torvalds's avatar
Linus Torvalds committed
516
	wq->status = status;
Ian Kent's avatar
Ian Kent committed
517
518
	wake_up_interruptible(&wq->queue);
	if (!--wq->wait_ctr)
Linus Torvalds's avatar
Linus Torvalds committed
519
		kfree(wq);
Ian Kent's avatar
Ian Kent committed
520
	mutex_unlock(&sbi->wq_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
521
522
523
524

	return 0;
}