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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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