| Start/ | End/ | |||
| True | False | - | Line | Source |
| 1 | /* -*- c -*- --------------------------------------------------------------- * | |||
| 2 | * | |||
| 3 | * linux/fs/autofs/waitq.c | |||
| 4 | * | |||
| 5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved | |||
| 6 | * Copyright 2001-2003 Ian Kent <raven@themaw.net> | |||
| 7 | * | |||
| 8 | * This file is part of the Linux kernel and is made available under | |||
| 9 | * the terms of the GNU General Public License, version 2, or at your | |||
| 10 | * option, any later version, incorporated herein by reference. | |||
| 11 | * | |||
| 12 | * ------------------------------------------------------------------------- */ | |||
| 13 | ||||
| 14 | #include <linux/slab.h> | |||
| 15 | #include <linux/time.h> | |||
| 16 | #include <linux/signal.h> | |||
| 17 | #include <linux/file.h> | |||
| 18 | #include "autofs_i.h" | |||
| 19 | ||||
| 20 | /* We make this a static variable rather than a part of the superblock; it | |||
| 21 | is better if we don't reassign numbers easily even across filesystems */ | |||
| 22 | static autofs_wqt_t autofs4_next_wait_queue = 1; | |||
| 23 | ||||
| 24 | /* These are the signals we allow interrupting a pending mount */ | |||
| 25 | #define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT)) | |||
| 26 | ||||
| 0 | 0 | - | 27 | void autofs4_catatonic_mode(struct autofs_sb_info *sbi) |
| 28 | { | |||
| 29 | struct autofs_wait_queue *wq, *nwq; | |||
| 30 | ||||
| 31 | DPRINTK("entering catatonic mode"); | |||
| 0 | 0 | - | 31 | do-while (0) |
| 32 | ||||
| 33 | sbi->catatonic = 1; | |||
| 34 | wq = sbi->queues; | |||
| 35 | sbi->queues = NULL; /* Erase all wait queues */ | |||
| 0 | 0 | - | 36 | while ( wq ) { |
| 37 | nwq = wq->next; | |||
| 38 | wq->status = -ENOENT; /* Magic is gone - report failure */ | |||
| 39 | kfree(wq->name); | |||
| 40 | wq->name = NULL; | |||
| 41 | wake_up_interruptible(&wq->queue); | |||
| 42 | wq = nwq; | |||
| 43 | } | |||
| 0 | 0 | - | 44 | if (sbi->pipe) { |
| 45 | fput(sbi->pipe); /* Close the pipe */ | |||
| 46 | sbi->pipe = NULL; | |||
| 47 | } | |||
| 48 | ||||
| 49 | shrink_dcache_sb(sbi->sb); | |||
| 50 | } | |||
| 51 | ||||
| 0 | 0 | - | 52 | static int autofs4_write(struct file *file, const void *addr, int bytes) |
| 53 | { | |||
| 54 | unsigned long sigpipe, flags; | |||
| 55 | mm_segment_t fs; | |||
| 56 | const char *data = (const char *)addr; | |||
| 57 | ssize_t wr = 0; | |||
| 58 | ||||
| 59 | /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/ | |||
| 60 | ||||
| 61 | sigpipe = sigismember(¤t->pending.signal, SIGPIPE); | |||
| 62 | ||||
| 63 | /* Save pointer to user space and point back to kernel space */ | |||
| 64 | fs = get_fs(); | |||
| 65 | set_fs(KERNEL_DS); | |||
| 66 | ||||
| 67 | while (bytes && | |||
| 0 | 0 | - | 68 | (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) { |
| 0 | - | 68 | T && T | |
| 0 | - | 68 | T && F | |
| 0 | - | 68 | F && _ | |
| 69 | data += wr; | |||
| 70 | bytes -= wr; | |||
| 71 | } | |||
| 72 | ||||
| 73 | set_fs(fs); | |||
| 74 | ||||
| 75 | /* Keep the currently executing process from receiving a | |||
| 76 | SIGPIPE unless it was already supposed to get one */ | |||
| 0 | 0 | - | 77 | if (wr == -EPIPE && !sigpipe) { |
| 0 | - | 77 | T && T | |
| 0 | - | 77 | T && F | |
| 0 | - | 77 | F && _ | |
| 78 | spin_lock_irqsave(¤t->sighand->siglock, flags); | |||
| 78 | do | |||
| 78 | do | |||
| 0 | 0 | - | 78 | do-while (0) |
| 0 | 0 | - | 78 | do-while (0) |
| 78 | do | |||
| 78 | do | |||
| 0 | 0 | - | 78 | do-while (0) |
| 0 | 0 | - | 78 | do-while (0) |
| 0 | 0 | - | 78 | do-while (0) |
| 79 | sigdelset(¤t->pending.signal, SIGPIPE); | |||
| 80 | recalc_sigpending(); | |||
| 81 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | |||
| 81 | do | |||
| 81 | do | |||
| 0 | 0 | - | 81 | do-while (0) |
| 0 | 0 | - | 81 | do-while (0) |
| 0 | 0 | - | 81 | do-while (0) |
| 82 | } | |||
| 83 | ||||
| 0 | - | 84 | return (bytes > 0); | |
| 85 | } | |||
| 86 | ||||
| 0 | 0 | - | 87 | static void autofs4_notify_daemon(struct autofs_sb_info *sbi, |
| 88 | struct autofs_wait_queue *wq, | |||
| 89 | int type) | |||
| 90 | { | |||
| 91 | union autofs_packet_union pkt; | |||
| 92 | size_t pktsz; | |||
| 93 | ||||
| 94 | DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d", | |||
| 0 | 0 | - | 94 | do-while (0) |
| 95 | wq->wait_queue_token, wq->len, wq->name, type); | |||
| 96 | ||||
| 97 | memset(&pkt,0,sizeof pkt); /* For security reasons */ | |||
| 98 | ||||
| 99 | pkt.hdr.proto_version = sbi->version; | |||
| 100 | pkt.hdr.type = type; | |||
| 0 | 0 | - | 101 | if (type == autofs_ptype_missing) { |
| 102 | struct autofs_packet_missing *mp = &pkt.missing; | |||
| 103 | ||||
| 104 | pktsz = sizeof(*mp); | |||
| 105 | ||||
| 106 | mp->wait_queue_token = wq->wait_queue_token; | |||
| 107 | mp->len = wq->len; | |||
| 108 | memcpy(mp->name, wq->name, wq->len); | |||
| 109 | mp->name[wq->len] = '\0'; | |||
| 0 | 0 | - | 110 | } else if (type == autofs_ptype_expire_multi) { |
| 111 | struct autofs_packet_expire_multi *ep = &pkt.expire_multi; | |||
| 112 | ||||
| 113 | pktsz = sizeof(*ep); | |||
| 114 | ||||
| 115 | ep->wait_queue_token = wq->wait_queue_token; | |||
| 116 | ep->len = wq->len; | |||
| 117 | memcpy(ep->name, wq->name, wq->len); | |||
| 118 | ep->name[wq->len] = '\0'; | |||
| 119 | } else { | |||
| 120 | printk("autofs4_notify_daemon: bad type %d!\n", type); | |||
| 0 | - | 121 | return; | |
| 122 | } | |||
| 123 | ||||
| 0 | 0 | - | 124 | if (autofs4_write(sbi->pipe, &pkt, pktsz)) |
| 125 | autofs4_catatonic_mode(sbi); | |||
| 126 | } | |||
| 127 | ||||
| 0 | 0 | - | 128 | static int autofs4_getpath(struct autofs_sb_info *sbi, |
| 129 | struct dentry *dentry, char **name) | |||
| 130 | { | |||
| 131 | struct dentry *root = sbi->sb->s_root; | |||
| 132 | struct dentry *tmp; | |||
| 133 | char *buf = *name; | |||
| 134 | char *p; | |||
| 135 | int len = 0; | |||
| 136 | ||||
| 137 | spin_lock(&dcache_lock); | |||
| 137 | do | |||
| 0 | 0 | - | 137 | do-while (0) |
| 0 | 0 | - | 137 | do-while (0) |
| 0 | 0 | - | 138 | for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent) |
| 139 | len += tmp->d_name.len + 1; | |||
| 140 | ||||
| 0 | 0 | - | 141 | if (--len > NAME_MAX) { |
| 142 | spin_unlock(&dcache_lock); | |||
| 142 | do | |||
| 0 | 0 | - | 142 | do-while (0) |
| 0 | 0 | - | 142 | do-while (0) |
| 0 | - | 143 | return 0; | |
| 144 | } | |||
| 145 | ||||
| 146 | *(buf + len) = '\0'; | |||
| 147 | p = buf + len - dentry->d_name.len; | |||
| 148 | strncpy(p, dentry->d_name.name, dentry->d_name.len); | |||
| 149 | ||||
| 0 | 0 | - | 150 | for (tmp = dentry->d_parent; tmp != root ; tmp = tmp->d_parent) { |
| 151 | *(--p) = '/'; | |||
| 152 | p -= tmp->d_name.len; | |||
| 153 | strncpy(p, tmp->d_name.name, tmp->d_name.len); | |||
| 154 | } | |||
| 155 | spin_unlock(&dcache_lock); | |||
| 155 | do | |||
| 0 | 0 | - | 155 | do-while (0) |
| 0 | 0 | - | 155 | do-while (0) |
| 156 | ||||
| 0 | - | 157 | return len; | |
| 158 | } | |||
| 159 | ||||
| 0 | 0 | - | 160 | int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, |
| 161 | enum autofs_notify notify) | |||
| 162 | { | |||
| 163 | struct autofs_wait_queue *wq; | |||
| 164 | char *name; | |||
| 165 | int len, status; | |||
| 166 | ||||
| 167 | /* In catatonic mode, we don't wait for nobody */ | |||
| 0 | 0 | - | 168 | if ( sbi->catatonic ) |
| 0 | - | 169 | return -ENOENT; | |
| 170 | ||||
| 171 | name = kmalloc(NAME_MAX + 1, GFP_KERNEL); | |||
| 0 | 0 | - | 172 | if (!name) |
| 0 | - | 173 | return -ENOMEM; | |
| 174 | ||||
| 175 | len = autofs4_getpath(sbi, dentry, &name); | |||
| 0 | 0 | - | 176 | if (!len) { |
| 177 | kfree(name); | |||
| 0 | - | 178 | return -ENOENT; | |
| 179 | } | |||
| 180 | ||||
| 0 | 0 | - | 181 | if (down_interruptible(&sbi->wq_sem)) { |
| 182 | kfree(name); | |||
| 0 | - | 183 | return -EINTR; | |
| 184 | } | |||
| 185 | ||||
| 0 | 0 | - | 186 | for (wq = sbi->queues ; wq ; wq = wq->next) { |
| 187 | if (wq->hash == dentry->d_name.hash && | |||
| 188 | wq->len == len && | |||
| 0 | 0 | - | 189 | wq->name && !memcmp(wq->name, name, len)) |
| 0 | - | 189 | T && T && T && T | |
| 0 | - | 189 | T && T && T && F | |
| 0 | - | 189 | T && T && F && _ | |
| 0 | - | 189 | T && F && _ && _ | |
| 0 | - | 189 | F && _ && _ && _ | |
| 0 | - | 190 | break; | |
| 191 | } | |||
| 192 | ||||
| 0 | 0 | - | 193 | if ( !wq ) { |
| 194 | /* Can't wait for an expire if there's no mount */ | |||
| 0 | 0 | - | 195 | if (notify == NFY_NONE && !d_mountpoint(dentry)) { |
| 0 | - | 195 | T && T | |
| 0 | - | 195 | T && F | |
| 0 | - | 195 | F && _ | |
| 196 | kfree(name); | |||
| 197 | up(&sbi->wq_sem); | |||
| 0 | - | 198 | return -ENOENT; | |
| 199 | } | |||
| 200 | ||||
| 201 | /* Create a new wait queue */ | |||
| 202 | wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); | |||
| 0 | 0 | - | 203 | if ( !wq ) { |
| 204 | kfree(name); | |||
| 205 | up(&sbi->wq_sem); | |||
| 0 | - | 206 | return -ENOMEM; | |
| 207 | } | |||
| 208 | ||||
| 209 | wq->wait_queue_token = autofs4_next_wait_queue; | |||
| 0 | 0 | - | 210 | if (++autofs4_next_wait_queue == 0) |
| 211 | autofs4_next_wait_queue = 1; | |||
| 212 | wq->next = sbi->queues; | |||
| 213 | sbi->queues = wq; | |||
| 214 | init_waitqueue_head(&wq->queue); | |||
| 215 | wq->hash = dentry->d_name.hash; | |||
| 216 | wq->name = name; | |||
| 217 | wq->len = len; | |||
| 218 | wq->status = -EINTR; /* Status return if interrupted */ | |||
| 219 | atomic_set(&wq->wait_ctr, 2); | |||
| 220 | atomic_set(&wq->notified, 1); | |||
| 221 | up(&sbi->wq_sem); | |||
| 222 | } else { | |||
| 223 | atomic_inc(&wq->wait_ctr); | |||
| 224 | up(&sbi->wq_sem); | |||
| 225 | kfree(name); | |||
| 226 | DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", | |||
| 0 | 0 | - | 226 | do-while (0) |
| 227 | (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); | |||
| 228 | } | |||
| 229 | ||||
| 0 | 0 | - | 230 | if (notify != NFY_NONE && atomic_dec_and_test(&wq->notified)) { |
| 0 | - | 230 | T && T | |
| 0 | - | 230 | T && F | |
| 0 | - | 230 | F && _ | |
| 231 | int type = (notify == NFY_MOUNT ? | |||
| 0 | 0 | - | 231 | ternary-?: notify == NFY_MOUNT |
| 232 | autofs_ptype_missing : autofs_ptype_expire_multi); | |||
| 233 | ||||
| 234 | DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", | |||
| 0 | 0 | - | 234 | do-while (0) |
| 235 | (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); | |||
| 236 | ||||
| 237 | /* autofs4_notify_daemon() may block */ | |||
| 238 | autofs4_notify_daemon(sbi, wq, type); | |||
| 239 | } | |||
| 240 | ||||
| 241 | /* wq->name is NULL if and only if the lock is already released */ | |||
| 242 | ||||
| 0 | 0 | - | 243 | if ( sbi->catatonic ) { |
| 244 | /* We might have slept, so check again for catatonic mode */ | |||
| 245 | wq->status = -ENOENT; | |||
| 246 | kfree(wq->name); | |||
| 247 | wq->name = NULL; | |||
| 248 | } | |||
| 249 | ||||
| 0 | 0 | - | 250 | if ( wq->name ) { |
| 251 | /* Block all but "shutdown" signals while waiting */ | |||
| 252 | sigset_t oldset; | |||
| 253 | unsigned long irqflags; | |||
| 254 | ||||
| 255 | spin_lock_irqsave(¤t->sighand->siglock, irqflags); | |||
| 255 | do | |||
| 255 | do | |||
| 0 | 0 | - | 255 | do-while (0) |
| 0 | 0 | - | 255 | do-while (0) |
| 255 | do | |||
| 255 | do | |||
| 0 | 0 | - | 255 | do-while (0) |
| 0 | 0 | - | 255 | do-while (0) |
| 0 | 0 | - | 255 | do-while (0) |
| 256 | oldset = current->blocked; | |||
| 257 | siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); | |||
| 258 | recalc_sigpending(); | |||
| 259 | spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); | |||
| 259 | do | |||
| 259 | do | |||
| 0 | 0 | - | 259 | do-while (0) |
| 0 | 0 | - | 259 | do-while (0) |
| 0 | 0 | - | 259 | do-while (0) |
| 260 | ||||
| 261 | wait_event_interruptible(wq->queue, wq->name == NULL); | |||
| 262 | ||||
| 263 | spin_lock_irqsave(¤t->sighand->siglock, irqflags); | |||
| 263 | do | |||
| 263 | do | |||
| 0 | 0 | - | 263 | do-while (0) |
| 0 | 0 | - | 263 | do-while (0) |
| 263 | do | |||
| 263 | do | |||
| 0 | 0 | - | 263 | do-while (0) |
| 0 | 0 | - | 263 | do-while (0) |
| 0 | 0 | - | 263 | do-while (0) |
| 264 | current->blocked = oldset; | |||
| 265 | recalc_sigpending(); | |||
| 266 | spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); | |||
| 266 | do | |||
| 266 | do | |||
| 0 | 0 | - | 266 | do-while (0) |
| 0 | 0 | - | 266 | do-while (0) |
| 0 | 0 | - | 266 | do-while (0) |
| 267 | } else { | |||
| 268 | DPRINTK("skipped sleeping"); | |||
| 0 | 0 | - | 268 | do-while (0) |
| 269 | } | |||
| 270 | ||||
| 271 | status = wq->status; | |||
| 272 | ||||
| 273 | /* Are we the last process to need status? */ | |||
| 0 | 0 | - | 274 | if (atomic_dec_and_test(&wq->wait_ctr)) |
| 275 | kfree(wq); | |||
| 276 | ||||
| 0 | - | 277 | return status; | |
| 278 | } | |||
| 279 | ||||
| 280 | ||||
| 0 | 0 | - | 281 | int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status) |
| 282 | { | |||
| 283 | struct autofs_wait_queue *wq, **wql; | |||
| 284 | ||||
| 285 | down(&sbi->wq_sem); | |||
| 0 | 0 | - | 286 | for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) { |
| 0 | 0 | - | 287 | if ( wq->wait_queue_token == wait_queue_token ) |
| 0 | - | 288 | break; | |
| 289 | } | |||
| 290 | ||||
| 0 | 0 | - | 291 | if ( !wq ) { |
| 292 | up(&sbi->wq_sem); | |||
| 0 | - | 293 | return -EINVAL; | |
| 294 | } | |||
| 295 | ||||
| 296 | *wql = wq->next; /* Unlink from chain */ | |||
| 297 | up(&sbi->wq_sem); | |||
| 298 | kfree(wq->name); | |||
| 299 | wq->name = NULL; /* Do not wait on this queue */ | |||
| 300 | ||||
| 301 | wq->status = status; | |||
| 302 | ||||
| 0 | 0 | - | 303 | if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */ |
| 304 | kfree(wq); | |||
| 305 | else | |||
| 306 | wake_up_interruptible(&wq->queue); | |||
| 307 | ||||
| 0 | - | 308 | return 0; | |
| 309 | } | |||
| 310 | ||||
| ***TER 0% (0/166) of SOURCE FILE waitq.c | ||||