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