xref: /openssh-portable/sftp-server.c (revision e7d0a285)
131d8d231Sdjm@openbsd.org /* $OpenBSD: sftp-server.c,v 1.127 2021/04/03 06:18:41 djm Exp $ */
27b28dc5eSDamien Miller /*
337bd3663SDarren Tucker  * Copyright (c) 2000-2004 Markus Friedl.  All rights reserved.
47b28dc5eSDamien Miller  *
537bd3663SDarren Tucker  * Permission to use, copy, modify, and distribute this software for any
637bd3663SDarren Tucker  * purpose with or without fee is hereby granted, provided that the above
737bd3663SDarren Tucker  * copyright notice and this permission notice appear in all copies.
87b28dc5eSDamien Miller  *
937bd3663SDarren Tucker  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1037bd3663SDarren Tucker  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1137bd3663SDarren Tucker  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1237bd3663SDarren Tucker  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1337bd3663SDarren Tucker  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1437bd3663SDarren Tucker  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1537bd3663SDarren Tucker  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167b28dc5eSDamien Miller  */
17d7834353SDamien Miller 
187b28dc5eSDamien Miller #include "includes.h"
19f17883e6SDamien Miller 
20f17883e6SDamien Miller #include <sys/types.h>
21f17883e6SDamien Miller #include <sys/stat.h>
22788cbc5bSdjm@openbsd.org #include <sys/resource.h>
239aec9194SDamien Miller #ifdef HAVE_SYS_TIME_H
249aec9194SDamien Miller # include <sys/time.h>
259aec9194SDamien Miller #endif
265b2e2ba9SDarren Tucker #ifdef HAVE_SYS_MOUNT_H
27d671e5a9SDamien Miller #include <sys/mount.h>
285b2e2ba9SDarren Tucker #endif
295b2e2ba9SDarren Tucker #ifdef HAVE_SYS_STATVFS_H
30d671e5a9SDamien Miller #include <sys/statvfs.h>
315b2e2ba9SDarren Tucker #endif
3288f254b9SDamien Miller 
3388f254b9SDamien Miller #include <dirent.h>
3439972493SDarren Tucker #include <errno.h>
3557cf6385SDamien Miller #include <fcntl.h>
369f2abc47SDamien Miller #include <pwd.h>
37e7a1e5cfSDamien Miller #include <stdlib.h>
38a7a73ee3SDamien Miller #include <stdio.h>
39e3476ed0SDamien Miller #include <string.h>
405598b4f1SDamien Miller #include <time.h>
41e3476ed0SDamien Miller #include <unistd.h>
42d7834353SDamien Miller #include <stdarg.h>
437b28dc5eSDamien Miller 
447b28dc5eSDamien Miller #include "xmalloc.h"
457d845f4aSdjm@openbsd.org #include "sshbuf.h"
467d845f4aSdjm@openbsd.org #include "ssherr.h"
47d7834353SDamien Miller #include "log.h"
48ce321d8aSDarren Tucker #include "misc.h"
496eaeebf2SDamien Miller #include "match.h"
50fef95ad8SDamien Miller #include "uidswap.h"
517b28dc5eSDamien Miller 
522f959b4cSBen Lindstrom #include "sftp.h"
5333804263SDamien Miller #include "sftp-common.h"
547b28dc5eSDamien Miller 
55569b650fSdjm@openbsd.org char *sftp_realpath(const char *, char *); /* sftp-realpath.c */
56569b650fSdjm@openbsd.org 
57788cbc5bSdjm@openbsd.org /* Maximum data read that we are willing to accept */
581269b8a6Sdjm@openbsd.org #define SFTP_MAX_READ_LENGTH (SFTP_MAX_MSG_LENGTH - 1024)
59788cbc5bSdjm@openbsd.org 
60fef95ad8SDamien Miller /* Our verbosity */
616eaeebf2SDamien Miller static LogLevel log_level = SYSLOG_LEVEL_ERROR;
62fef95ad8SDamien Miller 
63fef95ad8SDamien Miller /* Our client */
646eaeebf2SDamien Miller static struct passwd *pw = NULL;
656eaeebf2SDamien Miller static char *client_addr = NULL;
6649a79c09SBen Lindstrom 
677b28dc5eSDamien Miller /* input and output queue */
687d845f4aSdjm@openbsd.org struct sshbuf *iqueue;
697d845f4aSdjm@openbsd.org struct sshbuf *oqueue;
707b28dc5eSDamien Miller 
71058316f0SDamien Miller /* Version of client */
726eaeebf2SDamien Miller static u_int version;
736eaeebf2SDamien Miller 
746eaeebf2SDamien Miller /* SSH2_FXP_INIT received */
756eaeebf2SDamien Miller static int init_done;
76058316f0SDamien Miller 
77db7bf825SDarren Tucker /* Disable writes */
786eaeebf2SDamien Miller static int readonly;
796eaeebf2SDamien Miller 
806eaeebf2SDamien Miller /* Requests that are allowed/denied */
81fc270bafSdjm@openbsd.org static char *request_allowlist, *request_denylist;
82db7bf825SDarren Tucker 
83a6612d43SDarren Tucker /* portable attributes, etc. */
847b28dc5eSDamien Miller typedef struct Stat Stat;
857b28dc5eSDamien Miller 
8633804263SDamien Miller struct Stat {
877b28dc5eSDamien Miller 	char *name;
887b28dc5eSDamien Miller 	char *long_name;
897b28dc5eSDamien Miller 	Attrib attrib;
907b28dc5eSDamien Miller };
917b28dc5eSDamien Miller 
926eaeebf2SDamien Miller /* Packet handlers */
936eaeebf2SDamien Miller static void process_open(u_int32_t id);
946eaeebf2SDamien Miller static void process_close(u_int32_t id);
956eaeebf2SDamien Miller static void process_read(u_int32_t id);
966eaeebf2SDamien Miller static void process_write(u_int32_t id);
976eaeebf2SDamien Miller static void process_stat(u_int32_t id);
986eaeebf2SDamien Miller static void process_lstat(u_int32_t id);
996eaeebf2SDamien Miller static void process_fstat(u_int32_t id);
1006eaeebf2SDamien Miller static void process_setstat(u_int32_t id);
1016eaeebf2SDamien Miller static void process_fsetstat(u_int32_t id);
1026eaeebf2SDamien Miller static void process_opendir(u_int32_t id);
1036eaeebf2SDamien Miller static void process_readdir(u_int32_t id);
1046eaeebf2SDamien Miller static void process_remove(u_int32_t id);
1056eaeebf2SDamien Miller static void process_mkdir(u_int32_t id);
1066eaeebf2SDamien Miller static void process_rmdir(u_int32_t id);
1076eaeebf2SDamien Miller static void process_realpath(u_int32_t id);
1086eaeebf2SDamien Miller static void process_rename(u_int32_t id);
1096eaeebf2SDamien Miller static void process_readlink(u_int32_t id);
1106eaeebf2SDamien Miller static void process_symlink(u_int32_t id);
1116eaeebf2SDamien Miller static void process_extended_posix_rename(u_int32_t id);
1126eaeebf2SDamien Miller static void process_extended_statvfs(u_int32_t id);
1136eaeebf2SDamien Miller static void process_extended_fstatvfs(u_int32_t id);
1146eaeebf2SDamien Miller static void process_extended_hardlink(u_int32_t id);
115f29238e6SDamien Miller static void process_extended_fsync(u_int32_t id);
116dbbc7e0eSdjm@openbsd.org static void process_extended_lsetstat(u_int32_t id);
117788cbc5bSdjm@openbsd.org static void process_extended_limits(u_int32_t id);
1186eaeebf2SDamien Miller static void process_extended(u_int32_t id);
1196eaeebf2SDamien Miller 
1206eaeebf2SDamien Miller struct sftp_handler {
1216eaeebf2SDamien Miller 	const char *name;	/* user-visible name for fine-grained perms */
1226eaeebf2SDamien Miller 	const char *ext_name;	/* extended request name */
1236eaeebf2SDamien Miller 	u_int type;		/* packet type, for non extended packets */
1246eaeebf2SDamien Miller 	void (*handler)(u_int32_t);
1256eaeebf2SDamien Miller 	int does_write;		/* if nonzero, banned for readonly mode */
1266eaeebf2SDamien Miller };
1276eaeebf2SDamien Miller 
1285bed70afSdjm@openbsd.org static const struct sftp_handler handlers[] = {
1296eaeebf2SDamien Miller 	/* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */
1306eaeebf2SDamien Miller 	{ "open", NULL, SSH2_FXP_OPEN, process_open, 0 },
1316eaeebf2SDamien Miller 	{ "close", NULL, SSH2_FXP_CLOSE, process_close, 0 },
1326eaeebf2SDamien Miller 	{ "read", NULL, SSH2_FXP_READ, process_read, 0 },
1336eaeebf2SDamien Miller 	{ "write", NULL, SSH2_FXP_WRITE, process_write, 1 },
1346eaeebf2SDamien Miller 	{ "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 },
1356eaeebf2SDamien Miller 	{ "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 },
1366eaeebf2SDamien Miller 	{ "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 },
1376eaeebf2SDamien Miller 	{ "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 },
1386eaeebf2SDamien Miller 	{ "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 },
1396eaeebf2SDamien Miller 	{ "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 },
1406eaeebf2SDamien Miller 	{ "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 },
1416eaeebf2SDamien Miller 	{ "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 },
1426eaeebf2SDamien Miller 	{ "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 },
1436eaeebf2SDamien Miller 	{ "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 },
1446eaeebf2SDamien Miller 	{ "stat", NULL, SSH2_FXP_STAT, process_stat, 0 },
1456eaeebf2SDamien Miller 	{ "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 },
1466eaeebf2SDamien Miller 	{ "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 },
1476eaeebf2SDamien Miller 	{ "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 },
1486eaeebf2SDamien Miller 	{ NULL, NULL, 0, NULL, 0 }
1496eaeebf2SDamien Miller };
1506eaeebf2SDamien Miller 
1516eaeebf2SDamien Miller /* SSH2_FXP_EXTENDED submessages */
1525bed70afSdjm@openbsd.org static const struct sftp_handler extended_handlers[] = {
1536eaeebf2SDamien Miller 	{ "posix-rename", "posix-rename@openssh.com", 0,
1546eaeebf2SDamien Miller 	    process_extended_posix_rename, 1 },
1556eaeebf2SDamien Miller 	{ "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 },
1566eaeebf2SDamien Miller 	{ "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 },
1576eaeebf2SDamien Miller 	{ "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 },
158f29238e6SDamien Miller 	{ "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 },
159dbbc7e0eSdjm@openbsd.org 	{ "lsetstat", "lsetstat@openssh.com", 0, process_extended_lsetstat, 1 },
160788cbc5bSdjm@openbsd.org 	{ "limits", "limits@openssh.com", 0, process_extended_limits, 1 },
1616eaeebf2SDamien Miller 	{ NULL, NULL, 0, NULL, 0 }
1626eaeebf2SDamien Miller };
1636eaeebf2SDamien Miller 
1646653c612Sdjm@openbsd.org static const struct sftp_handler *
extended_handler_byname(const char * name)1656653c612Sdjm@openbsd.org extended_handler_byname(const char *name)
1666653c612Sdjm@openbsd.org {
1676653c612Sdjm@openbsd.org 	int i;
1686653c612Sdjm@openbsd.org 
1696653c612Sdjm@openbsd.org 	for (i = 0; extended_handlers[i].handler != NULL; i++) {
1706653c612Sdjm@openbsd.org 		if (strcmp(name, extended_handlers[i].ext_name) == 0)
1716653c612Sdjm@openbsd.org 			return &extended_handlers[i];
1726653c612Sdjm@openbsd.org 	}
1736653c612Sdjm@openbsd.org 	return NULL;
1746653c612Sdjm@openbsd.org }
1756653c612Sdjm@openbsd.org 
1766eaeebf2SDamien Miller static int
request_permitted(const struct sftp_handler * h)1775bed70afSdjm@openbsd.org request_permitted(const struct sftp_handler *h)
1786eaeebf2SDamien Miller {
1796eaeebf2SDamien Miller 	char *result;
1806eaeebf2SDamien Miller 
1816eaeebf2SDamien Miller 	if (readonly && h->does_write) {
1826eaeebf2SDamien Miller 		verbose("Refusing %s request in read-only mode", h->name);
1836eaeebf2SDamien Miller 		return 0;
1846eaeebf2SDamien Miller 	}
185fc270bafSdjm@openbsd.org 	if (request_denylist != NULL &&
186fc270bafSdjm@openbsd.org 	    ((result = match_list(h->name, request_denylist, NULL))) != NULL) {
1876eaeebf2SDamien Miller 		free(result);
188fc270bafSdjm@openbsd.org 		verbose("Refusing denylisted %s request", h->name);
1896eaeebf2SDamien Miller 		return 0;
1906eaeebf2SDamien Miller 	}
191fc270bafSdjm@openbsd.org 	if (request_allowlist != NULL &&
192fc270bafSdjm@openbsd.org 	    ((result = match_list(h->name, request_allowlist, NULL))) != NULL) {
1936eaeebf2SDamien Miller 		free(result);
194fc270bafSdjm@openbsd.org 		debug2("Permitting allowlisted %s request", h->name);
1956eaeebf2SDamien Miller 		return 1;
1966eaeebf2SDamien Miller 	}
197fc270bafSdjm@openbsd.org 	if (request_allowlist != NULL) {
198fc270bafSdjm@openbsd.org 		verbose("Refusing non-allowlisted %s request", h->name);
1996eaeebf2SDamien Miller 		return 0;
2006eaeebf2SDamien Miller 	}
2016eaeebf2SDamien Miller 	return 1;
2026eaeebf2SDamien Miller }
2036eaeebf2SDamien Miller 
204bba81213SBen Lindstrom static int
errno_to_portable(int unixerrno)2057b28dc5eSDamien Miller errno_to_portable(int unixerrno)
2067b28dc5eSDamien Miller {
2077b28dc5eSDamien Miller 	int ret = 0;
2081addabd4SBen Lindstrom 
2097b28dc5eSDamien Miller 	switch (unixerrno) {
2107b28dc5eSDamien Miller 	case 0:
2112f959b4cSBen Lindstrom 		ret = SSH2_FX_OK;
2127b28dc5eSDamien Miller 		break;
2137b28dc5eSDamien Miller 	case ENOENT:
2147b28dc5eSDamien Miller 	case ENOTDIR:
2157b28dc5eSDamien Miller 	case EBADF:
2167b28dc5eSDamien Miller 	case ELOOP:
2172f959b4cSBen Lindstrom 		ret = SSH2_FX_NO_SUCH_FILE;
2187b28dc5eSDamien Miller 		break;
2197b28dc5eSDamien Miller 	case EPERM:
2207b28dc5eSDamien Miller 	case EACCES:
2217b28dc5eSDamien Miller 	case EFAULT:
2222f959b4cSBen Lindstrom 		ret = SSH2_FX_PERMISSION_DENIED;
2237b28dc5eSDamien Miller 		break;
2247b28dc5eSDamien Miller 	case ENAMETOOLONG:
2257b28dc5eSDamien Miller 	case EINVAL:
2262f959b4cSBen Lindstrom 		ret = SSH2_FX_BAD_MESSAGE;
2277b28dc5eSDamien Miller 		break;
228422c34c9SDarren Tucker 	case ENOSYS:
229422c34c9SDarren Tucker 		ret = SSH2_FX_OP_UNSUPPORTED;
230422c34c9SDarren Tucker 		break;
2317b28dc5eSDamien Miller 	default:
2322f959b4cSBen Lindstrom 		ret = SSH2_FX_FAILURE;
2337b28dc5eSDamien Miller 		break;
2347b28dc5eSDamien Miller 	}
2357b28dc5eSDamien Miller 	return ret;
2367b28dc5eSDamien Miller }
2377b28dc5eSDamien Miller 
238bba81213SBen Lindstrom static int
flags_from_portable(int pflags)2397b28dc5eSDamien Miller flags_from_portable(int pflags)
2407b28dc5eSDamien Miller {
2417b28dc5eSDamien Miller 	int flags = 0;
2421addabd4SBen Lindstrom 
24336592518SBen Lindstrom 	if ((pflags & SSH2_FXF_READ) &&
24436592518SBen Lindstrom 	    (pflags & SSH2_FXF_WRITE)) {
2457b28dc5eSDamien Miller 		flags = O_RDWR;
2462f959b4cSBen Lindstrom 	} else if (pflags & SSH2_FXF_READ) {
2477b28dc5eSDamien Miller 		flags = O_RDONLY;
2482f959b4cSBen Lindstrom 	} else if (pflags & SSH2_FXF_WRITE) {
2497b28dc5eSDamien Miller 		flags = O_WRONLY;
2507b28dc5eSDamien Miller 	}
251e9fc72edSDamien Miller 	if (pflags & SSH2_FXF_APPEND)
252e9fc72edSDamien Miller 		flags |= O_APPEND;
2532f959b4cSBen Lindstrom 	if (pflags & SSH2_FXF_CREAT)
2547b28dc5eSDamien Miller 		flags |= O_CREAT;
2552f959b4cSBen Lindstrom 	if (pflags & SSH2_FXF_TRUNC)
2567b28dc5eSDamien Miller 		flags |= O_TRUNC;
2572f959b4cSBen Lindstrom 	if (pflags & SSH2_FXF_EXCL)
2587b28dc5eSDamien Miller 		flags |= O_EXCL;
2597b28dc5eSDamien Miller 	return flags;
2607b28dc5eSDamien Miller }
2617b28dc5eSDamien Miller 
262fef95ad8SDamien Miller static const char *
string_from_portable(int pflags)263fef95ad8SDamien Miller string_from_portable(int pflags)
264fef95ad8SDamien Miller {
265fef95ad8SDamien Miller 	static char ret[128];
266fef95ad8SDamien Miller 
267fef95ad8SDamien Miller 	*ret = '\0';
268fef95ad8SDamien Miller 
269fef95ad8SDamien Miller #define PAPPEND(str)	{				\
270fef95ad8SDamien Miller 		if (*ret != '\0')			\
271fef95ad8SDamien Miller 			strlcat(ret, ",", sizeof(ret));	\
272fef95ad8SDamien Miller 		strlcat(ret, str, sizeof(ret));		\
273fef95ad8SDamien Miller 	}
274fef95ad8SDamien Miller 
275fef95ad8SDamien Miller 	if (pflags & SSH2_FXF_READ)
276fef95ad8SDamien Miller 		PAPPEND("READ")
277fef95ad8SDamien Miller 	if (pflags & SSH2_FXF_WRITE)
278fef95ad8SDamien Miller 		PAPPEND("WRITE")
279e9fc72edSDamien Miller 	if (pflags & SSH2_FXF_APPEND)
280e9fc72edSDamien Miller 		PAPPEND("APPEND")
281fef95ad8SDamien Miller 	if (pflags & SSH2_FXF_CREAT)
282fef95ad8SDamien Miller 		PAPPEND("CREATE")
283fef95ad8SDamien Miller 	if (pflags & SSH2_FXF_TRUNC)
284fef95ad8SDamien Miller 		PAPPEND("TRUNCATE")
285fef95ad8SDamien Miller 	if (pflags & SSH2_FXF_EXCL)
286fef95ad8SDamien Miller 		PAPPEND("EXCL")
287fef95ad8SDamien Miller 
288fef95ad8SDamien Miller 	return ret;
289fef95ad8SDamien Miller }
290fef95ad8SDamien Miller 
2917b28dc5eSDamien Miller /* handle handles */
2927b28dc5eSDamien Miller 
2937b28dc5eSDamien Miller typedef struct Handle Handle;
2947b28dc5eSDamien Miller struct Handle {
2957b28dc5eSDamien Miller 	int use;
2967b28dc5eSDamien Miller 	DIR *dirp;
2977b28dc5eSDamien Miller 	int fd;
298e9fc72edSDamien Miller 	int flags;
2997b28dc5eSDamien Miller 	char *name;
300fef95ad8SDamien Miller 	u_int64_t bytes_read, bytes_write;
3013397d0e0SDamien Miller 	int next_unused;
3027b28dc5eSDamien Miller };
3031addabd4SBen Lindstrom 
3047b28dc5eSDamien Miller enum {
3057b28dc5eSDamien Miller 	HANDLE_UNUSED,
3067b28dc5eSDamien Miller 	HANDLE_DIR,
3077b28dc5eSDamien Miller 	HANDLE_FILE
3087b28dc5eSDamien Miller };
3091addabd4SBen Lindstrom 
3105bed70afSdjm@openbsd.org static Handle *handles = NULL;
3115bed70afSdjm@openbsd.org static u_int num_handles = 0;
3125bed70afSdjm@openbsd.org static int first_unused_handle = -1;
3137b28dc5eSDamien Miller 
handle_unused(int i)3143397d0e0SDamien Miller static void handle_unused(int i)
3157b28dc5eSDamien Miller {
3167b28dc5eSDamien Miller 	handles[i].use = HANDLE_UNUSED;
3173397d0e0SDamien Miller 	handles[i].next_unused = first_unused_handle;
3183397d0e0SDamien Miller 	first_unused_handle = i;
3197b28dc5eSDamien Miller }
3207b28dc5eSDamien Miller 
321bba81213SBen Lindstrom static int
handle_new(int use,const char * name,int fd,int flags,DIR * dirp)322e9fc72edSDamien Miller handle_new(int use, const char *name, int fd, int flags, DIR *dirp)
3237b28dc5eSDamien Miller {
3243397d0e0SDamien Miller 	int i;
3251addabd4SBen Lindstrom 
3263397d0e0SDamien Miller 	if (first_unused_handle == -1) {
3273397d0e0SDamien Miller 		if (num_handles + 1 <= num_handles)
3283397d0e0SDamien Miller 			return -1;
3293397d0e0SDamien Miller 		num_handles++;
330657a5fbcSderaadt@openbsd.org 		handles = xreallocarray(handles, num_handles, sizeof(Handle));
3313397d0e0SDamien Miller 		handle_unused(num_handles - 1);
3323397d0e0SDamien Miller 	}
3333397d0e0SDamien Miller 
3343397d0e0SDamien Miller 	i = first_unused_handle;
3353397d0e0SDamien Miller 	first_unused_handle = handles[i].next_unused;
3363397d0e0SDamien Miller 
3377b28dc5eSDamien Miller 	handles[i].use = use;
3387b28dc5eSDamien Miller 	handles[i].dirp = dirp;
3397b28dc5eSDamien Miller 	handles[i].fd = fd;
340e9fc72edSDamien Miller 	handles[i].flags = flags;
3410011138dSDamien Miller 	handles[i].name = xstrdup(name);
342fef95ad8SDamien Miller 	handles[i].bytes_read = handles[i].bytes_write = 0;
3433397d0e0SDamien Miller 
3447b28dc5eSDamien Miller 	return i;
3457b28dc5eSDamien Miller }
3467b28dc5eSDamien Miller 
347bba81213SBen Lindstrom static int
handle_is_ok(int i,int type)3487b28dc5eSDamien Miller handle_is_ok(int i, int type)
3497b28dc5eSDamien Miller {
3503397d0e0SDamien Miller 	return i >= 0 && (u_int)i < num_handles && handles[i].use == type;
3517b28dc5eSDamien Miller }
3527b28dc5eSDamien Miller 
353bba81213SBen Lindstrom static int
handle_to_string(int handle,u_char ** stringp,int * hlenp)3547d845f4aSdjm@openbsd.org handle_to_string(int handle, u_char **stringp, int *hlenp)
3557b28dc5eSDamien Miller {
3567b28dc5eSDamien Miller 	if (stringp == NULL || hlenp == NULL)
3577b28dc5eSDamien Miller 		return -1;
358bf555ba6SBen Lindstrom 	*stringp = xmalloc(sizeof(int32_t));
3593f941889SDamien Miller 	put_u32(*stringp, handle);
360bf555ba6SBen Lindstrom 	*hlenp = sizeof(int32_t);
3617b28dc5eSDamien Miller 	return 0;
3627b28dc5eSDamien Miller }
3637b28dc5eSDamien Miller 
364bba81213SBen Lindstrom static int
handle_from_string(const u_char * handle,u_int hlen)3657d845f4aSdjm@openbsd.org handle_from_string(const u_char *handle, u_int hlen)
3667b28dc5eSDamien Miller {
367bf555ba6SBen Lindstrom 	int val;
3681addabd4SBen Lindstrom 
369bf555ba6SBen Lindstrom 	if (hlen != sizeof(int32_t))
3707b28dc5eSDamien Miller 		return -1;
3713f941889SDamien Miller 	val = get_u32(handle);
3727b28dc5eSDamien Miller 	if (handle_is_ok(val, HANDLE_FILE) ||
3737b28dc5eSDamien Miller 	    handle_is_ok(val, HANDLE_DIR))
3747b28dc5eSDamien Miller 		return val;
3757b28dc5eSDamien Miller 	return -1;
3767b28dc5eSDamien Miller }
3777b28dc5eSDamien Miller 
378bba81213SBen Lindstrom static char *
handle_to_name(int handle)3797b28dc5eSDamien Miller handle_to_name(int handle)
3807b28dc5eSDamien Miller {
3817b28dc5eSDamien Miller 	if (handle_is_ok(handle, HANDLE_DIR)||
3827b28dc5eSDamien Miller 	    handle_is_ok(handle, HANDLE_FILE))
3837b28dc5eSDamien Miller 		return handles[handle].name;
3847b28dc5eSDamien Miller 	return NULL;
3857b28dc5eSDamien Miller }
3867b28dc5eSDamien Miller 
387bba81213SBen Lindstrom static DIR *
handle_to_dir(int handle)3887b28dc5eSDamien Miller handle_to_dir(int handle)
3897b28dc5eSDamien Miller {
3907b28dc5eSDamien Miller 	if (handle_is_ok(handle, HANDLE_DIR))
3917b28dc5eSDamien Miller 		return handles[handle].dirp;
3927b28dc5eSDamien Miller 	return NULL;
3937b28dc5eSDamien Miller }
3947b28dc5eSDamien Miller 
395bba81213SBen Lindstrom static int
handle_to_fd(int handle)3967b28dc5eSDamien Miller handle_to_fd(int handle)
3977b28dc5eSDamien Miller {
3987b28dc5eSDamien Miller 	if (handle_is_ok(handle, HANDLE_FILE))
3997b28dc5eSDamien Miller 		return handles[handle].fd;
4007b28dc5eSDamien Miller 	return -1;
4017b28dc5eSDamien Miller }
4027b28dc5eSDamien Miller 
403e9fc72edSDamien Miller static int
handle_to_flags(int handle)404e9fc72edSDamien Miller handle_to_flags(int handle)
405e9fc72edSDamien Miller {
406e9fc72edSDamien Miller 	if (handle_is_ok(handle, HANDLE_FILE))
407e9fc72edSDamien Miller 		return handles[handle].flags;
408e9fc72edSDamien Miller 	return 0;
409e9fc72edSDamien Miller }
410e9fc72edSDamien Miller 
411fef95ad8SDamien Miller static void
handle_update_read(int handle,ssize_t bytes)412fef95ad8SDamien Miller handle_update_read(int handle, ssize_t bytes)
413fef95ad8SDamien Miller {
414fef95ad8SDamien Miller 	if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
415fef95ad8SDamien Miller 		handles[handle].bytes_read += bytes;
416fef95ad8SDamien Miller }
417fef95ad8SDamien Miller 
418fef95ad8SDamien Miller static void
handle_update_write(int handle,ssize_t bytes)419fef95ad8SDamien Miller handle_update_write(int handle, ssize_t bytes)
420fef95ad8SDamien Miller {
421fef95ad8SDamien Miller 	if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
422fef95ad8SDamien Miller 		handles[handle].bytes_write += bytes;
423fef95ad8SDamien Miller }
424fef95ad8SDamien Miller 
425fef95ad8SDamien Miller static u_int64_t
handle_bytes_read(int handle)426fef95ad8SDamien Miller handle_bytes_read(int handle)
427fef95ad8SDamien Miller {
428fef95ad8SDamien Miller 	if (handle_is_ok(handle, HANDLE_FILE))
429fef95ad8SDamien Miller 		return (handles[handle].bytes_read);
430fef95ad8SDamien Miller 	return 0;
431fef95ad8SDamien Miller }
432fef95ad8SDamien Miller 
433fef95ad8SDamien Miller static u_int64_t
handle_bytes_write(int handle)434fef95ad8SDamien Miller handle_bytes_write(int handle)
435fef95ad8SDamien Miller {
436fef95ad8SDamien Miller 	if (handle_is_ok(handle, HANDLE_FILE))
437fef95ad8SDamien Miller 		return (handles[handle].bytes_write);
438fef95ad8SDamien Miller 	return 0;
439fef95ad8SDamien Miller }
440fef95ad8SDamien Miller 
441bba81213SBen Lindstrom static int
handle_close(int handle)4427b28dc5eSDamien Miller handle_close(int handle)
4437b28dc5eSDamien Miller {
4447b28dc5eSDamien Miller 	int ret = -1;
4451addabd4SBen Lindstrom 
4467b28dc5eSDamien Miller 	if (handle_is_ok(handle, HANDLE_FILE)) {
4477b28dc5eSDamien Miller 		ret = close(handles[handle].fd);
448a627d42eSDarren Tucker 		free(handles[handle].name);
4493397d0e0SDamien Miller 		handle_unused(handle);
4507b28dc5eSDamien Miller 	} else if (handle_is_ok(handle, HANDLE_DIR)) {
4517b28dc5eSDamien Miller