1 /* $NetBSD: psbuf.c,v 1.18 2010/01/08 10:53:31 pooka Exp $ */
4 * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
29 * buffering functions for network input/output. slightly different
30 * from the average joe buffer routines, as is usually the case ...
31 * these use efuns for now.
34 #include <sys/types.h>
35 #include <sys/endian.h>
37 #include <sys/socket.h>
38 #include <sys/vnode.h>
46 #include "sftp_proto.h"
47 #include "util_compat.h"
49 #define FAILRV(x) do { int rv; if ((rv=x)) return (rv); } while (/*CONSTCOND*/0)
50 #define READSTATE_LENGTH(off) (off < 4)
53 #define SFTP_TYPEOFF 4
54 #define SFTP_REQIDOFF 5
56 #define CHECK(v) if (!(v)) abort()
58 #define HTOBE16(x) (x) = htobe16((uint16_t)(x))
59 #define HTOBE32(x) (x) = htobe32((uint32_t)(x))
60 #define HTOBE64(x) (x) = htobe64((uint64_t)(x))
61 #define BE16TOH(x) (x) = be16toh((uint16_t)(x))
62 #define BE32TOH(x) (x) = be32toh((uint32_t)(x))
63 #define BE64TOH(x) (x) = be64toh((uint64_t)(x))
66 psbuf_get_type(struct puffs_framebuf *pb)
70 puffs_framebuf_getdata_atoff(pb, SFTP_TYPEOFF, &type, 1);
75 psbuf_get_len(struct puffs_framebuf *pb)
79 puffs_framebuf_getdata_atoff(pb, SFTP_LENOFF, &len, 4);
84 psbuf_get_reqid(struct puffs_framebuf *pb)
88 puffs_framebuf_getdata_atoff(pb, SFTP_REQIDOFF, &req, 4);
92 #define CUROFF(pb) (puffs_framebuf_telloff(pb))
94 psbuf_read(struct puffs_usermount *pu, struct puffs_framebuf *pb,
99 size_t howmuch, winlen;
103 if ((lenstate = READSTATE_LENGTH(CUROFF(pb))))
104 howmuch = 4 - CUROFF(pb);
106 howmuch = psbuf_get_len(pb) - (CUROFF(pb) - 4);
108 if (puffs_framebuf_reserve_space(pb, howmuch) == -1)
113 if (puffs_framebuf_getwindow(pb, CUROFF(pb), &win, &winlen)==-1)
115 n = recv(fd, win, winlen, MSG_NOSIGNAL);
125 puffs_framebuf_seekset(pb, CUROFF(pb) + n);
131 /* XXX: initial exchange shorter.. but don't worry, be happy */
132 puffs_framebuf_seekset(pb, 9);
140 psbuf_write(struct puffs_usermount *pu, struct puffs_framebuf *pb,
145 size_t winlen, howmuch;
147 /* finalize buffer.. could be elsewhere ... */
148 if (CUROFF(pb) == 0) {
151 len = htobe32(puffs_framebuf_tellsize(pb) - 4);
152 puffs_framebuf_putdata_atoff(pb, 0, &len, 4);
155 howmuch = puffs_framebuf_tellsize(pb) - CUROFF(pb);
158 if (puffs_framebuf_getwindow(pb, CUROFF(pb), &win, &winlen)==-1)
160 n = send(fd, win, winlen, MSG_NOSIGNAL);
170 puffs_framebuf_seekset(pb, CUROFF(pb) + n);
181 psbuf_cmp(struct puffs_usermount *pu,
182 struct puffs_framebuf *cmp1, struct puffs_framebuf *cmp2, int *notresp)
185 return psbuf_get_reqid(cmp1) != psbuf_get_reqid(cmp2);
188 struct puffs_framebuf *
191 struct puffs_framebuf *pb;
193 pb = puffs_framebuf_make();
194 puffs_framebuf_seekset(pb, 4);
199 psbuf_recycleout(struct puffs_framebuf *pb)
202 puffs_framebuf_recycle(pb);
203 puffs_framebuf_seekset(pb, 4);
207 psbuf_put_1(struct puffs_framebuf *pb, uint8_t val)
211 rv = puffs_framebuf_putdata(pb, &val, 1);
216 psbuf_put_2(struct puffs_framebuf *pb, uint16_t val)
221 rv = puffs_framebuf_putdata(pb, &val, 2);
226 psbuf_put_4(struct puffs_framebuf *pb, uint32_t val)
231 rv = puffs_framebuf_putdata(pb, &val, 4);
236 psbuf_put_8(struct puffs_framebuf *pb, uint64_t val)
241 rv = puffs_framebuf_putdata(pb, &val, 8);
246 psbuf_put_data(struct puffs_framebuf *pb, const void *data, uint32_t dlen)
250 psbuf_put_4(pb, dlen);
251 rv = puffs_framebuf_putdata(pb, data, dlen);
256 psbuf_put_str(struct puffs_framebuf *pb, const char *str)
259 psbuf_put_data(pb, str, strlen(str));
263 psbuf_put_vattr(struct puffs_framebuf *pb, const struct vattr *va,
264 const struct psshfs_ctx *pctx)
267 uint32_t theuid = -1, thegid = -1;
270 if (va->va_size != (uint64_t)PUFFS_VNOVAL)
271 flags |= SSH_FILEXFER_ATTR_SIZE;
272 if (va->va_uid != (uid_t)PUFFS_VNOVAL) {
274 if (pctx->domangleuid && theuid == pctx->myuid)
275 theuid = pctx->mangleuid;
276 flags |= SSH_FILEXFER_ATTR_UIDGID;
278 if (va->va_gid != (gid_t)PUFFS_VNOVAL) {
280 if (pctx->domanglegid && thegid == pctx->mygid)
281 thegid = pctx->manglegid;
282 flags |= SSH_FILEXFER_ATTR_UIDGID;
284 if (va->va_mode != (mode_t)PUFFS_VNOVAL)
285 flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
287 if (va->va_atime.tv_sec != PUFFS_VNOVAL)
288 flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
290 psbuf_put_4(pb, flags);
291 if (flags & SSH_FILEXFER_ATTR_SIZE)
292 psbuf_put_8(pb, va->va_size);
293 if (flags & SSH_FILEXFER_ATTR_UIDGID) {
294 psbuf_put_4(pb, theuid);
295 psbuf_put_4(pb, thegid);
297 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS)
298 psbuf_put_4(pb, va->va_mode);
300 /* XXX: this is totally wrong for protocol v3, see OpenSSH */
301 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
302 psbuf_put_4(pb, va->va_atime.tv_sec);
303 psbuf_put_4(pb, va->va_mtime.tv_sec);
307 #define ERETURN(rv) return ((rv) == -1 ? errno : 0)
310 psbuf_get_1(struct puffs_framebuf *pb, uint8_t *val)
313 ERETURN(puffs_framebuf_getdata(pb, val, 1));
317 psbuf_get_2(struct puffs_framebuf *pb, uint16_t *val)
321 rv = puffs_framebuf_getdata(pb, val, 2);
328 psbuf_get_4(struct puffs_framebuf *pb, uint32_t *val)
332 rv = puffs_framebuf_getdata(pb, val, 4);
339 psbuf_get_8(struct puffs_framebuf *pb, uint64_t *val)
343 rv = puffs_framebuf_getdata(pb, val, 8);
350 psbuf_get_str(struct puffs_framebuf *pb, char **strp, uint32_t *strlenp)
355 FAILRV(psbuf_get_4(pb, &len));
357 if (puffs_framebuf_remaining(pb) < len)
360 str = emalloc(len+1);
361 puffs_framebuf_getdata(pb, str, len);
372 psbuf_get_vattr(struct puffs_framebuf *pb, struct vattr *vap)
378 puffs_vattr_null(vap);
380 FAILRV(psbuf_get_4(pb, &flags));
382 if (flags & SSH_FILEXFER_ATTR_SIZE) {
383 FAILRV(psbuf_get_8(pb, &vap->va_size));
384 vap->va_bytes = vap->va_size;
386 if (flags & SSH_FILEXFER_ATTR_UIDGID) {
387 FAILRV(psbuf_get_4(pb, &vap->va_uid));
388 FAILRV(psbuf_get_4(pb, &vap->va_gid));
390 if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
391 FAILRV(psbuf_get_4(pb, &tmpval));
392 vap->va_mode = tmpval;
393 vap->va_type = puffs_mode2vt(vap->va_mode);
395 if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
397 * XXX: this is utterly wrong if we want to speak
398 * protocol version 3, but it seems like the
399 * "internet standard" for doing this
401 FAILRV(psbuf_get_4(pb, &val));
402 vap->va_atime.tv_sec = val;
403 FAILRV(psbuf_get_4(pb, &val));
404 vap->va_mtime.tv_sec = val;
405 /* make ctime the same as mtime */
406 vap->va_ctime.tv_sec = val;
408 vap->va_atime.tv_nsec = 0;
409 vap->va_ctime.tv_nsec = 0;
410 vap->va_mtime.tv_nsec = 0;
417 * Buffer content helpers. Caller frees all data.
421 * error mapping.. most are not expected for a file system, but
422 * should help with diagnosing a possible error
424 static int emap[] = {
427 ENOENT, /* NO_SUCH_FILE */
428 EPERM, /* PERMISSION_DENIED */
430 EBADMSG, /* BAD_MESSAGE */
431 ENOTCONN, /* NO_CONNECTION */
432 ECONNRESET, /* CONNECTION_LOST */
433 EOPNOTSUPP, /* OP_UNSUPPORTED */
434 EINVAL, /* INVALID_HANDLE */
435 ENXIO, /* NO_SUCH_PATH */
436 EEXIST, /* FILE_ALREADY_EXISTS */
437 ENODEV /* WRITE_PROTECT */
439 #define NERRORS ((int)(sizeof(emap) / sizeof(emap[0])))
442 sftperr_to_errno(int error)
448 if (error >= NERRORS || error < 0)
454 #define INVALRESPONSE EPROTO
457 expectcode(struct puffs_framebuf *pb, int value)
462 type = psbuf_get_type(pb);
466 if (type != SSH_FXP_STATUS)
467 return INVALRESPONSE;
469 FAILRV(psbuf_get_4(pb, &error));
471 return sftperr_to_errno(error);
474 #define CHECKCODE(pb,val) \
477 rv = expectcode(pb, val); \
480 } while (/*CONSTCOND*/0)
483 psbuf_expect_status(struct puffs_framebuf *pb)
487 if (psbuf_get_type(pb) != SSH_FXP_STATUS)
488 return INVALRESPONSE;
490 FAILRV(psbuf_get_4(pb, &error));
492 return sftperr_to_errno(error);
496 psbuf_expect_handle(struct puffs_framebuf *pb, char **hand, uint32_t *handlen)
499 CHECKCODE(pb, SSH_FXP_HANDLE);
500 FAILRV(psbuf_get_str(pb, hand, handlen));
505 /* no memory allocation, direct copy */
507 psbuf_do_data(struct puffs_framebuf *pb, uint8_t *data, uint32_t *dlen)
510 size_t bufoff, winlen;
511 uint32_t len, dataoff;
513 if (psbuf_get_type(pb) != SSH_FXP_DATA) {
516 if (psbuf_get_type(pb) != SSH_FXP_STATUS)
517 return INVALRESPONSE;
519 if (psbuf_get_4(pb, &val) != 0)
520 return INVALRESPONSE;
522 if (val != SSH_FX_EOF)
523 return sftperr_to_errno(val);
528 if (psbuf_get_4(pb, &len) != 0)
529 return INVALRESPONSE;
537 while (dataoff < len) {
538 winlen = len-dataoff;
539 bufoff = puffs_framebuf_telloff(pb);
540 if (puffs_framebuf_getwindow(pb, bufoff,
541 &win, &winlen) == -1)
546 memcpy(data + dataoff, win, winlen);
556 psbuf_expect_name(struct puffs_framebuf *pb, uint32_t *count)
559 CHECKCODE(pb, SSH_FXP_NAME);
560 FAILRV(psbuf_get_4(pb, count));
566 psbuf_expect_attrs(struct puffs_framebuf *pb, struct vattr *vap)
569 CHECKCODE(pb, SSH_FXP_ATTRS);
570 FAILRV(psbuf_get_vattr(pb, vap));
576 * More helpers: larger-scale put functions
580 psbuf_req_data(struct puffs_framebuf *pb, int type, uint32_t reqid,
581 const void *data, uint32_t dlen)
584 psbuf_put_1(pb, type);
585 psbuf_put_4(pb, reqid);
586 psbuf_put_data(pb, data, dlen);
590 psbuf_req_str(struct puffs_framebuf *pb, int type, uint32_t reqid,
594 psbuf_req_data(pb, type, reqid, str, strlen(str));