1 /* $NetBSD: psbuf.c,v 1.13 2008/09/06 12:29:57 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2006, 2007 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.13 2008/09/06 12:29:57 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 61 uint8_t 62 psbuf_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 70 uint32_t 71 psbuf_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 79 uint32_t 80 psbuf_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)) 89 int 90 psbuf_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 135 int 136 psbuf_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 176 int 177 psbuf_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 184 struct puffs_framebuf * 185 psbuf_makeout() 186 { 187 struct puffs_framebuf *pb; 188 189 pb = puffs_framebuf_make(); 190 puffs_framebuf_seekset(pb, 4); 191 return pb; 192 } 193 194 void 195 psbuf_recycleout(struct puffs_framebuf *pb) 196 { 197 198 puffs_framebuf_recycle(pb); 199 puffs_framebuf_seekset(pb, 4); 200 } 201 202 void 203 psbuf_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 211 void 212 psbuf_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 221 void 222 psbuf_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 231 void 232 psbuf_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 241 void 242 psbuf_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 251 void 252 psbuf_put_str(struct puffs_framebuf *pb, const char *str) 253 { 254 255 psbuf_put_data(pb, str, strlen(str)); 256 } 257 258 void 259 psbuf_put_vattr(struct puffs_framebuf *pb, const struct vattr *va) 260 { 261 uint32_t flags; 262 flags = 0; 263 264 if (va->va_size != PUFFS_VNOVAL) 265 flags |= SSH_FILEXFER_ATTR_SIZE; 266 if (va->va_uid != PUFFS_VNOVAL) 267 flags |= SSH_FILEXFER_ATTR_UIDGID; 268 if (va->va_mode != PUFFS_VNOVAL) 269 flags |= SSH_FILEXFER_ATTR_PERMISSIONS; 270 271 if (va->va_atime.tv_sec != PUFFS_VNOVAL) 272 flags |= SSH_FILEXFER_ATTR_ACCESSTIME; 273 274 psbuf_put_4(pb, flags); 275 if (flags & SSH_FILEXFER_ATTR_SIZE) 276 psbuf_put_8(pb, va->va_size); 277 if (flags & SSH_FILEXFER_ATTR_UIDGID) { 278 psbuf_put_4(pb, va->va_uid); 279 psbuf_put_4(pb, va->va_gid); 280 } 281 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) 282 psbuf_put_4(pb, va->va_mode); 283 284 /* XXX: this is totally wrong for protocol v3, see OpenSSH */ 285 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { 286 psbuf_put_4(pb, va->va_atime.tv_sec); 287 psbuf_put_4(pb, va->va_mtime.tv_sec); 288 } 289 } 290 291 #define ERETURN(rv) return ((rv) == -1 ? errno : 0) 292 293 int 294 psbuf_get_1(struct puffs_framebuf *pb, uint8_t *val) 295 { 296 297 ERETURN(puffs_framebuf_getdata(pb, val, 1)); 298 } 299 300 int 301 psbuf_get_2(struct puffs_framebuf *pb, uint16_t *val) 302 { 303 int rv; 304 305 rv = puffs_framebuf_getdata(pb, val, 2); 306 BE16TOH(*val); 307 308 ERETURN(rv); 309 } 310 311 int 312 psbuf_get_4(struct puffs_framebuf *pb, uint32_t *val) 313 { 314 int rv; 315 316 rv = puffs_framebuf_getdata(pb, val, 4); 317 BE32TOH(*val); 318 319 ERETURN(rv); 320 } 321 322 int 323 psbuf_get_8(struct puffs_framebuf *pb, uint64_t *val) 324 { 325 int rv; 326 327 rv = puffs_framebuf_getdata(pb, val, 8); 328 BE64TOH(*val); 329 330 ERETURN(rv); 331 } 332 333 int 334 psbuf_get_str(struct puffs_framebuf *pb, char **strp, uint32_t *strlenp) 335 { 336 char *str; 337 uint32_t len; 338 339 FAILRV(psbuf_get_4(pb, &len)); 340 341 if (puffs_framebuf_remaining(pb) < len) 342 return EPROTO; 343 344 str = emalloc(len+1); 345 puffs_framebuf_getdata(pb, str, len); 346 str[len] = '\0'; 347 *strp = str; 348 349 if (strlenp) 350 *strlenp = len; 351 352 return 0; 353 } 354 355 int 356 psbuf_get_vattr(struct puffs_framebuf *pb, struct vattr *vap) 357 { 358 uint32_t flags; 359 uint32_t val; 360 361 puffs_vattr_null(vap); 362 363 FAILRV(psbuf_get_4(pb, &flags)); 364 365 if (flags & SSH_FILEXFER_ATTR_SIZE) { 366 FAILRV(psbuf_get_8(pb, &vap->va_size)); 367 vap->va_bytes = vap->va_size; 368 } 369 if (flags & SSH_FILEXFER_ATTR_UIDGID) { 370 FAILRV(psbuf_get_4(pb, &vap->va_uid)); 371 FAILRV(psbuf_get_4(pb, &vap->va_gid)); 372 } 373 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { 374 FAILRV(psbuf_get_4(pb, &vap->va_mode)); 375 vap->va_type = puffs_mode2vt(vap->va_mode); 376 } 377 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { 378 /* 379 * XXX: this is utterly wrong if we want to speak 380 * protocol version 3, but it seems like the 381 * "internet standard" for doing this 382 */ 383 FAILRV(psbuf_get_4(pb, &val)); 384 vap->va_atime.tv_sec = val; 385 FAILRV(psbuf_get_4(pb, &val)); 386 vap->va_mtime.tv_sec = val; 387 /* make ctime the same as mtime */ 388 vap->va_ctime.tv_sec = val; 389 390 vap->va_atime.tv_nsec = 0; 391 vap->va_ctime.tv_nsec = 0; 392 vap->va_mtime.tv_nsec = 0; 393 } 394 395 return 0; 396 } 397 398 /* 399 * Buffer content helpers. Caller frees all data. 400 */ 401 402 /* 403 * error mapping.. most are not expected for a file system, but 404 * should help with diagnosing a possible error 405 */ 406 static int emap[] = { 407 0, /* OK */ 408 0, /* EOF */ 409 ENOENT, /* NO_SUCH_FILE */ 410 EPERM, /* PERMISSION_DENIED */ 411 EIO, /* FAILURE */ 412 EBADMSG, /* BAD_MESSAGE */ 413 ENOTCONN, /* NO_CONNECTION */ 414 ECONNRESET, /* CONNECTION_LOST */ 415 EOPNOTSUPP, /* OP_UNSUPPORTED */ 416 EINVAL, /* INVALID_HANDLE */ 417 ENXIO, /* NO_SUCH_PATH */ 418 EEXIST, /* FILE_ALREADY_EXISTS */ 419 ENODEV /* WRITE_PROTECT */ 420 }; 421 #define NERRORS (sizeof(emap) / sizeof(emap[0])) 422 423 static int 424 sftperr_to_errno(int error) 425 { 426 427 if (!error) 428 return 0; 429 430 if (error >= NERRORS || error < 0) 431 return EPROTO; 432 433 return emap[error]; 434 } 435 436 #define INVALRESPONSE EPROTO 437 438 static int 439 expectcode(struct puffs_framebuf *pb, int value) 440 { 441 uint32_t error; 442 uint8_t type; 443 444 type = psbuf_get_type(pb); 445 if (type == value) 446 return 0; 447 448 if (type != SSH_FXP_STATUS) 449 return INVALRESPONSE; 450 451 FAILRV(psbuf_get_4(pb, &error)); 452 453 return sftperr_to_errno(error); 454 } 455 456 #define CHECKCODE(pb,val) \ 457 do { \ 458 int rv; \ 459 rv = expectcode(pb, val); \ 460 if (rv) \ 461 return rv; \ 462 } while (/*CONSTCOND*/0) 463 464 int 465 psbuf_expect_status(struct puffs_framebuf *pb) 466 { 467 uint32_t error; 468 469 if (psbuf_get_type(pb) != SSH_FXP_STATUS) 470 return INVALRESPONSE; 471 472 FAILRV(psbuf_get_4(pb, &error)); 473 474 return sftperr_to_errno(error); 475 } 476 477 int 478 psbuf_expect_handle(struct puffs_framebuf *pb, char **hand, uint32_t *handlen) 479 { 480 481 CHECKCODE(pb, SSH_FXP_HANDLE); 482 FAILRV(psbuf_get_str(pb, hand, handlen)); 483 484 return 0; 485 } 486 487 /* no memory allocation, direct copy */ 488 int 489 psbuf_do_data(struct puffs_framebuf *pb, uint8_t *data, uint32_t *dlen) 490 { 491 void *win; 492 size_t bufoff, winlen; 493 uint32_t len, dataoff; 494 495 if (psbuf_get_type(pb) != SSH_FXP_DATA) { 496 uint32_t val; 497 498 if (psbuf_get_type(pb) != SSH_FXP_STATUS) 499 return INVALRESPONSE; 500 501 if (psbuf_get_4(pb, &val) != 0) 502 return INVALRESPONSE; 503 504 if (val != SSH_FX_EOF) 505 return sftperr_to_errno(val); 506 507 *dlen = 0; 508 return 0; 509 } 510 if (psbuf_get_4(pb, &len) != 0) 511 return INVALRESPONSE; 512 513 if (*dlen < len) 514 return EINVAL; 515 516 *dlen = 0; 517 518 dataoff = 0; 519 while (dataoff < len) { 520 winlen = len-dataoff; 521 bufoff = puffs_framebuf_telloff(pb); 522 if (puffs_framebuf_getwindow(pb, bufoff, 523 &win, &winlen) == -1) 524 return EINVAL; 525 if (winlen == 0) 526 break; 527 528 memcpy(data + dataoff, win, winlen); 529 dataoff += winlen; 530 } 531 532 *dlen = dataoff; 533 534 return 0; 535 } 536 537 int 538 psbuf_expect_name(struct puffs_framebuf *pb, uint32_t *count) 539 { 540 541 CHECKCODE(pb, SSH_FXP_NAME); 542 FAILRV(psbuf_get_4(pb, count)); 543 544 return 0; 545 } 546 547 int 548 psbuf_expect_attrs(struct puffs_framebuf *pb, struct vattr *vap) 549 { 550 551 CHECKCODE(pb, SSH_FXP_ATTRS); 552 FAILRV(psbuf_get_vattr(pb, vap)); 553 554 return 0; 555 } 556 557 /* 558 * More helpers: larger-scale put functions 559 */ 560 561 void 562 psbuf_req_data(struct puffs_framebuf *pb, int type, uint32_t reqid, 563 const void *data, uint32_t dlen) 564 { 565 566 psbuf_put_1(pb, type); 567 psbuf_put_4(pb, reqid); 568 psbuf_put_data(pb, data, dlen); 569 } 570 571 void 572 psbuf_req_str(struct puffs_framebuf *pb, int type, uint32_t reqid, 573 const char *str) 574 { 575 576 psbuf_req_data(pb, type, reqid, str, strlen(str)); 577 } 578