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