1 /* $NetBSD: node.c,v 1.42 2007/10/21 19:27:12 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2006 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.42 2007/10/21 19:27:12 pooka Exp $"); 31 #endif /* !lint */ 32 33 #include <assert.h> 34 #include <errno.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 38 #include "psshfs.h" 39 #include "sftp_proto.h" 40 41 int 42 psshfs_node_lookup(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni, 43 const struct puffs_cn *pcn) 44 { 45 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 46 struct psshfs_ctx *pctx = puffs_getspecific(pu); 47 struct puffs_node *pn_dir = opc; 48 struct psshfs_node *psn, *psn_dir = pn_dir->pn_data; 49 struct puffs_node *pn; 50 struct psshfs_dir *pd; 51 struct vattr va; 52 int rv; 53 54 if (PCNISDOTDOT(pcn)) { 55 psn = psn_dir->parent->pn_data; 56 psn->stat &= ~PSN_RECLAIMED; 57 58 puffs_newinfo_setcookie(pni, psn_dir->parent); 59 puffs_newinfo_setvtype(pni, VDIR); 60 return 0; 61 } 62 63 rv = sftp_readdir(pcc, pctx, pn_dir); 64 if (rv) { 65 if (rv != EPERM) 66 return rv; 67 68 /* 69 * Can't read the directory. We still might be 70 * able to find the node with getattr in -r+x dirs 71 */ 72 rv = getpathattr(pcc, PCNPATH(pcn), &va); 73 if (rv) 74 return rv; 75 76 /* guess */ 77 if (va.va_type == VDIR) 78 va.va_nlink = 2; 79 else 80 va.va_nlink = 1; 81 82 pn = allocnode(pu, pn_dir, pcn->pcn_name, &va); 83 psn = pn->pn_data; 84 psn->attrread = time(NULL); 85 } else { 86 pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name); 87 if (!pd) { 88 return ENOENT; 89 } 90 91 if (pd->entry) 92 pn = pd->entry; 93 else 94 pn = makenode(pu, pn_dir, pd, &pd->va); 95 psn = pn->pn_data; 96 } 97 98 psn->stat &= ~PSN_RECLAIMED; 99 100 puffs_newinfo_setcookie(pni, pn); 101 puffs_newinfo_setvtype(pni, pn->pn_va.va_type); 102 puffs_newinfo_setsize(pni, pn->pn_va.va_size); 103 104 return 0; 105 } 106 107 int 108 psshfs_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *vap, 109 const struct puffs_cred *pcr, const struct puffs_cid *pcid) 110 { 111 struct puffs_node *pn = opc; 112 int rv; 113 114 rv = getnodeattr(pcc, pn); 115 if (rv) 116 return rv; 117 118 memcpy(vap, &pn->pn_va, sizeof(struct vattr)); 119 120 return 0; 121 } 122 123 int 124 psshfs_node_setattr(struct puffs_cc *pcc, void *opc, 125 const struct vattr *va, const struct puffs_cred *pcr, 126 const struct puffs_cid *pcid) 127 { 128 PSSHFSAUTOVAR(pcc); 129 struct vattr kludgeva; 130 struct puffs_node *pn = opc; 131 132 psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn)); 133 134 memcpy(&kludgeva, va, sizeof(struct vattr)); 135 136 /* XXX: kludge due to openssh server implementation */ 137 if (va->va_atime.tv_sec != PUFFS_VNOVAL 138 && va->va_mtime.tv_sec == PUFFS_VNOVAL) { 139 if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL) 140 kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; 141 else 142 kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec; 143 } 144 if (va->va_mtime.tv_sec != PUFFS_VNOVAL 145 && va->va_atime.tv_sec == PUFFS_VNOVAL) { 146 if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL) 147 kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; 148 else 149 kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec; 150 } 151 152 psbuf_put_vattr(pb, &kludgeva); 153 GETRESPONSE(pb); 154 155 rv = psbuf_expect_status(pb); 156 if (rv == 0) 157 puffs_setvattr(&pn->pn_va, &kludgeva); 158 159 out: 160 PSSHFSRETURN(rv); 161 } 162 163 int 164 psshfs_node_create(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni, 165 const struct puffs_cn *pcn, const struct vattr *va) 166 { 167 PSSHFSAUTOVAR(pcc); 168 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 169 struct puffs_node *pn = opc; 170 struct puffs_node *pn_new; 171 char *fhand = NULL; 172 uint32_t fhandlen; 173 174 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 175 if (!pn_new) { 176 rv = ENOMEM; 177 goto out; 178 } 179 180 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn)); 181 psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); 182 psbuf_put_vattr(pb, va); 183 GETRESPONSE(pb); 184 185 rv = psbuf_expect_handle(pb, &fhand, &fhandlen); 186 if (rv == 0) 187 puffs_newinfo_setcookie(pni, pn_new); 188 else 189 goto out; 190 191 reqid = NEXTREQ(pctx); 192 psbuf_recycleout(pb); 193 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen); 194 JUSTSEND(pb); 195 free(fhand); 196 return 0; 197 198 out: 199 free(fhand); 200 PSSHFSRETURN(rv); 201 } 202 203 int 204 psshfs_node_mmap(struct puffs_cc* pcc, void *opc, vm_prot_t prot, 205 const struct puffs_cred *pcr, const struct puffs_cid *pcid) 206 { 207 struct puffs_node *pn = opc; 208 struct psshfs_node *psn = pn->pn_data; 209 210 if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) 211 psn->stat |= PSN_READMAP; 212 if (prot & VM_PROT_WRITE) 213 psn->stat |= PSN_WRITEMAP; 214 215 return 0; 216 } 217 218 int 219 psshfs_node_open(struct puffs_cc *pcc, void *opc, int mode, 220 const struct puffs_cred *pcr, const struct puffs_cid *pcid) 221 { 222 PSSHFSAUTOVAR(pcc); 223 struct vattr va; 224 struct puffs_node *pn = opc; 225 struct psshfs_node *psn = pn->pn_data; 226 227 if (pn->pn_va.va_type == VDIR) 228 goto out; 229 230 puffs_setback(pcc, PUFFS_SETBACK_INACT_N1); 231 puffs_vattr_null(&va); 232 if (mode & FREAD && psn->fhand_r == NULL) { 233 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); 234 psbuf_put_4(pb, SSH_FXF_READ); 235 psbuf_put_vattr(pb, &va); 236 GETRESPONSE(pb); 237 238 rv = psbuf_expect_handle(pb, &psn->fhand_r, &psn->fhand_r_len); 239 if (rv) 240 goto out; 241 psbuf_recycleout(pb); 242 } 243 if (mode & FWRITE && psn->fhand_w == NULL) { 244 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); 245 psbuf_put_4(pb, SSH_FXF_WRITE); 246 psbuf_put_vattr(pb, &va); 247 GETRESPONSE(pb); 248 249 rv = psbuf_expect_handle(pb, &psn->fhand_w, &psn->fhand_w_len); 250 if (rv) 251 goto out; 252 } 253 254 out: 255 if (rv == 0) 256 psn->opencount++; 257 258 PSSHFSRETURN(rv); 259 } 260 261 static void 262 closehandles(struct puffs_cc *pcc, struct psshfs_node *psn) 263 { 264 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 265 struct psshfs_ctx *pctx = puffs_cc_getspecific(pcc); 266 struct puffs_framebuf *pb1, *pb2; 267 uint32_t reqid = NEXTREQ(pctx); 268 int rv; /* macro magic */ 269 270 if ((psn->stat & PSN_READMAP) == 0 && psn->fhand_r) { 271 pb1 = psbuf_makeout(); 272 psbuf_req_data(pb1, SSH_FXP_CLOSE, reqid, 273 psn->fhand_r, psn->fhand_r_len); 274 JUSTSEND(pb1); 275 free(psn->fhand_r); 276 psn->fhand_r = NULL; 277 } 278 if ((psn->stat & PSN_WRITEMAP) == 0 && psn->fhand_w) { 279 pb2 = psbuf_makeout(); 280 psbuf_req_data(pb2, SSH_FXP_CLOSE, reqid, 281 psn->fhand_w, psn->fhand_w_len); 282 JUSTSEND(pb2); 283 free(psn->fhand_w); 284 psn->fhand_w = NULL; 285 } 286 287 out: 288 return; 289 } 290 291 int 292 psshfs_node_close(struct puffs_cc *pcc, void *opc, int flags, 293 const struct puffs_cred *pcr, const struct puffs_cid *pcid) 294 { 295 struct puffs_node *pn = opc; 296 struct psshfs_node *psn = pn->pn_data; 297 298 if (psn->opencount > 0) 299 if (--psn->opencount == 0) 300 closehandles(pcc, psn); 301 302 return 0; 303 } 304 305 int 306 psshfs_node_inactive(struct puffs_cc *pcc, void *opc, 307 const struct puffs_cid *pcid) 308 { 309 struct puffs_node *pn = opc; 310 struct psshfs_node *psn = pn->pn_data; 311 312 assert(psn->opencount == 0); 313 psn->stat &= ~(PSN_READMAP | PSN_WRITEMAP); 314 closehandles(pcc, psn); 315 316 return 0; 317 } 318 319 int 320 psshfs_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent, 321 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr, 322 int *eofflag, off_t *cookies, size_t *ncookies) 323 { 324 struct psshfs_ctx *pctx = puffs_cc_getspecific(pcc); 325 struct puffs_node *pn = opc; 326 struct psshfs_node *psn = pn->pn_data; 327 struct psshfs_dir *pd; 328 int i, rv, set_readdir; 329 330 if (psn->stat & PSN_READDIR) { 331 struct psshfs_wait pw; 332 333 set_readdir = 0; 334 pw.pw_cc = pcc; 335 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 336 puffs_cc_yield(pcc); 337 TAILQ_REMOVE(&psn->pw, &pw, pw_entries); 338 } else { 339 psn->stat |= PSN_READDIR; 340 set_readdir = 1; 341 } 342 343 *ncookies = 0; 344 rv = sftp_readdir(pcc, pctx, pn); 345 if (rv) 346 goto out; 347 348 /* find next dirent */ 349 for (i = *readoff;;i++) { 350 if (i >= psn->dentnext) 351 goto out; 352 pd = &psn->dir[i]; 353 if (pd->valid) 354 break; 355 } 356 357 for (;;) { 358 *readoff = i; 359 if (!puffs_nextdent(&dent, pd->entryname, 360 pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { 361 rv = 0; 362 goto out; 363 } 364 365 /* find next entry, store possible nfs key */ 366 do { 367 if (++i >= psn->dentnext) 368 goto out; 369 pd = &psn->dir[i]; 370 } while (pd->valid == 0); 371 PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); 372 } 373 374 out: 375 if (rv == 0) { 376 if (i >= psn->dentnext) 377 *eofflag = 1; 378 379 *readoff = i; 380 } 381 382 if (set_readdir) { 383 struct psshfs_wait *pw; 384 385 /* all will likely run to completion because of cache */ 386 TAILQ_FOREACH(pw, &psn->pw, pw_entries) 387 puffs_cc_schedule(pw->pw_cc); 388 389 psn->stat &= ~PSN_READDIR; 390 } 391 392 return rv; 393 } 394 395 int 396 psshfs_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf, 397 off_t offset, size_t *resid, const struct puffs_cred *pcr, 398 int ioflag) 399 { 400 PSSHFSAUTOVAR(pcc); 401 struct puffs_node *pn = opc; 402 struct psshfs_node *psn = pn->pn_data; 403 uint32_t readlen; 404 405 if (pn->pn_va.va_type == VDIR) { 406 rv = EISDIR; 407 goto farout; 408 } 409 410 readlen = *resid; 411 psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); 412 psbuf_put_8(pb, offset); 413 psbuf_put_4(pb, readlen); 414 415 /* 416 * Do this *after* accessing the file, the handle might not 417 * exist after blocking. 418 */ 419 if (max_reads && ++psn->readcount > max_reads) { 420 struct psshfs_wait pw; 421 422 pw.pw_cc = pcc; 423 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 424 puffs_cc_yield(pcc); 425 } 426 427 GETRESPONSE(pb); 428 429 rv = psbuf_do_data(pb, buf, &readlen); 430 if (rv == 0) 431 *resid -= readlen; 432 433 out: 434 if (max_reads && --psn->readcount >= max_reads) { 435 struct psshfs_wait *pw; 436 437 pw = TAILQ_FIRST(&psn->pw); 438 assert(pw != NULL); 439 TAILQ_REMOVE(&psn->pw, pw, pw_entries); 440 puffs_cc_schedule(pw->pw_cc); 441 } 442 443 farout: 444 PSSHFSRETURN(rv); 445 } 446 447 /* XXX: we should getattr for size */ 448 int 449 psshfs_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf, 450 off_t offset, size_t *resid, const struct puffs_cred *cred, 451 int ioflag) 452 { 453 PSSHFSAUTOVAR(pcc); 454 struct puffs_node *pn = opc; 455 struct psshfs_node *psn = pn->pn_data; 456 uint32_t writelen; 457 458 if (pn->pn_va.va_type == VDIR) { 459 rv = EISDIR; 460 goto out; 461 } 462 463 writelen = *resid; 464 psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); 465 psbuf_put_8(pb, offset); 466 psbuf_put_data(pb, buf, writelen); 467 GETRESPONSE(pb); 468 469 rv = psbuf_expect_status(pb); 470 if (rv == 0) 471 *resid = 0; 472 473 if (pn->pn_va.va_size < offset + writelen) 474 pn->pn_va.va_size = offset + writelen; 475 476 out: 477 PSSHFSRETURN(rv); 478 } 479 480 int 481 psshfs_node_readlink(struct puffs_cc *pcc, void *opc, 482 const struct puffs_cred *cred, char *linkvalue, size_t *linklen) 483 { 484 PSSHFSAUTOVAR(pcc); 485 struct puffs_node *pn = opc; 486 char *linktmp = NULL; 487 uint32_t count; 488 489 if (pctx->protover < 3) { 490 rv = EOPNOTSUPP; 491 goto out; 492 } 493 494 psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); 495 GETRESPONSE(pb); 496 497 rv = psbuf_expect_name(pb, &count); 498 if (rv) 499 goto out; 500 if (count != 1) { 501 rv = EPROTO; 502 goto out; 503 } 504 505 rv = psbuf_get_str(pb, &linktmp, (uint32_t *)linklen); 506 if (rv) 507 goto out; 508 (void) memcpy(linkvalue, linktmp, *linklen); 509 510 out: 511 free(linktmp); 512 PSSHFSRETURN(rv); 513 } 514 515 static int 516 doremove(struct puffs_cc *pcc, struct puffs_node *pn_dir, 517 struct puffs_node *pn, const char *name) 518 { 519 PSSHFSAUTOVAR(pcc); 520 int op; 521 522 if (pn->pn_va.va_type == VDIR) 523 op = SSH_FXP_RMDIR; 524 else 525 op = SSH_FXP_REMOVE; 526 527 psbuf_req_str(pb, op, reqid, PNPATH(pn)); 528 GETRESPONSE(pb); 529 530 rv = psbuf_expect_status(pb); 531 if (rv == 0) 532 nukenode(pn, name, 0); 533 534 out: 535 PSSHFSRETURN(rv); 536 } 537 538 int 539 psshfs_node_remove(struct puffs_cc *pcc, void *opc, void *targ, 540 const struct puffs_cn *pcn) 541 { 542 struct puffs_node *pn_targ = targ; 543 int rv; 544 545 assert(pn_targ->pn_va.va_type != VDIR); 546 547 rv = doremove(pcc, opc, targ, pcn->pcn_name); 548 if (rv == 0) 549 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2); 550 551 return rv; 552 } 553 554 int 555 psshfs_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ, 556 const struct puffs_cn *pcn) 557 { 558 struct puffs_node *pn_targ = targ; 559 int rv; 560 561 assert(pn_targ->pn_va.va_type == VDIR); 562 563 rv = doremove(pcc, opc, targ, pcn->pcn_name); 564 if (rv == 0) 565 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2); 566 567 return rv; 568 } 569 570 int 571 psshfs_node_mkdir(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni, 572 const struct puffs_cn *pcn, const struct vattr *va) 573 { 574 PSSHFSAUTOVAR(pcc); 575 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 576 struct puffs_node *pn = opc; 577 struct puffs_node *pn_new; 578 579 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 580 if (!pn_new) { 581 rv = ENOMEM; 582 goto out; 583 } 584 585 psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); 586 psbuf_put_vattr(pb, va); 587 GETRESPONSE(pb); 588 589 rv = psbuf_expect_status(pb); 590 591 if (rv == 0) 592 puffs_newinfo_setcookie(pni, pn_new); 593 else 594 nukenode(pn_new, pcn->pcn_name, 1); 595 596 out: 597 PSSHFSRETURN(rv); 598 } 599 600 int 601 psshfs_node_symlink(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni, 602 const struct puffs_cn *pcn, const struct vattr *va, 603 const char *link_target) 604 { 605 PSSHFSAUTOVAR(pcc); 606 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 607 struct puffs_node *pn = opc; 608 struct puffs_node *pn_new; 609 610 if (pctx->protover < 3) { 611 rv = EOPNOTSUPP; 612 goto out; 613 } 614 615 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 616 if (!pn_new) { 617 rv = ENOMEM; 618 goto out; 619 } 620 621 /* 622 * XXX: ietf says: source, target. openssh says: ietf who? 623 * Let's go with openssh and build quirk tables later if we care 624 */ 625 psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); 626 psbuf_put_str(pb, PCNPATH(pcn)); 627 GETRESPONSE(pb); 628 629 rv = psbuf_expect_status(pb); 630 if (rv == 0) 631 puffs_newinfo_setcookie(pni, pn_new); 632 else 633 nukenode(pn_new, pcn->pcn_name, 1); 634 635 out: 636 PSSHFSRETURN(rv); 637 } 638 639 int 640 psshfs_node_rename(struct puffs_cc *pcc, void *opc, void *src, 641 const struct puffs_cn *pcn_src, void *targ_dir, void *targ, 642 const struct puffs_cn *pcn_targ) 643 { 644 PSSHFSAUTOVAR(pcc); 645 struct puffs_node *pn_sf = src; 646 struct puffs_node *pn_td = targ_dir, *pn_tf = targ; 647 struct psshfs_node *psn_targdir = pn_td->pn_data; 648 649 if (pctx->protover < 2) { 650 rv = EOPNOTSUPP; 651 goto out; 652 } 653 654 if (pn_tf) { 655 rv = doremove(pcc, targ_dir, pn_tf, pcn_targ->pcn_name); 656 if (rv) 657 goto out; 658 } 659 660 psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); 661 psbuf_put_str(pb, PCNPATH(pcn_targ)); 662 GETRESPONSE(pb); 663 664 rv = psbuf_expect_status(pb); 665 if (rv == 0) { 666 struct psshfs_dir *pd; 667 668 /* 669 * XXX: interfaces didn't quite work with rename.. 670 * the song remains the same. go figure .. ;) 671 */ 672 nukenode(pn_sf, pcn_src->pcn_name, 0); 673 pd = direnter(pn_td, pcn_targ->pcn_name); 674 pd->entry = pn_sf; 675 puffs_setvattr(&pd->va, &pn_sf->pn_va); 676 677 if (opc != targ_dir) { 678 psn_targdir->childcount++; 679 if (pn_sf->pn_va.va_type == VDIR) 680 pn_td->pn_va.va_nlink++; 681 } 682 } 683 684 out: 685 PSSHFSRETURN(rv); 686 } 687 688 /* 689 * So this file system happened to be written in such a way that 690 * lookup for ".." is hard if we lose the in-memory node. We'd 691 * need to recreate the entire directory structure from the root 692 * node up to the ".." node we're looking up. 693 * 694 * And since our entire fs structure is purely fictional (i.e. it's 695 * only in-memory, not fetchable from the server), the easiest way 696 * to deal with it is to not allow nodes with children to be 697 * reclaimed. 698 * 699 * If a node with children is being attempted to be reclaimed, we 700 * just mark it "reclaimed" but leave it as is until all its children 701 * have been reclaimed. If a lookup for that node is done meanwhile, 702 * it will be found by lookup() and we just remove the "reclaimed" 703 * bit. 704 */ 705 int 706 psshfs_node_reclaim(struct puffs_cc *pcc, void *opc, 707 const struct puffs_cid *pcid) 708 { 709 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 710 struct puffs_node *pn = opc, *pn_next, *pn_root; 711 struct psshfs_node *psn = pn->pn_data; 712 713 /* 714 * don't reclaim if we have file handle issued, otherwise 715 * we can't do fhtonode 716 */ 717 if (psn->stat & PSN_HASFH) 718 return 0; 719 720 psn->stat |= PSN_RECLAIMED; 721 pn_root = puffs_getroot(pu); 722 for (; pn != pn_root; pn = pn_next) { 723 psn = pn->pn_data; 724 if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) 725 break; 726 727 pn_next = psn->parent; 728 doreclaim(pn); 729 } 730 731 return 0; 732 } 733