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