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