psbuf.c revision 1.1 1 /* $NetBSD: psbuf.c,v 1.1 2006/12/29 15:35:39 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.1 2006/12/29 15:35:39 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 if (va->va_atime.tv_sec != PUFFS_VNOVAL)
312 flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
313 if (va->va_ctime.tv_sec != PUFFS_VNOVAL)
314 flags |= SSH_FILEXFER_ATTR_CREATETIME;
315 if (va->va_mtime.tv_sec != PUFFS_VNOVAL)
316 flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
317
318 psbuf_put_4(pb, flags);
319 if (flags & SSH_FILEXFER_ATTR_SIZE)
320 psbuf_put_8(pb, va->va_size);
321 if (flags & SSH_FILEXFER_ATTR_UIDGID) {
322 psbuf_put_4(pb, va->va_uid);
323 psbuf_put_4(pb, va->va_gid);
324 }
325 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS)
326 psbuf_put_4(pb, va->va_mode);
327 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME)
328 psbuf_put_4(pb, va->va_atime.tv_sec);
329 if (flags & SSH_FILEXFER_ATTR_CREATETIME)
330 psbuf_put_4(pb, va->va_ctime.tv_sec);
331 if (flags & SSH_FILEXFER_ATTR_MODIFYTIME)
332 psbuf_put_4(pb, va->va_mtime.tv_sec);
333
334 return 1;
335 }
336
337
338 int
339 psbuf_get_1(struct psbuf *pb, uint8_t *val)
340 {
341
342 assert(pb->state == PSBUF_GETREADY);
343
344 if (pb->remain < 1)
345 return 0;
346
347 memcpy(val, pb->buf + pb->offset, 1);
348 pb->offset += 1;
349 pb->remain -= 1;
350
351 return 1;
352 }
353
354 int
355 psbuf_get_2(struct psbuf *pb, uint16_t *val)
356 {
357 uint16_t v;
358
359 assert(pb->state == PSBUF_GETREADY);
360
361 if (pb->remain < 2)
362 return 0;
363
364 memcpy(&v, pb->buf + pb->offset, 2);
365 pb->offset += 2;
366 pb->remain -= 2;
367
368 *val = ntohs(v);
369
370 return 1;
371 }
372
373 int
374 psbuf_get_4(struct psbuf *pb, uint32_t *val)
375 {
376 uint32_t v;
377
378 assert(pb->state == PSBUF_GETREADY);
379
380 if (pb->remain < 4)
381 return 0;
382
383 memcpy(&v, pb->buf + pb->offset, 4);
384 pb->offset += 4;
385 pb->remain -= 4;
386
387 *val = ntohl(v);
388
389 return 1;
390 }
391
392 int
393 psbuf_get_8(struct psbuf *pb, uint64_t *val)
394 {
395 uint64_t v;
396
397 assert(pb->state == PSBUF_GETREADY);
398
399 if (pb->remain < 8)
400 return 0;
401
402 memcpy(&v, pb->buf + pb->offset, 8);
403 pb->offset += 8;
404 pb->remain -= 8;
405
406 #if BYTE_ORDER == LITTLE_ENDIAN
407 v = bswap64(v);
408 #endif
409 *val = v;
410
411 return 1;
412
413 }
414
415 int
416 psbuf_get_str(struct psbuf *pb, char **strp, uint32_t *strlenp)
417 {
418 char *str;
419 uint32_t len;
420
421 assert(pb->state == PSBUF_GETREADY);
422
423 FAILRV(psbuf_get_4(pb, &len), 0);
424
425 if (pb->remain < len)
426 return 0;
427
428 str = emalloc(len+1);
429 memcpy(str, pb->buf + pb->offset, len);
430 str[len] = '\0';
431 *strp = str;
432
433 pb->offset += len;
434 pb->remain -= len;
435
436 if (strlenp)
437 *strlenp = len;
438
439 return 1;
440 }
441
442 int
443 psbuf_get_vattr(struct psbuf *pb, struct vattr *vap)
444 {
445 uint32_t flags;
446 uint32_t val;
447
448 puffs_vattr_null(vap);
449
450 FAILRV(psbuf_get_4(pb, &flags), 0);
451
452 if (flags & SSH_FILEXFER_ATTR_SIZE) {
453 FAILRV(psbuf_get_8(pb, &vap->va_size), 0);
454 vap->va_bytes = vap->va_size;
455 }
456 if (flags & SSH_FILEXFER_ATTR_UIDGID) {
457 FAILRV(psbuf_get_4(pb, &vap->va_uid), 0);
458 FAILRV(psbuf_get_4(pb, &vap->va_gid), 0);
459 }
460 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
461 FAILRV(psbuf_get_4(pb, &vap->va_mode), 0);
462 vap->va_type = puffs_mode2vt(vap->va_mode);
463 }
464 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
465 /*
466 * XXX: this is utterly wrong if we want to speak
467 * protocol version 3, but it seems like the
468 * "internet standard" for doing this
469 */
470 FAILRV(psbuf_get_4(pb, &val), 0);
471 vap->va_atime.tv_sec = val;
472 FAILRV(psbuf_get_4(pb, &val), 0);
473 vap->va_mtime.tv_sec = val;
474 /* make ctime the same as mtime */
475 vap->va_ctime.tv_sec = val;
476
477 vap->va_atime.tv_nsec = 0;
478 vap->va_ctime.tv_nsec = 0;
479 vap->va_mtime.tv_nsec = 0;
480 }
481
482 return 1;
483 }
484
485 /*
486 * Buffer content helpers. Caller frees all data.
487 */
488
489 /*
490 * error mapping.. most are not expected for a file system, but
491 * should help with diagnosing a possible error
492 */
493 static int emap[] = {
494 0, /* OK */
495 0, /* EOF */
496 ENOENT, /* NO_SUCH_FILE */
497 EPERM, /* PERMISSION_DENIED */
498 EIO, /* FAILURE */
499 EBADMSG, /* BAD_MESSAGE */
500 ENOTCONN, /* NO_CONNECTION */
501 ECONNRESET, /* CONNECTION_LOST */
502 EOPNOTSUPP, /* OP_UNSUPPORTED */
503 EINVAL, /* INVALID_HANDLE */
504 ENXIO, /* NO_SUCH_PATH */
505 EEXIST, /* FILE_ALREADY_EXISTS */
506 ENODEV /* WRITE_PROTECT */
507 };
508 #define NERRORS (sizeof(emap) / sizeof(emap[0]))
509
510 static int
511 sftperr_to_errno(int error)
512 {
513
514 if (!error)
515 return 0;
516
517 if (error >= NERRORS || error < 0)
518 return EPROTO;
519
520 return emap[error];
521 }
522
523 #define INVALRESPONSE EPROTO
524
525 static int
526 expectcode(struct psbuf *pb, int value)
527 {
528 uint32_t error;
529
530 if (pb->type == value)
531 return 0;
532
533 if (pb->type != SSH_FXP_STATUS)
534 return INVALRESPONSE;
535
536 FAILRV(psbuf_get_4(pb, &error), INVALRESPONSE);
537
538 return sftperr_to_errno(error);
539 }
540
541 #define CHECKCODE(pb,val) \
542 do { \
543 int rv; \
544 rv = expectcode(pb, val); \
545 if (rv) \
546 return rv; \
547 } while (/*CONSTCOND*/0)
548
549 int
550 psbuf_expect_status(struct psbuf *pb)
551 {
552 uint32_t error;
553
554 if (pb->type != SSH_FXP_STATUS)
555 return INVALRESPONSE;
556
557 FAILRV(psbuf_get_4(pb, &error), INVALRESPONSE);
558
559 return sftperr_to_errno(error);
560 }
561
562 int
563 psbuf_expect_handle(struct psbuf *pb, char **hand, size_t *handlen)
564 {
565
566 CHECKCODE(pb, SSH_FXP_HANDLE);
567 FAILRV(psbuf_get_str(pb, hand, handlen), INVALRESPONSE);
568
569 return 0;
570 }
571
572 /* no memory allocation, direct copy */
573 int
574 psbuf_do_data(struct psbuf *pb, uint8_t *data, uint32_t *dlen)
575 {
576 uint32_t len;
577
578 if (pb->type != SSH_FXP_DATA) {
579 uint32_t val;
580
581 if (pb->type != SSH_FXP_STATUS)
582 return INVALRESPONSE;
583
584 if (!psbuf_get_4(pb, &val))
585 return INVALRESPONSE;
586
587 if (val != SSH_FX_EOF)
588 return sftperr_to_errno(val);
589
590 *dlen = 0;
591 return 0;
592 }
593 if (!psbuf_get_4(pb, &len))
594 return INVALRESPONSE;
595
596 if (*dlen < len)
597 return EINVAL;
598
599 memcpy(data, pb->buf + pb->offset, len);
600
601 pb->offset += len;
602 pb->remain -= len;
603
604 *dlen = len;
605
606 return 0;
607 }
608
609 int
610 psbuf_expect_name(struct psbuf *pb, uint32_t *count)
611 {
612
613 CHECKCODE(pb, SSH_FXP_NAME);
614 FAILRV(psbuf_get_4(pb, count), INVALRESPONSE);
615
616 return 0;
617 }
618
619 int
620 psbuf_expect_attrs(struct psbuf *pb, struct vattr *vap)
621 {
622
623 CHECKCODE(pb, SSH_FXP_ATTRS);
624 FAILRV(psbuf_get_vattr(pb, vap), INVALRESPONSE);
625
626 return 0;
627 }
628
629 /*
630 * More helpers: larger-scale put functions
631 */
632
633 int
634 psbuf_req_data(struct psbuf *pb, int type, uint32_t reqid, const void *data,
635 size_t dlen)
636 {
637
638 psbuf_put_1(pb, type);
639 psbuf_put_4(pb, reqid);
640 psbuf_put_data(pb, data, dlen);
641
642 return 1;
643 }
644
645 int
646 psbuf_req_str(struct psbuf *pb, int type, uint32_t reqid, const char *str)
647 {
648
649 return psbuf_req_data(pb, type, reqid, str, strlen(str));
650 }
651