1 /* $NetBSD: node.c,v 1.31 2022/03/02 07:48:20 ozaki-r Exp $ */
2
3 /*
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #ifndef lint
30 __RCSID("$NetBSD: node.c,v 1.31 2022/03/02 07:48:20 ozaki-r Exp $");
31 #endif /* !lint */
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <puffs.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38
39 #include "ninepuffs.h"
40 #include "nineproto.h"
41
42 static void *
nodecmp(struct puffs_usermount * pu,struct puffs_node * pn,void * arg)43 nodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
44 {
45 struct vattr *vap = &pn->pn_va;
46 struct qid9p *qid = arg;
47
48 if (vap->va_fileid == qid->qidpath && vap->va_gen == qid->qidvers)
49 return pn;
50
51 return NULL;
52 }
53
54 static int
do_getattr(struct puffs_usermount * pu,struct puffs_node * pn,struct vattr * vap)55 do_getattr(struct puffs_usermount *pu, struct puffs_node *pn, struct vattr *vap)
56 {
57 AUTOVAR(pu);
58 struct p9pnode *p9n = pn->pn_data;
59
60 tag = NEXTTAG(p9p);
61 p9pbuf_put_1(pb, P9PROTO_T_STAT);
62 p9pbuf_put_2(pb, tag);
63 p9pbuf_put_4(pb, p9n->fid_base);
64 GETRESPONSE(pb);
65
66 if (p9pbuf_get_type(pb) != P9PROTO_R_STAT) {
67 rv = proto_handle_rerror(pu, pb);
68 goto out;
69 }
70 rv = proto_expect_stat(pu, pb, vap);
71
72 out:
73 RETURN(rv);
74 }
75
76 int
puffs9p_node_getattr(struct puffs_usermount * pu,void * opc,struct vattr * vap,const struct puffs_cred * pcr)77 puffs9p_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *vap,
78 const struct puffs_cred *pcr)
79 {
80 struct puffs_node *pn = opc;
81 int rv;
82
83 rv = do_getattr(pu, pn, &pn->pn_va);
84 if (rv == 0)
85 memcpy(vap, &pn->pn_va, sizeof(struct vattr));
86 return rv;
87 }
88
89 int
puffs9p_node_lookup(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)90 puffs9p_node_lookup(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni,
91 const struct puffs_cn *pcn)
92 {
93 AUTOVAR(pu);
94 struct vattr va;
95 struct puffs_node *pn, *pn_dir = opc;
96 struct p9pnode *p9n_dir = pn_dir->pn_data;
97 p9pfid_t tfid = NEXTFID(p9p);
98 struct qid9p newqid;
99 uint16_t nqid;
100
101 tag = NEXTTAG(p9p);
102 p9pbuf_put_1(pb, P9PROTO_T_WALK);
103 p9pbuf_put_2(pb, tag);
104 p9pbuf_put_4(pb, p9n_dir->fid_base);
105 p9pbuf_put_4(pb, tfid);
106 p9pbuf_put_2(pb, 1);
107 p9pbuf_put_str(pb, pcn->pcn_name);
108 GETRESPONSE(pb);
109
110 rv = proto_expect_walk_nqids(pu, pb, &nqid);
111 if (rv) {
112 rv = ENOENT;
113 goto out;
114 }
115 if (nqid != 1) {
116 rv = EPROTO;
117 goto out;
118 }
119 if ((rv = proto_getqid(pb, &newqid)))
120 goto out;
121
122 /* we get the parent vers in walk(?) compensate */
123 p9pbuf_recycleout(pb);
124 tag = NEXTTAG(p9p);
125 p9pbuf_put_1(pb, P9PROTO_T_STAT);
126 p9pbuf_put_2(pb, tag);
127 p9pbuf_put_4(pb, tfid);
128 GETRESPONSE(pb);
129 if ((rv = proto_expect_stat(pu, pb, &va)) != 0) {
130 proto_cc_clunkfid(pu, tfid, 0);
131 rv = ENOENT;
132 goto out;
133 }
134 if (newqid.qidpath != va.va_fileid) {
135 proto_cc_clunkfid(pu, tfid, 0);
136 rv = EPROTO;
137 goto out;
138 }
139 newqid.qidvers = va.va_gen;
140
141 pn = puffs_pn_nodewalk(pu, nodecmp, &newqid);
142 if (pn == NULL)
143 pn = newp9pnode_qid(pu, &newqid, tfid);
144 else
145 proto_cc_clunkfid(pu, tfid, 0);
146 /* assert pn */
147 memcpy(&pn->pn_va, &va, sizeof(va));
148
149 puffs_newinfo_setcookie(pni, pn);
150 puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
151 puffs_newinfo_setsize(pni, pn->pn_va.va_size);
152 puffs_newinfo_setrdev(pni, pn->pn_va.va_rdev);
153
154 out:
155 RETURN(rv);
156 }
157
158 /*
159 * Problem is that 9P doesn't allow seeking into a directory. So we
160 * maintain a list of active fids for any given directory. They
161 * start living at the first read and exist either until the directory
162 * is closed or until they reach the end.
163 */
164 int
puffs9p_node_readdir(struct puffs_usermount * pu,void * opc,struct dirent * dent,off_t * readoff,size_t * reslen,const struct puffs_cred * pcr,int * eofflag,off_t * cookies,size_t * ncookies)165 puffs9p_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent,
166 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr,
167 int *eofflag, off_t *cookies, size_t *ncookies)
168 {
169 AUTOVAR(pu);
170 struct puffs_node *pn = opc;
171 struct p9pnode *p9n = pn->pn_data;
172 struct vattr va;
173 struct dirfid *dfp;
174 char *name;
175 uint32_t count;
176 uint16_t statsize;
177
178 rv = getdfwithoffset(pu, p9n, *readoff, &dfp);
179 if (rv)
180 goto out;
181
182 tag = NEXTTAG(p9p);
183 p9pbuf_put_1(pb, P9PROTO_T_READ);
184 p9pbuf_put_2(pb, tag);
185 p9pbuf_put_4(pb, dfp->fid);
186 p9pbuf_put_8(pb, *readoff);
187 p9pbuf_put_4(pb, *reslen); /* XXX */
188 GETRESPONSE(pb);
189
190 if (p9pbuf_get_type(pb) != P9PROTO_R_READ) {
191 rv = proto_handle_rerror(pu, pb);
192 goto out;
193 }
194
195 p9pbuf_get_4(pb, &count);
196
197 /*
198 * if count is 0, assume we at end-of-dir. dfp is no longer
199 * useful, so nuke it
200 */
201 if (count == 0) {
202 *eofflag = 1;
203 releasedf(pu, dfp);
204 goto out;
205 }
206
207 while (count > 0) {
208 if ((rv = proto_getstat(pu, pb, &va, &name, &statsize))) {
209 /*
210 * If there was an error, it's unlikely we'll be
211 * coming back, so just nuke the dfp. If we do
212 * come back for some strange reason, we'll just
213 * regen it.
214 */
215 releasedf(pu, dfp);
216 goto out;
217 }
218
219 puffs_nextdent(&dent, name, va.va_fileid,
220 puffs_vtype2dt(va.va_type), reslen);
221
222 count -= statsize;
223 *readoff += statsize;
224 dfp->seekoff += statsize;
225 free(name);
226 }
227
228 storedf(p9n, dfp);
229
230 out:
231 RETURN(rv);
232 }
233
234 int
puffs9p_node_setattr(struct puffs_usermount * pu,void * opc,const struct vattr * va,const struct puffs_cred * pcr)235 puffs9p_node_setattr(struct puffs_usermount *pu, void *opc,
236 const struct vattr *va, const struct puffs_cred *pcr)
237 {
238 AUTOVAR(pu);
239 struct puffs_node *pn = opc;
240 struct p9pnode *p9n = pn->pn_data;
241
242 tag = NEXTTAG(p9p);
243 p9pbuf_put_1(pb, P9PROTO_T_WSTAT);
244 p9pbuf_put_2(pb, tag);
245 p9pbuf_put_4(pb, p9n->fid_base);
246 proto_make_stat(pu, pb, va, NULL, pn->pn_va.va_type);
247 GETRESPONSE(pb);
248
249 if (p9pbuf_get_type(pb) != P9PROTO_R_WSTAT) {
250 rv = proto_handle_rerror(pu, pb);
251 }
252
253 out:
254 RETURN(rv);
255 }
256
257 /*
258 * Ok, time to get clever. There are two possible cases: we are
259 * opening a file or we are opening a directory.
260 *
261 * If it's a directory, don't bother opening it here, but rather
262 * wait until readdir, since it's probable we need to be able to
263 * open a directory there in any case.
264 *
265 * If it's a regular file, open it here with whatever credentials
266 * we happen to have. Let the upper layers of the kernel worry
267 * about permission control.
268 */
269 int
puffs9p_node_open(struct puffs_usermount * pu,void * opc,int mode,const struct puffs_cred * pcr)270 puffs9p_node_open(struct puffs_usermount *pu, void *opc, int mode,
271 const struct puffs_cred *pcr)
272 {
273 struct puffs_cc *pcc = puffs_cc_getcc(pu);
274 struct puffs9p *p9p = puffs_getspecific(pu);
275 struct puffs_node *pn = opc;
276 struct p9pnode *p9n = pn->pn_data;
277 p9pfid_t nfid;
278 int error = 0;
279
280 puffs_setback(pcc, PUFFS_SETBACK_INACT_N1);
281 if (pn->pn_va.va_type != VDIR) {
282 /* Always require read access for page cache */
283 mode |= FREAD;
284 if (mode & FREAD && p9n->fid_read == P9P_INVALFID) {
285 nfid = NEXTFID(p9p);
286 error = proto_cc_open(pu, p9n->fid_base, nfid,
287 P9PROTO_OMODE_READ);
288 if (error)
289 return error;
290 p9n->fid_read = nfid;
291 }
292 if (mode & FWRITE && p9n->fid_write == P9P_INVALFID) {
293 nfid = NEXTFID(p9p);
294 error = proto_cc_open(pu, p9n->fid_base, nfid,
295 P9PROTO_OMODE_WRITE);
296 if (error)
297 return error;
298 p9n->fid_write = nfid;
299 }
300 }
301
302 return 0;
303 }
304
305 int
puffs9p_node_inactive(struct puffs_usermount * pu,void * opc)306 puffs9p_node_inactive(struct puffs_usermount *pu, void *opc)
307 {
308 struct puffs_node *pn = opc;
309 struct p9pnode *p9n = pn->pn_data;
310
311 if (pn->pn_va.va_type == VDIR) {
312 nukealldf(pu, p9n);
313 } else {
314 if (p9n->fid_read != P9P_INVALFID) {
315 proto_cc_clunkfid(pu, p9n->fid_read, 0);
316 p9n->fid_read = P9P_INVALFID;
317 }
318 if (p9n->fid_write != P9P_INVALFID) {
319 proto_cc_clunkfid(pu, p9n->fid_write, 0);
320 p9n->fid_write = P9P_INVALFID;
321 }
322 }
323
324 return 0;
325 }
326
327 int
puffs9p_node_read(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)328 puffs9p_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
329 off_t offset, size_t *resid, const struct puffs_cred *pcr,
330 int ioflag)
331 {
332 AUTOVAR(pu);
333 struct puffs_node *pn = opc;
334 struct p9pnode *p9n = pn->pn_data;
335 uint32_t count;
336 size_t nread;
337
338 nread = 0;
339 while (*resid > 0 && (uint64_t)(offset+nread) < pn->pn_va.va_size) {
340 tag = NEXTTAG(p9p);
341 p9pbuf_put_1(pb, P9PROTO_T_READ);
342 p9pbuf_put_2(pb, tag);
343 p9pbuf_put_4(pb, p9n->fid_read);
344 p9pbuf_put_8(pb, offset+nread);
345 p9pbuf_put_4(pb, MIN((uint32_t)*resid,p9p->maxreq-24));
346 GETRESPONSE(pb);
347
348 if (p9pbuf_get_type(pb) != P9PROTO_R_READ) {
349 rv = proto_handle_rerror(pu, pb);
350 break;
351 }
352
353 p9pbuf_get_4(pb, &count);
354 if ((rv = p9pbuf_read_data(pb, buf + nread, count)))
355 break;
356
357 if (count == 0)
358 break;
359
360 *resid -= count;
361 nread += count;
362
363 p9pbuf_recycleout(pb);
364 }
365
366 out:
367 RETURN(rv);
368 }
369
370 int
puffs9p_node_write(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * cred,int ioflag)371 puffs9p_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
372 off_t offset, size_t *resid, const struct puffs_cred *cred,
373 int ioflag)
374 {
375 AUTOVAR(pu);
376 struct puffs_node *pn = opc;
377 struct p9pnode *p9n = pn->pn_data;
378 uint32_t chunk, count;
379 size_t nwrite;
380
381 if (ioflag & PUFFS_IO_APPEND)
382 offset = pn->pn_va.va_size;
383
384 nwrite = 0;
385 while (*resid > 0) {
386 chunk = MIN(*resid, p9p->maxreq-32);
387
388 tag = NEXTTAG(p9p);
389 p9pbuf_put_1(pb, P9PROTO_T_WRITE);
390 p9pbuf_put_2(pb, tag);
391 p9pbuf_put_4(pb, p9n->fid_write);
392 p9pbuf_put_8(pb, offset+nwrite);
393 p9pbuf_put_4(pb, chunk);
394 p9pbuf_write_data(pb, buf+nwrite, chunk);
395 GETRESPONSE(pb);
396
397 if (p9pbuf_get_type(pb) != P9PROTO_R_WRITE) {
398 rv = proto_handle_rerror(pu, pb);
399 break;
400 }
401
402 p9pbuf_get_4(pb, &count);
403 *resid -= count;
404 nwrite += count;
405
406 if (count != chunk) {
407 rv = EPROTO;
408 break;
409 }
410
411 p9pbuf_recycleout(pb);
412 }
413
414 out:
415 RETURN(rv);
416 }
417
418 static int
nodecreate(struct puffs_usermount * pu,struct puffs_node * pn,struct puffs_newinfo * pni,const char * name,const struct vattr * vap,uint32_t dirbit)419 nodecreate(struct puffs_usermount *pu, struct puffs_node *pn,
420 struct puffs_newinfo *pni, const char *name,
421 const struct vattr *vap, uint32_t dirbit)
422 {
423 AUTOVAR(pu);
424 struct puffs_node *pn_new;
425 struct p9pnode *p9n = pn->pn_data;
426 p9pfid_t nfid = NEXTFID(p9p);
427 struct qid9p nqid;
428 int tries = 0;
429
430 again:
431 if (++tries > 5) {
432 rv = EPROTO;
433 goto out;
434 }
435
436 rv = proto_cc_dupfid(pu, p9n->fid_base, nfid);
437 if (rv)
438 goto out;
439
440 tag = NEXTTAG(p9p);
441 p9pbuf_put_1(pb, P9PROTO_T_CREATE);
442 p9pbuf_put_2(pb, tag);
443 p9pbuf_put_4(pb, nfid);
444 p9pbuf_put_str(pb, name);
445 p9pbuf_put_4(pb, dirbit | (vap->va_mode & 0777));
446 p9pbuf_put_1(pb, 0);
447 if (p9p->protover == P9PROTO_VERSION_U)
448 p9pbuf_put_str(pb, ""); /* extension[s] */
449 GETRESPONSE(pb);
450
451 rv = proto_expect_qid(pu, pb, P9PROTO_R_CREATE, &nqid);
452 if (rv)
453 goto out;
454
455 /*
456 * Now, little problem here: create returns an *open* fid.
457 * So, clunk it and walk the parent directory to get a fid
458 * which is not open for I/O yet.
459 */
460 proto_cc_clunkfid(pu, nfid, 0);
461 nfid = NEXTFID(p9p);
462
463 p9pbuf_recycleout(pb);
464 tag = NEXTTAG(p9p);
465 p9pbuf_put_1(pb, P9PROTO_T_WALK);
466 p9pbuf_put_2(pb, tag);
467 p9pbuf_put_4(pb, p9n->fid_base);
468 p9pbuf_put_4(pb, nfid);
469 p9pbuf_put_2(pb, 1);
470 p9pbuf_put_str(pb, name);
471 GETRESPONSE(pb);
472
473 /*
474 * someone removed it already? try again
475 * note: this is kind of lose/lose
476 */
477 if (p9pbuf_get_type(pb) != P9PROTO_R_WALK)
478 goto again;
479
480 pn_new = newp9pnode_va(pu, vap, nfid);
481 qid2vattr(&pn_new->pn_va, &nqid);
482 puffs_newinfo_setcookie(pni, pn_new);
483
484 out:
485 RETURN(rv);
486 }
487
488 int
puffs9p_node_create(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)489 puffs9p_node_create(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni,
490 const struct puffs_cn *pcn, const struct vattr *va)
491 {
492
493 return nodecreate(pu, opc, pni, pcn->pcn_name, va, 0);
494 }
495
496 int
puffs9p_node_mkdir(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)497 puffs9p_node_mkdir(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni,
498 const struct puffs_cn *pcn, const struct vattr *va)
499 {
500
501 return nodecreate(pu, opc, pni, pcn->pcn_name,
502 va, P9PROTO_CPERM_DIR);
503 }
504
505 /*
506 * Need to be a bit clever again: the fid is clunked no matter if
507 * the remove succeeds or not. Re-getting a fid would be way too
508 * difficult in case the remove failed for a valid reason (directory
509 * not empty etcetc.). So walk ourselves another fid to prod the
510 * ice with.
511 */
512 static int
noderemove(struct puffs_usermount * pu,struct puffs_node * pn)513 noderemove(struct puffs_usermount *pu, struct puffs_node *pn)
514 {
515 AUTOVAR(pu);
516 struct p9pnode *p9n = pn->pn_data;
517 p9pfid_t testfid = NEXTFID(p9p);
518
519 rv = proto_cc_dupfid(pu, p9n->fid_base, testfid);
520 if (rv)
521 goto out;
522
523 tag = NEXTTAG(p9p);
524 p9pbuf_put_1(pb, P9PROTO_T_REMOVE);
525 p9pbuf_put_2(pb, tag);
526 p9pbuf_put_4(pb, testfid);
527
528 /*
529 * XXX: error handling isn't very robust, but doom is impending
530 * anyway, so just accept we're going belly up and play dead
531 */
532 GETRESPONSE(pb);
533
534 if (p9pbuf_get_type(pb) != P9PROTO_R_REMOVE) {
535 rv = proto_handle_rerror(pu, pb);
536 } else {
537 proto_cc_clunkfid(pu, p9n->fid_base, 0);
538 p9n->fid_base = P9P_INVALFID;
539 puffs_pn_remove(pn);
540 }
541
542 out:
543 RETURN(rv);
544 }
545
546 int
puffs9p_node_remove(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)547 puffs9p_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
548 const struct puffs_cn *pcn)
549 {
550 struct puffs_cc *pcc = puffs_cc_getcc(pu);
551 struct puffs_node *pn = targ;
552 int rv;
553
554 if (pn->pn_va.va_type == VDIR)
555 return EISDIR;
556
557 rv = noderemove(pu, pn);
558 if (rv == 0)
559 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2);
560
561 return rv;
562 }
563
564 int
puffs9p_node_rmdir(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)565 puffs9p_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
566 const struct puffs_cn *pcn)
567 {
568 struct puffs_cc *pcc = puffs_cc_getcc(pu);
569 struct puffs_node *pn = targ;
570 int rv;
571
572 if (pn->pn_va.va_type != VDIR)
573 return ENOTDIR;
574
575 rv = noderemove(pu, pn);
576 if (rv == 0)
577 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2);
578
579 return rv;
580 }
581
582 int
puffs9p_node_rename(struct puffs_usermount * pu,void * src_dir,void * src,const struct puffs_cn * pcn_src,void * targ_dir,void * targ,const struct puffs_cn * pcn_targ)583 puffs9p_node_rename(struct puffs_usermount *pu,
584 void *src_dir, void *src, const struct puffs_cn *pcn_src,
585 void *targ_dir, void *targ, const struct puffs_cn *pcn_targ)
586 {
587 AUTOVAR(pu);
588 struct puffs_node *pn_src = src;
589 struct p9pnode *p9n_src = pn_src->pn_data;
590
591 /*
592 * 9P rename can only change the last pathname component.
593 * Return EXDEV for attempts to move a file to a different
594 * directory to make mv(1) fall back to copying.
595 */
596 if (src_dir != targ_dir) {
597 rv = EXDEV;
598 goto out;
599 }
600
601 /* 9P doesn't allow to overwrite in rename */
602 if (targ) {
603 struct puffs_node *pn_targ = targ;
604
605 rv = noderemove(pu, pn_targ);
606 if (rv)
607 goto out;
608 }
609
610 tag = NEXTTAG(p9p);
611 p9pbuf_put_1(pb, P9PROTO_T_WSTAT);
612 p9pbuf_put_2(pb, tag);
613 p9pbuf_put_4(pb, p9n_src->fid_base);
614 proto_make_stat(pu, pb, NULL, pcn_targ->pcn_name, pn_src->pn_va.va_type);
615 GETRESPONSE(pb);
616
617 if (p9pbuf_get_type(pb) != P9PROTO_R_WSTAT)
618 rv = proto_handle_rerror(pu, pb);
619
620 out:
621 RETURN(rv);
622 }
623
624 /*
625 * - "here's one"
626 * - "9P"
627 * ~ "i'm not dead"
628 * - "you're not fooling anyone you know, you'll be stone dead in a minute
629 * - "he says he's not quite dead"
630 * - "isn't there anything you could do?"
631 * - *clunk*!
632 * - "thanks"
633 */
634 int
puffs9p_node_reclaim(struct puffs_usermount * pu,void * opc)635 puffs9p_node_reclaim(struct puffs_usermount *pu, void *opc)
636 {
637 struct puffs_node *pn = opc;
638 struct p9pnode *p9n = pn->pn_data;
639
640 assert(LIST_EMPTY(&p9n->dir_openlist));
641 assert(p9n->fid_read == P9P_INVALFID && p9n->fid_write == P9P_INVALFID);
642
643 proto_cc_clunkfid(pu, p9n->fid_base, 0);
644 free(p9n);
645 puffs_pn_put(pn);
646
647 return 0;
648 }
649