1/* $NetBSD: psbuf.c,v 1.17 2010/01/07 21:19:45 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: psbuf.c,v 1.17 2010/01/07 21:19:45 pooka Exp $"); 31#endif /* !lint */ 32 33/* 34 * buffering functions for network input/output. slightly different 35 * from the average joe buffer routines, as is usually the case ... 36 * these use efuns for now. 37 */ 38 39#include <sys/types.h> 40#include <sys/time.h> 41#include <sys/vnode.h> 42 43#include <err.h> 44#include <errno.h> 45#include <stdlib.h> 46#include <util.h> 47#include <unistd.h> 48 49#include "psshfs.h" 50#include "sftp_proto.h" 51 52#define FAILRV(x) do { int rv; if ((rv=x)) return (rv); } while (/*CONSTCOND*/0) 53#define READSTATE_LENGTH(off) (off < 4) 54 55#define SFTP_LENOFF 0 56#define SFTP_TYPEOFF 4 57#define SFTP_REQIDOFF 5 58 59#define CHECK(v) if (!(v)) abort() 60 61uint8_t 62psbuf_get_type(struct puffs_framebuf *pb) 63{ 64 uint8_t type; 65 66 puffs_framebuf_getdata_atoff(pb, SFTP_TYPEOFF, &type, 1); 67 return type; 68} 69 70uint32_t 71psbuf_get_len(struct puffs_framebuf *pb) 72{ 73 uint32_t len; 74 75 puffs_framebuf_getdata_atoff(pb, SFTP_LENOFF, &len, 4); 76 return be32toh(len); 77} 78 79uint32_t 80psbuf_get_reqid(struct puffs_framebuf *pb) 81{ 82 uint32_t req; 83 84 puffs_framebuf_getdata_atoff(pb, SFTP_REQIDOFF, &req, 4); 85 return be32toh(req); 86} 87 88#define CUROFF(pb) (puffs_framebuf_telloff(pb)) 89int 90psbuf_read(struct puffs_usermount *pu, struct puffs_framebuf *pb, 91 int fd, int *done) 92{ 93 void *win; 94 ssize_t n; 95 size_t howmuch, winlen; 96 int lenstate; 97 98 the_next_level: 99 if ((lenstate = READSTATE_LENGTH(CUROFF(pb)))) 100 howmuch = 4 - CUROFF(pb); 101 else 102 howmuch = psbuf_get_len(pb) - (CUROFF(pb) - 4); 103 104 if (puffs_framebuf_reserve_space(pb, howmuch) == -1) 105 return errno; 106 107 while (howmuch) { 108 winlen = howmuch; 109 if (puffs_framebuf_getwindow(pb, CUROFF(pb), &win, &winlen)==-1) 110 return errno; 111 n = recv(fd, win, winlen, MSG_NOSIGNAL); 112 switch (n) { 113 case 0: 114 return ECONNRESET; 115 case -1: 116 if (errno == EAGAIN) 117 return 0; 118 return errno; 119 default: 120 howmuch -= n; 121 puffs_framebuf_seekset(pb, CUROFF(pb) + n); 122 break; 123 } 124 } 125 126 if (!lenstate) { 127 /* XXX: initial exchange shorter.. but don't worry, be happy */ 128 puffs_framebuf_seekset(pb, 9); 129 *done = 1; 130 return 0; 131 } else 132 goto the_next_level; 133} 134 135int 136psbuf_write(struct puffs_usermount *pu, struct puffs_framebuf *pb, 137 int fd, int *done) 138{ 139 void *win; 140 ssize_t n; 141 size_t winlen, howmuch; 142 143 /* finalize buffer.. could be elsewhere ... */ 144 if (CUROFF(pb) == 0) { 145 uint32_t len; 146 147 len = htobe32(puffs_framebuf_tellsize(pb) - 4); 148 puffs_framebuf_putdata_atoff(pb, 0, &len, 4); 149 } 150 151 howmuch = puffs_framebuf_tellsize(pb) - CUROFF(pb); 152 while (howmuch) { 153 winlen = howmuch; 154 if (puffs_framebuf_getwindow(pb, CUROFF(pb), &win, &winlen)==-1) 155 return errno; 156 n = send(fd, win, winlen, MSG_NOSIGNAL); 157 switch (n) { 158 case 0: 159 return ECONNRESET; 160 case -1: 161 if (errno == EAGAIN) 162 return 0; 163 return errno; 164 default: 165 howmuch -= n; 166 puffs_framebuf_seekset(pb, CUROFF(pb) + n); 167 break; 168 } 169 } 170 171 *done = 1; 172 return 0; 173} 174#undef CUROFF 175 176int 177psbuf_cmp(struct puffs_usermount *pu, 178 struct puffs_framebuf *cmp1, struct puffs_framebuf *cmp2, int *notresp) 179{ 180 181 return psbuf_get_reqid(cmp1) != psbuf_get_reqid(cmp2); 182} 183 184struct puffs_framebuf * 185psbuf_makeout() 186{ 187 struct puffs_framebuf *pb; 188 189 pb = puffs_framebuf_make(); 190 puffs_framebuf_seekset(pb, 4); 191 return pb; 192} 193 194void 195psbuf_recycleout(struct puffs_framebuf *pb) 196{ 197 198 puffs_framebuf_recycle(pb); 199 puffs_framebuf_seekset(pb, 4); 200} 201 202void 203psbuf_put_1(struct puffs_framebuf *pb, uint8_t val) 204{ 205 int rv; 206 207 rv = puffs_framebuf_putdata(pb, &val, 1); 208 CHECK(rv == 0); 209} 210 211void 212psbuf_put_2(struct puffs_framebuf *pb, uint16_t val) 213{ 214 int rv; 215 216 HTOBE16(val); 217 rv = puffs_framebuf_putdata(pb, &val, 2); 218 CHECK(rv == 0); 219} 220 221void 222psbuf_put_4(struct puffs_framebuf *pb, uint32_t val) 223{ 224 int rv; 225 226 HTOBE32(val); 227 rv = puffs_framebuf_putdata(pb, &val, 4); 228 CHECK(rv == 0); 229} 230 231void 232psbuf_put_8(struct puffs_framebuf *pb, uint64_t val) 233{ 234 int rv; 235 236 HTOBE64(val); 237 rv = puffs_framebuf_putdata(pb, &val, 8); 238 CHECK(rv == 0); 239} 240 241void 242psbuf_put_data(struct puffs_framebuf *pb, const void *data, uint32_t dlen) 243{ 244 int rv; 245 246 psbuf_put_4(pb, dlen); 247 rv = puffs_framebuf_putdata(pb, data, dlen); 248 CHECK(rv == 0); 249} 250 251void 252psbuf_put_str(struct puffs_framebuf *pb, const char *str) 253{ 254 255 psbuf_put_data(pb, str, strlen(str)); 256} 257 258void 259psbuf_put_vattr(struct puffs_framebuf *pb, const struct vattr *va, 260 const struct psshfs_ctx *pctx) 261{ 262 uint32_t flags; 263 uint32_t theuid = -1, thegid = -1; 264 flags = 0; 265 266 if (va->va_size != (uint64_t)PUFFS_VNOVAL) 267 flags |= SSH_FILEXFER_ATTR_SIZE; 268 if (va->va_uid != (uid_t)PUFFS_VNOVAL) { 269 theuid = va->va_uid; 270 if (pctx->domangleuid && theuid == pctx->myuid) 271 theuid = pctx->mangleuid; 272 flags |= SSH_FILEXFER_ATTR_UIDGID; 273 } 274 if (va->va_gid != (gid_t)PUFFS_VNOVAL) { 275 thegid = va->va_gid; 276 if (pctx->domanglegid && thegid == pctx->mygid) 277 thegid = pctx->manglegid; 278 flags |= SSH_FILEXFER_ATTR_UIDGID; 279 } 280 if (va->va_mode != (mode_t)PUFFS_VNOVAL) 281 flags |= SSH_FILEXFER_ATTR_PERMISSIONS; 282 283 if (va->va_atime.tv_sec != PUFFS_VNOVAL) 284 flags |= SSH_FILEXFER_ATTR_ACCESSTIME; 285 286 psbuf_put_4(pb, flags); 287 if (flags & SSH_FILEXFER_ATTR_SIZE) 288 psbuf_put_8(pb, va->va_size); 289 if (flags & SSH_FILEXFER_ATTR_UIDGID) { 290 psbuf_put_4(pb, theuid); 291 psbuf_put_4(pb, thegid); 292 } 293 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) 294 psbuf_put_4(pb, va->va_mode); 295 296 /* XXX: this is totally wrong for protocol v3, see OpenSSH */ 297 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { 298 psbuf_put_4(pb, va->va_atime.tv_sec); 299 psbuf_put_4(pb, va->va_mtime.tv_sec); 300 } 301} 302 303#define ERETURN(rv) return ((rv) == -1 ? errno : 0) 304 305int 306psbuf_get_1(struct puffs_framebuf *pb, uint8_t *val) 307{ 308 309 ERETURN(puffs_framebuf_getdata(pb, val, 1)); 310} 311 312int 313psbuf_get_2(struct puffs_framebuf *pb, uint16_t *val) 314{ 315 int rv; 316 317 rv = puffs_framebuf_getdata(pb, val, 2); 318 BE16TOH(*val); 319 320 ERETURN(rv); 321} 322 323int 324psbuf_get_4(struct puffs_framebuf *pb, uint32_t *val) 325{ 326 int rv; 327 328 rv = puffs_framebuf_getdata(pb, val, 4); 329 BE32TOH(*val); 330 331 ERETURN(rv); 332} 333 334int 335psbuf_get_8(struct puffs_framebuf *pb, uint64_t *val) 336{ 337 int rv; 338 339 rv = puffs_framebuf_getdata(pb, val, 8); 340 BE64TOH(*val); 341 342 ERETURN(rv); 343} 344 345int 346psbuf_get_str(struct puffs_framebuf *pb, char **strp, uint32_t *strlenp) 347{ 348 char *str; 349 uint32_t len; 350 351 FAILRV(psbuf_get_4(pb, &len)); 352 353 if (puffs_framebuf_remaining(pb) < len) 354 return EPROTO; 355 356 str = emalloc(len+1); 357 puffs_framebuf_getdata(pb, str, len); 358 str[len] = '\0'; 359 *strp = str; 360 361 if (strlenp) 362 *strlenp = len; 363 364 return 0; 365} 366 367int 368psbuf_get_vattr(struct puffs_framebuf *pb, struct vattr *vap) 369{ 370 uint32_t flags; 371 uint32_t val; 372 373 puffs_vattr_null(vap); 374 375 FAILRV(psbuf_get_4(pb, &flags)); 376 377 if (flags & SSH_FILEXFER_ATTR_SIZE) { 378 FAILRV(psbuf_get_8(pb, &vap->va_size)); 379 vap->va_bytes = vap->va_size; 380 } 381 if (flags & SSH_FILEXFER_ATTR_UIDGID) { 382 FAILRV(psbuf_get_4(pb, &vap->va_uid)); 383 FAILRV(psbuf_get_4(pb, &vap->va_gid)); 384 } 385 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { 386 FAILRV(psbuf_get_4(pb, &vap->va_mode)); 387 vap->va_type = puffs_mode2vt(vap->va_mode); 388 } 389 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { 390 /* 391 * XXX: this is utterly wrong if we want to speak 392 * protocol version 3, but it seems like the 393 * "internet standard" for doing this 394 */ 395 FAILRV(psbuf_get_4(pb, &val)); 396 vap->va_atime.tv_sec = val; 397 FAILRV(psbuf_get_4(pb, &val)); 398 vap->va_mtime.tv_sec = val; 399 /* make ctime the same as mtime */ 400 vap->va_ctime.tv_sec = val; 401 402 vap->va_atime.tv_nsec = 0; 403 vap->va_ctime.tv_nsec = 0; 404 vap->va_mtime.tv_nsec = 0; 405 } 406 407 return 0; 408} 409 410/* 411 * Buffer content helpers. Caller frees all data. 412 */ 413 414/* 415 * error mapping.. most are not expected for a file system, but 416 * should help with diagnosing a possible error 417 */ 418static int emap[] = { 419 0, /* OK */ 420 0, /* EOF */ 421 ENOENT, /* NO_SUCH_FILE */ 422 EPERM, /* PERMISSION_DENIED */ 423 EIO, /* FAILURE */ 424 EBADMSG, /* BAD_MESSAGE */ 425 ENOTCONN, /* NO_CONNECTION */ 426 ECONNRESET, /* CONNECTION_LOST */ 427 EOPNOTSUPP, /* OP_UNSUPPORTED */ 428 EINVAL, /* INVALID_HANDLE */ 429 ENXIO, /* NO_SUCH_PATH */ 430 EEXIST, /* FILE_ALREADY_EXISTS */ 431 ENODEV /* WRITE_PROTECT */ 432}; 433#define NERRORS ((int)(sizeof(emap) / sizeof(emap[0]))) 434 435static int 436sftperr_to_errno(int error) 437{ 438 439 if (!error) 440 return 0; 441 442 if (error >= NERRORS || error < 0) 443 return EPROTO; 444 445 return emap[error]; 446} 447 448#define INVALRESPONSE EPROTO 449 450static int 451expectcode(struct puffs_framebuf *pb, int value) 452{ 453 uint32_t error; 454 uint8_t type; 455 456 type = psbuf_get_type(pb); 457 if (type == value) 458 return 0; 459 460 if (type != SSH_FXP_STATUS) 461 return INVALRESPONSE; 462 463 FAILRV(psbuf_get_4(pb, &error)); 464 465 return sftperr_to_errno(error); 466} 467 468#define CHECKCODE(pb,val) \ 469do { \ 470 int rv; \ 471 rv = expectcode(pb, val); \ 472 if (rv) \ 473 return rv; \ 474} while (/*CONSTCOND*/0) 475 476int 477psbuf_expect_status(struct puffs_framebuf *pb) 478{ 479 uint32_t error; 480 481 if (psbuf_get_type(pb) != SSH_FXP_STATUS) 482 return INVALRESPONSE; 483 484 FAILRV(psbuf_get_4(pb, &error)); 485 486 return sftperr_to_errno(error); 487} 488 489int 490psbuf_expect_handle(struct puffs_framebuf *pb, char **hand, uint32_t *handlen) 491{ 492 493 CHECKCODE(pb, SSH_FXP_HANDLE); 494 FAILRV(psbuf_get_str(pb, hand, handlen)); 495 496 return 0; 497} 498 499/* no memory allocation, direct copy */ 500int 501psbuf_do_data(struct puffs_framebuf *pb, uint8_t *data, uint32_t *dlen) 502{ 503 void *win; 504 size_t bufoff, winlen; 505 uint32_t len, dataoff; 506 507 if (psbuf_get_type(pb) != SSH_FXP_DATA) { 508 uint32_t val; 509 510 if (psbuf_get_type(pb) != SSH_FXP_STATUS) 511 return INVALRESPONSE; 512 513 if (psbuf_get_4(pb, &val) != 0) 514 return INVALRESPONSE; 515 516 if (val != SSH_FX_EOF) 517 return sftperr_to_errno(val); 518 519 *dlen = 0; 520 return 0; 521 } 522 if (psbuf_get_4(pb, &len) != 0) 523 return INVALRESPONSE; 524 525 if (*dlen < len) 526 return EINVAL; 527 528 *dlen = 0; 529 530 dataoff = 0; 531 while (dataoff < len) { 532 winlen = len-dataoff; 533 bufoff = puffs_framebuf_telloff(pb); 534 if (puffs_framebuf_getwindow(pb, bufoff, 535 &win, &winlen) == -1) 536 return EINVAL; 537 if (winlen == 0) 538 break; 539 540 memcpy(data + dataoff, win, winlen); 541 dataoff += winlen; 542 } 543 544 *dlen = dataoff; 545 546 return 0; 547} 548 549int 550psbuf_expect_name(struct puffs_framebuf *pb, uint32_t *count) 551{ 552 553 CHECKCODE(pb, SSH_FXP_NAME); 554 FAILRV(psbuf_get_4(pb, count)); 555 556 return 0; 557} 558 559int 560psbuf_expect_attrs(struct puffs_framebuf *pb, struct vattr *vap) 561{ 562 563 CHECKCODE(pb, SSH_FXP_ATTRS); 564 FAILRV(psbuf_get_vattr(pb, vap)); 565 566 return 0; 567} 568 569/* 570 * More helpers: larger-scale put functions 571 */ 572 573void 574psbuf_req_data(struct puffs_framebuf *pb, int type, uint32_t reqid, 575 const void *data, uint32_t dlen) 576{ 577 578 psbuf_put_1(pb, type); 579 psbuf_put_4(pb, reqid); 580 psbuf_put_data(pb, data, dlen); 581} 582 583void 584psbuf_req_str(struct puffs_framebuf *pb, int type, uint32_t reqid, 585 const char *str) 586{ 587 588 psbuf_req_data(pb, type, reqid, str, strlen(str)); 589} 590