puffs_subr.c revision 1.24.2.1 1 /* $NetBSD: puffs_subr.c,v 1.24.2.1 2007/03/29 19:27:55 reinoud 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.24.2.1 2007/03/29 19:27:55 reinoud 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 struct pool puffs_pnpool;
55
56 #ifdef PUFFSDEBUG
57 int puffsdebug;
58 #endif
59
60 static __inline struct puffs_node_hashlist
61 *puffs_cookie2hashlist(struct puffs_mount *, void *);
62 static struct puffs_node *puffs_cookie2pnode(struct puffs_mount *, void *);
63
64 static void puffs_gop_size(struct vnode *, off_t, off_t *, int);
65 static void puffs_gop_markupdate(struct vnode *, int);
66
67 static const struct genfs_ops puffs_genfsops = {
68 .gop_size = puffs_gop_size,
69 .gop_write = genfs_gop_write,
70 .gop_markupdate = puffs_gop_markupdate,
71 #if 0
72 .gop_alloc, should ask userspace
73 #endif
74 };
75
76 /*
77 * Grab a vnode, intialize all the puffs-dependant stuff.
78 */
79 int
80 puffs_getvnode(struct mount *mp, void *cookie, enum vtype type,
81 voff_t vsize, dev_t rdev, struct vnode **vpp)
82 {
83 struct puffs_mount *pmp;
84 struct vnode *vp, *nvp;
85 struct puffs_node *pnode;
86 struct puffs_node_hashlist *plist;
87 int error;
88
89 pmp = MPTOPUFFSMP(mp);
90
91 /*
92 * XXX: there is a deadlock condition between vfs_busy() and
93 * vnode locks. For an unmounting file system the mountpoint
94 * is frozen, but in unmount(FORCE) vflush() wants to access all
95 * of the vnodes. If we are here waiting for the mountpoint
96 * lock while holding on to a vnode lock, well, we ain't
97 * just pining for the fjords anymore. If we release the
98 * vnode lock, we will be in the situation "mount point
99 * is dying" and panic() will ensue in insmntque. So as a
100 * temporary workaround, get a vnode without putting it on
101 * the mount point list, check if mount point is still alive
102 * and kicking and only then add the vnode to the list.
103 */
104 error = getnewvnode(VT_PUFFS, NULL, puffs_vnodeop_p, &vp);
105 if (error)
106 return error;
107 vp->v_vnlock = NULL;
108 vp->v_type = type;
109
110 /*
111 * Check what mount point isn't going away. This will work
112 * until we decide to remove biglock or make the kernel
113 * preemptive. But hopefully the real problem will be fixed
114 * by then.
115 *
116 * XXX: yes, should call vfs_busy(), but thar be rabbits with
117 * vicious streaks a mile wide ...
118 */
119 if (mp->mnt_iflag & IMNT_UNMOUNT) {
120 DPRINTF(("puffs_getvnode: mp %p unmount, unable to create "
121 "vnode for cookie %p\n", mp, cookie));
122 ungetnewvnode(vp);
123 return ENXIO;
124 }
125
126 /* So it's not dead yet.. good.. inform new vnode of its master */
127 simple_lock(&mntvnode_slock);
128 TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes);
129 simple_unlock(&mntvnode_slock);
130 vp->v_mount = mp;
131
132 /*
133 * clerical tasks & footwork
134 */
135
136 /* dances based on vnode type. almost ufs_vinit(), but not quite */
137 switch (type) {
138 case VCHR:
139 case VBLK:
140 /*
141 * replace vnode operation vector with the specops vector.
142 * our user server has very little control over the node
143 * if it decides its a character or block special file
144 */
145 vp->v_op = puffs_specop_p;
146
147 /* do the standard checkalias-dance */
148 if ((nvp = checkalias(vp, rdev, mp)) != NULL) {
149 /*
150 * found: release & unallocate aliased
151 * old (well, actually, new) node
152 */
153 vp->v_op = spec_vnodeop_p;
154 vp->v_flag &= ~VLOCKSWORK;
155 vrele(vp);
156 vgone(vp); /* cya */
157
158 /* init "new" vnode */
159 vp = nvp;
160 vp->v_vnlock = NULL;
161 vp->v_mount = mp;
162 }
163 break;
164
165 case VFIFO:
166 vp->v_op = puffs_fifoop_p;
167 break;
168
169 case VREG:
170 uvm_vnp_setsize(vp, vsize);
171 break;
172
173 case VDIR:
174 case VLNK:
175 case VSOCK:
176 break;
177 default:
178 #ifdef DIAGNOSTIC
179 panic("puffs_getvnode: invalid vtype %d", type);
180 #endif
181 break;
182 }
183
184 pnode = pool_get(&puffs_pnpool, PR_WAITOK);
185 pnode->pn_cookie = cookie;
186 pnode->pn_stat = 0;
187 plist = puffs_cookie2hashlist(pmp, cookie);
188 LIST_INSERT_HEAD(plist, pnode, pn_hashent);
189 vp->v_data = pnode;
190 vp->v_type = type;
191 pnode->pn_vp = vp;
192
193 genfs_node_init(vp, &puffs_genfsops);
194 *vpp = vp;
195
196 DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp,
197 pnode, pnode->pn_cookie));
198
199 return 0;
200 }
201
202 /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */
203 int
204 puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp,
205 void *cookie, struct componentname *cnp, enum vtype type, dev_t rdev)
206 {
207 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
208 struct vnode *vp;
209 int error;
210
211 /* userspace probably has this as a NULL op */
212 if (cookie == NULL) {
213 error = EOPNOTSUPP;
214 return error;
215 }
216
217 /*
218 * Check for previous node with the same designation.
219 * Explicitly check the root node cookie, since it might be
220 * reclaimed from the kernel when this check is made.
221 *
222 * XXX: technically this error check should punish the fs,
223 * not the caller.
224 */
225 mutex_enter(&pmp->pmp_lock);
226 if (cookie == pmp->pmp_rootcookie
227 || puffs_cookie2pnode(pmp, cookie) != NULL) {
228 mutex_exit(&pmp->pmp_lock);
229 error = EEXIST;
230 return error;
231 }
232 mutex_exit(&pmp->pmp_lock);
233
234 error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp);
235 if (error)
236 return error;
237
238 vp->v_type = type;
239 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
240 *vpp = vp;
241
242 if ((cnp->cn_flags & MAKEENTRY) && PUFFS_DOCACHE(pmp))
243 cache_enter(dvp, vp, cnp);
244
245 return 0;
246 }
247
248 void
249 puffs_putvnode(struct vnode *vp)
250 {
251 struct puffs_mount *pmp;
252 struct puffs_node *pnode;
253
254 pmp = VPTOPUFFSMP(vp);
255 pnode = VPTOPP(vp);
256
257 #ifdef DIAGNOSTIC
258 if (vp->v_tag != VT_PUFFS)
259 panic("puffs_putvnode: %p not a puffs vnode", vp);
260 #endif
261
262 LIST_REMOVE(pnode, pn_hashent);
263 genfs_node_destroy(vp);
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 mutex_enter(&pmp->pmp_lock);
332 pnode = puffs_cookie2pnode(pmp, cookie);
333
334 if (pnode == NULL) {
335 mutex_exit(&pmp->pmp_lock);
336 return NULL;
337 }
338 vp = pnode->pn_vp;
339
340 simple_lock(&vp->v_interlock);
341 mutex_exit(&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 puffs_node *pn;
421 struct timespec ts;
422
423 if (flags == 0)
424 return;
425
426 pn = VPTOPP(vp);
427 nanotime(&ts);
428
429 if (flags & PUFFS_UPDATEATIME) {
430 pn->pn_mc_atime = ts;
431 pn->pn_stat |= PNODE_METACACHE_ATIME;
432 }
433 if (flags & PUFFS_UPDATECTIME) {
434 pn->pn_mc_ctime = ts;
435 pn->pn_stat |= PNODE_METACACHE_CTIME;
436 }
437 if (flags & PUFFS_UPDATEMTIME) {
438 pn->pn_mc_mtime = ts;
439 pn->pn_stat |= PNODE_METACACHE_MTIME;
440 }
441 if (flags & PUFFS_UPDATESIZE) {
442 pn->pn_mc_size = vp->v_size;
443 pn->pn_stat |= PNODE_METACACHE_SIZE;
444 }
445 }
446
447 void
448 puffs_updatevpsize(struct vnode *vp)
449 {
450 struct vattr va;
451
452 if (VOP_GETATTR(vp, &va, FSCRED, NULL))
453 return;
454
455 if (va.va_size != VNOVAL)
456 vp->v_size = va.va_size;
457 }
458