1 /* $NetBSD: node.c,v 1.53 2007/12/09 18:05:42 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.53 2007/12/09 18:05:42 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_usermount *pu, void *opc, 43 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 44 { 45 struct psshfs_ctx *pctx = puffs_getspecific(pu); 46 struct puffs_node *pn_dir = opc; 47 struct psshfs_node *psn, *psn_dir = pn_dir->pn_data; 48 struct puffs_node *pn; 49 struct psshfs_dir *pd; 50 struct vattr va; 51 int rv; 52 53 if (PCNISDOTDOT(pcn)) { 54 psn = psn_dir->parent->pn_data; 55 psn->stat &= ~PSN_RECLAIMED; 56 57 puffs_newinfo_setcookie(pni, psn_dir->parent); 58 puffs_newinfo_setvtype(pni, VDIR); 59 return 0; 60 } 61 62 rv = sftp_readdir(pu, pctx, pn_dir); 63 if (rv) { 64 if (rv != EPERM) 65 return rv; 66 67 /* 68 * Can't read the directory. We still might be 69 * able to find the node with getattr in -r+x dirs 70 */ 71 rv = getpathattr(pu, PCNPATH(pcn), &va); 72 if (rv) 73 return rv; 74 75 /* guess */ 76 if (va.va_type == VDIR) 77 va.va_nlink = 2; 78 else 79 va.va_nlink = 1; 80 81 pn = allocnode(pu, pn_dir, pcn->pcn_name, &va); 82 psn = pn->pn_data; 83 psn->attrread = time(NULL); 84 } else { 85 pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name); 86 if (!pd) { 87 return ENOENT; 88 } 89 90 if (pd->entry) 91 pn = pd->entry; 92 else 93 pn = makenode(pu, pn_dir, pd, &pd->va); 94 psn = pn->pn_data; 95 } 96 97 psn->stat &= ~PSN_RECLAIMED; 98 99 puffs_newinfo_setcookie(pni, pn); 100 puffs_newinfo_setvtype(pni, pn->pn_va.va_type); 101 puffs_newinfo_setsize(pni, pn->pn_va.va_size); 102 103 return 0; 104 } 105 106 int 107 psshfs_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *vap, 108 const struct puffs_cred *pcr) 109 { 110 struct puffs_node *pn = opc; 111 int rv; 112 113 rv = getnodeattr(pu, pn); 114 if (rv) 115 return rv; 116 117 memcpy(vap, &pn->pn_va, sizeof(struct vattr)); 118 119 return 0; 120 } 121 122 int 123 psshfs_node_setattr(struct puffs_usermount *pu, void *opc, 124 const struct vattr *va, const struct puffs_cred *pcr) 125 { 126 PSSHFSAUTOVAR(pu); 127 struct vattr kludgeva; 128 struct puffs_node *pn = opc; 129 130 psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn)); 131 132 memcpy(&kludgeva, va, sizeof(struct vattr)); 133 134 /* XXX: kludge due to openssh server implementation */ 135 if (va->va_atime.tv_sec != PUFFS_VNOVAL 136 && va->va_mtime.tv_sec == PUFFS_VNOVAL) { 137 if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL) 138 kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; 139 else 140 kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec; 141 } 142 if (va->va_mtime.tv_sec != PUFFS_VNOVAL 143 && va->va_atime.tv_sec == PUFFS_VNOVAL) { 144 if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL) 145 kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; 146 else 147 kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec; 148 } 149 150 psbuf_put_vattr(pb, &kludgeva); 151 GETRESPONSE(pb); 152 153 rv = psbuf_expect_status(pb); 154 if (rv == 0) 155 puffs_setvattr(&pn->pn_va, &kludgeva); 156 157 out: 158 PSSHFSRETURN(rv); 159 } 160 161 int 162 psshfs_node_create(struct puffs_usermount *pu, void *opc, 163 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 164 const struct vattr *va) 165 { 166 PSSHFSAUTOVAR(pu); 167 struct puffs_node *pn = opc; 168 struct puffs_node *pn_new; 169 char *fhand = NULL; 170 uint32_t fhandlen; 171 172 /* Create node on server first */ 173 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn)); 174 psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); 175 psbuf_put_vattr(pb, va); 176 GETRESPONSE(pb); 177 rv = psbuf_expect_handle(pb, &fhand, &fhandlen); 178 if (rv) 179 goto out; 180 181 /* 182 * Do *not* create the local node before getting a response 183 * from the server. Otherwise we might screw up consistency, 184 * namely that the node can be looked up before create has 185 * returned (mind you, the kernel will unlock the directory 186 * before the create call from userspace returns). 187 */ 188 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 189 if (!pn_new) { 190 struct puffs_framebuf *pb2 = psbuf_makeout(); 191 reqid = NEXTREQ(pctx); 192 psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); 193 JUSTSEND(pb2); 194 rv = ENOMEM; 195 } 196 197 if (pn_new) 198 puffs_newinfo_setcookie(pni, pn_new); 199 200 reqid = NEXTREQ(pctx); 201 psbuf_recycleout(pb); 202 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen); 203 JUSTSEND(pb); 204 free(fhand); 205 return rv; 206 207 out: 208 free(fhand); 209 PSSHFSRETURN(rv); 210 } 211 212 /* 213 * Open a file handle. This is used for read and write. We do not 214 * wait here for the success or failure of this operation. This is 215 * because otherwise opening and closing file handles would block 216 * reading potentially cached information. Rather, we defer the wait 217 * to read/write and therefore allow cached access without a wait. 218 * 219 * If we have not yet succesfully opened a type of handle, we do wait 220 * here. Also, if a lazy open fails, we revert back to the same 221 * state of waiting. 222 */ 223 int 224 psshfs_node_open(struct puffs_usermount *pu, void *opc, int mode, 225 const struct puffs_cred *pcr) 226 { 227 struct puffs_cc *pcc = puffs_cc_getcc(pu); 228 struct psshfs_ctx *pctx = puffs_getspecific(pu); 229 struct puffs_framebuf *pb, *pb2; 230 struct vattr va; 231 struct puffs_node *pn = opc; 232 struct psshfs_node *psn = pn->pn_data; 233 uint32_t reqid; 234 int didread, didwrite; 235 int rv = 0; 236 237 if (pn->pn_va.va_type == VDIR) 238 return 0; 239 240 puffs_setback(pcc, PUFFS_SETBACK_INACT_N1); 241 puffs_vattr_null(&va); 242 didread = didwrite = 0; 243 if (mode & FREAD && psn->fhand_r == NULL && psn->lazyopen_r == NULL) { 244 pb = psbuf_makeout(); 245 246 reqid = NEXTREQ(pctx); 247 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); 248 psbuf_put_4(pb, SSH_FXF_READ); 249 psbuf_put_vattr(pb, &va); 250 251 if (puffs_framev_enqueue_cb(pu, pctx->sshfd, pb, 252 lazyopen_rresp, psn, 0) == -1) { 253 puffs_framebuf_destroy(pb); 254 rv = errno; 255 goto out; 256 } 257 258 psn->lazyopen_r = pb; 259 didread = 1; 260 } 261 if (mode & FWRITE && psn->fhand_w == NULL && psn->lazyopen_w == NULL) { 262 pb2 = psbuf_makeout(); 263 264 reqid = NEXTREQ(pctx); 265 psbuf_req_str(pb2, SSH_FXP_OPEN, reqid, PNPATH(pn)); 266 psbuf_put_4(pb2, SSH_FXF_WRITE); 267 psbuf_put_vattr(pb2, &va); 268 269 if (puffs_framev_enqueue_cb(pu, pctx->sshfd, pb2, 270 lazyopen_wresp, psn, 0) == -1) { 271 puffs_framebuf_destroy(pb2); 272 rv = errno; 273 goto out; 274 } 275 276 psn->lazyopen_w = pb2; 277 didwrite = 1; 278 } 279 psn->stat &= ~PSN_HANDLECLOSE; 280 281 out: 282 /* wait? */ 283 if (didread && (psn->stat & PSN_DOLAZY_R) == 0) { 284 assert(psn->lazyopen_r); 285 286 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); 287 lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); 288 if (psn->fhand_r) { 289 psn->stat |= PSN_DOLAZY_R; 290 } else { 291 if (psn->lazyopen_err_r) 292 return psn->lazyopen_err_r; 293 return EINVAL; 294 } 295 } 296 297 /* wait? */ 298 if (didwrite && (psn->stat & PSN_DOLAZY_W) == 0) { 299 assert(psn->lazyopen_w); 300 301 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); 302 lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); 303 if (psn->fhand_w) { 304 psn->stat |= PSN_DOLAZY_W; 305 } else { 306 if (psn->lazyopen_err_w) 307 return psn->lazyopen_err_w; 308 return EINVAL; 309 } 310 } 311 312 return rv; 313 } 314 315 int 316 psshfs_node_inactive(struct puffs_usermount *pu, void *opc) 317 { 318 struct puffs_node *pn = opc; 319 320 closehandles(pu, pn->pn_data, HANDLE_READ | HANDLE_WRITE); 321 return 0; 322 } 323 324 int 325 psshfs_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent, 326 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr, 327 int *eofflag, off_t *cookies, size_t *ncookies) 328 { 329 struct puffs_cc *pcc = puffs_cc_getcc(pu); 330 struct psshfs_ctx *pctx = puffs_getspecific(pu); 331 struct puffs_node *pn = opc; 332 struct psshfs_node *psn = pn->pn_data; 333 struct psshfs_dir *pd; 334 int i, rv, set_readdir; 335 336 restart: 337 if (psn->stat & PSN_READDIR) { 338 struct psshfs_wait pw; 339 340 set_readdir = 0; 341 pw.pw_cc = pcc; 342 pw.pw_type = PWTYPE_READDIR; 343 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 344 puffs_cc_yield(pcc); 345 goto restart; 346 } else { 347 psn->stat |= PSN_READDIR; 348 set_readdir = 1; 349 } 350 351 *ncookies = 0; 352 rv = sftp_readdir(pu, pctx, pn); 353 if (rv) 354 goto out; 355 356 /* find next dirent */ 357 for (i = *readoff;;i++) { 358 if (i >= psn->dentnext) 359 goto out; 360 pd = &psn->dir[i]; 361 if (pd->valid) 362 break; 363 } 364 365 for (;;) { 366 *readoff = i; 367 if (!puffs_nextdent(&dent, pd->entryname, 368 pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { 369 rv = 0; 370 goto out; 371 } 372 373 /* find next entry, store possible nfs key */ 374 do { 375 if (++i >= psn->dentnext) 376 goto out; 377 pd = &psn->dir[i]; 378 } while (pd->valid == 0); 379 PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); 380 } 381 382 out: 383 if (rv == 0) { 384 if (i >= psn->dentnext) 385 *eofflag = 1; 386 387 *readoff = i; 388 } 389 390 if (set_readdir) { 391 struct psshfs_wait *pw; 392 393 /* all will likely run to completion because of cache */ 394 TAILQ_FOREACH(pw, &psn->pw, pw_entries) { 395 assert(pw->pw_type == PWTYPE_READDIR); 396 puffs_cc_schedule(pw->pw_cc); 397 TAILQ_REMOVE(&psn->pw, pw, pw_entries); 398 } 399 400 psn->stat &= ~PSN_READDIR; 401 } 402 403 return rv; 404 } 405 406 int 407 psshfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, 408 off_t offset, size_t *resid, const struct puffs_cred *pcr, 409 int ioflag) 410 { 411 PSSHFSAUTOVAR(pu); 412 struct puffs_node *pn = opc; 413 struct psshfs_node *psn = pn->pn_data; 414 struct psshfs_wait *pwp; 415 uint32_t readlen; 416 417 if (pn->pn_va.va_type == VDIR) { 418 rv = EISDIR; 419 goto farout; 420 } 421 422 /* check that a lazyopen didn't fail */ 423 if (!psn->fhand_r && !psn->lazyopen_r) { 424 rv = psn->lazyopen_err_r; 425 goto farout; 426 } 427 428 /* if someone is already waiting for the lazyopen, "just" wait */ 429 if (psn->stat & PSN_LAZYWAIT_R) { 430 struct psshfs_wait pw; 431 432 assert(psn->lazyopen_r); 433 434 pw.pw_cc = pcc; 435 pw.pw_type = PWTYPE_READ1; 436 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 437 puffs_cc_yield(pcc); 438 } 439 440 /* if lazyopening, wait for the result */ 441 if (psn->lazyopen_r) { 442 psn->stat |= PSN_LAZYWAIT_R; 443 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); 444 lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); 445 446 /* schedule extra waiters */ 447 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 448 if (pwp->pw_type == PWTYPE_READ1) { 449 puffs_cc_schedule(pwp->pw_cc); 450 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 451 } 452 psn->stat &= ~PSN_LAZYWAIT_R; 453 454 if ((rv = psn->lazyopen_err_r) != 0) 455 goto farout; 456 } 457 458 /* if there is still no handle, just refuse to live with this */ 459 if (!psn->fhand_r) { 460 rv = EINVAL; 461 goto farout; 462 } 463 464 readlen = *resid; 465 psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); 466 psbuf_put_8(pb, offset); 467 psbuf_put_4(pb, readlen); 468 469 /* 470 * Do this *after* accessing the file, the handle might not 471 * exist after blocking. 472 */ 473 if (max_reads && ++psn->readcount > max_reads) { 474 struct psshfs_wait pw; 475 476 pw.pw_cc = pcc; 477 pw.pw_type = PWTYPE_READ2; 478 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 479 puffs_cc_yield(pcc); 480 } 481 482 GETRESPONSE(pb); 483 484 rv = psbuf_do_data(pb, buf, &readlen); 485 if (rv == 0) 486 *resid -= readlen; 487 488 out: 489 if (max_reads && --psn->readcount >= max_reads) { 490 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 491 if (pwp->pw_type == PWTYPE_READ2) 492 break; 493 assert(pwp != NULL); 494 puffs_cc_schedule(pwp->pw_cc); 495 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 496 } 497 498 farout: 499 /* check if we need a lazyclose */ 500 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { 501 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 502 if (pwp->pw_type == PWTYPE_READ1) 503 break; 504 if (pwp == NULL) 505 closehandles(pu, psn, HANDLE_READ); 506 } 507 PSSHFSRETURN(rv); 508 } 509 510 /* XXX: we should getattr for size */ 511 int 512 psshfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, 513 off_t offset, size_t *resid, const struct puffs_cred *cred, 514 int ioflag) 515 { 516 PSSHFSAUTOVAR(pu); 517 struct psshfs_wait *pwp; 518 struct puffs_node *pn = opc; 519 struct psshfs_node *psn = pn->pn_data; 520 uint32_t writelen; 521 522 if (pn->pn_va.va_type == VDIR) { 523 rv = EISDIR; 524 goto out; 525 } 526 527 /* check that a lazyopen didn't fail */ 528 if (!psn->fhand_w && !psn->lazyopen_w) { 529 rv = psn->lazyopen_err_w; 530 goto out; 531 } 532 533 if (psn->stat & PSN_LAZYWAIT_W) { 534 struct psshfs_wait pw; 535 536 assert(psn->lazyopen_w); 537 538 pw.pw_cc = pcc; 539 pw.pw_type = PWTYPE_WRITE; 540 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 541 puffs_cc_yield(pcc); 542 } 543 544 /* 545 * If lazyopening, wait for the result. 546 * There can still be more than oen writer at a time in case 547 * the kernel issues write FAFs. 548 */ 549 if (psn->lazyopen_w) { 550 psn->stat |= PSN_LAZYWAIT_W; 551 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); 552 lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); 553 554 /* schedule extra waiters */ 555 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 556 if (pwp->pw_type == PWTYPE_WRITE) { 557 puffs_cc_schedule(pwp->pw_cc); 558 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 559 } 560 psn->stat &= ~PSN_LAZYWAIT_W; 561 562 if ((rv = psn->lazyopen_err_w) != 0) 563 goto out; 564 } 565 566 if (!psn->fhand_w) { 567 abort(); 568 rv = EINVAL; 569 goto out; 570 } 571 572 writelen = *resid; 573 psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); 574 psbuf_put_8(pb, offset); 575 psbuf_put_data(pb, buf, writelen); 576 GETRESPONSE(pb); 577 578 rv = psbuf_expect_status(pb); 579 if (rv == 0) 580 *resid = 0; 581 582 if (pn->pn_va.va_size < offset + writelen) 583 pn->pn_va.va_size = offset + writelen; 584 585 out: 586 /* check if we need a lazyclose */ 587 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { 588 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 589 if (pwp->pw_type == PWTYPE_WRITE) 590 break; 591 if (pwp == NULL) 592 closehandles(pu, psn, HANDLE_WRITE); 593 } 594 PSSHFSRETURN(rv); 595 } 596 597 int 598 psshfs_node_readlink(struct puffs_usermount *pu, void *opc, 599 const struct puffs_cred *cred, char *linkvalue, size_t *linklen) 600 { 601 PSSHFSAUTOVAR(pu); 602 struct puffs_node *pn = opc; 603 struct psshfs_node *psn = pn->pn_data; 604 uint32_t count; 605 606 if (pctx->protover < 3) { 607 rv = EOPNOTSUPP; 608 goto out; 609 } 610 611 /* 612 * check if we can use a cached version 613 * 614 * XXX: we might end up reading the same link multiple times 615 * from the server if we get many requests at once, but that's 616 * quite harmless as this routine is reentrant. 617 */ 618 if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) 619 goto copy; 620 621 if (psn->symlink) { 622 free(psn->symlink); 623 psn->symlink = NULL; 624 psn->slread = 0; 625 } 626 627 psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); 628 GETRESPONSE(pb); 629 630 rv = psbuf_expect_name(pb, &count); 631 if (rv) 632 goto out; 633 if (count != 1) { 634 rv = EPROTO; 635 goto out; 636 } 637 638 rv = psbuf_get_str(pb, &psn->symlink, NULL); 639 if (rv) 640 goto out; 641 psn->slread = time(NULL); 642 643 copy: 644 *linklen = strlen(psn->symlink); 645 (void) memcpy(linkvalue, psn->symlink, *linklen); 646 647 out: 648 PSSHFSRETURN(rv); 649 } 650 651 static int 652 doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, 653 struct puffs_node *pn, const char *name) 654 { 655 PSSHFSAUTOVAR(pu); 656 int op; 657 658 if (pn->pn_va.va_type == VDIR) 659 op = SSH_FXP_RMDIR; 660 else 661 op = SSH_FXP_REMOVE; 662 663 psbuf_req_str(pb, op, reqid, PNPATH(pn)); 664 GETRESPONSE(pb); 665 666 rv = psbuf_expect_status(pb); 667 if (rv == 0) 668 nukenode(pn, name, 0); 669 670 out: 671 PSSHFSRETURN(rv); 672 } 673 674 int 675 psshfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ, 676 const struct puffs_cn *pcn) 677 { 678 struct puffs_node *pn_targ = targ; 679 int rv; 680 681 assert(pn_targ->pn_va.va_type != VDIR); 682 683 rv = doremove(pu, opc, targ, pcn->pcn_name); 684 if (rv == 0) 685 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 686 687 return rv; 688 } 689 690 int 691 psshfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ, 692 const struct puffs_cn *pcn) 693 { 694 struct puffs_node *pn_targ = targ; 695 int rv; 696 697 assert(pn_targ->pn_va.va_type == VDIR); 698 699 rv = doremove(pu, opc, targ, pcn->pcn_name); 700 if (rv == 0) 701 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 702 703 return rv; 704 } 705 706 int 707 psshfs_node_mkdir(struct puffs_usermount *pu, void *opc, 708 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 709 const struct vattr *va) 710 { 711 PSSHFSAUTOVAR(pu); 712 struct puffs_node *pn = opc; 713 struct puffs_node *pn_new; 714 715 psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); 716 psbuf_put_vattr(pb, va); 717 GETRESPONSE(pb); 718 719 rv = psbuf_expect_status(pb); 720 if (rv) 721 goto out; 722 723 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 724 if (pn_new) { 725 puffs_newinfo_setcookie(pni, pn_new); 726 } else { 727 struct puffs_framebuf *pb2 = psbuf_makeout(); 728 reqid = NEXTREQ(pctx); 729 psbuf_recycleout(pb2); 730 psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); 731 JUSTSEND(pb2); 732 rv = ENOMEM; 733 } 734 735 out: 736 PSSHFSRETURN(rv); 737 } 738 739 int 740 psshfs_node_symlink(struct puffs_usermount *pu, void *opc, 741 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 742 const struct vattr *va, const char *link_target) 743 { 744 PSSHFSAUTOVAR(pu); 745 struct puffs_node *pn = opc; 746 struct puffs_node *pn_new; 747 748 if (pctx->protover < 3) { 749 rv = EOPNOTSUPP; 750 goto out; 751 } 752 753 /* 754 * XXX: ietf says: source, target. openssh says: ietf who? 755 * Let's go with openssh and build quirk tables later if we care 756 */ 757 psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); 758 psbuf_put_str(pb, PCNPATH(pcn)); 759 GETRESPONSE(pb); 760 761 rv = psbuf_expect_status(pb); 762 if (rv) 763 goto out; 764 765 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 766 if (pn_new) { 767 puffs_newinfo_setcookie(pni, pn_new); 768 } else { 769 struct puffs_framebuf *pb2 = psbuf_makeout(); 770 reqid = NEXTREQ(pctx); 771 psbuf_recycleout(pb2); 772 psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); 773 JUSTSEND(pb2); 774 rv = ENOMEM; 775 } 776 777 out: 778 PSSHFSRETURN(rv); 779 } 780 781 int 782 psshfs_node_rename(struct puffs_usermount *pu, void *opc, void *src, 783 const struct puffs_cn *pcn_src, void *targ_dir, void *targ, 784 const struct puffs_cn *pcn_targ) 785 { 786 PSSHFSAUTOVAR(pu); 787 struct puffs_node *pn_sf = src; 788 struct puffs_node *pn_td = targ_dir, *pn_tf = targ; 789 struct psshfs_node *psn_targdir = pn_td->pn_data; 790 791 if (pctx->protover < 2) { 792 rv = EOPNOTSUPP; 793 goto out; 794 } 795 796 if (pn_tf) { 797 rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); 798 if (rv) 799 goto out; 800 } 801 802 psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); 803 psbuf_put_str(pb, PCNPATH(pcn_targ)); 804 GETRESPONSE(pb); 805 806 rv = psbuf_expect_status(pb); 807 if (rv == 0) { 808 struct psshfs_dir *pd; 809 810 /* 811 * XXX: interfaces didn't quite work with rename.. 812 * the song remains the same. go figure .. ;) 813 */ 814 nukenode(pn_sf, pcn_src->pcn_name, 0); 815 pd = direnter(pn_td, pcn_targ->pcn_name); 816 pd->entry = pn_sf; 817 puffs_setvattr(&pd->va, &pn_sf->pn_va); 818 819 if (opc != targ_dir) { 820 psn_targdir->childcount++; 821 if (pn_sf->pn_va.va_type == VDIR) 822 pn_td->pn_va.va_nlink++; 823 } 824 } 825 826 out: 827 PSSHFSRETURN(rv); 828 } 829 830 /* 831 * So this file system happened to be written in such a way that 832 * lookup for ".." is hard if we lose the in-memory node. We'd 833 * need to recreate the entire directory structure from the root 834 * node up to the ".." node we're looking up. 835 * 836 * And since our entire fs structure is purely fictional (i.e. it's 837 * only in-memory, not fetchable from the server), the easiest way 838 * to deal with it is to not allow nodes with children to be 839 * reclaimed. 840 * 841 * If a node with children is being attempted to be reclaimed, we 842 * just mark it "reclaimed" but leave it as is until all its children 843 * have been reclaimed. If a lookup for that node is done meanwhile, 844 * it will be found by lookup() and we just remove the "reclaimed" 845 * bit. 846 */ 847 int 848 psshfs_node_reclaim(struct puffs_usermount *pu, void *opc) 849 { 850 struct puffs_node *pn = opc, *pn_next, *pn_root; 851 struct psshfs_node *psn = pn->pn_data; 852 853 /* 854 * don't reclaim if we have file handle issued, otherwise 855 * we can't do fhtonode 856 */ 857 if (psn->stat & PSN_HASFH) 858 return 0; 859 860 psn->stat |= PSN_RECLAIMED; 861 pn_root = puffs_getroot(pu); 862 for (; pn != pn_root; pn = pn_next) { 863 psn = pn->pn_data; 864 if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) 865 break; 866 867 pn_next = psn->parent; 868 doreclaim(pn); 869 } 870 871 return 0; 872 } 873