puffs_subr.c revision 1.20 1 /* $NetBSD: puffs_subr.c,v 1.20 2007/02/16 16:37:55 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Google Summer of Code program and the Ulla Tuominen Foundation.
8 * The Google SoC project was mentored by Bill Studenmund.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. The name of the company nor the name of the author may be used to
19 * endorse or promote products derived from this software without specific
20 * prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: puffs_subr.c,v 1.20 2007/02/16 16:37:55 pooka Exp $");
37
38 #include <sys/param.h>
39 #include <sys/conf.h>
40 #include <sys/hash.h>
41 #include <sys/malloc.h>
42 #include <sys/mount.h>
43 #include <sys/socketvar.h>
44 #include <sys/vnode.h>
45 #include <sys/kauth.h>
46 #include <sys/namei.h>
47
48 #include <fs/puffs/puffs_msgif.h>
49 #include <fs/puffs/puffs_sys.h>
50
51 #include <miscfs/genfs/genfs_node.h>
52 #include <miscfs/specfs/specdev.h>
53
54 POOL_INIT(puffs_pnpool, sizeof(struct puffs_node), 0, 0, 0, "puffspnpl",
55 &pool_allocator_nointr);
56
57 #ifdef PUFFSDEBUG
58 int puffsdebug;
59 #endif
60
61 static __inline struct puffs_node_hashlist
62 *puffs_cookie2hashlist(struct puffs_mount *, void *);
63 static struct puffs_node *puffs_cookie2pnode(struct puffs_mount *, void *);
64
65 static void puffs_gop_size(struct vnode *, off_t, off_t *, int);
66 static void puffs_gop_markupdate(struct vnode *, int);
67
68 static const struct genfs_ops puffs_genfsops = {
69 .gop_size = puffs_gop_size,
70 .gop_write = genfs_gop_write,
71 .gop_markupdate = puffs_gop_markupdate,
72 #if 0
73 .gop_alloc, should ask userspace
74 #endif
75 };
76
77 /*
78 * Grab a vnode, intialize all the puffs-dependant stuff.
79 */
80 int
81 puffs_getvnode(struct mount *mp, void *cookie, enum vtype type,
82 voff_t vsize, dev_t rdev, struct vnode **vpp)
83 {
84 struct puffs_mount *pmp;
85 struct vnode *vp, *nvp;
86 struct puffs_node *pnode;
87 struct puffs_node_hashlist *plist;
88 int error;
89
90 pmp = MPTOPUFFSMP(mp);
91
92 /*
93 * XXX: there is a deadlock condition between vfs_busy() and
94 * vnode locks. For an unmounting file system the mountpoint
95 * is frozen, but in unmount(FORCE) vflush() wants to access all
96 * of the vnodes. If we are here waiting for the mountpoint
97 * lock while holding on to a vnode lock, well, we ain't
98 * just pining for the fjords anymore. If we release the
99 * vnode lock, we will be in the situation "mount point
100 * is dying" and panic() will ensue in insmntque. So as a
101 * temporary workaround, get a vnode without putting it on
102 * the mount point list, check if mount point is still alive
103 * and kicking and only then add the vnode to the list.
104 */
105 error = getnewvnode(VT_PUFFS, NULL, puffs_vnodeop_p, &vp);
106 if (error)
107 return error;
108 vp->v_vnlock = NULL;
109 vp->v_type = type;
110
111 /*
112 * Check what mount point isn't going away. This will work
113 * until we decide to remove biglock or make the kernel
114 * preemptive. But hopefully the real problem will be fixed
115 * by then.
116 *
117 * XXX: yes, should call vfs_busy(), but thar be rabbits with
118 * vicious streaks a mile wide ...
119 */
120 if (mp->mnt_iflag & IMNT_UNMOUNT) {
121 DPRINTF(("puffs_getvnode: mp %p unmount, unable to create "
122 "vnode for cookie %p\n", mp, cookie));
123 ungetnewvnode(vp);
124 return ENXIO;
125 }
126
127 /* So it's not dead yet.. good.. inform new vnode of its master */
128 simple_lock(&mntvnode_slock);
129 TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes);
130 simple_unlock(&mntvnode_slock);
131 vp->v_mount = mp;
132
133 /*
134 * clerical tasks & footwork
135 */
136
137 /* dances based on vnode type. almost ufs_vinit(), but not quite */
138 switch (type) {
139 case VCHR:
140 case VBLK:
141 /*
142 * replace vnode operation vector with the specops vector.
143 * our user server has very little control over the node
144 * if it decides its a character or block special file
145 */
146 vp->v_op = puffs_specop_p;
147
148 /* do the standard checkalias-dance */
149 if ((nvp = checkalias(vp, rdev, mp)) != NULL) {
150 /*
151 * found: release & unallocate aliased
152 * old (well, actually, new) node
153 */
154 vp->v_op = spec_vnodeop_p;
155 vp->v_flag &= ~VLOCKSWORK;
156 vrele(vp);
157 vgone(vp); /* cya */
158
159 /* init "new" vnode */
160 vp = nvp;
161 vp->v_vnlock = NULL;
162 vp->v_mount = mp;
163 }
164 break;
165
166 case VFIFO:
167 vp->v_op = puffs_fifoop_p;
168 break;
169
170 case VREG:
171 uvm_vnp_setsize(vp, vsize);
172 break;
173
174 case VDIR:
175 case VLNK:
176 case VSOCK:
177 break;
178 default:
179 #ifdef DIAGNOSTIC
180 panic("puffs_getvnode: invalid vtype %d", type);
181 #endif
182 break;
183 }
184
185 pnode = pool_get(&puffs_pnpool, PR_WAITOK);
186 pnode->pn_cookie = cookie;
187 pnode->pn_stat = 0;
188 plist = puffs_cookie2hashlist(pmp, cookie);
189 LIST_INSERT_HEAD(plist, pnode, pn_hashent);
190 vp->v_data = pnode;
191 vp->v_type = type;
192 pnode->pn_vp = vp;
193
194 genfs_node_init(vp, &puffs_genfsops);
195 *vpp = vp;
196
197 DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp,
198 pnode, pnode->pn_cookie));
199
200 return 0;
201 }
202
203 /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */
204 int
205 puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp,
206 void *cookie, struct componentname *cnp, enum vtype type, dev_t rdev)
207 {
208 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
209 struct vnode *vp;
210 int error;
211
212 /* userspace probably has this as a NULL op */
213 if (cookie == NULL) {
214 error = EOPNOTSUPP;
215 return error;
216 }
217
218 /*
219 * Check for previous node with the same designation.
220 * Explicitly check the root node cookie, since it might be
221 * reclaimed from the kernel when this check is made.
222 *
223 * XXX: technically this error check should punish the fs,
224 * not the caller.
225 */
226 simple_lock(&pmp->pmp_lock);
227 if (cookie == pmp->pmp_rootcookie
228 || puffs_cookie2pnode(pmp, cookie) != NULL) {
229 simple_unlock(&pmp->pmp_lock);
230 error = EEXIST;
231 return error;
232 }
233 simple_unlock(&pmp->pmp_lock);
234
235 error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp);
236 if (error)
237 return error;
238
239 vp->v_type = type;
240 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
241 *vpp = vp;
242
243 if ((cnp->cn_flags & MAKEENTRY) && PUFFS_DOCACHE(pmp))
244 cache_enter(dvp, vp, cnp);
245
246 return 0;
247 }
248
249 void
250 puffs_putvnode(struct vnode *vp)
251 {
252 struct puffs_mount *pmp;
253 struct puffs_node *pnode;
254
255 pmp = VPTOPUFFSMP(vp);
256 pnode = VPTOPP(vp);
257
258 #ifdef DIAGNOSTIC
259 if (vp->v_tag != VT_PUFFS)
260 panic("puffs_putvnode: %p not a puffs vnode", vp);
261 #endif
262
263 LIST_REMOVE(pnode, pn_hashent);
264 pool_put(&puffs_pnpool, vp->v_data);
265 vp->v_data = NULL;
266
267 return;
268 }
269
270 static __inline struct puffs_node_hashlist *
271 puffs_cookie2hashlist(struct puffs_mount *pmp, void *cookie)
272 {
273 uint32_t hash;
274
275 hash = hash32_buf(&cookie, sizeof(void *), HASH32_BUF_INIT);
276 return &pmp->pmp_pnodehash[hash % pmp->pmp_npnodehash];
277 }
278
279 /*
280 * Translate cookie to puffs_node. Caller must hold mountpoint
281 * lock and it will be held upon return.
282 */
283 static struct puffs_node *
284 puffs_cookie2pnode(struct puffs_mount *pmp, void *cookie)
285 {
286 struct puffs_node_hashlist *plist;
287 struct puffs_node *pnode;
288
289 plist = puffs_cookie2hashlist(pmp, cookie);
290 LIST_FOREACH(pnode, plist, pn_hashent) {
291 if (pnode->pn_cookie == cookie)
292 break;
293 }
294
295 return pnode;
296 }
297
298 /*
299 * Locate the in-kernel vnode based on the cookie received given
300 * from userspace. Returns a vnode, if found, NULL otherwise.
301 * The parameter "lock" control whether to lock the possible or
302 * not. Locking always might cause us to lock against ourselves
303 * in situations where we want the vnode but don't care for the
304 * vnode lock, e.g. file server issued putpages.
305 */
306 struct vnode *
307 puffs_pnode2vnode(struct puffs_mount *pmp, void *cookie, int lock)
308 {
309 struct puffs_node *pnode;
310 struct vnode *vp;
311 int vgetflags;
312
313 /*
314 * If we're trying to get the root vnode, return it through
315 * puffs_root() to get all the right things set. Lock must
316 * be set, since VFS_ROOT() always locks the returned vnode.
317 */
318 if (cookie == pmp->pmp_rootcookie) {
319 if (!lock)
320 return NULL;
321 if (VFS_ROOT(pmp->pmp_mp, &vp))
322 return NULL;
323
324 return vp;
325 }
326
327 vgetflags = LK_INTERLOCK;
328 if (lock)
329 vgetflags |= LK_EXCLUSIVE | LK_RETRY;
330
331 simple_lock(&pmp->pmp_lock);
332 pnode = puffs_cookie2pnode(pmp, cookie);
333
334 if (pnode == NULL) {
335 simple_unlock(&pmp->pmp_lock);
336 return NULL;
337 }
338 vp = pnode->pn_vp;
339
340 simple_lock(&vp->v_interlock);
341 simple_unlock(&pmp->pmp_lock);
342
343 if (vget(vp, vgetflags))
344 return NULL;
345
346 return vp;
347 }
348
349 void
350 puffs_makecn(struct puffs_kcn *pkcn, const struct componentname *cn)
351 {
352
353 pkcn->pkcn_nameiop = cn->cn_nameiop;
354 pkcn->pkcn_flags = cn->cn_flags;
355 pkcn->pkcn_pid = cn->cn_lwp->l_proc->p_pid;
356 puffs_credcvt(&pkcn->pkcn_cred, cn->cn_cred);
357
358 (void)memcpy(&pkcn->pkcn_name, cn->cn_nameptr, cn->cn_namelen);
359 pkcn->pkcn_name[cn->cn_namelen] = '\0';
360 pkcn->pkcn_namelen = cn->cn_namelen;
361 }
362
363 /*
364 * Convert given credentials to struct puffs_cred for userspace.
365 */
366 void
367 puffs_credcvt(struct puffs_cred *pcr, const kauth_cred_t cred)
368 {
369
370 memset(pcr, 0, sizeof(struct puffs_cred));
371
372 if (cred == NOCRED || cred == FSCRED) {
373 pcr->pcr_type = PUFFCRED_TYPE_INTERNAL;
374 if (cred == NOCRED)
375 pcr->pcr_internal = PUFFCRED_CRED_NOCRED;
376 if (cred == FSCRED)
377 pcr->pcr_internal = PUFFCRED_CRED_FSCRED;
378 } else {
379 pcr->pcr_type = PUFFCRED_TYPE_UUC;
380 kauth_cred_to_uucred(&pcr->pcr_uuc, cred);
381 }
382 }
383
384 /*
385 * Return pid. In case the operation is coming from within the
386 * kernel without any process context, borrow the swapper's pid.
387 */
388 pid_t
389 puffs_lwp2pid(struct lwp *l)
390 {
391
392 return l ? l->l_proc->p_pid : 0;
393 }
394
395
396 static void
397 puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp,
398 int flags)
399 {
400
401 *eobp = size;
402 }
403
404 static void
405 puffs_gop_markupdate(struct vnode *vp, int flags)
406 {
407 int uflags = 0;
408
409 if (flags & GOP_UPDATE_ACCESSED)
410 uflags |= PUFFS_UPDATEATIME;
411 if (flags & GOP_UPDATE_MODIFIED)
412 uflags |= PUFFS_UPDATEMTIME;
413
414 puffs_updatenode(vp, uflags);
415 }
416
417 void
418 puffs_updatenode(struct vnode *vp, int flags)
419 {
420 struct timespec ts;
421 struct puffs_vnreq_setattr *setattr_arg;
422
423 if (flags == 0)
424 return;
425
426 setattr_arg = malloc(sizeof(struct puffs_vnreq_setattr), M_PUFFS,
427 M_NOWAIT | M_ZERO);
428 if (setattr_arg == NULL)
429 return; /* 2bad */
430
431 nanotime(&ts);
432
433 VATTR_NULL(&setattr_arg->pvnr_va);
434 if (flags & PUFFS_UPDATEATIME)
435 setattr_arg->pvnr_va.va_atime = ts;
436 if (flags & PUFFS_UPDATECTIME)
437 setattr_arg->pvnr_va.va_ctime = ts;
438 if (flags & PUFFS_UPDATEMTIME)
439 setattr_arg->pvnr_va.va_mtime = ts;
440 if (flags & PUFFS_UPDATESIZE)
441 setattr_arg->pvnr_va.va_size = vp->v_size;
442
443 setattr_arg->pvnr_pid = 0;
444 puffs_credcvt(&setattr_arg->pvnr_cred, NOCRED);
445
446 /* setattr_arg ownership shifted to callee */
447 puffs_vntouser_faf(MPTOPUFFSMP(vp->v_mount), PUFFS_VN_SETATTR,
448 setattr_arg, sizeof(struct puffs_vnreq_setattr), VPTOPNC(vp));
449 }
450
451 void
452 puffs_updatevpsize(struct vnode *vp)
453 {
454 struct vattr va;
455
456 if (VOP_GETATTR(vp, &va, FSCRED, NULL))
457 return;
458
459 if (va.va_size != VNOVAL)
460 vp->v_size = va.va_size;
461 }
462
463 /*
464 * We're dead, kaput, RIP, slightly more than merely pining for the
465 * fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
466 * our maker, ceased to be, etcetc. YASD. It's a dead FS!
467 *
468 * Caller must hold puffs spinlock.
469 */
470 void
471 puffs_userdead(struct puffs_mount *pmp)
472 {
473 struct puffs_park *park;
474
475 /*
476 * Mark filesystem status as dying so that operations don't
477 * attempt to march to userspace any longer.
478 */
479 pmp->pmp_status = PUFFSTAT_DYING;
480
481 /* and wakeup processes waiting for a reply from userspace */
482 TAILQ_FOREACH(park, &pmp->pmp_req_replywait, park_entries) {
483 park->park_preq->preq_rv = ENXIO;
484 TAILQ_REMOVE(&pmp->pmp_req_replywait, park, park_entries);
485 wakeup(park);
486 }
487
488 /* wakeup waiters for completion of vfs/vnode requests */
489 TAILQ_FOREACH(park, &pmp->pmp_req_touser, park_entries) {
490 park->park_preq->preq_rv = ENXIO;
491 TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries);
492 wakeup(park);
493 }
494 }
495