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