puffs_subr.c revision 1.16 1 /* $NetBSD: puffs_subr.c,v 1.16 2007/01/15 23:29:08 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.16 2007/01/15 23:29:08 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 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 simple_lock(&pmp->pmp_lock);
310 pnode = puffs_cookie2pnode(pmp, cookie);
311
312 if (pnode == NULL) {
313 simple_unlock(&pmp->pmp_lock);
314 return NULL;
315 }
316 vp = pnode->pn_vp;
317 simple_unlock(&pmp->pmp_lock);
318
319 if (lock)
320 vgetflags = LK_EXCLUSIVE | LK_RETRY;
321 else
322 vgetflags = 0;
323
324 if (vget(vp, vgetflags))
325 return NULL;
326
327 return vp;
328 }
329
330 void
331 puffs_makecn(struct puffs_kcn *pkcn, const struct componentname *cn)
332 {
333
334 pkcn->pkcn_nameiop = cn->cn_nameiop;
335 pkcn->pkcn_flags = cn->cn_flags;
336 pkcn->pkcn_pid = cn->cn_lwp->l_proc->p_pid;
337 puffs_credcvt(&pkcn->pkcn_cred, cn->cn_cred);
338
339 (void)memcpy(&pkcn->pkcn_name, cn->cn_nameptr, cn->cn_namelen);
340 pkcn->pkcn_name[cn->cn_namelen] = '\0';
341 pkcn->pkcn_namelen = cn->cn_namelen;
342 }
343
344 /*
345 * Convert given credentials to struct puffs_cred for userspace.
346 */
347 void
348 puffs_credcvt(struct puffs_cred *pcr, const kauth_cred_t cred)
349 {
350
351 memset(pcr, 0, sizeof(struct puffs_cred));
352
353 if (cred == NOCRED || cred == FSCRED) {
354 pcr->pcr_type = PUFFCRED_TYPE_INTERNAL;
355 if (cred == NOCRED)
356 pcr->pcr_internal = PUFFCRED_CRED_NOCRED;
357 if (cred == FSCRED)
358 pcr->pcr_internal = PUFFCRED_CRED_FSCRED;
359 } else {
360 pcr->pcr_type = PUFFCRED_TYPE_UUC;
361 kauth_cred_to_uucred(&pcr->pcr_uuc, cred);
362 }
363 }
364
365 /*
366 * Return pid. In case the operation is coming from within the
367 * kernel without any process context, borrow the swapper's pid.
368 */
369 pid_t
370 puffs_lwp2pid(struct lwp *l)
371 {
372
373 return l ? l->l_proc->p_pid : 0;
374 }
375
376
377 static void
378 puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp,
379 int flags)
380 {
381
382 *eobp = size;
383 }
384
385 static void
386 puffs_gop_markupdate(struct vnode *vp, int flags)
387 {
388 int uflags = 0;
389
390 if (flags & GOP_UPDATE_ACCESSED)
391 uflags |= PUFFS_UPDATEATIME;
392 if (flags & GOP_UPDATE_MODIFIED)
393 uflags |= PUFFS_UPDATEMTIME;
394
395 puffs_updatenode(vp, uflags);
396 }
397
398 void
399 puffs_updatenode(struct vnode *vp, int flags)
400 {
401 struct timespec ts;
402 struct puffs_vnreq_setattr *setattr_arg;
403
404 if (flags == 0)
405 return;
406
407 setattr_arg = malloc(sizeof(struct puffs_vnreq_setattr), M_PUFFS,
408 M_NOWAIT | M_ZERO);
409 if (setattr_arg == NULL)
410 return; /* 2bad */
411
412 nanotime(&ts);
413
414 VATTR_NULL(&setattr_arg->pvnr_va);
415 if (flags & PUFFS_UPDATEATIME)
416 setattr_arg->pvnr_va.va_atime = ts;
417 if (flags & PUFFS_UPDATECTIME)
418 setattr_arg->pvnr_va.va_ctime = ts;
419 if (flags & PUFFS_UPDATEMTIME)
420 setattr_arg->pvnr_va.va_mtime = ts;
421 if (flags & PUFFS_UPDATESIZE)
422 setattr_arg->pvnr_va.va_size = vp->v_size;
423
424 setattr_arg->pvnr_pid = 0;
425 puffs_credcvt(&setattr_arg->pvnr_cred, NOCRED);
426
427 /* setattr_arg ownership shifted to callee */
428 puffs_vntouser_faf(MPTOPUFFSMP(vp->v_mount), PUFFS_VN_SETATTR,
429 setattr_arg, sizeof(struct puffs_vnreq_setattr), VPTOPNC(vp));
430 }
431
432 void
433 puffs_updatevpsize(struct vnode *vp)
434 {
435 struct vattr va;
436
437 if (VOP_GETATTR(vp, &va, FSCRED, NULL))
438 return;
439
440 if (va.va_size != VNOVAL)
441 vp->v_size = va.va_size;
442 }
443
444 /*
445 * We're dead, kaput, RIP, slightly more than merely pining for the
446 * fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
447 * our maker, ceased to be, etcetc. YASD. It's a dead FS!
448 */
449 void
450 puffs_userdead(struct puffs_mount *pmp)
451 {
452 struct puffs_park *park;
453
454 simple_lock(&pmp->pmp_lock);
455
456 /*
457 * Mark filesystem status as dying so that operations don't
458 * attempt to march to userspace any longer.
459 */
460 pmp->pmp_status = PUFFSTAT_DYING;
461
462 /* and wakeup processes waiting for a reply from userspace */
463 TAILQ_FOREACH(park, &pmp->pmp_req_replywait, park_entries) {
464 park->park_preq->preq_rv = ENXIO;
465 TAILQ_REMOVE(&pmp->pmp_req_replywait, park, park_entries);
466 wakeup(park);
467 }
468
469 /* wakeup waiters for completion of vfs/vnode requests */
470 TAILQ_FOREACH(park, &pmp->pmp_req_touser, park_entries) {
471 park->park_preq->preq_rv = ENXIO;
472 TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries);
473 wakeup(park);
474 }
475
476 simple_unlock(&pmp->pmp_lock);
477 }
478