psbuf.c revision 1.2 1 /* $NetBSD: psbuf.c,v 1.2 2007/01/07 19:31:48 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2006 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 * 3. The name of the company nor the name of the author may be used to
15 * endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #ifndef lint
33 __RCSID("$NetBSD: psbuf.c,v 1.2 2007/01/07 19:31:48 pooka Exp $");
34 #endif /* !lint */
35
36 /*
37 * buffering functions for network input/output. slightly different
38 * from the average joe buffer routines, as is usually the case ...
39 * these use efuns for now.
40 */
41
42 #include <sys/types.h>
43 #include <sys/time.h>
44 #include <sys/vnode.h>
45
46 #include <assert.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <stdlib.h>
50 #include <util.h>
51 #include <unistd.h>
52
53 #include "psshfs.h"
54 #include "sftp_proto.h"
55
56 #define FAILRV(x, rv) do { if (!(x)) return (rv); } while (/*CONSTCOND*/0)
57
58 int
59 psbuf_read(struct psshfs_ctx *pctx, struct psbuf *pb)
60 {
61 ssize_t n;
62
63 assert(pb->state != PSBUF_PUT);
64
65 again:
66 n = read(pctx->sshfd, pb->buf+pb->offset, pb->remain);
67 switch (n) {
68 case 0:
69 errno = EIO;
70 return -1;
71 case -1:
72 if (errno == EAGAIN)
73 return 0;
74 return -1;
75 default:
76 pb->offset += n;
77 pb->remain -= n;
78 }
79
80 if (pb->remain != 0)
81 return 0;
82
83 /* ok, at least there's something to do */
84 assert(pb->state == PSBUF_GETLEN || pb->state == PSBUF_GETDATA);
85
86 if (pb->state == PSBUF_GETLEN) {
87 memcpy(&pb->len, pb->buf, 4);
88 pb->len = htonl(pb->len);
89 pb->remain = pb->len;
90 pb->offset = 0;
91
92 free(pb->buf); /* XXX */
93 pb->buf = emalloc(pb->len);
94 pb->state = PSBUF_GETDATA;
95 goto again;
96
97 } else if (pb->state == PSBUF_GETDATA) {
98 pb->remain = pb->offset;
99 pb->offset = 0;
100
101 pb->state = PSBUF_GETREADY;
102
103 /* sloppy */
104 if (!psbuf_get_1(pb, &pb->type))
105 errx(1, "invalid server response, no type");
106 if (!psbuf_get_4(pb, &pb->reqid))
107 errx(1, "invalid server response, no reqid");
108
109 return 1;
110 }
111
112 return -1; /* XXX: impossible */
113 }
114
115 int
116 psbuf_write(struct psshfs_ctx *pctx, struct psbuf *pb)
117 {
118 ssize_t n;
119
120 if (pb->state == PSBUF_PUT) {
121 uint32_t len;
122
123 len = htonl(pb->offset - sizeof(len));
124 memcpy(pb->buf, &len, sizeof(len));
125
126 pb->remain = pb->offset;
127 pb->offset = 0;
128
129 pb->state = PSBUF_PUTDONE;
130 }
131
132 assert(pb->state == PSBUF_PUTDONE);
133
134 n = write(pctx->sshfd, pb->buf + pb->offset, pb->remain);
135 if (n == 0) {
136 errno = EIO;
137 return -1;
138 }
139
140 if (n == -1) {
141 if (errno == EAGAIN)
142 return 0;
143 else
144 return -1;
145 }
146
147 pb->offset += n;
148 pb->remain -= n;
149
150 if (pb->remain == 0)
151 return 1;
152 else
153 return 0;
154 }
155
156 struct psbuf *
157 psbuf_make(int incoming)
158 {
159 struct psbuf *pb;
160
161 pb = emalloc(sizeof(struct psbuf));
162 memset(pb, 0, sizeof(struct psbuf));
163 pb->buf = emalloc(PSDEFALLOC);
164 pb->len = PSDEFALLOC;
165
166 psbuf_recycle(pb, incoming);
167
168 return pb;
169 }
170
171 void
172 psbuf_destroy(struct psbuf *pb)
173 {
174
175 free(pb->buf);
176 free(pb);
177 }
178
179 void
180 psbuf_recycle(struct psbuf *pb, int incoming)
181 {
182
183 if (incoming) {
184 pb->offset = 0;
185 pb->remain = 4;
186 pb->state = PSBUF_GETLEN;
187 } else {
188 /* save space for len */
189 pb->remain = pb->len - 4;
190 pb->offset = 4;
191
192 pb->state = PSBUF_PUT;
193 }
194 }
195
196 static void
197 psbuf_putspace(struct psbuf *pb, size_t space)
198 {
199 size_t morespace;
200 uint8_t *nb;
201
202 if (pb->remain >= space)
203 return;
204
205 for (morespace = PSDEFALLOC; morespace < space; morespace += PSDEFALLOC)
206 if (morespace > PSBUFMAX)
207 err(1, "too much memory");
208
209 nb = erealloc(pb->buf, pb->len + morespace);
210 pb->len += morespace;
211 pb->remain += morespace;
212 pb->buf = nb;
213 }
214
215 int
216 psbuf_put_1(struct psbuf *pb, uint8_t val)
217 {
218
219 assert(pb->state == PSBUF_PUT);
220
221 psbuf_putspace(pb, 1);
222 memcpy(pb->buf + pb->offset, &val, 1);
223 pb->offset += 1;
224 pb->remain -= 1;
225
226 return 1;
227 }
228
229 int
230 psbuf_put_2(struct psbuf *pb, uint16_t val)
231 {
232
233 assert(pb->state == PSBUF_PUT);
234
235 psbuf_putspace(pb, 2);
236 val = htons(val);
237 memcpy(pb->buf + pb->offset, &val, 2);
238 pb->offset += 2;
239 pb->remain -= 2;
240
241 return 1;
242 }
243
244 int
245 psbuf_put_4(struct psbuf *pb, uint32_t val)
246 {
247
248 assert(pb->state == PSBUF_PUT);
249
250 psbuf_putspace(pb, 4);
251 val = htonl(val);
252 memcpy(pb->buf + pb->offset, &val, 4);
253 pb->offset += 4;
254 pb->remain -= 4;
255
256 return 1;
257 }
258
259 int
260 psbuf_put_8(struct psbuf *pb, uint64_t val)
261 {
262
263 assert(pb->state == PSBUF_PUT);
264
265 psbuf_putspace(pb, 8);
266 #if BYTE_ORDER == LITTLE_ENDIAN
267 val = bswap64(val);
268 #endif
269 memcpy(pb->buf + pb->offset, &val, 8);
270 pb->offset += 8;
271 pb->remain -= 8;
272
273 return 1;
274 }
275
276 int
277 psbuf_put_data(struct psbuf *pb, const void *data, uint32_t dlen)
278 {
279
280 assert(pb->state == PSBUF_PUT);
281
282 psbuf_put_4(pb, dlen);
283
284 psbuf_putspace(pb, dlen);
285 memcpy(pb->buf + pb->offset, data, dlen);
286 pb->offset += dlen;
287 pb->remain -= dlen;
288
289 return 1;
290 }
291
292 int
293 psbuf_put_str(struct psbuf *pb, const char *str)
294 {
295
296 return psbuf_put_data(pb, str, strlen(str));
297 }
298
299 int
300 psbuf_put_vattr(struct psbuf *pb, const struct vattr *va)
301 {
302 uint32_t flags;
303 flags = 0;
304
305 if (va->va_size != PUFFS_VNOVAL)
306 flags |= SSH_FILEXFER_ATTR_SIZE;
307 if (va->va_uid != PUFFS_VNOVAL)
308 flags |= SSH_FILEXFER_ATTR_UIDGID;
309 if (va->va_mode != PUFFS_VNOVAL)
310 flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
311
312 if (va->va_atime.tv_sec != PUFFS_VNOVAL)
313 flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
314
315 psbuf_put_4(pb, flags);
316 if (flags & SSH_FILEXFER_ATTR_SIZE)
317 psbuf_put_8(pb, va->va_size);
318 if (flags & SSH_FILEXFER_ATTR_UIDGID) {
319 psbuf_put_4(pb, va->va_uid);
320 psbuf_put_4(pb, va->va_gid);
321 }
322 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS)
323 psbuf_put_4(pb, va->va_mode);
324
325 /* XXX: this is totally wrong for protocol v3, see OpenSSH */
326 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
327 psbuf_put_4(pb, va->va_atime.tv_sec);
328 psbuf_put_4(pb, va->va_mtime.tv_sec);
329 }
330
331 return 1;
332 }
333
334
335 int
336 psbuf_get_1(struct psbuf *pb, uint8_t *val)
337 {
338
339 assert(pb->state == PSBUF_GETREADY);
340
341 if (pb->remain < 1)
342 return 0;
343
344 memcpy(val, pb->buf + pb->offset, 1);
345 pb->offset += 1;
346 pb->remain -= 1;
347
348 return 1;
349 }
350
351 int
352 psbuf_get_2(struct psbuf *pb, uint16_t *val)
353 {
354 uint16_t v;
355
356 assert(pb->state == PSBUF_GETREADY);
357
358 if (pb->remain < 2)
359 return 0;
360
361 memcpy(&v, pb->buf + pb->offset, 2);
362 pb->offset += 2;
363 pb->remain -= 2;
364
365 *val = ntohs(v);
366
367 return 1;
368 }
369
370 int
371 psbuf_get_4(struct psbuf *pb, uint32_t *val)
372 {
373 uint32_t v;
374
375 assert(pb->state == PSBUF_GETREADY);
376
377 if (pb->remain < 4)
378 return 0;
379
380 memcpy(&v, pb->buf + pb->offset, 4);
381 pb->offset += 4;
382 pb->remain -= 4;
383
384 *val = ntohl(v);
385
386 return 1;
387 }
388
389 int
390 psbuf_get_8(struct psbuf *pb, uint64_t *val)
391 {
392 uint64_t v;
393
394 assert(pb->state == PSBUF_GETREADY);
395
396 if (pb->remain < 8)
397 return 0;
398
399 memcpy(&v, pb->buf + pb->offset, 8);
400 pb->offset += 8;
401 pb->remain -= 8;
402
403 #if BYTE_ORDER == LITTLE_ENDIAN
404 v = bswap64(v);
405 #endif
406 *val = v;
407
408 return 1;
409
410 }
411
412 int
413 psbuf_get_str(struct psbuf *pb, char **strp, uint32_t *strlenp)
414 {
415 char *str;
416 uint32_t len;
417
418 assert(pb->state == PSBUF_GETREADY);
419
420 FAILRV(psbuf_get_4(pb, &len), 0);
421
422 if (pb->remain < len)
423 return 0;
424
425 str = emalloc(len+1);
426 memcpy(str, pb->buf + pb->offset, len);
427 str[len] = '\0';
428 *strp = str;
429
430 pb->offset += len;
431 pb->remain -= len;
432
433 if (strlenp)
434 *strlenp = len;
435
436 return 1;
437 }
438
439 int
440 psbuf_get_vattr(struct psbuf *pb, struct vattr *vap)
441 {
442 uint32_t flags;
443 uint32_t val;
444
445 puffs_vattr_null(vap);
446
447 FAILRV(psbuf_get_4(pb, &flags), 0);
448
449 if (flags & SSH_FILEXFER_ATTR_SIZE) {
450 FAILRV(psbuf_get_8(pb, &vap->va_size), 0);
451 vap->va_bytes = vap->va_size;
452 }
453 if (flags & SSH_FILEXFER_ATTR_UIDGID) {
454 FAILRV(psbuf_get_4(pb, &vap->va_uid), 0);
455 FAILRV(psbuf_get_4(pb, &vap->va_gid), 0);
456 }
457 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
458 FAILRV(psbuf_get_4(pb, &vap->va_mode), 0);
459 vap->va_type = puffs_mode2vt(vap->va_mode);
460 }
461 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
462 /*
463 * XXX: this is utterly wrong if we want to speak
464 * protocol version 3, but it seems like the
465 * "internet standard" for doing this
466 */
467 FAILRV(psbuf_get_4(pb, &val), 0);
468 vap->va_atime.tv_sec = val;
469 FAILRV(psbuf_get_4(pb, &val), 0);
470 vap->va_mtime.tv_sec = val;
471 /* make ctime the same as mtime */
472 vap->va_ctime.tv_sec = val;
473
474 vap->va_atime.tv_nsec = 0;
475 vap->va_ctime.tv_nsec = 0;
476 vap->va_mtime.tv_nsec = 0;
477 }
478
479 return 1;
480 }
481
482 /*
483 * Buffer content helpers. Caller frees all data.
484 */
485
486 /*
487 * error mapping.. most are not expected for a file system, but
488 * should help with diagnosing a possible error
489 */
490 static int emap[] = {
491 0, /* OK */
492 0, /* EOF */
493 ENOENT, /* NO_SUCH_FILE */
494 EPERM, /* PERMISSION_DENIED */
495 EIO, /* FAILURE */
496 EBADMSG, /* BAD_MESSAGE */
497 ENOTCONN, /* NO_CONNECTION */
498 ECONNRESET, /* CONNECTION_LOST */
499 EOPNOTSUPP, /* OP_UNSUPPORTED */
500 EINVAL, /* INVALID_HANDLE */
501 ENXIO, /* NO_SUCH_PATH */
502 EEXIST, /* FILE_ALREADY_EXISTS */
503 ENODEV /* WRITE_PROTECT */
504 };
505 #define NERRORS (sizeof(emap) / sizeof(emap[0]))
506
507 static int
508 sftperr_to_errno(int error)
509 {
510
511 if (!error)
512 return 0;
513
514 if (error >= NERRORS || error < 0)
515 return EPROTO;
516
517 return emap[error];
518 }
519
520 #define INVALRESPONSE EPROTO
521
522 static int
523 expectcode(struct psbuf *pb, int value)
524 {
525 uint32_t error;
526
527 if (pb->type == value)
528 return 0;
529
530 if (pb->type != SSH_FXP_STATUS)
531 return INVALRESPONSE;
532
533 FAILRV(psbuf_get_4(pb, &error), INVALRESPONSE);
534
535 return sftperr_to_errno(error);
536 }
537
538 #define CHECKCODE(pb,val) \
539 do { \
540 int rv; \
541 rv = expectcode(pb, val); \
542 if (rv) \
543 return rv; \
544 } while (/*CONSTCOND*/0)
545
546 int
547 psbuf_expect_status(struct psbuf *pb)
548 {
549 uint32_t error;
550
551 if (pb->type != SSH_FXP_STATUS)
552 return INVALRESPONSE;
553
554 FAILRV(psbuf_get_4(pb, &error), INVALRESPONSE);
555
556 return sftperr_to_errno(error);
557 }
558
559 int
560 psbuf_expect_handle(struct psbuf *pb, char **hand, size_t *handlen)
561 {
562
563 CHECKCODE(pb, SSH_FXP_HANDLE);
564 FAILRV(psbuf_get_str(pb, hand, handlen), INVALRESPONSE);
565
566 return 0;
567 }
568
569 /* no memory allocation, direct copy */
570 int
571 psbuf_do_data(struct psbuf *pb, uint8_t *data, uint32_t *dlen)
572 {
573 uint32_t len;
574
575 if (pb->type != SSH_FXP_DATA) {
576 uint32_t val;
577
578 if (pb->type != SSH_FXP_STATUS)
579 return INVALRESPONSE;
580
581 if (!psbuf_get_4(pb, &val))
582 return INVALRESPONSE;
583
584 if (val != SSH_FX_EOF)
585 return sftperr_to_errno(val);
586
587 *dlen = 0;
588 return 0;
589 }
590 if (!psbuf_get_4(pb, &len))
591 return INVALRESPONSE;
592
593 if (*dlen < len)
594 return EINVAL;
595
596 memcpy(data, pb->buf + pb->offset, len);
597
598 pb->offset += len;
599 pb->remain -= len;
600
601 *dlen = len;
602
603 return 0;
604 }
605
606 int
607 psbuf_expect_name(struct psbuf *pb, uint32_t *count)
608 {
609
610 CHECKCODE(pb, SSH_FXP_NAME);
611 FAILRV(psbuf_get_4(pb, count), INVALRESPONSE);
612
613 return 0;
614 }
615
616 int
617 psbuf_expect_attrs(struct psbuf *pb, struct vattr *vap)
618 {
619
620 CHECKCODE(pb, SSH_FXP_ATTRS);
621 FAILRV(psbuf_get_vattr(pb, vap), INVALRESPONSE);
622
623 return 0;
624 }
625
626 /*
627 * More helpers: larger-scale put functions
628 */
629
630 int
631 psbuf_req_data(struct psbuf *pb, int type, uint32_t reqid, const void *data,
632 size_t dlen)
633 {
634
635 psbuf_put_1(pb, type);
636 psbuf_put_4(pb, reqid);
637 psbuf_put_data(pb, data, dlen);
638
639 return 1;
640 }
641
642 int
643 psbuf_req_str(struct psbuf *pb, int type, uint32_t reqid, const char *str)
644 {
645
646 return psbuf_req_data(pb, type, reqid, str, strlen(str));
647 }
648