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