1 /* $NetBSD: node.c,v 1.63 2011/08/12 04:14:00 riastradh 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.63 2011/08/12 04:14:00 riastradh 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 readlen = *resid; 529 psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); 530 psbuf_put_8(pb, offset); 531 psbuf_put_4(pb, readlen); 532 533 /* 534 * Do this *after* accessing the file, the handle might not 535 * exist after blocking. 536 */ 537 if (max_reads && ++psn->readcount > max_reads) { 538 struct psshfs_wait pw; 539 540 pw.pw_cc = pcc; 541 pw.pw_type = PWTYPE_READ2; 542 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 543 puffs_cc_yield(pcc); 544 } 545 546 GETRESPONSE(pb, pctx->sshfd_data); 547 548 rv = psbuf_do_data(pb, buf, &readlen); 549 if (rv == 0) 550 *resid -= readlen; 551 552 out: 553 if (max_reads && --psn->readcount >= max_reads) { 554 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 555 if (pwp->pw_type == PWTYPE_READ2) 556 break; 557 assert(pwp != NULL); 558 puffs_cc_schedule(pwp->pw_cc); 559 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 560 } 561 562 farout: 563 /* check if we need a lazyclose */ 564 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { 565 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 566 if (pwp->pw_type == PWTYPE_READ1) 567 break; 568 if (pwp == NULL) 569 closehandles(pu, psn, HANDLE_READ); 570 } 571 PSSHFSRETURN(rv); 572 } 573 574 /* XXX: we should getattr for size */ 575 int 576 psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, 577 off_t offset, size_t *resid, const struct puffs_cred *cred, 578 int ioflag) 579 { 580 PSSHFSAUTOVAR(pu); 581 struct psshfs_wait *pwp; 582 struct puffs_node *pn = opc; 583 struct psshfs_node *psn = pn->pn_data; 584 uint32_t writelen; 585 586 if (pn->pn_va.va_type == VDIR) { 587 rv = EISDIR; 588 goto out; 589 } 590 591 /* check that a lazyopen didn't fail */ 592 if (!psn->fhand_w && !psn->lazyopen_w) { 593 rv = psn->lazyopen_err_w; 594 goto out; 595 } 596 597 if (psn->stat & PSN_LAZYWAIT_W) { 598 struct psshfs_wait pw; 599 600 assert(psn->lazyopen_w); 601 602 pw.pw_cc = pcc; 603 pw.pw_type = PWTYPE_WRITE; 604 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 605 puffs_cc_yield(pcc); 606 } 607 608 /* 609 * If lazyopening, wait for the result. 610 * There can still be more than oen writer at a time in case 611 * the kernel issues write FAFs. 612 */ 613 if (psn->lazyopen_w) { 614 psn->stat |= PSN_LAZYWAIT_W; 615 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); 616 lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); 617 618 /* schedule extra waiters */ 619 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 620 if (pwp->pw_type == PWTYPE_WRITE) { 621 puffs_cc_schedule(pwp->pw_cc); 622 TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 623 } 624 psn->stat &= ~PSN_LAZYWAIT_W; 625 626 if ((rv = psn->lazyopen_err_w) != 0) 627 goto out; 628 } 629 630 if (!psn->fhand_w) { 631 abort(); 632 rv = EINVAL; 633 goto out; 634 } 635 636 writelen = *resid; 637 psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); 638 psbuf_put_8(pb, offset); 639 psbuf_put_data(pb, buf, writelen); 640 GETRESPONSE(pb, pctx->sshfd_data); 641 642 rv = psbuf_expect_status(pb); 643 if (rv == 0) 644 *resid = 0; 645 646 if (pn->pn_va.va_size < (uint64_t)offset + writelen) 647 pn->pn_va.va_size = offset + writelen; 648 649 out: 650 /* check if we need a lazyclose */ 651 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { 652 TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 653 if (pwp->pw_type == PWTYPE_WRITE) 654 break; 655 if (pwp == NULL) 656 closehandles(pu, psn, HANDLE_WRITE); 657 } 658 PSSHFSRETURN(rv); 659 } 660 661 int 662 psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, 663 const struct puffs_cred *cred, char *linkvalue, size_t *linklen) 664 { 665 PSSHFSAUTOVAR(pu); 666 struct puffs_node *pn = opc; 667 struct psshfs_node *psn = pn->pn_data; 668 uint32_t count; 669 670 if (pctx->protover < 3) { 671 rv = EOPNOTSUPP; 672 goto out; 673 } 674 675 /* 676 * check if we can use a cached version 677 * 678 * XXX: we might end up reading the same link multiple times 679 * from the server if we get many requests at once, but that's 680 * quite harmless as this routine is reentrant. 681 */ 682 if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) 683 goto copy; 684 685 if (psn->symlink) { 686 free(psn->symlink); 687 psn->symlink = NULL; 688 psn->slread = 0; 689 } 690 691 psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); 692 GETRESPONSE(pb, pctx->sshfd); 693 694 rv = psbuf_expect_name(pb, &count); 695 if (rv) 696 goto out; 697 if (count != 1) { 698 rv = EPROTO; 699 goto out; 700 } 701 702 rv = psbuf_get_str(pb, &psn->symlink, NULL); 703 if (rv) 704 goto out; 705 psn->slread = time(NULL); 706 707 copy: 708 *linklen = strlen(psn->symlink); 709 (void) memcpy(linkvalue, psn->symlink, *linklen); 710 711 out: 712 PSSHFSRETURN(rv); 713 } 714 715 static int 716 doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, 717 struct puffs_node *pn, const char *name) 718 { 719 PSSHFSAUTOVAR(pu); 720 int op; 721 722 if (pn->pn_va.va_type == VDIR) 723 op = SSH_FXP_RMDIR; 724 else 725 op = SSH_FXP_REMOVE; 726 727 psbuf_req_str(pb, op, reqid, PNPATH(pn)); 728 GETRESPONSE(pb, pctx->sshfd); 729 730 rv = psbuf_expect_status(pb); 731 if (rv == 0) 732 nukenode(pn, name, 0); 733 734 out: 735 PSSHFSRETURN(rv); 736 } 737 738 int 739 psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, 740 puffs_cookie_t targ, const struct puffs_cn *pcn) 741 { 742 struct puffs_node *pn_targ = targ; 743 int rv; 744 745 assert(pn_targ->pn_va.va_type != VDIR); 746 747 rv = doremove(pu, opc, targ, pcn->pcn_name); 748 if (rv == 0) 749 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 750 751 return rv; 752 } 753 754 int 755 psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, 756 puffs_cookie_t targ, const struct puffs_cn *pcn) 757 { 758 struct puffs_node *pn_targ = targ; 759 int rv; 760 761 assert(pn_targ->pn_va.va_type == VDIR); 762 763 rv = doremove(pu, opc, targ, pcn->pcn_name); 764 if (rv == 0) 765 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 766 767 return rv; 768 } 769 770 int 771 psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, 772 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 773 const struct vattr *va) 774 { 775 PSSHFSAUTOVAR(pu); 776 struct puffs_node *pn = opc; 777 struct puffs_node *pn_new; 778 779 psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); 780 psbuf_put_vattr(pb, va, pctx); 781 GETRESPONSE(pb, pctx->sshfd); 782 783 rv = psbuf_expect_status(pb); 784 if (rv) 785 goto out; 786 787 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 788 if (pn_new) { 789 puffs_newinfo_setcookie(pni, pn_new); 790 } else { 791 struct puffs_framebuf *pb2 = psbuf_makeout(); 792 reqid = NEXTREQ(pctx); 793 psbuf_recycleout(pb2); 794 psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); 795 JUSTSEND(pb2, pctx->sshfd); 796 rv = ENOMEM; 797 } 798 799 out: 800 PSSHFSRETURN(rv); 801 } 802 803 int 804 psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, 805 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 806 const struct vattr *va, const char *link_target) 807 { 808 PSSHFSAUTOVAR(pu); 809 struct puffs_node *pn = opc; 810 struct puffs_node *pn_new; 811 812 if (pctx->protover < 3) { 813 rv = EOPNOTSUPP; 814 goto out; 815 } 816 817 /* 818 * XXX: ietf says: source, target. openssh says: ietf who? 819 * Let's go with openssh and build quirk tables later if we care 820 */ 821 psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); 822 psbuf_put_str(pb, PCNPATH(pcn)); 823 GETRESPONSE(pb, pctx->sshfd); 824 825 rv = psbuf_expect_status(pb); 826 if (rv) 827 goto out; 828 829 pn_new = allocnode(pu, pn, pcn->pcn_name, va); 830 if (pn_new) { 831 puffs_newinfo_setcookie(pni, pn_new); 832 } else { 833 struct puffs_framebuf *pb2 = psbuf_makeout(); 834 reqid = NEXTREQ(pctx); 835 psbuf_recycleout(pb2); 836 psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); 837 JUSTSEND(pb2, pctx->sshfd); 838 rv = ENOMEM; 839 } 840 841 out: 842 PSSHFSRETURN(rv); 843 } 844 845 int 846 psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, 847 puffs_cookie_t src, const struct puffs_cn *pcn_src, 848 puffs_cookie_t targ_dir, puffs_cookie_t targ, 849 const struct puffs_cn *pcn_targ) 850 { 851 PSSHFSAUTOVAR(pu); 852 struct puffs_node *pn_sf = src; 853 struct puffs_node *pn_td = targ_dir, *pn_tf = targ; 854 struct psshfs_node *psn_src = pn_sf->pn_data; 855 struct psshfs_node *psn_targdir = pn_td->pn_data; 856 857 if (pctx->protover < 2) { 858 rv = EOPNOTSUPP; 859 goto out; 860 } 861 862 if (pn_tf) { 863 rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); 864 if (rv) 865 goto out; 866 } 867 868 psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); 869 psbuf_put_str(pb, PCNPATH(pcn_targ)); 870 GETRESPONSE(pb, pctx->sshfd); 871 872 rv = psbuf_expect_status(pb); 873 if (rv == 0) { 874 struct psshfs_dir *pd; 875 876 /* 877 * XXX: interfaces didn't quite work with rename.. 878 * the song remains the same. go figure .. ;) 879 */ 880 nukenode(pn_sf, pcn_src->pcn_name, 0); 881 pd = direnter(pn_td, pcn_targ->pcn_name); 882 pd->entry = pn_sf; 883 puffs_setvattr(&pd->va, &pn_sf->pn_va); 884 885 if (opc != targ_dir) { 886 psn_targdir->childcount++; 887 psn_src->parent = pn_td; 888 if (pn_sf->pn_va.va_type == VDIR) 889 pn_td->pn_va.va_nlink++; 890 } 891 } 892 893 out: 894 PSSHFSRETURN(rv); 895 } 896 897 /* 898 * So this file system happened to be written in such a way that 899 * lookup for ".." is hard if we lose the in-memory node. We'd 900 * need to recreate the entire directory structure from the root 901 * node up to the ".." node we're looking up. 902 * 903 * And since our entire fs structure is purely fictional (i.e. it's 904 * only in-memory, not fetchable from the server), the easiest way 905 * to deal with it is to not allow nodes with children to be 906 * reclaimed. 907 * 908 * If a node with children is being attempted to be reclaimed, we 909 * just mark it "reclaimed" but leave it as is until all its children 910 * have been reclaimed. If a lookup for that node is done meanwhile, 911 * it will be found by lookup() and we just remove the "reclaimed" 912 * bit. 913 */ 914 int 915 psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) 916 { 917 struct puffs_node *pn = opc, *pn_next, *pn_root; 918 struct psshfs_node *psn = pn->pn_data; 919 920 /* 921 * don't reclaim if we have file handle issued, otherwise 922 * we can't do fhtonode 923 */ 924 if (psn->stat & PSN_HASFH) 925 return 0; 926 927 psn->stat |= PSN_RECLAIMED; 928 pn_root = puffs_getroot(pu); 929 for (; pn != pn_root; pn = pn_next) { 930 psn = pn->pn_data; 931 if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) 932 break; 933 934 pn_next = psn->parent; 935 doreclaim(pn); 936 } 937 938 return 0; 939 } 940