1 /* $NetBSD: node.c,v 1.29 2020/06/01 13:30:52 uwe 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.29 2020/06/01 13:30:52 uwe 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 if (mode & FREAD && p9n->fid_read == P9P_INVALFID) { 278 nfid = NEXTFID(p9p); 279 error = proto_cc_open(pu, p9n->fid_base, nfid, 280 P9PROTO_OMODE_READ); 281 if (error) 282 return error; 283 p9n->fid_read = nfid; 284 } 285 if (mode & FWRITE && p9n->fid_write == P9P_INVALFID) { 286 nfid = NEXTFID(p9p); 287 error = proto_cc_open(pu, p9n->fid_base, nfid, 288 P9PROTO_OMODE_WRITE); 289 if (error) 290 return error; 291 p9n->fid_write = nfid; 292 } 293 } 294 295 return 0; 296 } 297 298 int 299 puffs9p_node_inactive(struct puffs_usermount *pu, void *opc) 300 { 301 struct puffs_node *pn = opc; 302 struct p9pnode *p9n = pn->pn_data; 303 304 if (pn->pn_va.va_type == VDIR) { 305 nukealldf(pu, p9n); 306 } else { 307 if (p9n->fid_read != P9P_INVALFID) { 308 proto_cc_clunkfid(pu, p9n->fid_read, 0); 309 p9n->fid_read = P9P_INVALFID; 310 } 311 if (p9n->fid_write != P9P_INVALFID) { 312 proto_cc_clunkfid(pu, p9n->fid_write, 0); 313 p9n->fid_write = P9P_INVALFID; 314 } 315 } 316 317 return 0; 318 } 319 320 int 321 puffs9p_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, 322 off_t offset, size_t *resid, const struct puffs_cred *pcr, 323 int ioflag) 324 { 325 AUTOVAR(pu); 326 struct puffs_node *pn = opc; 327 struct p9pnode *p9n = pn->pn_data; 328 uint32_t count; 329 size_t nread; 330 331 nread = 0; 332 while (*resid > 0 && (uint64_t)(offset+nread) < pn->pn_va.va_size) { 333 tag = NEXTTAG(p9p); 334 p9pbuf_put_1(pb, P9PROTO_T_READ); 335 p9pbuf_put_2(pb, tag); 336 p9pbuf_put_4(pb, p9n->fid_read); 337 p9pbuf_put_8(pb, offset+nread); 338 p9pbuf_put_4(pb, MIN((uint32_t)*resid,p9p->maxreq-24)); 339 GETRESPONSE(pb); 340 341 if (p9pbuf_get_type(pb) != P9PROTO_R_READ) { 342 rv = proto_handle_rerror(pu, pb); 343 break; 344 } 345 346 p9pbuf_get_4(pb, &count); 347 if ((rv = p9pbuf_read_data(pb, buf + nread, count))) 348 break; 349 350 if (count == 0) 351 break; 352 353 *resid -= count; 354 nread += count; 355 356 p9pbuf_recycleout(pb); 357 } 358 359 out: 360 RETURN(rv); 361 } 362 363 int 364 puffs9p_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, 365 off_t offset, size_t *resid, const struct puffs_cred *cred, 366 int ioflag) 367 { 368 AUTOVAR(pu); 369 struct puffs_node *pn = opc; 370 struct p9pnode *p9n = pn->pn_data; 371 uint32_t chunk, count; 372 size_t nwrite; 373 374 if (ioflag & PUFFS_IO_APPEND) 375 offset = pn->pn_va.va_size; 376 377 nwrite = 0; 378 while (*resid > 0) { 379 chunk = MIN(*resid, p9p->maxreq-32); 380 381 tag = NEXTTAG(p9p); 382 p9pbuf_put_1(pb, P9PROTO_T_WRITE); 383 p9pbuf_put_2(pb, tag); 384 p9pbuf_put_4(pb, p9n->fid_write); 385 p9pbuf_put_8(pb, offset+nwrite); 386 p9pbuf_put_4(pb, chunk); 387 p9pbuf_write_data(pb, buf+nwrite, chunk); 388 GETRESPONSE(pb); 389 390 if (p9pbuf_get_type(pb) != P9PROTO_R_WRITE) { 391 rv = proto_handle_rerror(pu, pb); 392 break; 393 } 394 395 p9pbuf_get_4(pb, &count); 396 *resid -= count; 397 nwrite += count; 398 399 if (count != chunk) { 400 rv = EPROTO; 401 break; 402 } 403 404 p9pbuf_recycleout(pb); 405 } 406 407 out: 408 RETURN(rv); 409 } 410 411 static int 412 nodecreate(struct puffs_usermount *pu, struct puffs_node *pn, 413 struct puffs_newinfo *pni, const char *name, 414 const struct vattr *vap, uint32_t dirbit) 415 { 416 AUTOVAR(pu); 417 struct puffs_node *pn_new; 418 struct p9pnode *p9n = pn->pn_data; 419 p9pfid_t nfid = NEXTFID(p9p); 420 struct qid9p nqid; 421 int tries = 0; 422 423 again: 424 if (++tries > 5) { 425 rv = EPROTO; 426 goto out; 427 } 428 429 rv = proto_cc_dupfid(pu, p9n->fid_base, nfid); 430 if (rv) 431 goto out; 432 433 tag = NEXTTAG(p9p); 434 p9pbuf_put_1(pb, P9PROTO_T_CREATE); 435 p9pbuf_put_2(pb, tag); 436 p9pbuf_put_4(pb, nfid); 437 p9pbuf_put_str(pb, name); 438 p9pbuf_put_4(pb, dirbit | (vap->va_mode & 0777)); 439 p9pbuf_put_1(pb, 0); 440 if (p9p->protover == P9PROTO_VERSION_U) 441 p9pbuf_put_str(pb, ""); /* extension[s] */ 442 GETRESPONSE(pb); 443 444 rv = proto_expect_qid(pu, pb, P9PROTO_R_CREATE, &nqid); 445 if (rv) 446 goto out; 447 448 /* 449 * Now, little problem here: create returns an *open* fid. 450 * So, clunk it and walk the parent directory to get a fid 451 * which is not open for I/O yet. 452 */ 453 proto_cc_clunkfid(pu, nfid, 0); 454 nfid = NEXTFID(p9p); 455 456 p9pbuf_recycleout(pb); 457 tag = NEXTTAG(p9p); 458 p9pbuf_put_1(pb, P9PROTO_T_WALK); 459 p9pbuf_put_2(pb, tag); 460 p9pbuf_put_4(pb, p9n->fid_base); 461 p9pbuf_put_4(pb, nfid); 462 p9pbuf_put_2(pb, 1); 463 p9pbuf_put_str(pb, name); 464 GETRESPONSE(pb); 465 466 /* 467 * someone removed it already? try again 468 * note: this is kind of lose/lose 469 */ 470 if (p9pbuf_get_type(pb) != P9PROTO_R_WALK) 471 goto again; 472 473 pn_new = newp9pnode_va(pu, vap, nfid); 474 qid2vattr(&pn_new->pn_va, &nqid); 475 puffs_newinfo_setcookie(pni, pn_new); 476 477 out: 478 RETURN(rv); 479 } 480 481 int 482 puffs9p_node_create(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni, 483 const struct puffs_cn *pcn, const struct vattr *va) 484 { 485 486 return nodecreate(pu, opc, pni, pcn->pcn_name, va, 0); 487 } 488 489 int 490 puffs9p_node_mkdir(struct puffs_usermount *pu, void *opc, struct puffs_newinfo *pni, 491 const struct puffs_cn *pcn, const struct vattr *va) 492 { 493 494 return nodecreate(pu, opc, pni, pcn->pcn_name, 495 va, P9PROTO_CPERM_DIR); 496 } 497 498 /* 499 * Need to be a bit clever again: the fid is clunked no matter if 500 * the remove succeeds or not. Re-getting a fid would be way too 501 * difficult in case the remove failed for a valid reason (directory 502 * not empty etcetc.). So walk ourselves another fid to prod the 503 * ice with. 504 */ 505 static int 506 noderemove(struct puffs_usermount *pu, struct puffs_node *pn) 507 { 508 AUTOVAR(pu); 509 struct p9pnode *p9n = pn->pn_data; 510 p9pfid_t testfid = NEXTFID(p9p); 511 512 rv = proto_cc_dupfid(pu, p9n->fid_base, testfid); 513 if (rv) 514 goto out; 515 516 tag = NEXTTAG(p9p); 517 p9pbuf_put_1(pb, P9PROTO_T_REMOVE); 518 p9pbuf_put_2(pb, tag); 519 p9pbuf_put_4(pb, testfid); 520 521 /* 522 * XXX: error handling isn't very robust, but doom is impending 523 * anyway, so just accept we're going belly up and play dead 524 */ 525 GETRESPONSE(pb); 526 527 if (p9pbuf_get_type(pb) != P9PROTO_R_REMOVE) { 528 rv = proto_handle_rerror(pu, pb); 529 } else { 530 proto_cc_clunkfid(pu, p9n->fid_base, 0); 531 p9n->fid_base = P9P_INVALFID; 532 puffs_pn_remove(pn); 533 } 534 535 out: 536 RETURN(rv); 537 } 538 539 int 540 puffs9p_node_remove(struct puffs_usermount *pu, void *opc, void *targ, 541 const struct puffs_cn *pcn) 542 { 543 struct puffs_cc *pcc = puffs_cc_getcc(pu); 544 struct puffs_node *pn = targ; 545 int rv; 546 547 if (pn->pn_va.va_type == VDIR) 548 return EISDIR; 549 550 rv = noderemove(pu, pn); 551 if (rv == 0) 552 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2); 553 554 return rv; 555 } 556 557 int 558 puffs9p_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ, 559 const struct puffs_cn *pcn) 560 { 561 struct puffs_cc *pcc = puffs_cc_getcc(pu); 562 struct puffs_node *pn = targ; 563 int rv; 564 565 if (pn->pn_va.va_type != VDIR) 566 return ENOTDIR; 567 568 rv = noderemove(pu, pn); 569 if (rv == 0) 570 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2); 571 572 return rv; 573 } 574 575 int 576 puffs9p_node_rename(struct puffs_usermount *pu, 577 void *src_dir, void *src, const struct puffs_cn *pcn_src, 578 void *targ_dir, void *targ, const struct puffs_cn *pcn_targ) 579 { 580 AUTOVAR(pu); 581 struct puffs_node *pn_src = src; 582 struct p9pnode *p9n_src = pn_src->pn_data; 583 584 /* 585 * 9P rename can only change the last pathname component. 586 * Return EXDEV for attempts to move a file to a different 587 * directory to make mv(1) fall back to copying. 588 */ 589 if (src_dir != targ_dir) { 590 rv = EXDEV; 591 goto out; 592 } 593 594 /* 9P doesn't allow to overwrite in rename */ 595 if (targ) { 596 struct puffs_node *pn_targ = targ; 597 598 rv = noderemove(pu, pn_targ); 599 if (rv) 600 goto out; 601 } 602 603 tag = NEXTTAG(p9p); 604 p9pbuf_put_1(pb, P9PROTO_T_WSTAT); 605 p9pbuf_put_2(pb, tag); 606 p9pbuf_put_4(pb, p9n_src->fid_base); 607 proto_make_stat(pu, pb, NULL, pcn_targ->pcn_name, pn_src->pn_va.va_type); 608 GETRESPONSE(pb); 609 610 if (p9pbuf_get_type(pb) != P9PROTO_R_WSTAT) 611 rv = proto_handle_rerror(pu, pb); 612 613 out: 614 RETURN(rv); 615 } 616 617 /* 618 * - "here's one" 619 * - "9P" 620 * ~ "i'm not dead" 621 * - "you're not fooling anyone you know, you'll be stone dead in a minute 622 * - "he says he's not quite dead" 623 * - "isn't there anything you could do?" 624 * - *clunk*! 625 * - "thanks" 626 */ 627 int 628 puffs9p_node_reclaim(struct puffs_usermount *pu, void *opc) 629 { 630 struct puffs_node *pn = opc; 631 struct p9pnode *p9n = pn->pn_data; 632 633 assert(LIST_EMPTY(&p9n->dir_openlist)); 634 assert(p9n->fid_read == P9P_INVALFID && p9n->fid_write == P9P_INVALFID); 635 636 proto_cc_clunkfid(pu, p9n->fid_base, 0); 637 free(p9n); 638 puffs_pn_put(pn); 639 640 return 0; 641 } 642