node.c revision 1.30 1 /* $NetBSD: node.c,v 1.30 2022/03/02 04:11:41 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.30 2022/03/02 04:11:41 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 *
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
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
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
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
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 p9pbuf_get_4(pb, &count);
191
192 /*
193 * if count is 0, assume we at end-of-dir. dfp is no longer
194 * useful, so nuke it
195 */
196 if (count == 0) {
197 *eofflag = 1;
198 releasedf(pu, dfp);
199 goto out;
200 }
201
202 while (count > 0) {
203 if ((rv = proto_getstat(pu, pb, &va, &name, &statsize))) {
204 /*
205 * If there was an error, it's unlikely we'll be
206 * coming back, so just nuke the dfp. If we do
207 * come back for some strange reason, we'll just
208 * regen it.
209 */
210 releasedf(pu, dfp);
211 goto out;
212 }
213
214 puffs_nextdent(&dent, name, va.va_fileid,
215 puffs_vtype2dt(va.va_type), reslen);
216
217 count -= statsize;
218 *readoff += statsize;
219 dfp->seekoff += statsize;
220 free(name);
221 }
222
223 storedf(p9n, dfp);
224
225 out:
226 RETURN(rv);
227 }
228
229 int
230 puffs9p_node_setattr(struct puffs_usermount *pu, void *opc,
231 const struct vattr *va, const struct puffs_cred *pcr)
232 {
233 AUTOVAR(pu);
234 struct puffs_node *pn = opc;
235 struct p9pnode *p9n = pn->pn_data;
236
237 tag = NEXTTAG(p9p);
238 p9pbuf_put_1(pb, P9PROTO_T_WSTAT);
239 p9pbuf_put_2(pb, tag);
240 p9pbuf_put_4(pb, p9n->fid_base);
241 proto_make_stat(pu, pb, va, NULL, pn->pn_va.va_type);
242 GETRESPONSE(pb);
243
244 if (p9pbuf_get_type(pb) != P9PROTO_R_WSTAT) {
245 rv = proto_handle_rerror(pu, pb);
246 }
247
248 out:
249 RETURN(rv);
250 }
251
252 /*
253 * Ok, time to get clever. There are two possible cases: we are
254 * opening a file or we are opening a directory.
255 *
256 * If it's a directory, don't bother opening it here, but rather
257 * wait until readdir, since it's probable we need to be able to
258 * open a directory there in any case.
259 *
260 * If it's a regular file, open it here with whatever credentials
261 * we happen to have. Let the upper layers of the kernel worry
262 * about permission control.
263 */
264 int
265 puffs9p_node_open(struct puffs_usermount *pu, void *opc, int mode,
266 const struct puffs_cred *pcr)
267 {
268 struct puffs_cc *pcc = puffs_cc_getcc(pu);
269 struct puffs9p *p9p = puffs_getspecific(pu);
270 struct puffs_node *pn = opc;
271 struct p9pnode *p9n = pn->pn_data;
272 p9pfid_t nfid;
273 int error = 0;
274
275 puffs_setback(pcc, PUFFS_SETBACK_INACT_N1);
276 if (pn->pn_va.va_type != VDIR) {
277 /* Always require read access for page cache */
278 mode |= FREAD;
279 if (mode & FREAD && p9n->fid_read == P9P_INVALFID) {
280 nfid = NEXTFID(p9p);
281 error = proto_cc_open(pu, p9n->fid_base, nfid,
282 P9PROTO_OMODE_READ);
283 if (error)
284 return error;
285 p9n->fid_read = nfid;
286 }
287 if (mode & FWRITE && p9n->fid_write == P9P_INVALFID) {
288 nfid = NEXTFID(p9p);
289 error = proto_cc_open(pu, p9n->fid_base, nfid,
290 P9PROTO_OMODE_WRITE);
291 if (error)
292 return error;
293 p9n->fid_write = nfid;
294 }
295 }
296
297 return 0;
298 }
299
300 int
301 puffs9p_node_inactive(struct puffs_usermount *pu, void *opc)
302 {
303 struct puffs_node *pn = opc;
304 struct p9pnode *p9n = pn->pn_data;
305
306 if (pn->pn_va.va_type == VDIR) {
307 nukealldf(pu, p9n);
308 } else {
309 if (p9n->fid_read != P9P_INVALFID) {
310 proto_cc_clunkfid(pu, p9n->fid_read, 0);
311 p9n->fid_read = P9P_INVALFID;
312 }
313 if (p9n->fid_write != P9P_INVALFID) {
314 proto_cc_clunkfid(pu, p9n->fid_write, 0);
315 p9n->fid_write = P9P_INVALFID;
316 }
317 }
318
319 return 0;
320 }
321
322 int
323 puffs9p_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
324 off_t offset, size_t *resid, const struct puffs_cred *pcr,
325 int ioflag)
326 {
327 AUTOVAR(pu);
328 struct puffs_node *pn = opc;
329 struct p9pnode *p9n = pn->pn_data;
330 uint32_t count;
331 size_t nread;
332
333 nread = 0;
334 while (*resid > 0 && (uint64_t)(offset+nread) < pn->pn_va.va_size) {
335 tag = NEXTTAG(p9p);
336 p9pbuf_put_1(pb, P9PROTO_T_READ);
337 p9pbuf_put_2(pb, tag);
338 p9pbuf_put_4(pb, p9n->fid_read);
339 p9pbuf_put_8(pb, offset+nread);
340 p9pbuf_put_4(pb, MIN((uint32_t)*resid,p9p->maxreq-24));
341 GETRESPONSE(pb);
342
343 if (p9pbuf_get_type(pb) != P9PROTO_R_READ) {
344 rv = proto_handle_rerror(pu, pb);
345 break;
346 }
347
348 p9pbuf_get_4(pb, &count);
349 if ((rv = p9pbuf_read_data(pb, buf + nread, count)))
350 break;
351
352 if (count == 0)
353 break;
354
355 *resid -= count;
356 nread += count;
357
358 p9pbuf_recycleout(pb);
359 }
360
361 out:
362 RETURN(rv);
363 }
364
365 int
366 puffs9p_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
367 off_t offset, size_t *resid, const struct puffs_cred *cred,
368 int ioflag)
369 {
370 AUTOVAR(pu);
371 struct puffs_node *pn = opc;
372 struct p9pnode *p9n = pn->pn_data;
373 uint32_t chunk, count;
374 size_t nwrite;
375
376 if (ioflag & PUFFS_IO_APPEND)
377 offset = pn->pn_va.va_size;
378
379 nwrite = 0;
380 while (*resid > 0) {
381 chunk = MIN(*resid, p9p->maxreq-32);
382
383 tag = NEXTTAG(p9p);
384 p9pbuf_put_1(pb, P9PROTO_T_WRITE);
385 p9pbuf_put_2(pb, tag);
386 p9pbuf_put_4(pb, p9n->fid_write);
387 p9pbuf_put_8(pb, offset+nwrite);
388 p9pbuf_put_4(pb, chunk);
389 p9pbuf_write_data(pb, buf+nwrite, chunk);
390 GETRESPONSE(pb);
391
392 if (p9pbuf_get_type(pb) != P9PROTO_R_WRITE) {
393 rv = proto_handle_rerror(pu, pb);
394 break;
395 }
396
397 p9pbuf_get_4(pb, &count);
398 *resid -= count;
399 nwrite += count;
400
401 if (count != chunk) {
402 rv = EPROTO;
403 break;
404 }
405
406 p9pbuf_recycleout(pb);
407 }
408
409 out:
410 RETURN(rv);
411 }
412
413 static int
414 nodecreate(struct puffs_usermount *pu, struct puffs_node *pn,
415 struct puffs_newinfo *pni, const char *name,
416 const struct vattr *vap, uint32_t dirbit)
417 {
418 AUTOVAR(pu);
419 struct puffs_node *pn_new;
420 struct p9pnode *p9n = pn->pn_data;
421 p9pfid_t nfid = NEXTFID(p9p);
422 struct qid9p nqid;
423 int tries = 0;
424
425 again:
426 if (++tries > 5) {
427 rv = EPROTO;
428 goto out;
429 }
430
431 rv = proto_cc_dupfid(pu, p9n->fid_base, nfid);
432 if (rv)
433 goto out;
434
435 tag = NEXTTAG(p9p);
436 p9pbuf_put_1(pb, P9PROTO_T_CREATE);
437 p9pbuf_put_2(pb, tag);
438 p9pbuf_put_4(pb, nfid);
439 p9pbuf_put_str(pb, name);
440 p9pbuf_put_4(pb, dirbit | (vap->va_mode & 0777));
441 p9pbuf_put_1(pb, 0);
442 if (p9p->protover == P9PROTO_VERSION_U)
443 p9pbuf_put_str(pb, ""); /* extension[s] */
444 GETRESPONSE(pb);
445
446 rv = proto_expect_qid(pu, pb, P9PROTO_R_CREATE, &nqid);
447 if (rv)
448 goto out;
449
450 /*
451 * Now, little problem here: create returns an *open* fid.
452 * So, clunk it and walk the parent directory to get a fid
453 * which is not open for I/O yet.
454 */
455 proto_cc_clunkfid(pu, nfid, 0);
456 nfid = NEXTFID(p9p);
457
458 p9pbuf_recycleout(pb);
459 tag = NEXTTAG(p9p);
460 p9pbuf_put_1(pb, P9PROTO_T_WALK);
461 p9pbuf_put_2(pb, tag);
462 p9pbuf_put_4(pb, p9n->fid_base);
463 p9pbuf_put_4(pb, nfid);
464 p9pbuf_put_2(pb, 1);
465 p9pbuf_put_str(pb, name);
466 GETRESPONSE(pb);
467
468 /*
469 * someone removed it already? try again
470 * note: this is kind of lose/lose
471 */
472 if (p9pbuf_get_type(pb) != P9PROTO_R_WALK)
473 goto again;
474
475 pn_new = newp9pnode_va(pu, vap, nfid);
476 qid2vattr(&pn_new->pn_va, &nqid);
477 puffs_newinfo_setcookie(pni, pn_new);
478
479 out:
480 RETURN(rv);
481 }
482
483 int
484 puffs9p_node_create(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni,
485 const struct puffs_cn *pcn, const struct vattr *va)
486 {
487
488 return nodecreate(pu, opc, pni, pcn->pcn_name, va, 0);
489 }
490
491 int
492 puffs9p_node_mkdir(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni,
493 const struct puffs_cn *pcn, const struct vattr *va)
494 {
495
496 return nodecreate(pu, opc, pni, pcn->pcn_name,
497 va, P9PROTO_CPERM_DIR);
498 }
499
500 /*
501 * Need to be a bit clever again: the fid is clunked no matter if
502 * the remove succeeds or not. Re-getting a fid would be way too
503 * difficult in case the remove failed for a valid reason (directory
504 * not empty etcetc.). So walk ourselves another fid to prod the
505 * ice with.
506 */
507 static int
508 noderemove(struct puffs_usermount *pu, struct puffs_node *pn)
509 {
510 AUTOVAR(pu);
511 struct p9pnode *p9n = pn->pn_data;
512 p9pfid_t testfid = NEXTFID(p9p);
513
514 rv = proto_cc_dupfid(pu, p9n->fid_base, testfid);
515 if (rv)
516 goto out;
517
518 tag = NEXTTAG(p9p);
519 p9pbuf_put_1(pb, P9PROTO_T_REMOVE);
520 p9pbuf_put_2(pb, tag);
521 p9pbuf_put_4(pb, testfid);
522
523 /*
524 * XXX: error handling isn't very robust, but doom is impending
525 * anyway, so just accept we're going belly up and play dead
526 */
527 GETRESPONSE(pb);
528
529 if (p9pbuf_get_type(pb) != P9PROTO_R_REMOVE) {
530 rv = proto_handle_rerror(pu, pb);
531 } else {
532 proto_cc_clunkfid(pu, p9n->fid_base, 0);
533 p9n->fid_base = P9P_INVALFID;
534 puffs_pn_remove(pn);
535 }
536
537 out:
538 RETURN(rv);
539 }
540
541 int
542 puffs9p_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
543 const struct puffs_cn *pcn)
544 {
545 struct puffs_cc *pcc = puffs_cc_getcc(pu);
546 struct puffs_node *pn = targ;
547 int rv;
548
549 if (pn->pn_va.va_type == VDIR)
550 return EISDIR;
551
552 rv = noderemove(pu, pn);
553 if (rv == 0)
554 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2);
555
556 return rv;
557 }
558
559 int
560 puffs9p_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
561 const struct puffs_cn *pcn)
562 {
563 struct puffs_cc *pcc = puffs_cc_getcc(pu);
564 struct puffs_node *pn = targ;
565 int rv;
566
567 if (pn->pn_va.va_type != VDIR)
568 return ENOTDIR;
569
570 rv = noderemove(pu, pn);
571 if (rv == 0)
572 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2);
573
574 return rv;
575 }
576
577 int
578 puffs9p_node_rename(struct puffs_usermount *pu,
579 void *src_dir, void *src, const struct puffs_cn *pcn_src,
580 void *targ_dir, void *targ, const struct puffs_cn *pcn_targ)
581 {
582 AUTOVAR(pu);
583 struct puffs_node *pn_src = src;
584 struct p9pnode *p9n_src = pn_src->pn_data;
585
586 /*
587 * 9P rename can only change the last pathname component.
588 * Return EXDEV for attempts to move a file to a different
589 * directory to make mv(1) fall back to copying.
590 */
591 if (src_dir != targ_dir) {
592 rv = EXDEV;
593 goto out;
594 }
595
596 /* 9P doesn't allow to overwrite in rename */
597 if (targ) {
598 struct puffs_node *pn_targ = targ;
599
600 rv = noderemove(pu, pn_targ);
601 if (rv)
602 goto out;
603 }
604
605 tag = NEXTTAG(p9p);
606 p9pbuf_put_1(pb, P9PROTO_T_WSTAT);
607 p9pbuf_put_2(pb, tag);
608 p9pbuf_put_4(pb, p9n_src->fid_base);
609 proto_make_stat(pu, pb, NULL, pcn_targ->pcn_name, pn_src->pn_va.va_type);
610 GETRESPONSE(pb);
611
612 if (p9pbuf_get_type(pb) != P9PROTO_R_WSTAT)
613 rv = proto_handle_rerror(pu, pb);
614
615 out:
616 RETURN(rv);
617 }
618
619 /*
620 * - "here's one"
621 * - "9P"
622 * ~ "i'm not dead"
623 * - "you're not fooling anyone you know, you'll be stone dead in a minute
624 * - "he says he's not quite dead"
625 * - "isn't there anything you could do?"
626 * - *clunk*!
627 * - "thanks"
628 */
629 int
630 puffs9p_node_reclaim(struct puffs_usermount *pu, void *opc)
631 {
632 struct puffs_node *pn = opc;
633 struct p9pnode *p9n = pn->pn_data;
634
635 assert(LIST_EMPTY(&p9n->dir_openlist));
636 assert(p9n->fid_read == P9P_INVALFID && p9n->fid_write == P9P_INVALFID);
637
638 proto_cc_clunkfid(pu, p9n->fid_base, 0);
639 free(p9n);
640 puffs_pn_put(pn);
641
642 return 0;
643 }
644