puffs_subr.c revision 1.9.2.4 1 /* $NetBSD: puffs_subr.c,v 1.9.2.4 2007/02/01 08:48:33 ad 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.9.2.4 2007/02/01 08:48:33 ad 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 DEBUG
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 *
221 * XXX: technically this error check should punish the fs,
222 * not the caller.
223 */
224 simple_lock(&pmp->pmp_lock);
225 if (puffs_cookie2pnode(pmp, cookie) != NULL) {
226 simple_unlock(&pmp->pmp_lock);
227 error = EEXIST;
228 return error;
229 }
230 simple_unlock(&pmp->pmp_lock);
231
232 error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp);
233 if (error)
234 return error;
235
236 vp->v_type = type;
237 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
238 *vpp = vp;
239
240 if ((cnp->cn_flags & MAKEENTRY) && PUFFS_DOCACHE(pmp))
241 cache_enter(dvp, vp, cnp);
242
243 return 0;
244 }
245
246 void
247 puffs_putvnode(struct vnode *vp)
248 {
249 struct puffs_mount *pmp;
250 struct puffs_node *pnode;
251
252 pmp = VPTOPUFFSMP(vp);
253 pnode = VPTOPP(vp);
254
255 #ifdef DIAGNOSTIC
256 if (vp->v_tag != VT_PUFFS)
257 panic("puffs_putvnode: %p not a puffs vnode", vp);
258 #endif
259
260 LIST_REMOVE(pnode, pn_hashent);
261 pool_put(&puffs_pnpool, vp->v_data);
262 vp->v_data = NULL;
263
264 return;
265 }
266
267 static __inline struct puffs_node_hashlist *
268 puffs_cookie2hashlist(struct puffs_mount *pmp, void *cookie)
269 {
270 uint32_t hash;
271
272 hash = hash32_buf(&cookie, sizeof(void *), HASH32_BUF_INIT);
273 return &pmp->pmp_pnodehash[hash % pmp->pmp_npnodehash];
274 }
275
276 /*
277 * Translate cookie to puffs_node. Caller must hold mountpoint
278 * lock and it will be held upon return.
279 */
280 static struct puffs_node *
281 puffs_cookie2pnode(struct puffs_mount *pmp, void *cookie)
282 {
283 struct puffs_node_hashlist *plist;
284 struct puffs_node *pnode;
285
286 plist = puffs_cookie2hashlist(pmp, cookie);
287 LIST_FOREACH(pnode, plist, pn_hashent) {
288 if (pnode->pn_cookie == cookie)
289 break;
290 }
291
292 return pnode;
293 }
294
295 /*
296 * Locate the in-kernel vnode based on the cookie received given
297 * from userspace. Returns a locked & referenced vnode, if found,
298 * NULL otherwise.
299 *
300 * XXX: lists, although lookup cache mostly shields us from this
301 */
302 struct vnode *
303 puffs_pnode2vnode(struct puffs_mount *pmp, void *cookie, int lock)
304 {
305 struct puffs_node *pnode;
306 struct vnode *vp;
307 int vgetflags;
308
309 vgetflags = LK_INTERLOCK;
310 if (lock)
311 vgetflags |= LK_EXCLUSIVE | LK_RETRY;
312
313 simple_lock(&pmp->pmp_lock);
314 pnode = puffs_cookie2pnode(pmp, cookie);
315
316 if (pnode == NULL) {
317 simple_unlock(&pmp->pmp_lock);
318 return NULL;
319 }
320 vp = pnode->pn_vp;
321
322 simple_lock(&vp->v_interlock);
323 simple_unlock(&pmp->pmp_lock);
324
325 if (vget(vp, vgetflags))
326 return NULL;
327
328 return vp;
329 }
330
331 void
332 puffs_makecn(struct puffs_kcn *pkcn, const struct componentname *cn)
333 {
334
335 pkcn->pkcn_nameiop = cn->cn_nameiop;
336 pkcn->pkcn_flags = cn->cn_flags;
337 pkcn->pkcn_pid = cn->cn_lwp->l_proc->p_pid;
338 puffs_credcvt(&pkcn->pkcn_cred, cn->cn_cred);
339
340 (void)memcpy(&pkcn->pkcn_name, cn->cn_nameptr, cn->cn_namelen);
341 pkcn->pkcn_name[cn->cn_namelen] = '\0';
342 pkcn->pkcn_namelen = cn->cn_namelen;
343 }
344
345 /*
346 * Convert given credentials to struct puffs_cred for userspace.
347 */
348 void
349 puffs_credcvt(struct puffs_cred *pcr, const kauth_cred_t cred)
350 {
351
352 memset(pcr, 0, sizeof(struct puffs_cred));
353
354 if (cred == NOCRED || cred == FSCRED) {
355 pcr->pcr_type = PUFFCRED_TYPE_INTERNAL;
356 if (cred == NOCRED)
357 pcr->pcr_internal = PUFFCRED_CRED_NOCRED;
358 if (cred == FSCRED)
359 pcr->pcr_internal = PUFFCRED_CRED_FSCRED;
360 } else {
361 pcr->pcr_type = PUFFCRED_TYPE_UUC;
362 kauth_cred_to_uucred(&pcr->pcr_uuc, cred);
363 }
364 }
365
366 /*
367 * Return pid. In case the operation is coming from within the
368 * kernel without any process context, borrow the swapper's pid.
369 */
370 pid_t
371 puffs_lwp2pid(struct lwp *l)
372 {
373
374 return l ? l->l_proc->p_pid : 0;
375 }
376
377
378 static void
379 puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp,
380 int flags)
381 {
382
383 *eobp = size;
384 }
385
386 static void
387 puffs_gop_markupdate(struct vnode *vp, int flags)
388 {
389 int uflags = 0;
390
391 if (flags & GOP_UPDATE_ACCESSED)
392 uflags |= PUFFS_UPDATEATIME;
393 if (flags & GOP_UPDATE_MODIFIED)
394 uflags |= PUFFS_UPDATEMTIME;
395
396 puffs_updatenode(vp, uflags);
397 }
398
399 void
400 puffs_updatenode(struct vnode *vp, int flags)
401 {
402 struct timespec ts;
403 struct puffs_vnreq_setattr *setattr_arg;
404
405 if (flags == 0)
406 return;
407
408 setattr_arg = malloc(sizeof(struct puffs_vnreq_setattr), M_PUFFS,
409 M_NOWAIT | M_ZERO);
410 if (setattr_arg == NULL)
411 return; /* 2bad */
412
413 nanotime(&ts);
414
415 VATTR_NULL(&setattr_arg->pvnr_va);
416 if (flags & PUFFS_UPDATEATIME)
417 setattr_arg->pvnr_va.va_atime = ts;
418 if (flags & PUFFS_UPDATECTIME)
419 setattr_arg->pvnr_va.va_ctime = ts;
420 if (flags & PUFFS_UPDATEMTIME)
421 setattr_arg->pvnr_va.va_mtime = ts;
422 if (flags & PUFFS_UPDATESIZE)
423 setattr_arg->pvnr_va.va_size = vp->v_size;
424
425 setattr_arg->pvnr_pid = 0;
426 puffs_credcvt(&setattr_arg->pvnr_cred, NOCRED);
427
428 /* setattr_arg ownership shifted to callee */
429 puffs_vntouser_faf(MPTOPUFFSMP(vp->v_mount), PUFFS_VN_SETATTR,
430 setattr_arg, sizeof(struct puffs_vnreq_setattr), VPTOPNC(vp));
431 }
432
433 void
434 puffs_updatevpsize(struct vnode *vp)
435 {
436 struct vattr va;
437
438 if (VOP_GETATTR(vp, &va, FSCRED, NULL))
439 return;
440
441 if (va.va_size != VNOVAL)
442 vp->v_size = va.va_size;
443 }
444
445 /*
446 * We're dead, kaput, RIP, slightly more than merely pining for the
447 * fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
448 * our maker, ceased to be, etcetc. YASD. It's a dead FS!
449 *
450 * Caller must hold puffs spinlock.
451 */
452 void
453 puffs_userdead(struct puffs_mount *pmp)
454 {
455 struct puffs_park *park;
456
457 /*
458 * Mark filesystem status as dying so that operations don't
459 * attempt to march to userspace any longer.
460 */
461 pmp->pmp_status = PUFFSTAT_DYING;
462
463 /* and wakeup processes waiting for a reply from userspace */
464 TAILQ_FOREACH(park, &pmp->pmp_req_replywait, park_entries) {
465 park->park_preq->preq_rv = ENXIO;
466 TAILQ_REMOVE(&pmp->pmp_req_replywait, park, park_entries);
467 wakeup(park);
468 }
469
470 /* wakeup waiters for completion of vfs/vnode requests */
471 TAILQ_FOREACH(park, &pmp->pmp_req_touser, park_entries) {
472 park->park_preq->preq_rv = ENXIO;
473 TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries);
474 wakeup(park);
475 }
476 }
477