17aee6ffeSDag-Erling Smørgrav /* $OpenBSD: sftp-server.c,v 1.85 2009/04/14 16:33:42 stevesk Exp $ */ 2b66f2d16SKris Kennaway /* 3efcad6b7SDag-Erling Smørgrav * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 4b66f2d16SKris Kennaway * 5efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8b66f2d16SKris Kennaway * 9efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b66f2d16SKris Kennaway */ 17b66f2d16SKris Kennaway 18761efaa7SDag-Erling Smørgrav #include "includes.h" 19761efaa7SDag-Erling Smørgrav 20761efaa7SDag-Erling Smørgrav #include <sys/types.h> 21761efaa7SDag-Erling Smørgrav #include <sys/param.h> 22761efaa7SDag-Erling Smørgrav #include <sys/stat.h> 23761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 24761efaa7SDag-Erling Smørgrav # include <sys/time.h> 25761efaa7SDag-Erling Smørgrav #endif 26d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_MOUNT_H 27d4af9e69SDag-Erling Smørgrav #include <sys/mount.h> 28d4af9e69SDag-Erling Smørgrav #endif 29d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_STATVFS_H 30d4af9e69SDag-Erling Smørgrav #include <sys/statvfs.h> 31d4af9e69SDag-Erling Smørgrav #endif 32761efaa7SDag-Erling Smørgrav 33761efaa7SDag-Erling Smørgrav #include <dirent.h> 34761efaa7SDag-Erling Smørgrav #include <errno.h> 35761efaa7SDag-Erling Smørgrav #include <fcntl.h> 36761efaa7SDag-Erling Smørgrav #include <pwd.h> 37761efaa7SDag-Erling Smørgrav #include <stdlib.h> 38761efaa7SDag-Erling Smørgrav #include <stdio.h> 39761efaa7SDag-Erling Smørgrav #include <string.h> 40761efaa7SDag-Erling Smørgrav #include <pwd.h> 41761efaa7SDag-Erling Smørgrav #include <time.h> 42761efaa7SDag-Erling Smørgrav #include <unistd.h> 43761efaa7SDag-Erling Smørgrav #include <stdarg.h> 44761efaa7SDag-Erling Smørgrav 45b66f2d16SKris Kennaway #include "xmalloc.h" 46761efaa7SDag-Erling Smørgrav #include "buffer.h" 47761efaa7SDag-Erling Smørgrav #include "log.h" 48021d409fSDag-Erling Smørgrav #include "misc.h" 49761efaa7SDag-Erling Smørgrav #include "uidswap.h" 50b66f2d16SKris Kennaway 511e8db6e2SBrian Feldman #include "sftp.h" 521e8db6e2SBrian Feldman #include "sftp-common.h" 53b66f2d16SKris Kennaway 54b66f2d16SKris Kennaway /* helper */ 551e8db6e2SBrian Feldman #define get_int64() buffer_get_int64(&iqueue); 56b66f2d16SKris Kennaway #define get_int() buffer_get_int(&iqueue); 57b66f2d16SKris Kennaway #define get_string(lenp) buffer_get_string(&iqueue, lenp); 58b66f2d16SKris Kennaway 59761efaa7SDag-Erling Smørgrav /* Our verbosity */ 60761efaa7SDag-Erling Smørgrav LogLevel log_level = SYSLOG_LEVEL_ERROR; 61761efaa7SDag-Erling Smørgrav 62761efaa7SDag-Erling Smørgrav /* Our client */ 63761efaa7SDag-Erling Smørgrav struct passwd *pw = NULL; 64761efaa7SDag-Erling Smørgrav char *client_addr = NULL; 6583d2307dSDag-Erling Smørgrav 66b66f2d16SKris Kennaway /* input and output queue */ 67b66f2d16SKris Kennaway Buffer iqueue; 68b66f2d16SKris Kennaway Buffer oqueue; 69b66f2d16SKris Kennaway 701e8db6e2SBrian Feldman /* Version of client */ 711e8db6e2SBrian Feldman int version; 721e8db6e2SBrian Feldman 73d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 74b66f2d16SKris Kennaway 75b66f2d16SKris Kennaway typedef struct Stat Stat; 76b66f2d16SKris Kennaway 771e8db6e2SBrian Feldman struct Stat { 78b66f2d16SKris Kennaway char *name; 79b66f2d16SKris Kennaway char *long_name; 80b66f2d16SKris Kennaway Attrib attrib; 81b66f2d16SKris Kennaway }; 82b66f2d16SKris Kennaway 83ae1f160dSDag-Erling Smørgrav static int 84b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 85b66f2d16SKris Kennaway { 86b66f2d16SKris Kennaway int ret = 0; 871e8db6e2SBrian Feldman 88b66f2d16SKris Kennaway switch (unixerrno) { 89b66f2d16SKris Kennaway case 0: 901e8db6e2SBrian Feldman ret = SSH2_FX_OK; 91b66f2d16SKris Kennaway break; 92b66f2d16SKris Kennaway case ENOENT: 93b66f2d16SKris Kennaway case ENOTDIR: 94b66f2d16SKris Kennaway case EBADF: 95b66f2d16SKris Kennaway case ELOOP: 961e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 97b66f2d16SKris Kennaway break; 98b66f2d16SKris Kennaway case EPERM: 99b66f2d16SKris Kennaway case EACCES: 100b66f2d16SKris Kennaway case EFAULT: 1011e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 102b66f2d16SKris Kennaway break; 103b66f2d16SKris Kennaway case ENAMETOOLONG: 104b66f2d16SKris Kennaway case EINVAL: 1051e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 106b66f2d16SKris Kennaway break; 107d4af9e69SDag-Erling Smørgrav case ENOSYS: 108d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 109d4af9e69SDag-Erling Smørgrav break; 110b66f2d16SKris Kennaway default: 1111e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 112b66f2d16SKris Kennaway break; 113b66f2d16SKris Kennaway } 114b66f2d16SKris Kennaway return ret; 115b66f2d16SKris Kennaway } 116b66f2d16SKris Kennaway 117ae1f160dSDag-Erling Smørgrav static int 118b66f2d16SKris Kennaway flags_from_portable(int pflags) 119b66f2d16SKris Kennaway { 120b66f2d16SKris Kennaway int flags = 0; 1211e8db6e2SBrian Feldman 1221e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 1231e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 124b66f2d16SKris Kennaway flags = O_RDWR; 1251e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 126b66f2d16SKris Kennaway flags = O_RDONLY; 1271e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 128b66f2d16SKris Kennaway flags = O_WRONLY; 129b66f2d16SKris Kennaway } 1301e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 131b66f2d16SKris Kennaway flags |= O_CREAT; 1321e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 133b66f2d16SKris Kennaway flags |= O_TRUNC; 1341e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 135b66f2d16SKris Kennaway flags |= O_EXCL; 136b66f2d16SKris Kennaway return flags; 137b66f2d16SKris Kennaway } 138b66f2d16SKris Kennaway 139761efaa7SDag-Erling Smørgrav static const char * 140761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 141761efaa7SDag-Erling Smørgrav { 142761efaa7SDag-Erling Smørgrav static char ret[128]; 143761efaa7SDag-Erling Smørgrav 144761efaa7SDag-Erling Smørgrav *ret = '\0'; 145761efaa7SDag-Erling Smørgrav 146761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 147761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 148761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 149761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 150761efaa7SDag-Erling Smørgrav } 151761efaa7SDag-Erling Smørgrav 152761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 153761efaa7SDag-Erling Smørgrav PAPPEND("READ") 154761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 155761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 156761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 157761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 158761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 159761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 160761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 161761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 162761efaa7SDag-Erling Smørgrav 163761efaa7SDag-Erling Smørgrav return ret; 164761efaa7SDag-Erling Smørgrav } 165761efaa7SDag-Erling Smørgrav 166ae1f160dSDag-Erling Smørgrav static Attrib * 167b66f2d16SKris Kennaway get_attrib(void) 168b66f2d16SKris Kennaway { 169b66f2d16SKris Kennaway return decode_attrib(&iqueue); 170b66f2d16SKris Kennaway } 171b66f2d16SKris Kennaway 172b66f2d16SKris Kennaway /* handle handles */ 173b66f2d16SKris Kennaway 174b66f2d16SKris Kennaway typedef struct Handle Handle; 175b66f2d16SKris Kennaway struct Handle { 176b66f2d16SKris Kennaway int use; 177b66f2d16SKris Kennaway DIR *dirp; 178b66f2d16SKris Kennaway int fd; 179b66f2d16SKris Kennaway char *name; 180761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 181d4af9e69SDag-Erling Smørgrav int next_unused; 182b66f2d16SKris Kennaway }; 1831e8db6e2SBrian Feldman 184b66f2d16SKris Kennaway enum { 185b66f2d16SKris Kennaway HANDLE_UNUSED, 186b66f2d16SKris Kennaway HANDLE_DIR, 187b66f2d16SKris Kennaway HANDLE_FILE 188b66f2d16SKris Kennaway }; 1891e8db6e2SBrian Feldman 190d4af9e69SDag-Erling Smørgrav Handle *handles = NULL; 191d4af9e69SDag-Erling Smørgrav u_int num_handles = 0; 192d4af9e69SDag-Erling Smørgrav int first_unused_handle = -1; 193b66f2d16SKris Kennaway 194d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 195b66f2d16SKris Kennaway { 196b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 197d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 198d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 199b66f2d16SKris Kennaway } 200b66f2d16SKris Kennaway 201ae1f160dSDag-Erling Smørgrav static int 202efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp) 203b66f2d16SKris Kennaway { 204d4af9e69SDag-Erling Smørgrav int i; 2051e8db6e2SBrian Feldman 206d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 207d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 208d4af9e69SDag-Erling Smørgrav return -1; 209d4af9e69SDag-Erling Smørgrav num_handles++; 210d4af9e69SDag-Erling Smørgrav handles = xrealloc(handles, num_handles, sizeof(Handle)); 211d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 212d4af9e69SDag-Erling Smørgrav } 213d4af9e69SDag-Erling Smørgrav 214d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 215d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 216d4af9e69SDag-Erling Smørgrav 217b66f2d16SKris Kennaway handles[i].use = use; 218b66f2d16SKris Kennaway handles[i].dirp = dirp; 219b66f2d16SKris Kennaway handles[i].fd = fd; 220d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 221761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 222d4af9e69SDag-Erling Smørgrav 223b66f2d16SKris Kennaway return i; 224b66f2d16SKris Kennaway } 225b66f2d16SKris Kennaway 226ae1f160dSDag-Erling Smørgrav static int 227b66f2d16SKris Kennaway handle_is_ok(int i, int type) 228b66f2d16SKris Kennaway { 229d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 230b66f2d16SKris Kennaway } 231b66f2d16SKris Kennaway 232ae1f160dSDag-Erling Smørgrav static int 233b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 234b66f2d16SKris Kennaway { 235b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 236b66f2d16SKris Kennaway return -1; 2371e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 238761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 2391e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 240b66f2d16SKris Kennaway return 0; 241b66f2d16SKris Kennaway } 242b66f2d16SKris Kennaway 243ae1f160dSDag-Erling Smørgrav static int 244efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen) 245b66f2d16SKris Kennaway { 2461e8db6e2SBrian Feldman int val; 2471e8db6e2SBrian Feldman 2481e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 249b66f2d16SKris Kennaway return -1; 250761efaa7SDag-Erling Smørgrav val = get_u32(handle); 251b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 252b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 253b66f2d16SKris Kennaway return val; 254b66f2d16SKris Kennaway return -1; 255b66f2d16SKris Kennaway } 256b66f2d16SKris Kennaway 257ae1f160dSDag-Erling Smørgrav static char * 258b66f2d16SKris Kennaway handle_to_name(int handle) 259b66f2d16SKris Kennaway { 260b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 261b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 262b66f2d16SKris Kennaway return handles[handle].name; 263b66f2d16SKris Kennaway return NULL; 264b66f2d16SKris Kennaway } 265b66f2d16SKris Kennaway 266ae1f160dSDag-Erling Smørgrav static DIR * 267b66f2d16SKris Kennaway handle_to_dir(int handle) 268b66f2d16SKris Kennaway { 269b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 270b66f2d16SKris Kennaway return handles[handle].dirp; 271b66f2d16SKris Kennaway return NULL; 272b66f2d16SKris Kennaway } 273b66f2d16SKris Kennaway 274ae1f160dSDag-Erling Smørgrav static int 275b66f2d16SKris Kennaway handle_to_fd(int handle) 276b66f2d16SKris Kennaway { 277b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 278b66f2d16SKris Kennaway return handles[handle].fd; 279b66f2d16SKris Kennaway return -1; 280b66f2d16SKris Kennaway } 281b66f2d16SKris Kennaway 282761efaa7SDag-Erling Smørgrav static void 283761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 284761efaa7SDag-Erling Smørgrav { 285761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 286761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 287761efaa7SDag-Erling Smørgrav } 288761efaa7SDag-Erling Smørgrav 289761efaa7SDag-Erling Smørgrav static void 290761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 291761efaa7SDag-Erling Smørgrav { 292761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 293761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 294761efaa7SDag-Erling Smørgrav } 295761efaa7SDag-Erling Smørgrav 296761efaa7SDag-Erling Smørgrav static u_int64_t 297761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 298761efaa7SDag-Erling Smørgrav { 299761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 300761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 301761efaa7SDag-Erling Smørgrav return 0; 302761efaa7SDag-Erling Smørgrav } 303761efaa7SDag-Erling Smørgrav 304761efaa7SDag-Erling Smørgrav static u_int64_t 305761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 306761efaa7SDag-Erling Smørgrav { 307761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 308761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 309761efaa7SDag-Erling Smørgrav return 0; 310761efaa7SDag-Erling Smørgrav } 311761efaa7SDag-Erling Smørgrav 312ae1f160dSDag-Erling Smørgrav static int 313b66f2d16SKris Kennaway handle_close(int handle) 314b66f2d16SKris Kennaway { 315b66f2d16SKris Kennaway int ret = -1; 3161e8db6e2SBrian Feldman 317b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 318b66f2d16SKris Kennaway ret = close(handles[handle].fd); 319d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 320d4af9e69SDag-Erling Smørgrav handle_unused(handle); 321b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 322b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 323d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 324d4af9e69SDag-Erling Smørgrav handle_unused(handle); 325b66f2d16SKris Kennaway } else { 326b66f2d16SKris Kennaway errno = ENOENT; 327b66f2d16SKris Kennaway } 328b66f2d16SKris Kennaway return ret; 329b66f2d16SKris Kennaway } 330b66f2d16SKris Kennaway 331761efaa7SDag-Erling Smørgrav static void 332761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 333761efaa7SDag-Erling Smørgrav { 334761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 335761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 336761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 337761efaa7SDag-Erling Smørgrav handle_to_name(handle), 338d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 339d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 340761efaa7SDag-Erling Smørgrav } else { 341761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 342761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 343761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 344761efaa7SDag-Erling Smørgrav } 345761efaa7SDag-Erling Smørgrav } 346761efaa7SDag-Erling Smørgrav 347761efaa7SDag-Erling Smørgrav static void 348761efaa7SDag-Erling Smørgrav handle_log_exit(void) 349761efaa7SDag-Erling Smørgrav { 350761efaa7SDag-Erling Smørgrav u_int i; 351761efaa7SDag-Erling Smørgrav 352d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 353761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 354761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 355761efaa7SDag-Erling Smørgrav } 356761efaa7SDag-Erling Smørgrav 357ae1f160dSDag-Erling Smørgrav static int 358b66f2d16SKris Kennaway get_handle(void) 359b66f2d16SKris Kennaway { 360b66f2d16SKris Kennaway char *handle; 3611e8db6e2SBrian Feldman int val = -1; 362b66f2d16SKris Kennaway u_int hlen; 3631e8db6e2SBrian Feldman 364b66f2d16SKris Kennaway handle = get_string(&hlen); 3651e8db6e2SBrian Feldman if (hlen < 256) 366b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 367b66f2d16SKris Kennaway xfree(handle); 368b66f2d16SKris Kennaway return val; 369b66f2d16SKris Kennaway } 370b66f2d16SKris Kennaway 371b66f2d16SKris Kennaway /* send replies */ 372b66f2d16SKris Kennaway 373ae1f160dSDag-Erling Smørgrav static void 374b66f2d16SKris Kennaway send_msg(Buffer *m) 375b66f2d16SKris Kennaway { 376b66f2d16SKris Kennaway int mlen = buffer_len(m); 3771e8db6e2SBrian Feldman 378b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 379b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 380b66f2d16SKris Kennaway buffer_consume(m, mlen); 381b66f2d16SKris Kennaway } 382b66f2d16SKris Kennaway 383761efaa7SDag-Erling Smørgrav static const char * 384761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 385b66f2d16SKris Kennaway { 3861e8db6e2SBrian Feldman const char *status_messages[] = { 3871e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 3881e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 3891e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 3901e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 3911e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 3921e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 3931e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 3941e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 3951e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 3961e8db6e2SBrian Feldman "Unknown error" /* Others */ 3971e8db6e2SBrian Feldman }; 398761efaa7SDag-Erling Smørgrav return (status_messages[MIN(status,SSH2_FX_MAX)]); 399761efaa7SDag-Erling Smørgrav } 4001e8db6e2SBrian Feldman 401761efaa7SDag-Erling Smørgrav static void 402761efaa7SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 403761efaa7SDag-Erling Smørgrav { 404761efaa7SDag-Erling Smørgrav Buffer msg; 405761efaa7SDag-Erling Smørgrav 406761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 407761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 408761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 409761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 410b66f2d16SKris Kennaway buffer_init(&msg); 4111e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 412b66f2d16SKris Kennaway buffer_put_int(&msg, id); 413d74d50a8SDag-Erling Smørgrav buffer_put_int(&msg, status); 4141e8db6e2SBrian Feldman if (version >= 3) { 415761efaa7SDag-Erling Smørgrav buffer_put_cstring(&msg, status_to_message(status)); 4161e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 4171e8db6e2SBrian Feldman } 418b66f2d16SKris Kennaway send_msg(&msg); 419b66f2d16SKris Kennaway buffer_free(&msg); 420b66f2d16SKris Kennaway } 421ae1f160dSDag-Erling Smørgrav static void 422efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 423b66f2d16SKris Kennaway { 424b66f2d16SKris Kennaway Buffer msg; 4251e8db6e2SBrian Feldman 426b66f2d16SKris Kennaway buffer_init(&msg); 427b66f2d16SKris Kennaway buffer_put_char(&msg, type); 428b66f2d16SKris Kennaway buffer_put_int(&msg, id); 429b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 430b66f2d16SKris Kennaway send_msg(&msg); 431b66f2d16SKris Kennaway buffer_free(&msg); 432b66f2d16SKris Kennaway } 433b66f2d16SKris Kennaway 434ae1f160dSDag-Erling Smørgrav static void 435efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen) 436b66f2d16SKris Kennaway { 437761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 4381e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 439b66f2d16SKris Kennaway } 440b66f2d16SKris Kennaway 441ae1f160dSDag-Erling Smørgrav static void 442b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 443b66f2d16SKris Kennaway { 444b66f2d16SKris Kennaway char *string; 445b66f2d16SKris Kennaway int hlen; 4461e8db6e2SBrian Feldman 447b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 448761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 4491e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 450b66f2d16SKris Kennaway xfree(string); 451b66f2d16SKris Kennaway } 452b66f2d16SKris Kennaway 453ae1f160dSDag-Erling Smørgrav static void 454efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 455b66f2d16SKris Kennaway { 456b66f2d16SKris Kennaway Buffer msg; 457b66f2d16SKris Kennaway int i; 4581e8db6e2SBrian Feldman 459b66f2d16SKris Kennaway buffer_init(&msg); 4601e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 461b66f2d16SKris Kennaway buffer_put_int(&msg, id); 462b66f2d16SKris Kennaway buffer_put_int(&msg, count); 463761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 464b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 465b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 466b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 467b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 468b66f2d16SKris Kennaway } 469b66f2d16SKris Kennaway send_msg(&msg); 470b66f2d16SKris Kennaway buffer_free(&msg); 471b66f2d16SKris Kennaway } 472b66f2d16SKris Kennaway 473ae1f160dSDag-Erling Smørgrav static void 474efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 475b66f2d16SKris Kennaway { 476b66f2d16SKris Kennaway Buffer msg; 4771e8db6e2SBrian Feldman 478761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 479b66f2d16SKris Kennaway buffer_init(&msg); 4801e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 481b66f2d16SKris Kennaway buffer_put_int(&msg, id); 482b66f2d16SKris Kennaway encode_attrib(&msg, a); 483b66f2d16SKris Kennaway send_msg(&msg); 484b66f2d16SKris Kennaway buffer_free(&msg); 485b66f2d16SKris Kennaway } 486b66f2d16SKris Kennaway 487d4af9e69SDag-Erling Smørgrav static void 488d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 489d4af9e69SDag-Erling Smørgrav { 490d4af9e69SDag-Erling Smørgrav Buffer msg; 491d4af9e69SDag-Erling Smørgrav u_int64_t flag; 492d4af9e69SDag-Erling Smørgrav 493d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 494d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 495d4af9e69SDag-Erling Smørgrav 496d4af9e69SDag-Erling Smørgrav buffer_init(&msg); 497d4af9e69SDag-Erling Smørgrav buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY); 498d4af9e69SDag-Erling Smørgrav buffer_put_int(&msg, id); 499d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bsize); 500d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_frsize); 501d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_blocks); 502d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bfree); 503d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bavail); 504d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_files); 505d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_ffree); 506d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_favail); 507d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid)); 508d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, flag); 509d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_namemax); 510d4af9e69SDag-Erling Smørgrav send_msg(&msg); 511d4af9e69SDag-Erling Smørgrav buffer_free(&msg); 512d4af9e69SDag-Erling Smørgrav } 513d4af9e69SDag-Erling Smørgrav 514b66f2d16SKris Kennaway /* parse incoming */ 515b66f2d16SKris Kennaway 516ae1f160dSDag-Erling Smørgrav static void 517b66f2d16SKris Kennaway process_init(void) 518b66f2d16SKris Kennaway { 519b66f2d16SKris Kennaway Buffer msg; 520b66f2d16SKris Kennaway 521545d5ecaSDag-Erling Smørgrav version = get_int(); 522761efaa7SDag-Erling Smørgrav verbose("received client version %d", version); 523b66f2d16SKris Kennaway buffer_init(&msg); 5241e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 5251e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 526d4af9e69SDag-Erling Smørgrav /* POSIX rename extension */ 527d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "posix-rename@openssh.com"); 528d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "1"); /* version */ 529d4af9e69SDag-Erling Smørgrav /* statvfs extension */ 530d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "statvfs@openssh.com"); 531d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 532d4af9e69SDag-Erling Smørgrav /* fstatvfs extension */ 533d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 534d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 535b66f2d16SKris Kennaway send_msg(&msg); 536b66f2d16SKris Kennaway buffer_free(&msg); 537b66f2d16SKris Kennaway } 538b66f2d16SKris Kennaway 539ae1f160dSDag-Erling Smørgrav static void 540b66f2d16SKris Kennaway process_open(void) 541b66f2d16SKris Kennaway { 542b66f2d16SKris Kennaway u_int32_t id, pflags; 543b66f2d16SKris Kennaway Attrib *a; 544b66f2d16SKris Kennaway char *name; 5451e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 546b66f2d16SKris Kennaway 547b66f2d16SKris Kennaway id = get_int(); 548b66f2d16SKris Kennaway name = get_string(NULL); 5491e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 550761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 551b66f2d16SKris Kennaway a = get_attrib(); 552b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 5531e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 554761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 555761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 556b66f2d16SKris Kennaway fd = open(name, flags, mode); 557b66f2d16SKris Kennaway if (fd < 0) { 558b66f2d16SKris Kennaway status = errno_to_portable(errno); 559b66f2d16SKris Kennaway } else { 560d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, NULL); 561b66f2d16SKris Kennaway if (handle < 0) { 562b66f2d16SKris Kennaway close(fd); 563b66f2d16SKris Kennaway } else { 564b66f2d16SKris Kennaway send_handle(id, handle); 5651e8db6e2SBrian Feldman status = SSH2_FX_OK; 566b66f2d16SKris Kennaway } 567b66f2d16SKris Kennaway } 5681e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 569b66f2d16SKris Kennaway send_status(id, status); 570b66f2d16SKris Kennaway xfree(name); 571b66f2d16SKris Kennaway } 572b66f2d16SKris Kennaway 573ae1f160dSDag-Erling Smørgrav static void 574b66f2d16SKris Kennaway process_close(void) 575b66f2d16SKris Kennaway { 576b66f2d16SKris Kennaway u_int32_t id; 5771e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 578b66f2d16SKris Kennaway 579b66f2d16SKris Kennaway id = get_int(); 580b66f2d16SKris Kennaway handle = get_handle(); 581761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 582761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 583b66f2d16SKris Kennaway ret = handle_close(handle); 5841e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 585b66f2d16SKris Kennaway send_status(id, status); 586b66f2d16SKris Kennaway } 587b66f2d16SKris Kennaway 588ae1f160dSDag-Erling Smørgrav static void 589b66f2d16SKris Kennaway process_read(void) 590b66f2d16SKris Kennaway { 591b66f2d16SKris Kennaway char buf[64*1024]; 5921e8db6e2SBrian Feldman u_int32_t id, len; 5931e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 594b66f2d16SKris Kennaway u_int64_t off; 595b66f2d16SKris Kennaway 596b66f2d16SKris Kennaway id = get_int(); 597b66f2d16SKris Kennaway handle = get_handle(); 5981e8db6e2SBrian Feldman off = get_int64(); 599b66f2d16SKris Kennaway len = get_int(); 600b66f2d16SKris Kennaway 601761efaa7SDag-Erling Smørgrav debug("request %u: read \"%s\" (handle %d) off %llu len %d", 602761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 603b66f2d16SKris Kennaway if (len > sizeof buf) { 604b66f2d16SKris Kennaway len = sizeof buf; 605761efaa7SDag-Erling Smørgrav debug2("read change len %d", len); 606b66f2d16SKris Kennaway } 607b66f2d16SKris Kennaway fd = handle_to_fd(handle); 608b66f2d16SKris Kennaway if (fd >= 0) { 609b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 610b66f2d16SKris Kennaway error("process_read: seek failed"); 611b66f2d16SKris Kennaway status = errno_to_portable(errno); 612b66f2d16SKris Kennaway } else { 613b66f2d16SKris Kennaway ret = read(fd, buf, len); 614b66f2d16SKris Kennaway if (ret < 0) { 615b66f2d16SKris Kennaway status = errno_to_portable(errno); 616b66f2d16SKris Kennaway } else if (ret == 0) { 6171e8db6e2SBrian Feldman status = SSH2_FX_EOF; 618b66f2d16SKris Kennaway } else { 619b66f2d16SKris Kennaway send_data(id, buf, ret); 6201e8db6e2SBrian Feldman status = SSH2_FX_OK; 621761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 622b66f2d16SKris Kennaway } 623b66f2d16SKris Kennaway } 624b66f2d16SKris Kennaway } 6251e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 626b66f2d16SKris Kennaway send_status(id, status); 627b66f2d16SKris Kennaway } 628b66f2d16SKris Kennaway 629ae1f160dSDag-Erling Smørgrav static void 630b66f2d16SKris Kennaway process_write(void) 631b66f2d16SKris Kennaway { 6321e8db6e2SBrian Feldman u_int32_t id; 633b66f2d16SKris Kennaway u_int64_t off; 634b66f2d16SKris Kennaway u_int len; 6351e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 636b66f2d16SKris Kennaway char *data; 637b66f2d16SKris Kennaway 638b66f2d16SKris Kennaway id = get_int(); 639b66f2d16SKris Kennaway handle = get_handle(); 6401e8db6e2SBrian Feldman off = get_int64(); 641b66f2d16SKris Kennaway data = get_string(&len); 642b66f2d16SKris Kennaway 643761efaa7SDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %d", 644761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 645b66f2d16SKris Kennaway fd = handle_to_fd(handle); 646b66f2d16SKris Kennaway if (fd >= 0) { 647b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 648b66f2d16SKris Kennaway status = errno_to_portable(errno); 649b66f2d16SKris Kennaway error("process_write: seek failed"); 650b66f2d16SKris Kennaway } else { 651b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 652b66f2d16SKris Kennaway ret = write(fd, data, len); 653043840dfSDag-Erling Smørgrav if (ret < 0) { 654b66f2d16SKris Kennaway error("process_write: write failed"); 655b66f2d16SKris Kennaway status = errno_to_portable(errno); 656043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 6571e8db6e2SBrian Feldman status = SSH2_FX_OK; 658761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 659b66f2d16SKris Kennaway } else { 660761efaa7SDag-Erling Smørgrav debug2("nothing at all written"); 661b66f2d16SKris Kennaway } 662b66f2d16SKris Kennaway } 663b66f2d16SKris Kennaway } 664b66f2d16SKris Kennaway send_status(id, status); 665b66f2d16SKris Kennaway xfree(data); 666b66f2d16SKris Kennaway } 667b66f2d16SKris Kennaway 668ae1f160dSDag-Erling Smørgrav static void 669b66f2d16SKris Kennaway process_do_stat(int do_lstat) 670b66f2d16SKris Kennaway { 6711e8db6e2SBrian Feldman Attrib a; 672b66f2d16SKris Kennaway struct stat st; 673b66f2d16SKris Kennaway u_int32_t id; 674b66f2d16SKris Kennaway char *name; 6751e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 676b66f2d16SKris Kennaway 677b66f2d16SKris Kennaway id = get_int(); 678b66f2d16SKris Kennaway name = get_string(NULL); 679761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 680761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 681b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 682b66f2d16SKris Kennaway if (ret < 0) { 683b66f2d16SKris Kennaway status = errno_to_portable(errno); 684b66f2d16SKris Kennaway } else { 6851e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 6861e8db6e2SBrian Feldman send_attrib(id, &a); 6871e8db6e2SBrian Feldman status = SSH2_FX_OK; 688b66f2d16SKris Kennaway } 6891e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 690b66f2d16SKris Kennaway send_status(id, status); 691b66f2d16SKris Kennaway xfree(name); 692b66f2d16SKris Kennaway } 693b66f2d16SKris Kennaway 694ae1f160dSDag-Erling Smørgrav static void 695b66f2d16SKris Kennaway process_stat(void) 696b66f2d16SKris Kennaway { 697b66f2d16SKris Kennaway process_do_stat(0); 698b66f2d16SKris Kennaway } 699b66f2d16SKris Kennaway 700ae1f160dSDag-Erling Smørgrav static void 701b66f2d16SKris Kennaway process_lstat(void) 702b66f2d16SKris Kennaway { 703b66f2d16SKris Kennaway process_do_stat(1); 704b66f2d16SKris Kennaway } 705b66f2d16SKris Kennaway 706ae1f160dSDag-Erling Smørgrav static void 707b66f2d16SKris Kennaway process_fstat(void) 708b66f2d16SKris Kennaway { 7091e8db6e2SBrian Feldman Attrib a; 710b66f2d16SKris Kennaway struct stat st; 711b66f2d16SKris Kennaway u_int32_t id; 7121e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 713b66f2d16SKris Kennaway 714b66f2d16SKris Kennaway id = get_int(); 715b66f2d16SKris Kennaway handle = get_handle(); 716761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 717761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 718b66f2d16SKris Kennaway fd = handle_to_fd(handle); 719b66f2d16SKris Kennaway if (fd >= 0) { 720b66f2d16SKris Kennaway ret = fstat(fd, &st); 721b66f2d16SKris Kennaway if (ret < 0) { 722b66f2d16SKris Kennaway status = errno_to_portable(errno); 723b66f2d16SKris Kennaway } else { 7241e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 7251e8db6e2SBrian Feldman send_attrib(id, &a); 7261e8db6e2SBrian Feldman status = SSH2_FX_OK; 727b66f2d16SKris Kennaway } 728b66f2d16SKris Kennaway } 7291e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 730b66f2d16SKris Kennaway send_status(id, status); 731b66f2d16SKris Kennaway } 732b66f2d16SKris Kennaway 733ae1f160dSDag-Erling Smørgrav static struct timeval * 734efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 735b66f2d16SKris Kennaway { 736b66f2d16SKris Kennaway static struct timeval tv[2]; 7371e8db6e2SBrian Feldman 738b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 739b66f2d16SKris Kennaway tv[0].tv_usec = 0; 740b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 741b66f2d16SKris Kennaway tv[1].tv_usec = 0; 742b66f2d16SKris Kennaway return tv; 743b66f2d16SKris Kennaway } 744b66f2d16SKris Kennaway 745ae1f160dSDag-Erling Smørgrav static void 746b66f2d16SKris Kennaway process_setstat(void) 747b66f2d16SKris Kennaway { 748b66f2d16SKris Kennaway Attrib *a; 749b66f2d16SKris Kennaway u_int32_t id; 750b66f2d16SKris Kennaway char *name; 751ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 752b66f2d16SKris Kennaway 753b66f2d16SKris Kennaway id = get_int(); 754b66f2d16SKris Kennaway name = get_string(NULL); 755b66f2d16SKris Kennaway a = get_attrib(); 756761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 757ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 758d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 759d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 760ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 761ae1f160dSDag-Erling Smørgrav if (ret == -1) 762ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 763ae1f160dSDag-Erling Smørgrav } 7641e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 765761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 766d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 767b66f2d16SKris Kennaway if (ret == -1) 768b66f2d16SKris Kennaway status = errno_to_portable(errno); 769b66f2d16SKris Kennaway } 7701e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 771761efaa7SDag-Erling Smørgrav char buf[64]; 772761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 773761efaa7SDag-Erling Smørgrav 774761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 775761efaa7SDag-Erling Smørgrav localtime(&t)); 776761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 777b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 778b66f2d16SKris Kennaway if (ret == -1) 779b66f2d16SKris Kennaway status = errno_to_portable(errno); 780b66f2d16SKris Kennaway } 7811e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 782761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 783761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 7841e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 7851e8db6e2SBrian Feldman if (ret == -1) 7861e8db6e2SBrian Feldman status = errno_to_portable(errno); 7871e8db6e2SBrian Feldman } 788b66f2d16SKris Kennaway send_status(id, status); 789b66f2d16SKris Kennaway xfree(name); 790b66f2d16SKris Kennaway } 791b66f2d16SKris Kennaway 792ae1f160dSDag-Erling Smørgrav static void 793b66f2d16SKris Kennaway process_fsetstat(void) 794b66f2d16SKris Kennaway { 795b66f2d16SKris Kennaway Attrib *a; 796b66f2d16SKris Kennaway u_int32_t id; 797b66f2d16SKris Kennaway int handle, fd, ret; 7981e8db6e2SBrian Feldman int status = SSH2_FX_OK; 799b66f2d16SKris Kennaway 800b66f2d16SKris Kennaway id = get_int(); 801b66f2d16SKris Kennaway handle = get_handle(); 802b66f2d16SKris Kennaway a = get_attrib(); 803761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 804b66f2d16SKris Kennaway fd = handle_to_fd(handle); 805761efaa7SDag-Erling Smørgrav if (fd < 0) { 8061e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 807b66f2d16SKris Kennaway } else { 808761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 809761efaa7SDag-Erling Smørgrav 810ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 811d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 812d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 813ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 814ae1f160dSDag-Erling Smørgrav if (ret == -1) 815ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 816ae1f160dSDag-Erling Smørgrav } 8171e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 818761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 81983d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 820d4af9e69SDag-Erling Smørgrav ret = fchmod(fd, a->perm & 07777); 82183d2307dSDag-Erling Smørgrav #else 822d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 82383d2307dSDag-Erling Smørgrav #endif 824b66f2d16SKris Kennaway if (ret == -1) 825b66f2d16SKris Kennaway status = errno_to_portable(errno); 826b66f2d16SKris Kennaway } 8271e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 828761efaa7SDag-Erling Smørgrav char buf[64]; 829761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 830761efaa7SDag-Erling Smørgrav 831761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 832761efaa7SDag-Erling Smørgrav localtime(&t)); 833761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 83483d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 835b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 83683d2307dSDag-Erling Smørgrav #else 83783d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 83883d2307dSDag-Erling Smørgrav #endif 839b66f2d16SKris Kennaway if (ret == -1) 840b66f2d16SKris Kennaway status = errno_to_portable(errno); 841b66f2d16SKris Kennaway } 8421e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 843761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 844761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 84583d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 8461e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 84783d2307dSDag-Erling Smørgrav #else 84883d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 84983d2307dSDag-Erling Smørgrav #endif 8501e8db6e2SBrian Feldman if (ret == -1) 8511e8db6e2SBrian Feldman status = errno_to_portable(errno); 8521e8db6e2SBrian Feldman } 853b66f2d16SKris Kennaway } 854b66f2d16SKris Kennaway send_status(id, status); 855b66f2d16SKris Kennaway } 856b66f2d16SKris Kennaway 857ae1f160dSDag-Erling Smørgrav static void 858b66f2d16SKris Kennaway process_opendir(void) 859b66f2d16SKris Kennaway { 860b66f2d16SKris Kennaway DIR *dirp = NULL; 861b66f2d16SKris Kennaway char *path; 8621e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 863b66f2d16SKris Kennaway u_int32_t id; 864b66f2d16SKris Kennaway 865b66f2d16SKris Kennaway id = get_int(); 866b66f2d16SKris Kennaway path = get_string(NULL); 867761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 868761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 869b66f2d16SKris Kennaway dirp = opendir(path); 870b66f2d16SKris Kennaway if (dirp == NULL) { 871b66f2d16SKris Kennaway status = errno_to_portable(errno); 872b66f2d16SKris Kennaway } else { 873d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, dirp); 874b66f2d16SKris Kennaway if (handle < 0) { 875b66f2d16SKris Kennaway closedir(dirp); 876b66f2d16SKris Kennaway } else { 877b66f2d16SKris Kennaway send_handle(id, handle); 8781e8db6e2SBrian Feldman status = SSH2_FX_OK; 879b66f2d16SKris Kennaway } 880b66f2d16SKris Kennaway 881b66f2d16SKris Kennaway } 8821e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 883b66f2d16SKris Kennaway send_status(id, status); 884b66f2d16SKris Kennaway xfree(path); 885b66f2d16SKris Kennaway } 886b66f2d16SKris Kennaway 887ae1f160dSDag-Erling Smørgrav static void 888b66f2d16SKris Kennaway process_readdir(void) 889b66f2d16SKris Kennaway { 890b66f2d16SKris Kennaway DIR *dirp; 891b66f2d16SKris Kennaway struct dirent *dp; 892b66f2d16SKris Kennaway char *path; 893b66f2d16SKris Kennaway int handle; 894b66f2d16SKris Kennaway u_int32_t id; 895b66f2d16SKris Kennaway 896b66f2d16SKris Kennaway id = get_int(); 897b66f2d16SKris Kennaway handle = get_handle(); 898761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 899761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 900b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 901b66f2d16SKris Kennaway path = handle_to_name(handle); 902b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 9031e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 904b66f2d16SKris Kennaway } else { 905b66f2d16SKris Kennaway struct stat st; 906761efaa7SDag-Erling Smørgrav char pathname[MAXPATHLEN]; 907b66f2d16SKris Kennaway Stat *stats; 908b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 909ee21a45fSDag-Erling Smørgrav 910761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 911b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 912b66f2d16SKris Kennaway if (count >= nstats) { 913b66f2d16SKris Kennaway nstats *= 2; 914761efaa7SDag-Erling Smørgrav stats = xrealloc(stats, nstats, sizeof(Stat)); 915b66f2d16SKris Kennaway } 916b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 917ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 918ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 919b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 920b66f2d16SKris Kennaway continue; 9211e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 922b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 9234b17dab0SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0); 924b66f2d16SKris Kennaway count++; 925b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 9261e8db6e2SBrian Feldman /* XXX check packet size instead */ 927b66f2d16SKris Kennaway if (count == 100) 928b66f2d16SKris Kennaway break; 929b66f2d16SKris Kennaway } 9301e8db6e2SBrian Feldman if (count > 0) { 931b66f2d16SKris Kennaway send_names(id, count, stats); 932b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 933b66f2d16SKris Kennaway xfree(stats[i].name); 934b66f2d16SKris Kennaway xfree(stats[i].long_name); 935b66f2d16SKris Kennaway } 9361e8db6e2SBrian Feldman } else { 9371e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 9381e8db6e2SBrian Feldman } 939b66f2d16SKris Kennaway xfree(stats); 940b66f2d16SKris Kennaway } 941b66f2d16SKris Kennaway } 942b66f2d16SKris Kennaway 943ae1f160dSDag-Erling Smørgrav static void 944b66f2d16SKris Kennaway process_remove(void) 945b66f2d16SKris Kennaway { 946b66f2d16SKris Kennaway char *name; 947b66f2d16SKris Kennaway u_int32_t id; 9481e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 949b66f2d16SKris Kennaway int ret; 950b66f2d16SKris Kennaway 951b66f2d16SKris Kennaway id = get_int(); 952b66f2d16SKris Kennaway name = get_string(NULL); 953761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 954761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 9551e8db6e2SBrian Feldman ret = unlink(name); 9561e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 957b66f2d16SKris Kennaway send_status(id, status); 958b66f2d16SKris Kennaway xfree(name); 959b66f2d16SKris Kennaway } 960b66f2d16SKris Kennaway 961ae1f160dSDag-Erling Smørgrav static void 962b66f2d16SKris Kennaway process_mkdir(void) 963b66f2d16SKris Kennaway { 964b66f2d16SKris Kennaway Attrib *a; 965b66f2d16SKris Kennaway u_int32_t id; 966b66f2d16SKris Kennaway char *name; 9671e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 968b66f2d16SKris Kennaway 969b66f2d16SKris Kennaway id = get_int(); 970b66f2d16SKris Kennaway name = get_string(NULL); 971b66f2d16SKris Kennaway a = get_attrib(); 9721e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 973d4af9e69SDag-Erling Smørgrav a->perm & 07777 : 0777; 974761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 975761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 976b66f2d16SKris Kennaway ret = mkdir(name, mode); 9771e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 978b66f2d16SKris Kennaway send_status(id, status); 979b66f2d16SKris Kennaway xfree(name); 980b66f2d16SKris Kennaway } 981b66f2d16SKris Kennaway 982ae1f160dSDag-Erling Smørgrav static void 983b66f2d16SKris Kennaway process_rmdir(void) 984b66f2d16SKris Kennaway { 985b66f2d16SKris Kennaway u_int32_t id; 986b66f2d16SKris Kennaway char *name; 987b66f2d16SKris Kennaway int ret, status; 988b66f2d16SKris Kennaway 989b66f2d16SKris Kennaway id = get_int(); 990b66f2d16SKris Kennaway name = get_string(NULL); 991761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 992761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 993b66f2d16SKris Kennaway ret = rmdir(name); 9941e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 995b66f2d16SKris Kennaway send_status(id, status); 996b66f2d16SKris Kennaway xfree(name); 997b66f2d16SKris Kennaway } 998b66f2d16SKris Kennaway 999ae1f160dSDag-Erling Smørgrav static void 1000b66f2d16SKris Kennaway process_realpath(void) 1001b66f2d16SKris Kennaway { 1002b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 1003b66f2d16SKris Kennaway u_int32_t id; 1004b66f2d16SKris Kennaway char *path; 1005b66f2d16SKris Kennaway 1006b66f2d16SKris Kennaway id = get_int(); 1007b66f2d16SKris Kennaway path = get_string(NULL); 10081e8db6e2SBrian Feldman if (path[0] == '\0') { 10091e8db6e2SBrian Feldman xfree(path); 10101e8db6e2SBrian Feldman path = xstrdup("."); 10111e8db6e2SBrian Feldman } 1012761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1013761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 1014b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 1015b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1016b66f2d16SKris Kennaway } else { 1017b66f2d16SKris Kennaway Stat s; 1018b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1019b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1020b66f2d16SKris Kennaway send_names(id, 1, &s); 1021b66f2d16SKris Kennaway } 1022b66f2d16SKris Kennaway xfree(path); 1023b66f2d16SKris Kennaway } 1024b66f2d16SKris Kennaway 1025ae1f160dSDag-Erling Smørgrav static void 1026b66f2d16SKris Kennaway process_rename(void) 1027b66f2d16SKris Kennaway { 1028b66f2d16SKris Kennaway u_int32_t id; 1029b66f2d16SKris Kennaway char *oldpath, *newpath; 1030d0c8c0bcSDag-Erling Smørgrav int status; 1031d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1032b66f2d16SKris Kennaway 1033b66f2d16SKris Kennaway id = get_int(); 1034b66f2d16SKris Kennaway oldpath = get_string(NULL); 1035b66f2d16SKris Kennaway newpath = get_string(NULL); 1036761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1037761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1038d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1039d0c8c0bcSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 1040d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1041d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1042d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1043d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 10447aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1045d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1046d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1047d4af9e69SDag-Erling Smørgrav #endif 1048d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1049d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1050d74d50a8SDag-Erling Smørgrav #endif 1051d74d50a8SDag-Erling Smørgrav ) { 1052d74d50a8SDag-Erling Smørgrav struct stat st; 1053d74d50a8SDag-Erling Smørgrav 1054d74d50a8SDag-Erling Smørgrav /* 1055d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1056d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1057d74d50a8SDag-Erling Smørgrav */ 1058d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1059d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1060d74d50a8SDag-Erling Smørgrav status = 1061d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1062d74d50a8SDag-Erling Smørgrav else 1063d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1064d74d50a8SDag-Erling Smørgrav } 1065d74d50a8SDag-Erling Smørgrav } else { 1066d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1067d74d50a8SDag-Erling Smørgrav } 1068d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1069d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1070d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1071d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1072d0c8c0bcSDag-Erling Smørgrav } else 1073d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1074d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1075d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1076d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1077d0c8c0bcSDag-Erling Smørgrav else 1078d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 10791e8db6e2SBrian Feldman } 1080b66f2d16SKris Kennaway send_status(id, status); 1081b66f2d16SKris Kennaway xfree(oldpath); 1082b66f2d16SKris Kennaway xfree(newpath); 1083b66f2d16SKris Kennaway } 1084b66f2d16SKris Kennaway 1085ae1f160dSDag-Erling Smørgrav static void 10861e8db6e2SBrian Feldman process_readlink(void) 10871e8db6e2SBrian Feldman { 10881e8db6e2SBrian Feldman u_int32_t id; 1089ae1f160dSDag-Erling Smørgrav int len; 1090d74d50a8SDag-Erling Smørgrav char buf[MAXPATHLEN]; 10911e8db6e2SBrian Feldman char *path; 10921e8db6e2SBrian Feldman 10931e8db6e2SBrian Feldman id = get_int(); 10941e8db6e2SBrian Feldman path = get_string(NULL); 1095761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1096761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1097d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 10981e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 10991e8db6e2SBrian Feldman else { 11001e8db6e2SBrian Feldman Stat s; 11011e8db6e2SBrian Feldman 1102d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 11031e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1104d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 11051e8db6e2SBrian Feldman send_names(id, 1, &s); 11061e8db6e2SBrian Feldman } 11071e8db6e2SBrian Feldman xfree(path); 11081e8db6e2SBrian Feldman } 11091e8db6e2SBrian Feldman 1110ae1f160dSDag-Erling Smørgrav static void 11111e8db6e2SBrian Feldman process_symlink(void) 11121e8db6e2SBrian Feldman { 11131e8db6e2SBrian Feldman u_int32_t id; 11141e8db6e2SBrian Feldman char *oldpath, *newpath; 1115d0c8c0bcSDag-Erling Smørgrav int ret, status; 11161e8db6e2SBrian Feldman 11171e8db6e2SBrian Feldman id = get_int(); 11181e8db6e2SBrian Feldman oldpath = get_string(NULL); 11191e8db6e2SBrian Feldman newpath = get_string(NULL); 1120761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1121761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1122d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 11231e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 11241e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 11251e8db6e2SBrian Feldman send_status(id, status); 11261e8db6e2SBrian Feldman xfree(oldpath); 11271e8db6e2SBrian Feldman xfree(newpath); 11281e8db6e2SBrian Feldman } 11291e8db6e2SBrian Feldman 1130ae1f160dSDag-Erling Smørgrav static void 1131d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1132d4af9e69SDag-Erling Smørgrav { 1133d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1134d4af9e69SDag-Erling Smørgrav 1135d4af9e69SDag-Erling Smørgrav oldpath = get_string(NULL); 1136d4af9e69SDag-Erling Smørgrav newpath = get_string(NULL); 1137d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1138d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1139d4af9e69SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1140d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1141d4af9e69SDag-Erling Smørgrav else 1142d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_OK); 1143d4af9e69SDag-Erling Smørgrav xfree(oldpath); 1144d4af9e69SDag-Erling Smørgrav xfree(newpath); 1145d4af9e69SDag-Erling Smørgrav } 1146d4af9e69SDag-Erling Smørgrav 1147d4af9e69SDag-Erling Smørgrav static void 1148d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1149d4af9e69SDag-Erling Smørgrav { 1150d4af9e69SDag-Erling Smørgrav char *path; 1151d4af9e69SDag-Erling Smørgrav struct statvfs st; 1152d4af9e69SDag-Erling Smørgrav 1153d4af9e69SDag-Erling Smørgrav path = get_string(NULL); 1154d4af9e69SDag-Erling Smørgrav debug3("request %u: statfs", id); 1155d4af9e69SDag-Erling Smørgrav logit("statfs \"%s\"", path); 1156d4af9e69SDag-Erling Smørgrav 1157d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1158d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1159d4af9e69SDag-Erling Smørgrav else 1160d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1161d4af9e69SDag-Erling Smørgrav xfree(path); 1162d4af9e69SDag-Erling Smørgrav } 1163d4af9e69SDag-Erling Smørgrav 1164d4af9e69SDag-Erling Smørgrav static void 1165d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1166d4af9e69SDag-Erling Smørgrav { 1167d4af9e69SDag-Erling Smørgrav int handle, fd; 1168d4af9e69SDag-Erling Smørgrav struct statvfs st; 1169d4af9e69SDag-Erling Smørgrav 1170d4af9e69SDag-Erling Smørgrav handle = get_handle(); 1171d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1172d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1173d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1174d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1175d4af9e69SDag-Erling Smørgrav return; 1176d4af9e69SDag-Erling Smørgrav } 1177d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1178d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1179d4af9e69SDag-Erling Smørgrav else 1180d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1181d4af9e69SDag-Erling Smørgrav } 1182d4af9e69SDag-Erling Smørgrav 1183d4af9e69SDag-Erling Smørgrav static void 11841e8db6e2SBrian Feldman process_extended(void) 11851e8db6e2SBrian Feldman { 11861e8db6e2SBrian Feldman u_int32_t id; 11871e8db6e2SBrian Feldman char *request; 11881e8db6e2SBrian Feldman 11891e8db6e2SBrian Feldman id = get_int(); 11901e8db6e2SBrian Feldman request = get_string(NULL); 1191d4af9e69SDag-Erling Smørgrav if (strcmp(request, "posix-rename@openssh.com") == 0) 1192d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(id); 1193d4af9e69SDag-Erling Smørgrav else if (strcmp(request, "statvfs@openssh.com") == 0) 1194d4af9e69SDag-Erling Smørgrav process_extended_statvfs(id); 1195d4af9e69SDag-Erling Smørgrav else if (strcmp(request, "fstatvfs@openssh.com") == 0) 1196d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(id); 1197d4af9e69SDag-Erling Smørgrav else 11981e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 11991e8db6e2SBrian Feldman xfree(request); 12001e8db6e2SBrian Feldman } 1201b66f2d16SKris Kennaway 1202b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1203b66f2d16SKris Kennaway 1204ae1f160dSDag-Erling Smørgrav static void 1205b66f2d16SKris Kennaway process(void) 1206b66f2d16SKris Kennaway { 12071e8db6e2SBrian Feldman u_int msg_len; 1208545d5ecaSDag-Erling Smørgrav u_int buf_len; 1209545d5ecaSDag-Erling Smørgrav u_int consumed; 12101e8db6e2SBrian Feldman u_int type; 12111e8db6e2SBrian Feldman u_char *cp; 1212b66f2d16SKris Kennaway 1213545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 1214545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1215b66f2d16SKris Kennaway return; /* Incomplete message. */ 1216ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 1217761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1218021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1219761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1220761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1221d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1222b66f2d16SKris Kennaway } 1223545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1224b66f2d16SKris Kennaway return; 1225b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 1226545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1227b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 1228b66f2d16SKris Kennaway switch (type) { 12291e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1230b66f2d16SKris Kennaway process_init(); 1231b66f2d16SKris Kennaway break; 12321e8db6e2SBrian Feldman case SSH2_FXP_OPEN: 1233b66f2d16SKris Kennaway process_open(); 1234b66f2d16SKris Kennaway break; 12351e8db6e2SBrian Feldman case SSH2_FXP_CLOSE: 1236b66f2d16SKris Kennaway process_close(); 1237b66f2d16SKris Kennaway break; 12381e8db6e2SBrian Feldman case SSH2_FXP_READ: 1239b66f2d16SKris Kennaway process_read(); 1240b66f2d16SKris Kennaway break; 12411e8db6e2SBrian Feldman case SSH2_FXP_WRITE: 1242b66f2d16SKris Kennaway process_write(); 1243b66f2d16SKris Kennaway break; 12441e8db6e2SBrian Feldman case SSH2_FXP_LSTAT: 1245b66f2d16SKris Kennaway process_lstat(); 1246b66f2d16SKris Kennaway break; 12471e8db6e2SBrian Feldman case SSH2_FXP_FSTAT: 1248b66f2d16SKris Kennaway process_fstat(); 1249b66f2d16SKris Kennaway break; 12501e8db6e2SBrian Feldman case SSH2_FXP_SETSTAT: 1251b66f2d16SKris Kennaway process_setstat(); 1252b66f2d16SKris Kennaway break; 12531e8db6e2SBrian Feldman case SSH2_FXP_FSETSTAT: 1254b66f2d16SKris Kennaway process_fsetstat(); 1255b66f2d16SKris Kennaway break; 12561e8db6e2SBrian Feldman case SSH2_FXP_OPENDIR: 1257b66f2d16SKris Kennaway process_opendir(); 1258b66f2d16SKris Kennaway break; 12591e8db6e2SBrian Feldman case SSH2_FXP_READDIR: 1260b66f2d16SKris Kennaway process_readdir(); 1261b66f2d16SKris Kennaway break; 12621e8db6e2SBrian Feldman case SSH2_FXP_REMOVE: 1263b66f2d16SKris Kennaway process_remove(); 1264b66f2d16SKris Kennaway break; 12651e8db6e2SBrian Feldman case SSH2_FXP_MKDIR: 1266b66f2d16SKris Kennaway process_mkdir(); 1267b66f2d16SKris Kennaway break; 12681e8db6e2SBrian Feldman case SSH2_FXP_RMDIR: 1269b66f2d16SKris Kennaway process_rmdir(); 1270b66f2d16SKris Kennaway break; 12711e8db6e2SBrian Feldman case SSH2_FXP_REALPATH: 1272b66f2d16SKris Kennaway process_realpath(); 1273b66f2d16SKris Kennaway break; 12741e8db6e2SBrian Feldman case SSH2_FXP_STAT: 1275b66f2d16SKris Kennaway process_stat(); 1276b66f2d16SKris Kennaway break; 12771e8db6e2SBrian Feldman case SSH2_FXP_RENAME: 1278b66f2d16SKris Kennaway process_rename(); 1279b66f2d16SKris Kennaway break; 12801e8db6e2SBrian Feldman case SSH2_FXP_READLINK: 12811e8db6e2SBrian Feldman process_readlink(); 12821e8db6e2SBrian Feldman break; 12831e8db6e2SBrian Feldman case SSH2_FXP_SYMLINK: 12841e8db6e2SBrian Feldman process_symlink(); 12851e8db6e2SBrian Feldman break; 12861e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 12871e8db6e2SBrian Feldman process_extended(); 12881e8db6e2SBrian Feldman break; 1289b66f2d16SKris Kennaway default: 1290b66f2d16SKris Kennaway error("Unknown message %d", type); 1291b66f2d16SKris Kennaway break; 1292b66f2d16SKris Kennaway } 1293545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1294d4af9e69SDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) { 1295d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1296d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1297d4af9e69SDag-Erling Smørgrav } 1298545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1299d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1300d4af9e69SDag-Erling Smørgrav error("msg_len %d < consumed %d", msg_len, consumed); 1301d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1302d4af9e69SDag-Erling Smørgrav } 1303545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1304545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1305b66f2d16SKris Kennaway } 1306b66f2d16SKris Kennaway 1307761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1308761efaa7SDag-Erling Smørgrav void 1309d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1310761efaa7SDag-Erling Smørgrav { 1311761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1312761efaa7SDag-Erling Smørgrav handle_log_exit(); 1313761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1314761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1315761efaa7SDag-Erling Smørgrav } 1316761efaa7SDag-Erling Smørgrav _exit(i); 1317761efaa7SDag-Erling Smørgrav } 1318761efaa7SDag-Erling Smørgrav 1319761efaa7SDag-Erling Smørgrav static void 1320d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1321761efaa7SDag-Erling Smørgrav { 1322761efaa7SDag-Erling Smørgrav extern char *__progname; 1323761efaa7SDag-Erling Smørgrav 1324761efaa7SDag-Erling Smørgrav fprintf(stderr, 1325761efaa7SDag-Erling Smørgrav "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); 1326761efaa7SDag-Erling Smørgrav exit(1); 1327761efaa7SDag-Erling Smørgrav } 1328761efaa7SDag-Erling Smørgrav 1329b66f2d16SKris Kennaway int 1330d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1331b66f2d16SKris Kennaway { 13321e8db6e2SBrian Feldman fd_set *rset, *wset; 1333761efaa7SDag-Erling Smørgrav int in, out, max, ch, skipargs = 0, log_stderr = 0; 13341e8db6e2SBrian Feldman ssize_t len, olen, set_size; 1335761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 1336d4af9e69SDag-Erling Smørgrav char *cp, buf[4*4096]; 1337761efaa7SDag-Erling Smørgrav 1338761efaa7SDag-Erling Smørgrav extern char *optarg; 1339761efaa7SDag-Erling Smørgrav extern char *__progname; 13401e8db6e2SBrian Feldman 1341761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1342761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1343b66f2d16SKris Kennaway 13447aee6ffeSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, "f:l:che")) != -1) { 1345761efaa7SDag-Erling Smørgrav switch (ch) { 1346761efaa7SDag-Erling Smørgrav case 'c': 1347761efaa7SDag-Erling Smørgrav /* 1348761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1349761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1350761efaa7SDag-Erling Smørgrav */ 1351761efaa7SDag-Erling Smørgrav skipargs = 1; 1352761efaa7SDag-Erling Smørgrav break; 1353761efaa7SDag-Erling Smørgrav case 'e': 1354761efaa7SDag-Erling Smørgrav log_stderr = 1; 1355761efaa7SDag-Erling Smørgrav break; 1356761efaa7SDag-Erling Smørgrav case 'l': 1357761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1358761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1359761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1360761efaa7SDag-Erling Smørgrav break; 1361761efaa7SDag-Erling Smørgrav case 'f': 1362761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1363d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1364761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1365761efaa7SDag-Erling Smørgrav break; 1366761efaa7SDag-Erling Smørgrav case 'h': 1367761efaa7SDag-Erling Smørgrav default: 1368d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1369761efaa7SDag-Erling Smørgrav } 1370761efaa7SDag-Erling Smørgrav } 1371761efaa7SDag-Erling Smørgrav 1372761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1373761efaa7SDag-Erling Smørgrav 1374761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1375761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1376d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1377d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1378761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1379d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1380d4af9e69SDag-Erling Smørgrav } 1381761efaa7SDag-Erling Smørgrav *cp = '\0'; 1382761efaa7SDag-Erling Smørgrav } else 1383761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1384761efaa7SDag-Erling Smørgrav 1385d4af9e69SDag-Erling Smørgrav pw = pwcopy(user_pw); 1386761efaa7SDag-Erling Smørgrav 1387761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1388761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1389761efaa7SDag-Erling Smørgrav 1390b66f2d16SKris Kennaway in = dup(STDIN_FILENO); 1391b66f2d16SKris Kennaway out = dup(STDOUT_FILENO); 1392b66f2d16SKris Kennaway 139383d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 139483d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 139583d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 139683d2307dSDag-Erling Smørgrav #endif 139783d2307dSDag-Erling Smørgrav 1398b66f2d16SKris Kennaway max = 0; 1399b66f2d16SKris Kennaway if (in > max) 1400b66f2d16SKris Kennaway max = in; 1401b66f2d16SKris Kennaway if (out > max) 1402b66f2d16SKris Kennaway max = out; 1403b66f2d16SKris Kennaway 1404b66f2d16SKris Kennaway buffer_init(&iqueue); 1405b66f2d16SKris Kennaway buffer_init(&oqueue); 1406b66f2d16SKris Kennaway 14071e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 14081e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 14091e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1410b66f2d16SKris Kennaway 14111e8db6e2SBrian Feldman for (;;) { 14121e8db6e2SBrian Feldman memset(rset, 0, set_size); 14131e8db6e2SBrian Feldman memset(wset, 0, set_size); 14141e8db6e2SBrian Feldman 1415d4af9e69SDag-Erling Smørgrav /* 1416d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1417d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1418d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1419d4af9e69SDag-Erling Smørgrav */ 1420d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&iqueue, sizeof(buf)) && 1421d4af9e69SDag-Erling Smørgrav buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 14221e8db6e2SBrian Feldman FD_SET(in, rset); 1423d4af9e69SDag-Erling Smørgrav 1424b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1425b66f2d16SKris Kennaway if (olen > 0) 14261e8db6e2SBrian Feldman FD_SET(out, wset); 1427b66f2d16SKris Kennaway 14281e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1429b66f2d16SKris Kennaway if (errno == EINTR) 1430b66f2d16SKris Kennaway continue; 1431761efaa7SDag-Erling Smørgrav error("select: %s", strerror(errno)); 1432d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1433b66f2d16SKris Kennaway } 1434b66f2d16SKris Kennaway 1435b66f2d16SKris Kennaway /* copy stdin to iqueue */ 14361e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1437b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1438b66f2d16SKris Kennaway if (len == 0) { 1439b66f2d16SKris Kennaway debug("read eof"); 1440d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 1441b66f2d16SKris Kennaway } else if (len < 0) { 1442761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1443d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1444b66f2d16SKris Kennaway } else { 1445b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1446b66f2d16SKris Kennaway } 1447b66f2d16SKris Kennaway } 1448b66f2d16SKris Kennaway /* send oqueue to stdout */ 14491e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1450b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1451b66f2d16SKris Kennaway if (len < 0) { 1452761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1453d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1454b66f2d16SKris Kennaway } else { 1455b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1456b66f2d16SKris Kennaway } 1457b66f2d16SKris Kennaway } 1458d4af9e69SDag-Erling Smørgrav 1459d4af9e69SDag-Erling Smørgrav /* 1460d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1461d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1462d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1463d4af9e69SDag-Erling Smørgrav */ 1464d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1465b66f2d16SKris Kennaway process(); 1466b66f2d16SKris Kennaway } 1467b66f2d16SKris Kennaway } 1468