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