1 /* $NetBSD: node.c,v 1.55 2009/01/01 13:41:45 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.55 2009/01/01 13:41:45 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, puffs_cookie_t 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, puffs_cookie_t opc, 108 struct vattr *vap, 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, puffs_cookie_t 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, puffs_cookie_t 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, puffs_cookie_t 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, puffs_cookie_t 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, puffs_cookie_t opc, 326 struct dirent *dent, off_t *readoff, size_t *reslen, 327 const struct puffs_cred *pcr, int *eofflag, 328 off_t *cookies, size_t *ncookies) 329 { 330 struct puffs_cc *pcc = puffs_cc_getcc(pu); 331 struct psshfs_ctx *pctx = puffs_getspecific(pu); 332 struct puffs_node *pn = opc; 333 struct psshfs_node *psn = pn->pn_data; 334 struct psshfs_dir *pd; 335 int i, rv, set_readdir; 336 337 restart: 338 if (psn->stat & PSN_READDIR) { 339 struct psshfs_wait pw; 340 341 set_readdir = 0; 342 pw.pw_cc = pcc; 343 pw.pw_type = PWTYPE_READDIR; 344 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 345 puffs_cc_yield(pcc); 346 goto restart; 347 } else { 348 psn->stat |= PSN_READDIR; 349 set_readdir = 1; 350 } 351 352 *ncookies = 0; 353 rv = sftp_readdir(pu, pctx, pn); 354 if (rv) 355 goto out; 356 357 /* find next dirent */ 358 for (i = *readoff;;i++) { 359 if (i >= psn->dentnext) 360 goto out; 361 pd = &psn->dir[i]; 362 if (pd->valid) 363 break; 364 } 365 366 for (;;) { 367 *readoff = i; 368 if (!puffs_nextdent(&dent, pd->entryname, 369 pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { 370 rv = 0; 371 goto out; 372 } 373 374 /* find next entry, store possible nfs key */ 375 do { 376 if (++i >= psn->dentnext) 377 goto out; 378 pd = &psn->dir[i]; 379 } while (pd->valid == 0); 380 PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); 381 } 382 383 out: 384 if (rv == 0) { 385 if (i >= psn->dentnext) 386 *eofflag = 1; 387 388 *readoff = i; 389 } 390 391 if (set_readdir) { 392 struct psshfs_wait *pw; 393 394 /* all will likely run to completion because of cache */ 395 TAILQ_FOREACH(pw, &psn->pw, pw_entries) { 396 assert(pw->pw_type == PWTYPE_READDIR); 397 puffs_cc_schedule(pw->pw_cc); 398 TAILQ_REMOVE(&psn->pw, pw, pw_entries); 399 } 400 401 psn->stat &= ~PSN_READDIR; 402 } 403 404 return rv; 405 } 406 407 int 408 psshfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, 409 off_t offset, size_t *resid, const struct puffs_cred *pcr, 410 int ioflag) 411 { 412 PSSHFSAUTOVAR(pu); 413 struct puffs_node *pn = opc; 414 struct psshfs_node *psn = pn->pn_data; 415 struct psshfs_wait *pwp; 416 uint32_t readlen; 417 418 if (pn->pn_va.va_type == VDIR) { 419 rv = EISDIR; 420 goto farout; 421 } 422 423 /* check that a lazyopen didn't fail */ 424 if (!psn->fhand_r && !psn->lazyopen_r) { 425 rv = psn->lazyopen_err_r; 426 goto farout; 427 } 428 429 /* if someone is already waiting for the lazyopen, "just" wait */ 430 if (psn->stat & PSN_LAZYWAIT_R) { 431 struct psshfs_wait pw; 432 433 assert(psn->lazyopen_r); 434 435 pw.pw_cc = pcc; 436 pw.pw_type = PWTYPE_READ1; 437 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 438 puffs_cc_yield(pcc); 439 } 440 441 /* if lazyopening, wait for the result */ 442 if (psn->lazyopen_r) { 443 psn->stat |= PSN_LAZYWAIT_R; 444 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); 445 lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); 446 447 /* schedule extra waiters */ 448 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 449 if (pwp->pw_type == PWTYPE_READ1) { 450 puffs_cc_schedule(pwp->pw_cc); 451 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 452 } 453 psn->stat &= ~PSN_LAZYWAIT_R; 454 455 if ((rv = psn->lazyopen_err_r) != 0) 456 goto farout; 457 } 458 459 /* if there is still no handle, just refuse to live with this */ 460 if (!psn->fhand_r) { 461 rv = EINVAL; 462 goto farout; 463 } 464 465 readlen = *resid; 466 psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); 467 psbuf_put_8(pb, offset); 468 psbuf_put_4(pb, readlen); 469 470 /* 471 * Do this *after* accessing the file, the handle might not 472 * exist after blocking. 473 */ 474 if (max_reads && ++psn->readcount > max_reads) { 475 struct psshfs_wait pw; 476 477 pw.pw_cc = pcc; 478 pw.pw_type = PWTYPE_READ2; 479 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 480 puffs_cc_yield(pcc); 481 } 482 483 GETRESPONSE(pb); 484 485 rv = psbuf_do_data(pb, buf, &readlen); 486 if (rv == 0) 487 *resid -= readlen; 488 489 out: 490 if (max_reads && --psn->readcount >= max_reads) { 491 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 492 if (pwp->pw_type == PWTYPE_READ2) 493 break; 494 assert(pwp != NULL); 495 puffs_cc_schedule(pwp->pw_cc); 496 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 497 } 498 499 farout: 500 /* check if we need a lazyclose */ 501 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { 502 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 503 if (pwp->pw_type == PWTYPE_READ1) 504 break; 505 if (pwp == NULL) 506 closehandles(pu, psn, HANDLE_READ); 507 } 508 PSSHFSRETURN(rv); 509 } 510 511 /* XXX: we should getattr for size */ 512 int 513 psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, 514 off_t offset, size_t *resid, const struct puffs_cred *cred, 515 int ioflag) 516 { 517 PSSHFSAUTOVAR(pu); 518 struct psshfs_wait *pwp; 519 struct puffs_node *pn = opc; 520 struct psshfs_node *psn = pn->pn_data; 521 uint32_t writelen; 522 523 if (pn->pn_va.va_type == VDIR) { 524 rv = EISDIR; 525 goto out; 526 } 527 528 /* check that a lazyopen didn't fail */ 529 if (!psn->fhand_w && !psn->lazyopen_w) { 530 rv = psn->lazyopen_err_w; 531 goto out; 532 } 533 534 if (psn->stat & PSN_LAZYWAIT_W) { 535 struct psshfs_wait pw; 536 537 assert(psn->lazyopen_w); 538 539 pw.pw_cc = pcc; 540 pw.pw_type = PWTYPE_WRITE; 541 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 542 puffs_cc_yield(pcc); 543 } 544 545 /* 546 * If lazyopening, wait for the result. 547 * There can still be more than oen writer at a time in case 548 * the kernel issues write FAFs. 549 */ 550 if (psn->lazyopen_w) { 551 psn->stat |= PSN_LAZYWAIT_W; 552 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); 553 lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); 554 555 /* schedule extra waiters */ 556 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 557 if (pwp->pw_type == PWTYPE_WRITE) { 558 puffs_cc_schedule(pwp->pw_cc); 559 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 560 } 561 psn->stat &= ~PSN_LAZYWAIT_W; 562 563 if ((rv = psn->lazyopen_err_w) != 0) 564 goto out; 565 } 566 567 if (!psn->fhand_w) { 568 abort(); 569 rv = EINVAL; 570 goto out; 571 } 572 573 writelen = *resid; 574 psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); 575 psbuf_put_8(pb, offset); 576 psbuf_put_data(pb, buf, writelen); 577 GETRESPONSE(pb); 578 579 rv = psbuf_expect_status(pb); 580 if (rv == 0) 581 *resid = 0; 582 583 if (pn->pn_va.va_size < offset + writelen) 584 pn->pn_va.va_size = offset + writelen; 585 586 out: 587 /* check if we need a lazyclose */ 588 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { 589 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 590 if (pwp->pw_type == PWTYPE_WRITE) 591 break; 592 if (pwp == NULL) 593 closehandles(pu, psn, HANDLE_WRITE); 594 } 595 PSSHFSRETURN(rv); 596 } 597 598 int 599 psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, 600 const struct puffs_cred *cred, char *linkvalue, size_t *linklen) 601 { 602 PSSHFSAUTOVAR(pu); 603 struct puffs_node *pn = opc; 604 struct psshfs_node *psn = pn->pn_data; 605 uint32_t count; 606 607 if (pctx->protover < 3) { 608 rv = EOPNOTSUPP; 609 goto out; 610 } 611 612 /* 613 * check if we can use a cached version 614 * 615 * XXX: we might end up reading the same link multiple times 616 * from the server if we get many requests at once, but that's 617 * quite harmless as this routine is reentrant. 618 */ 619 if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) 620 goto copy; 621 622 if (psn->symlink) { 623 free(psn->symlink); 624 psn->symlink = NULL; 625 psn->slread = 0; 626 } 627 628 psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); 629 GETRESPONSE(pb); 630 631 rv = psbuf_expect_name(pb, &count); 632 if (rv) 633 goto out; 634 if (count != 1) { 635 rv = EPROTO; 636 goto out; 637 } 638 639 rv = psbuf_get_str(pb, &psn->symlink, NULL); 640 if (rv) 641 goto out; 642 psn->slread = time(NULL); 643 644 copy: 645 *linklen = strlen(psn->symlink); 646 (void) memcpy(linkvalue, psn->symlink, *linklen); 647 648 out: 649 PSSHFSRETURN(rv); 650 } 651 652 static int 653 doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, 654 struct puffs_node *pn, const char *name) 655 { 656 PSSHFSAUTOVAR(pu); 657 int op; 658 659 if (pn->pn_va.va_type == VDIR) 660 op = SSH_FXP_RMDIR; 661 else 662 op = SSH_FXP_REMOVE; 663 664 psbuf_req_str(pb, op, reqid, PNPATH(pn)); 665 GETRESPONSE(pb); 666 667 rv = psbuf_expect_status(pb); 668 if (rv == 0) 669 nukenode(pn, name, 0); 670 671 out: 672 PSSHFSRETURN(rv); 673 } 674 675 int 676 psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, 677 puffs_cookie_t targ, const struct puffs_cn *pcn) 678 { 679 struct puffs_node *pn_targ = targ; 680 int rv; 681 682 assert(pn_targ->pn_va.va_type != VDIR); 683 684 rv = doremove(pu, opc, targ, pcn->pcn_name); 685 if (rv == 0) 686 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 687 688 return rv; 689 } 690 691 int 692 psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, 693 puffs_cookie_t targ, const struct puffs_cn *pcn) 694 { 695 struct puffs_node *pn_targ = targ; 696 int rv; 697 698 assert(pn_targ->pn_va.va_type == VDIR); 699 700 rv = doremove(pu, opc, targ, pcn->pcn_name); 701 if (rv == 0) 702 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 703 704 return rv; 705 } 706 707 int 708 psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, 709 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 710 const struct vattr *va) 711 { 712 PSSHFSAUTOVAR(pu); 713 struct puffs_node *pn = opc; 714 struct puffs_node *pn_new; 715 716 psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); 717 psbuf_put_vattr(pb, va); 718 GETRESPONSE(pb); 719 720 rv = psbuf_expect_status(pb); 721 if (rv) 722 goto out; 723 724 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 725 if (pn_new) { 726 puffs_newinfo_setcookie(pni, pn_new); 727 } else { 728 struct puffs_framebuf *pb2 = psbuf_makeout(); 729 reqid = NEXTREQ(pctx); 730 psbuf_recycleout(pb2); 731 psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); 732 JUSTSEND(pb2); 733 rv = ENOMEM; 734 } 735 736 out: 737 PSSHFSRETURN(rv); 738 } 739 740 int 741 psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, 742 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 743 const struct vattr *va, const char *link_target) 744 { 745 PSSHFSAUTOVAR(pu); 746 struct puffs_node *pn = opc; 747 struct puffs_node *pn_new; 748 749 if (pctx->protover < 3) { 750 rv = EOPNOTSUPP; 751 goto out; 752 } 753 754 /* 755 * XXX: ietf says: source, target. openssh says: ietf who? 756 * Let's go with openssh and build quirk tables later if we care 757 */ 758 psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); 759 psbuf_put_str(pb, PCNPATH(pcn)); 760 GETRESPONSE(pb); 761 762 rv = psbuf_expect_status(pb); 763 if (rv) 764 goto out; 765 766 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 767 if (pn_new) { 768 puffs_newinfo_setcookie(pni, pn_new); 769 } else { 770 struct puffs_framebuf *pb2 = psbuf_makeout(); 771 reqid = NEXTREQ(pctx); 772 psbuf_recycleout(pb2); 773 psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); 774 JUSTSEND(pb2); 775 rv = ENOMEM; 776 } 777 778 out: 779 PSSHFSRETURN(rv); 780 } 781 782 int 783 psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, 784 puffs_cookie_t src, const struct puffs_cn *pcn_src, 785 puffs_cookie_t targ_dir, puffs_cookie_t targ, 786 const struct puffs_cn *pcn_targ) 787 { 788 PSSHFSAUTOVAR(pu); 789 struct puffs_node *pn_sf = src; 790 struct puffs_node *pn_td = targ_dir, *pn_tf = targ; 791 struct psshfs_node *psn_src = pn_sf->pn_data; 792 struct psshfs_node *psn_targdir = pn_td->pn_data; 793 794 if (pctx->protover < 2) { 795 rv = EOPNOTSUPP; 796 goto out; 797 } 798 799 if (pn_tf) { 800 rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); 801 if (rv) 802 goto out; 803 } 804 805 psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); 806 psbuf_put_str(pb, PCNPATH(pcn_targ)); 807 GETRESPONSE(pb); 808 809 rv = psbuf_expect_status(pb); 810 if (rv == 0) { 811 struct psshfs_dir *pd; 812 813 /* 814 * XXX: interfaces didn't quite work with rename.. 815 * the song remains the same. go figure .. ;) 816 */ 817 nukenode(pn_sf, pcn_src->pcn_name, 0); 818 pd = direnter(pn_td, pcn_targ->pcn_name); 819 pd->entry = pn_sf; 820 puffs_setvattr(&pd->va, &pn_sf->pn_va); 821 822 if (opc != targ_dir) { 823 psn_targdir->childcount++; 824 psn_src->parent = pn_td; 825 if (pn_sf->pn_va.va_type == VDIR) 826 pn_td->pn_va.va_nlink++; 827 } 828 } 829 830 out: 831 PSSHFSRETURN(rv); 832 } 833 834 /* 835 * So this file system happened to be written in such a way that 836 * lookup for ".." is hard if we lose the in-memory node. We'd 837 * need to recreate the entire directory structure from the root 838 * node up to the ".." node we're looking up. 839 * 840 * And since our entire fs structure is purely fictional (i.e. it's 841 * only in-memory, not fetchable from the server), the easiest way 842 * to deal with it is to not allow nodes with children to be 843 * reclaimed. 844 * 845 * If a node with children is being attempted to be reclaimed, we 846 * just mark it "reclaimed" but leave it as is until all its children 847 * have been reclaimed. If a lookup for that node is done meanwhile, 848 * it will be found by lookup() and we just remove the "reclaimed" 849 * bit. 850 */ 851 int 852 psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) 853 { 854 struct puffs_node *pn = opc, *pn_next, *pn_root; 855 struct psshfs_node *psn = pn->pn_data; 856 857 /* 858 * don't reclaim if we have file handle issued, otherwise 859 * we can't do fhtonode 860 */ 861 if (psn->stat & PSN_HASFH) 862 return 0; 863 864 psn->stat |= PSN_RECLAIMED; 865 pn_root = puffs_getroot(pu); 866 for (; pn != pn_root; pn = pn_next) { 867 psn = pn->pn_data; 868 if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) 869 break; 870 871 pn_next = psn->parent; 872 doreclaim(pn); 873 } 874 875 return 0; 876 } 877