Blame view

fs/autofs4/waitq.c 10 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
  /* -*- c -*- --------------------------------------------------------------- *
   *
   * linux/fs/autofs/waitq.c
   *
   *  Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
6
   *  Copyright 2001-2006 Ian Kent <raven@themaw.net>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
   *
   * 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;
  
  	DPRINTK("entering catatonic mode");
  
  	sbi->catatonic = 1;
  	wq = sbi->queues;
  	sbi->queues = NULL;	/* Erase all wait queues */
e77fbddf7   Ian Kent   [PATCH] autofs4: ...
36
  	while (wq) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
43
  		nwq = wq->next;
  		wq->status = -ENOENT; /* Magic is gone - report failure */
  		kfree(wq->name);
  		wq->name = NULL;
  		wake_up_interruptible(&wq->queue);
  		wq = nwq;
  	}
ba8df43c0   Ian Kent   [PATCH] autofs4: ...
44
45
  	fput(sbi->pipe);	/* Close the pipe */
  	sbi->pipe = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
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
  }
  
  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)
  {
e8514478f   Ian Kent   [PATCH] autofs4: ...
87
88
89
90
91
  	union {
  		struct autofs_packet_hdr hdr;
  		union autofs_packet_union v4_pkt;
  		union autofs_v5_packet_union v5_pkt;
  	} pkt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
99
100
  	size_t pktsz;
  
  	DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d",
  		wq->wait_queue_token, wq->len, wq->name, type);
  
  	memset(&pkt,0,sizeof pkt); /* For security reasons */
  
  	pkt.hdr.proto_version = sbi->version;
  	pkt.hdr.type = type;
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
101
102
103
104
  	switch (type) {
  	/* Kernel protocol v4 missing and expire packets */
  	case autofs_ptype_missing:
  	{
e8514478f   Ian Kent   [PATCH] autofs4: ...
105
  		struct autofs_packet_missing *mp = &pkt.v4_pkt.missing;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
111
112
  
  		pktsz = sizeof(*mp);
  
  		mp->wait_queue_token = wq->wait_queue_token;
  		mp->len = wq->len;
  		memcpy(mp->name, wq->name, wq->len);
  		mp->name[wq->len] = '\0';
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
113
114
115
116
  		break;
  	}
  	case autofs_ptype_expire_multi:
  	{
e8514478f   Ian Kent   [PATCH] autofs4: ...
117
  		struct autofs_packet_expire_multi *ep = &pkt.v4_pkt.expire_multi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
  
  		pktsz = sizeof(*ep);
  
  		ep->wait_queue_token = wq->wait_queue_token;
  		ep->len = wq->len;
  		memcpy(ep->name, wq->name, wq->len);
  		ep->name[wq->len] = '\0';
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
125
126
127
128
129
130
131
132
133
134
135
  		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:
  	{
e8514478f   Ian Kent   [PATCH] autofs4: ...
136
  		struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet;
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  
  		pktsz = sizeof(*packet);
  
  		packet->wait_queue_token = wq->wait_queue_token;
  		packet->len = wq->len;
  		memcpy(packet->name, wq->name, wq->len);
  		packet->name[wq->len] = '\0';
  		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:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  		printk("autofs4_notify_daemon: bad type %d!
  ", type);
  		return;
  	}
  
  	if (autofs4_write(sbi->pipe, &pkt, pktsz))
  		autofs4_catatonic_mode(sbi);
  }
  
  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;
  
  	if (--len > NAME_MAX) {
  		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;
  }
a53705539   Ian Kent   [PATCH] autofs4: ...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  static struct autofs_wait_queue *
  autofs4_find_wait(struct autofs_sb_info *sbi,
  		  char *name, unsigned int hash, unsigned int len)
  {
  	struct autofs_wait_queue *wq;
  
  	for (wq = sbi->queues; wq; wq = wq->next) {
  		if (wq->hash == hash &&
  		    wq->len == len &&
  		    wq->name && !memcmp(wq->name, name, len))
  			break;
  	}
  	return wq;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
  int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
  		enum autofs_notify notify)
  {
a53705539   Ian Kent   [PATCH] autofs4: ...
210
  	struct autofs_info *ino;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
  	struct autofs_wait_queue *wq;
  	char *name;
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
213
214
  	unsigned int len = 0;
  	unsigned int hash = 0;
a53705539   Ian Kent   [PATCH] autofs4: ...
215
  	int status, type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
  
  	/* In catatonic mode, we don't wait for nobody */
e77fbddf7   Ian Kent   [PATCH] autofs4: ...
218
  	if (sbi->catatonic)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
223
  		return -ENOENT;
  	
  	name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
  	if (!name)
  		return -ENOMEM;
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
224
  	/* If this is a direct mount request create a dummy name */
44d53eb04   Ian Kent   [PATCH] autofs4: ...
225
  	if (IS_ROOT(dentry) && (sbi->type & AUTOFS_TYPE_DIRECT))
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
226
227
228
229
230
231
232
  		len = sprintf(name, "%p", dentry);
  	else {
  		len = autofs4_getpath(sbi, dentry, &name);
  		if (!len) {
  			kfree(name);
  			return -ENOENT;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
  	}
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
234
  	hash = full_name_hash(name, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235

1d5599e39   Ingo Molnar   [PATCH] sem2mutex...
236
  	if (mutex_lock_interruptible(&sbi->wq_mutex)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
  		kfree(name);
  		return -EINTR;
  	}
a53705539   Ian Kent   [PATCH] autofs4: ...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  	wq = autofs4_find_wait(sbi, name, hash, len);
  	ino = autofs4_dentry_ino(dentry);
  	if (!wq && ino && 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)) {
  				kfree(name);
  				return -EINTR;
  			}
  			wq = autofs4_find_wait(sbi, name, hash, len);
  			if (wq)
  				break;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261

a53705539   Ian Kent   [PATCH] autofs4: ...
262
263
264
265
266
267
  		/*
  		 * 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.
  		 */
  		if (!wq) {
cc9acc885   Ian Kent   [PATCH] autofs4: ...
268
  			kfree(name);
1d5599e39   Ingo Molnar   [PATCH] sem2mutex...
269
  			mutex_unlock(&sbi->wq_mutex);
a53705539   Ian Kent   [PATCH] autofs4: ...
270
  			return 0;
cc9acc885   Ian Kent   [PATCH] autofs4: ...
271
  		}
a53705539   Ian Kent   [PATCH] autofs4: ...
272
  	}
cc9acc885   Ian Kent   [PATCH] autofs4: ...
273

a53705539   Ian Kent   [PATCH] autofs4: ...
274
  	if (!wq) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
  		/* Create a new wait queue */
  		wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
e77fbddf7   Ian Kent   [PATCH] autofs4: ...
277
  		if (!wq) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  			kfree(name);
1d5599e39   Ingo Molnar   [PATCH] sem2mutex...
279
  			mutex_unlock(&sbi->wq_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
286
287
288
  			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);
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
289
  		wq->hash = hash;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
  		wq->name = name;
  		wq->len = len;
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
292
293
294
295
296
297
  		wq->dev = autofs4_get_dev(sbi);
  		wq->ino = autofs4_get_ino(sbi);
  		wq->uid = current->uid;
  		wq->gid = current->gid;
  		wq->pid = current->pid;
  		wq->tgid = current->tgid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
  		wq->status = -EINTR; /* Status return if interrupted */
  		atomic_set(&wq->wait_ctr, 2);
1d5599e39   Ingo Molnar   [PATCH] sem2mutex...
300
  		mutex_unlock(&sbi->wq_mutex);
3e7b19198   Ian Kent   [PATCH] autofs4: ...
301

5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
302
303
304
305
306
307
308
  		if (sbi->version < 5) {
  			if (notify == NFY_MOUNT)
  				type = autofs_ptype_missing;
  			else
  				type = autofs_ptype_expire_multi;
  		} else {
  			if (notify == NFY_MOUNT)
44d53eb04   Ian Kent   [PATCH] autofs4: ...
309
  				type = (sbi->type & AUTOFS_TYPE_DIRECT) ?
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
310
311
312
  					autofs_ptype_missing_direct :
  					 autofs_ptype_missing_indirect;
  			else
44d53eb04   Ian Kent   [PATCH] autofs4: ...
313
  				type = (sbi->type & AUTOFS_TYPE_DIRECT) ?
5c0a32fc2   Ian Kent   [PATCH] autofs4: ...
314
315
316
  					autofs_ptype_expire_direct :
  					autofs_ptype_expire_indirect;
  		}
4dcd00b18   Ian Kent   [PATCH] autofs4: ...
317

682d4fc93   Ian Kent   [PATCH] autofs4: ...
318
319
320
  		DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d
  ",
  			(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify);
4dcd00b18   Ian Kent   [PATCH] autofs4: ...
321
322
323
  
  		/* autofs4_notify_daemon() may block */
  		autofs4_notify_daemon(sbi, wq, type);
a53705539   Ian Kent   [PATCH] autofs4: ...
324
325
326
327
328
329
  	} else {
  		atomic_inc(&wq->wait_ctr);
  		mutex_unlock(&sbi->wq_mutex);
  		kfree(name);
  		DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d",
  			(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify);
4dcd00b18   Ian Kent   [PATCH] autofs4: ...
330
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  	/* wq->name is NULL if and only if the lock is already released */
e77fbddf7   Ian Kent   [PATCH] autofs4: ...
332
  	if (sbi->catatonic) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
  		/* We might have slept, so check again for catatonic mode */
  		wq->status = -ENOENT;
f99d49adf   Jesper Juhl   [PATCH] kfree cle...
335
336
  		kfree(wq->name);
  		wq->name = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
  	}
e77fbddf7   Ian Kent   [PATCH] autofs4: ...
338
  	if (wq->name) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
  		/* 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);
  
  		wait_event_interruptible(wq->queue, wq->name == NULL);
  
  		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;
  
  	/* Are we the last process to need status? */
  	if (atomic_dec_and_test(&wq->wait_ctr))
  		kfree(wq);
  
  	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;
1d5599e39   Ingo Molnar   [PATCH] sem2mutex...
372
  	mutex_lock(&sbi->wq_mutex);
e77fbddf7   Ian Kent   [PATCH] autofs4: ...
373
374
  	for (wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next) {
  		if (wq->wait_queue_token == wait_queue_token)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
  			break;
  	}
e77fbddf7   Ian Kent   [PATCH] autofs4: ...
377
  	if (!wq) {
1d5599e39   Ingo Molnar   [PATCH] sem2mutex...
378
  		mutex_unlock(&sbi->wq_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
  		return -EINVAL;
  	}
  
  	*wql = wq->next;	/* Unlink from chain */
1d5599e39   Ingo Molnar   [PATCH] sem2mutex...
383
  	mutex_unlock(&sbi->wq_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
389
390
391
392
393
394
395
  	kfree(wq->name);
  	wq->name = NULL;	/* Do not wait on this queue */
  
  	wq->status = status;
  
  	if (atomic_dec_and_test(&wq->wait_ctr))	/* Is anyone still waiting for this guy? */
  		kfree(wq);
  	else
  		wake_up_interruptible(&wq->queue);
  
  	return 0;
  }