1b15c8340SDag-Erling Smørgrav /* $OpenBSD: sftp-server.c,v 1.91 2010/01/13 01:40:16 djm 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 73b15c8340SDag-Erling Smørgrav /* Disable writes */ 74b15c8340SDag-Erling Smørgrav int readonly; 75b15c8340SDag-Erling Smørgrav 76d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 77b66f2d16SKris Kennaway 78b66f2d16SKris Kennaway typedef struct Stat Stat; 79b66f2d16SKris Kennaway 801e8db6e2SBrian Feldman struct Stat { 81b66f2d16SKris Kennaway char *name; 82b66f2d16SKris Kennaway char *long_name; 83b66f2d16SKris Kennaway Attrib attrib; 84b66f2d16SKris Kennaway }; 85b66f2d16SKris Kennaway 86ae1f160dSDag-Erling Smørgrav static int 87b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 88b66f2d16SKris Kennaway { 89b66f2d16SKris Kennaway int ret = 0; 901e8db6e2SBrian Feldman 91b66f2d16SKris Kennaway switch (unixerrno) { 92b66f2d16SKris Kennaway case 0: 931e8db6e2SBrian Feldman ret = SSH2_FX_OK; 94b66f2d16SKris Kennaway break; 95b66f2d16SKris Kennaway case ENOENT: 96b66f2d16SKris Kennaway case ENOTDIR: 97b66f2d16SKris Kennaway case EBADF: 98b66f2d16SKris Kennaway case ELOOP: 991e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 100b66f2d16SKris Kennaway break; 101b66f2d16SKris Kennaway case EPERM: 102b66f2d16SKris Kennaway case EACCES: 103b66f2d16SKris Kennaway case EFAULT: 1041e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 105b66f2d16SKris Kennaway break; 106b66f2d16SKris Kennaway case ENAMETOOLONG: 107b66f2d16SKris Kennaway case EINVAL: 1081e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 109b66f2d16SKris Kennaway break; 110d4af9e69SDag-Erling Smørgrav case ENOSYS: 111d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 112d4af9e69SDag-Erling Smørgrav break; 113b66f2d16SKris Kennaway default: 1141e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 115b66f2d16SKris Kennaway break; 116b66f2d16SKris Kennaway } 117b66f2d16SKris Kennaway return ret; 118b66f2d16SKris Kennaway } 119b66f2d16SKris Kennaway 120ae1f160dSDag-Erling Smørgrav static int 121b66f2d16SKris Kennaway flags_from_portable(int pflags) 122b66f2d16SKris Kennaway { 123b66f2d16SKris Kennaway int flags = 0; 1241e8db6e2SBrian Feldman 1251e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 1261e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 127b66f2d16SKris Kennaway flags = O_RDWR; 1281e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 129b66f2d16SKris Kennaway flags = O_RDONLY; 1301e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 131b66f2d16SKris Kennaway flags = O_WRONLY; 132b66f2d16SKris Kennaway } 1331e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 134b66f2d16SKris Kennaway flags |= O_CREAT; 1351e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 136b66f2d16SKris Kennaway flags |= O_TRUNC; 1371e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 138b66f2d16SKris Kennaway flags |= O_EXCL; 139b66f2d16SKris Kennaway return flags; 140b66f2d16SKris Kennaway } 141b66f2d16SKris Kennaway 142761efaa7SDag-Erling Smørgrav static const char * 143761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 144761efaa7SDag-Erling Smørgrav { 145761efaa7SDag-Erling Smørgrav static char ret[128]; 146761efaa7SDag-Erling Smørgrav 147761efaa7SDag-Erling Smørgrav *ret = '\0'; 148761efaa7SDag-Erling Smørgrav 149761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 150761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 151761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 152761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 153761efaa7SDag-Erling Smørgrav } 154761efaa7SDag-Erling Smørgrav 155761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 156761efaa7SDag-Erling Smørgrav PAPPEND("READ") 157761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 158761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 159761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 160761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 161761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 162761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 163761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 164761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 165761efaa7SDag-Erling Smørgrav 166761efaa7SDag-Erling Smørgrav return ret; 167761efaa7SDag-Erling Smørgrav } 168761efaa7SDag-Erling Smørgrav 169ae1f160dSDag-Erling Smørgrav static Attrib * 170b66f2d16SKris Kennaway get_attrib(void) 171b66f2d16SKris Kennaway { 172b66f2d16SKris Kennaway return decode_attrib(&iqueue); 173b66f2d16SKris Kennaway } 174b66f2d16SKris Kennaway 175b66f2d16SKris Kennaway /* handle handles */ 176b66f2d16SKris Kennaway 177b66f2d16SKris Kennaway typedef struct Handle Handle; 178b66f2d16SKris Kennaway struct Handle { 179b66f2d16SKris Kennaway int use; 180b66f2d16SKris Kennaway DIR *dirp; 181b66f2d16SKris Kennaway int fd; 182b66f2d16SKris Kennaway char *name; 183761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 184d4af9e69SDag-Erling Smørgrav int next_unused; 185b66f2d16SKris Kennaway }; 1861e8db6e2SBrian Feldman 187b66f2d16SKris Kennaway enum { 188b66f2d16SKris Kennaway HANDLE_UNUSED, 189b66f2d16SKris Kennaway HANDLE_DIR, 190b66f2d16SKris Kennaway HANDLE_FILE 191b66f2d16SKris Kennaway }; 1921e8db6e2SBrian Feldman 193d4af9e69SDag-Erling Smørgrav Handle *handles = NULL; 194d4af9e69SDag-Erling Smørgrav u_int num_handles = 0; 195d4af9e69SDag-Erling Smørgrav int first_unused_handle = -1; 196b66f2d16SKris Kennaway 197d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 198b66f2d16SKris Kennaway { 199b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 200d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 201d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 202b66f2d16SKris Kennaway } 203b66f2d16SKris Kennaway 204ae1f160dSDag-Erling Smørgrav static int 205efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp) 206b66f2d16SKris Kennaway { 207d4af9e69SDag-Erling Smørgrav int i; 2081e8db6e2SBrian Feldman 209d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 210d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 211d4af9e69SDag-Erling Smørgrav return -1; 212d4af9e69SDag-Erling Smørgrav num_handles++; 213d4af9e69SDag-Erling Smørgrav handles = xrealloc(handles, num_handles, sizeof(Handle)); 214d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 215d4af9e69SDag-Erling Smørgrav } 216d4af9e69SDag-Erling Smørgrav 217d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 218d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 219d4af9e69SDag-Erling Smørgrav 220b66f2d16SKris Kennaway handles[i].use = use; 221b66f2d16SKris Kennaway handles[i].dirp = dirp; 222b66f2d16SKris Kennaway handles[i].fd = fd; 223d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 224761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 225d4af9e69SDag-Erling Smørgrav 226b66f2d16SKris Kennaway return i; 227b66f2d16SKris Kennaway } 228b66f2d16SKris Kennaway 229ae1f160dSDag-Erling Smørgrav static int 230b66f2d16SKris Kennaway handle_is_ok(int i, int type) 231b66f2d16SKris Kennaway { 232d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 233b66f2d16SKris Kennaway } 234b66f2d16SKris Kennaway 235ae1f160dSDag-Erling Smørgrav static int 236b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 237b66f2d16SKris Kennaway { 238b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 239b66f2d16SKris Kennaway return -1; 2401e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 241761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 2421e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 243b66f2d16SKris Kennaway return 0; 244b66f2d16SKris Kennaway } 245b66f2d16SKris Kennaway 246ae1f160dSDag-Erling Smørgrav static int 247efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen) 248b66f2d16SKris Kennaway { 2491e8db6e2SBrian Feldman int val; 2501e8db6e2SBrian Feldman 2511e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 252b66f2d16SKris Kennaway return -1; 253761efaa7SDag-Erling Smørgrav val = get_u32(handle); 254b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 255b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 256b66f2d16SKris Kennaway return val; 257b66f2d16SKris Kennaway return -1; 258b66f2d16SKris Kennaway } 259b66f2d16SKris Kennaway 260ae1f160dSDag-Erling Smørgrav static char * 261b66f2d16SKris Kennaway handle_to_name(int handle) 262b66f2d16SKris Kennaway { 263b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 264b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 265b66f2d16SKris Kennaway return handles[handle].name; 266b66f2d16SKris Kennaway return NULL; 267b66f2d16SKris Kennaway } 268b66f2d16SKris Kennaway 269ae1f160dSDag-Erling Smørgrav static DIR * 270b66f2d16SKris Kennaway handle_to_dir(int handle) 271b66f2d16SKris Kennaway { 272b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 273b66f2d16SKris Kennaway return handles[handle].dirp; 274b66f2d16SKris Kennaway return NULL; 275b66f2d16SKris Kennaway } 276b66f2d16SKris Kennaway 277ae1f160dSDag-Erling Smørgrav static int 278b66f2d16SKris Kennaway handle_to_fd(int handle) 279b66f2d16SKris Kennaway { 280b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 281b66f2d16SKris Kennaway return handles[handle].fd; 282b66f2d16SKris Kennaway return -1; 283b66f2d16SKris Kennaway } 284b66f2d16SKris Kennaway 285761efaa7SDag-Erling Smørgrav static void 286761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 287761efaa7SDag-Erling Smørgrav { 288761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 289761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 290761efaa7SDag-Erling Smørgrav } 291761efaa7SDag-Erling Smørgrav 292761efaa7SDag-Erling Smørgrav static void 293761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 294761efaa7SDag-Erling Smørgrav { 295761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 296761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 297761efaa7SDag-Erling Smørgrav } 298761efaa7SDag-Erling Smørgrav 299761efaa7SDag-Erling Smørgrav static u_int64_t 300761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 301761efaa7SDag-Erling Smørgrav { 302761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 303761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 304761efaa7SDag-Erling Smørgrav return 0; 305761efaa7SDag-Erling Smørgrav } 306761efaa7SDag-Erling Smørgrav 307761efaa7SDag-Erling Smørgrav static u_int64_t 308761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 309761efaa7SDag-Erling Smørgrav { 310761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 311761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 312761efaa7SDag-Erling Smørgrav return 0; 313761efaa7SDag-Erling Smørgrav } 314761efaa7SDag-Erling Smørgrav 315ae1f160dSDag-Erling Smørgrav static int 316b66f2d16SKris Kennaway handle_close(int handle) 317b66f2d16SKris Kennaway { 318b66f2d16SKris Kennaway int ret = -1; 3191e8db6e2SBrian Feldman 320b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 321b66f2d16SKris Kennaway ret = close(handles[handle].fd); 322d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 323d4af9e69SDag-Erling Smørgrav handle_unused(handle); 324b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 325b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 326d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 327d4af9e69SDag-Erling Smørgrav handle_unused(handle); 328b66f2d16SKris Kennaway } else { 329b66f2d16SKris Kennaway errno = ENOENT; 330b66f2d16SKris Kennaway } 331b66f2d16SKris Kennaway return ret; 332b66f2d16SKris Kennaway } 333b66f2d16SKris Kennaway 334761efaa7SDag-Erling Smørgrav static void 335761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 336761efaa7SDag-Erling Smørgrav { 337761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 338761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 339761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 340761efaa7SDag-Erling Smørgrav handle_to_name(handle), 341d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 342d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 343761efaa7SDag-Erling Smørgrav } else { 344761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 345761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 346761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 347761efaa7SDag-Erling Smørgrav } 348761efaa7SDag-Erling Smørgrav } 349761efaa7SDag-Erling Smørgrav 350761efaa7SDag-Erling Smørgrav static void 351761efaa7SDag-Erling Smørgrav handle_log_exit(void) 352761efaa7SDag-Erling Smørgrav { 353761efaa7SDag-Erling Smørgrav u_int i; 354761efaa7SDag-Erling Smørgrav 355d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 356761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 357761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 358761efaa7SDag-Erling Smørgrav } 359761efaa7SDag-Erling Smørgrav 360ae1f160dSDag-Erling Smørgrav static int 361b66f2d16SKris Kennaway get_handle(void) 362b66f2d16SKris Kennaway { 363b66f2d16SKris Kennaway char *handle; 3641e8db6e2SBrian Feldman int val = -1; 365b66f2d16SKris Kennaway u_int hlen; 3661e8db6e2SBrian Feldman 367b66f2d16SKris Kennaway handle = get_string(&hlen); 3681e8db6e2SBrian Feldman if (hlen < 256) 369b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 370b66f2d16SKris Kennaway xfree(handle); 371b66f2d16SKris Kennaway return val; 372b66f2d16SKris Kennaway } 373b66f2d16SKris Kennaway 374b66f2d16SKris Kennaway /* send replies */ 375b66f2d16SKris Kennaway 376ae1f160dSDag-Erling Smørgrav static void 377b66f2d16SKris Kennaway send_msg(Buffer *m) 378b66f2d16SKris Kennaway { 379b66f2d16SKris Kennaway int mlen = buffer_len(m); 3801e8db6e2SBrian Feldman 381b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 382b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 383b66f2d16SKris Kennaway buffer_consume(m, mlen); 384b66f2d16SKris Kennaway } 385b66f2d16SKris Kennaway 386761efaa7SDag-Erling Smørgrav static const char * 387761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 388b66f2d16SKris Kennaway { 3891e8db6e2SBrian Feldman const char *status_messages[] = { 3901e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 3911e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 3921e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 3931e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 3941e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 3951e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 3961e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 3971e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 3981e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 3991e8db6e2SBrian Feldman "Unknown error" /* Others */ 4001e8db6e2SBrian Feldman }; 401761efaa7SDag-Erling Smørgrav return (status_messages[MIN(status,SSH2_FX_MAX)]); 402761efaa7SDag-Erling Smørgrav } 4031e8db6e2SBrian Feldman 404761efaa7SDag-Erling Smørgrav static void 405761efaa7SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 406761efaa7SDag-Erling Smørgrav { 407761efaa7SDag-Erling Smørgrav Buffer msg; 408761efaa7SDag-Erling Smørgrav 409761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 410761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 411761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 412761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 413b66f2d16SKris Kennaway buffer_init(&msg); 4141e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 415b66f2d16SKris Kennaway buffer_put_int(&msg, id); 416d74d50a8SDag-Erling Smørgrav buffer_put_int(&msg, status); 4171e8db6e2SBrian Feldman if (version >= 3) { 418761efaa7SDag-Erling Smørgrav buffer_put_cstring(&msg, status_to_message(status)); 4191e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 4201e8db6e2SBrian Feldman } 421b66f2d16SKris Kennaway send_msg(&msg); 422b66f2d16SKris Kennaway buffer_free(&msg); 423b66f2d16SKris Kennaway } 424ae1f160dSDag-Erling Smørgrav static void 425efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 426b66f2d16SKris Kennaway { 427b66f2d16SKris Kennaway Buffer msg; 4281e8db6e2SBrian Feldman 429b66f2d16SKris Kennaway buffer_init(&msg); 430b66f2d16SKris Kennaway buffer_put_char(&msg, type); 431b66f2d16SKris Kennaway buffer_put_int(&msg, id); 432b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 433b66f2d16SKris Kennaway send_msg(&msg); 434b66f2d16SKris Kennaway buffer_free(&msg); 435b66f2d16SKris Kennaway } 436b66f2d16SKris Kennaway 437ae1f160dSDag-Erling Smørgrav static void 438efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen) 439b66f2d16SKris Kennaway { 440761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 4411e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 442b66f2d16SKris Kennaway } 443b66f2d16SKris Kennaway 444ae1f160dSDag-Erling Smørgrav static void 445b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 446b66f2d16SKris Kennaway { 447b66f2d16SKris Kennaway char *string; 448b66f2d16SKris Kennaway int hlen; 4491e8db6e2SBrian Feldman 450b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 451761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 4521e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 453b66f2d16SKris Kennaway xfree(string); 454b66f2d16SKris Kennaway } 455b66f2d16SKris Kennaway 456ae1f160dSDag-Erling Smørgrav static void 457efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 458b66f2d16SKris Kennaway { 459b66f2d16SKris Kennaway Buffer msg; 460b66f2d16SKris Kennaway int i; 4611e8db6e2SBrian Feldman 462b66f2d16SKris Kennaway buffer_init(&msg); 4631e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 464b66f2d16SKris Kennaway buffer_put_int(&msg, id); 465b66f2d16SKris Kennaway buffer_put_int(&msg, count); 466761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 467b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 468b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 469b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 470b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 471b66f2d16SKris Kennaway } 472b66f2d16SKris Kennaway send_msg(&msg); 473b66f2d16SKris Kennaway buffer_free(&msg); 474b66f2d16SKris Kennaway } 475b66f2d16SKris Kennaway 476ae1f160dSDag-Erling Smørgrav static void 477efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 478b66f2d16SKris Kennaway { 479b66f2d16SKris Kennaway Buffer msg; 4801e8db6e2SBrian Feldman 481761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 482b66f2d16SKris Kennaway buffer_init(&msg); 4831e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 484b66f2d16SKris Kennaway buffer_put_int(&msg, id); 485b66f2d16SKris Kennaway encode_attrib(&msg, a); 486b66f2d16SKris Kennaway send_msg(&msg); 487b66f2d16SKris Kennaway buffer_free(&msg); 488b66f2d16SKris Kennaway } 489b66f2d16SKris Kennaway 490d4af9e69SDag-Erling Smørgrav static void 491d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 492d4af9e69SDag-Erling Smørgrav { 493d4af9e69SDag-Erling Smørgrav Buffer msg; 494d4af9e69SDag-Erling Smørgrav u_int64_t flag; 495d4af9e69SDag-Erling Smørgrav 496d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 497d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 498d4af9e69SDag-Erling Smørgrav 499d4af9e69SDag-Erling Smørgrav buffer_init(&msg); 500d4af9e69SDag-Erling Smørgrav buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY); 501d4af9e69SDag-Erling Smørgrav buffer_put_int(&msg, id); 502d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bsize); 503d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_frsize); 504d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_blocks); 505d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bfree); 506d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bavail); 507d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_files); 508d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_ffree); 509d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_favail); 510d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid)); 511d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, flag); 512d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_namemax); 513d4af9e69SDag-Erling Smørgrav send_msg(&msg); 514d4af9e69SDag-Erling Smørgrav buffer_free(&msg); 515d4af9e69SDag-Erling Smørgrav } 516d4af9e69SDag-Erling Smørgrav 517b66f2d16SKris Kennaway /* parse incoming */ 518b66f2d16SKris Kennaway 519ae1f160dSDag-Erling Smørgrav static void 520b66f2d16SKris Kennaway process_init(void) 521b66f2d16SKris Kennaway { 522b66f2d16SKris Kennaway Buffer msg; 523b66f2d16SKris Kennaway 524545d5ecaSDag-Erling Smørgrav version = get_int(); 525761efaa7SDag-Erling Smørgrav verbose("received client version %d", version); 526b66f2d16SKris Kennaway buffer_init(&msg); 5271e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 5281e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 529d4af9e69SDag-Erling Smørgrav /* POSIX rename extension */ 530d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "posix-rename@openssh.com"); 531d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "1"); /* version */ 532d4af9e69SDag-Erling Smørgrav /* statvfs extension */ 533d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "statvfs@openssh.com"); 534d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 535d4af9e69SDag-Erling Smørgrav /* fstatvfs extension */ 536d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 537d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 538b66f2d16SKris Kennaway send_msg(&msg); 539b66f2d16SKris Kennaway buffer_free(&msg); 540b66f2d16SKris Kennaway } 541b66f2d16SKris Kennaway 542ae1f160dSDag-Erling Smørgrav static void 543b66f2d16SKris Kennaway process_open(void) 544b66f2d16SKris Kennaway { 545b66f2d16SKris Kennaway u_int32_t id, pflags; 546b66f2d16SKris Kennaway Attrib *a; 547b66f2d16SKris Kennaway char *name; 5481e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 549b66f2d16SKris Kennaway 550b66f2d16SKris Kennaway id = get_int(); 551b66f2d16SKris Kennaway name = get_string(NULL); 5521e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 553761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 554b66f2d16SKris Kennaway a = get_attrib(); 555b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 5561e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 557761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 558761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 559b15c8340SDag-Erling Smørgrav if (readonly && 560b15c8340SDag-Erling Smørgrav ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) 561b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 562b15c8340SDag-Erling Smørgrav else { 563b66f2d16SKris Kennaway fd = open(name, flags, mode); 564b66f2d16SKris Kennaway if (fd < 0) { 565b66f2d16SKris Kennaway status = errno_to_portable(errno); 566b66f2d16SKris Kennaway } else { 567d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, NULL); 568b66f2d16SKris Kennaway if (handle < 0) { 569b66f2d16SKris Kennaway close(fd); 570b66f2d16SKris Kennaway } else { 571b66f2d16SKris Kennaway send_handle(id, handle); 5721e8db6e2SBrian Feldman status = SSH2_FX_OK; 573b66f2d16SKris Kennaway } 574b66f2d16SKris Kennaway } 575b15c8340SDag-Erling Smørgrav } 5761e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 577b66f2d16SKris Kennaway send_status(id, status); 578b66f2d16SKris Kennaway xfree(name); 579b66f2d16SKris Kennaway } 580b66f2d16SKris Kennaway 581ae1f160dSDag-Erling Smørgrav static void 582b66f2d16SKris Kennaway process_close(void) 583b66f2d16SKris Kennaway { 584b66f2d16SKris Kennaway u_int32_t id; 5851e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 586b66f2d16SKris Kennaway 587b66f2d16SKris Kennaway id = get_int(); 588b66f2d16SKris Kennaway handle = get_handle(); 589761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 590761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 591b66f2d16SKris Kennaway ret = handle_close(handle); 5921e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 593b66f2d16SKris Kennaway send_status(id, status); 594b66f2d16SKris Kennaway } 595b66f2d16SKris Kennaway 596ae1f160dSDag-Erling Smørgrav static void 597b66f2d16SKris Kennaway process_read(void) 598b66f2d16SKris Kennaway { 599b66f2d16SKris Kennaway char buf[64*1024]; 6001e8db6e2SBrian Feldman u_int32_t id, len; 6011e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 602b66f2d16SKris Kennaway u_int64_t off; 603b66f2d16SKris Kennaway 604b66f2d16SKris Kennaway id = get_int(); 605b66f2d16SKris Kennaway handle = get_handle(); 6061e8db6e2SBrian Feldman off = get_int64(); 607b66f2d16SKris Kennaway len = get_int(); 608b66f2d16SKris Kennaway 609761efaa7SDag-Erling Smørgrav debug("request %u: read \"%s\" (handle %d) off %llu len %d", 610761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 611b66f2d16SKris Kennaway if (len > sizeof buf) { 612b66f2d16SKris Kennaway len = sizeof buf; 613761efaa7SDag-Erling Smørgrav debug2("read change len %d", len); 614b66f2d16SKris Kennaway } 615b66f2d16SKris Kennaway fd = handle_to_fd(handle); 616b66f2d16SKris Kennaway if (fd >= 0) { 617b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 618b66f2d16SKris Kennaway error("process_read: seek failed"); 619b66f2d16SKris Kennaway status = errno_to_portable(errno); 620b66f2d16SKris Kennaway } else { 621b66f2d16SKris Kennaway ret = read(fd, buf, len); 622b66f2d16SKris Kennaway if (ret < 0) { 623b66f2d16SKris Kennaway status = errno_to_portable(errno); 624b66f2d16SKris Kennaway } else if (ret == 0) { 6251e8db6e2SBrian Feldman status = SSH2_FX_EOF; 626b66f2d16SKris Kennaway } else { 627b66f2d16SKris Kennaway send_data(id, buf, ret); 6281e8db6e2SBrian Feldman status = SSH2_FX_OK; 629761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 630b66f2d16SKris Kennaway } 631b66f2d16SKris Kennaway } 632b66f2d16SKris Kennaway } 6331e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 634b66f2d16SKris Kennaway send_status(id, status); 635b66f2d16SKris Kennaway } 636b66f2d16SKris Kennaway 637ae1f160dSDag-Erling Smørgrav static void 638b66f2d16SKris Kennaway process_write(void) 639b66f2d16SKris Kennaway { 6401e8db6e2SBrian Feldman u_int32_t id; 641b66f2d16SKris Kennaway u_int64_t off; 642b66f2d16SKris Kennaway u_int len; 643b15c8340SDag-Erling Smørgrav int handle, fd, ret, status; 644b66f2d16SKris Kennaway char *data; 645b66f2d16SKris Kennaway 646b66f2d16SKris Kennaway id = get_int(); 647b66f2d16SKris Kennaway handle = get_handle(); 6481e8db6e2SBrian Feldman off = get_int64(); 649b66f2d16SKris Kennaway data = get_string(&len); 650b66f2d16SKris Kennaway 651761efaa7SDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %d", 652761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 653b66f2d16SKris Kennaway fd = handle_to_fd(handle); 654b15c8340SDag-Erling Smørgrav 655b15c8340SDag-Erling Smørgrav if (fd < 0) 656b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 657b15c8340SDag-Erling Smørgrav else if (readonly) 658b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 659b15c8340SDag-Erling Smørgrav else { 660b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 661b66f2d16SKris Kennaway status = errno_to_portable(errno); 662b66f2d16SKris Kennaway error("process_write: seek failed"); 663b66f2d16SKris Kennaway } else { 664b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 665b66f2d16SKris Kennaway ret = write(fd, data, len); 666043840dfSDag-Erling Smørgrav if (ret < 0) { 667b66f2d16SKris Kennaway error("process_write: write failed"); 668b66f2d16SKris Kennaway status = errno_to_portable(errno); 669043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 6701e8db6e2SBrian Feldman status = SSH2_FX_OK; 671761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 672b66f2d16SKris Kennaway } else { 673761efaa7SDag-Erling Smørgrav debug2("nothing at all written"); 674b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 675b66f2d16SKris Kennaway } 676b66f2d16SKris Kennaway } 677b66f2d16SKris Kennaway } 678b66f2d16SKris Kennaway send_status(id, status); 679b66f2d16SKris Kennaway xfree(data); 680b66f2d16SKris Kennaway } 681b66f2d16SKris Kennaway 682ae1f160dSDag-Erling Smørgrav static void 683b66f2d16SKris Kennaway process_do_stat(int do_lstat) 684b66f2d16SKris Kennaway { 6851e8db6e2SBrian Feldman Attrib a; 686b66f2d16SKris Kennaway struct stat st; 687b66f2d16SKris Kennaway u_int32_t id; 688b66f2d16SKris Kennaway char *name; 6891e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 690b66f2d16SKris Kennaway 691b66f2d16SKris Kennaway id = get_int(); 692b66f2d16SKris Kennaway name = get_string(NULL); 693761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 694761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 695b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 696b66f2d16SKris Kennaway if (ret < 0) { 697b66f2d16SKris Kennaway status = errno_to_portable(errno); 698b66f2d16SKris Kennaway } else { 6991e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 7001e8db6e2SBrian Feldman send_attrib(id, &a); 7011e8db6e2SBrian Feldman status = SSH2_FX_OK; 702b66f2d16SKris Kennaway } 7031e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 704b66f2d16SKris Kennaway send_status(id, status); 705b66f2d16SKris Kennaway xfree(name); 706b66f2d16SKris Kennaway } 707b66f2d16SKris Kennaway 708ae1f160dSDag-Erling Smørgrav static void 709b66f2d16SKris Kennaway process_stat(void) 710b66f2d16SKris Kennaway { 711b66f2d16SKris Kennaway process_do_stat(0); 712b66f2d16SKris Kennaway } 713b66f2d16SKris Kennaway 714ae1f160dSDag-Erling Smørgrav static void 715b66f2d16SKris Kennaway process_lstat(void) 716b66f2d16SKris Kennaway { 717b66f2d16SKris Kennaway process_do_stat(1); 718b66f2d16SKris Kennaway } 719b66f2d16SKris Kennaway 720ae1f160dSDag-Erling Smørgrav static void 721b66f2d16SKris Kennaway process_fstat(void) 722b66f2d16SKris Kennaway { 7231e8db6e2SBrian Feldman Attrib a; 724b66f2d16SKris Kennaway struct stat st; 725b66f2d16SKris Kennaway u_int32_t id; 7261e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 727b66f2d16SKris Kennaway 728b66f2d16SKris Kennaway id = get_int(); 729b66f2d16SKris Kennaway handle = get_handle(); 730761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 731761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 732b66f2d16SKris Kennaway fd = handle_to_fd(handle); 733b66f2d16SKris Kennaway if (fd >= 0) { 734b66f2d16SKris Kennaway ret = fstat(fd, &st); 735b66f2d16SKris Kennaway if (ret < 0) { 736b66f2d16SKris Kennaway status = errno_to_portable(errno); 737b66f2d16SKris Kennaway } else { 7381e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 7391e8db6e2SBrian Feldman send_attrib(id, &a); 7401e8db6e2SBrian Feldman status = SSH2_FX_OK; 741b66f2d16SKris Kennaway } 742b66f2d16SKris Kennaway } 7431e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 744b66f2d16SKris Kennaway send_status(id, status); 745b66f2d16SKris Kennaway } 746b66f2d16SKris Kennaway 747ae1f160dSDag-Erling Smørgrav static struct timeval * 748efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 749b66f2d16SKris Kennaway { 750b66f2d16SKris Kennaway static struct timeval tv[2]; 7511e8db6e2SBrian Feldman 752b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 753b66f2d16SKris Kennaway tv[0].tv_usec = 0; 754b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 755b66f2d16SKris Kennaway tv[1].tv_usec = 0; 756b66f2d16SKris Kennaway return tv; 757b66f2d16SKris Kennaway } 758b66f2d16SKris Kennaway 759ae1f160dSDag-Erling Smørgrav static void 760b66f2d16SKris Kennaway process_setstat(void) 761b66f2d16SKris Kennaway { 762b66f2d16SKris Kennaway Attrib *a; 763b66f2d16SKris Kennaway u_int32_t id; 764b66f2d16SKris Kennaway char *name; 765ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 766b66f2d16SKris Kennaway 767b66f2d16SKris Kennaway id = get_int(); 768b66f2d16SKris Kennaway name = get_string(NULL); 769b66f2d16SKris Kennaway a = get_attrib(); 770761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 771b15c8340SDag-Erling Smørgrav if (readonly) { 772b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 773b15c8340SDag-Erling Smørgrav a->flags = 0; 774b15c8340SDag-Erling Smørgrav } 775ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 776d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 777d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 778ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 779ae1f160dSDag-Erling Smørgrav if (ret == -1) 780ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 781ae1f160dSDag-Erling Smørgrav } 7821e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 783761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 784d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 785b66f2d16SKris Kennaway if (ret == -1) 786b66f2d16SKris Kennaway status = errno_to_portable(errno); 787b66f2d16SKris Kennaway } 7881e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 789761efaa7SDag-Erling Smørgrav char buf[64]; 790761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 791761efaa7SDag-Erling Smørgrav 792761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 793761efaa7SDag-Erling Smørgrav localtime(&t)); 794761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 795b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 796b66f2d16SKris Kennaway if (ret == -1) 797b66f2d16SKris Kennaway status = errno_to_portable(errno); 798b66f2d16SKris Kennaway } 7991e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 800761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 801761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 8021e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 8031e8db6e2SBrian Feldman if (ret == -1) 8041e8db6e2SBrian Feldman status = errno_to_portable(errno); 8051e8db6e2SBrian Feldman } 806b66f2d16SKris Kennaway send_status(id, status); 807b66f2d16SKris Kennaway xfree(name); 808b66f2d16SKris Kennaway } 809b66f2d16SKris Kennaway 810ae1f160dSDag-Erling Smørgrav static void 811b66f2d16SKris Kennaway process_fsetstat(void) 812b66f2d16SKris Kennaway { 813b66f2d16SKris Kennaway Attrib *a; 814b66f2d16SKris Kennaway u_int32_t id; 815b66f2d16SKris Kennaway int handle, fd, ret; 8161e8db6e2SBrian Feldman int status = SSH2_FX_OK; 817b66f2d16SKris Kennaway 818b66f2d16SKris Kennaway id = get_int(); 819b66f2d16SKris Kennaway handle = get_handle(); 820b66f2d16SKris Kennaway a = get_attrib(); 821761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 822b66f2d16SKris Kennaway fd = handle_to_fd(handle); 823b15c8340SDag-Erling Smørgrav if (fd < 0) 8241e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 825b15c8340SDag-Erling Smørgrav else if (readonly) 826b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 827b15c8340SDag-Erling Smørgrav else { 828761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 829761efaa7SDag-Erling Smørgrav 830ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 831d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 832d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 833ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 834ae1f160dSDag-Erling Smørgrav if (ret == -1) 835ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 836ae1f160dSDag-Erling Smørgrav } 8371e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 838761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 83983d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 840d4af9e69SDag-Erling Smørgrav ret = fchmod(fd, a->perm & 07777); 84183d2307dSDag-Erling Smørgrav #else 842d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 84383d2307dSDag-Erling Smørgrav #endif 844b66f2d16SKris Kennaway if (ret == -1) 845b66f2d16SKris Kennaway status = errno_to_portable(errno); 846b66f2d16SKris Kennaway } 8471e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 848761efaa7SDag-Erling Smørgrav char buf[64]; 849761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 850761efaa7SDag-Erling Smørgrav 851761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 852761efaa7SDag-Erling Smørgrav localtime(&t)); 853761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 85483d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 855b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 85683d2307dSDag-Erling Smørgrav #else 85783d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 85883d2307dSDag-Erling Smørgrav #endif 859b66f2d16SKris Kennaway if (ret == -1) 860b66f2d16SKris Kennaway status = errno_to_portable(errno); 861b66f2d16SKris Kennaway } 8621e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 863761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 864761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 86583d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 8661e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 86783d2307dSDag-Erling Smørgrav #else 86883d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 86983d2307dSDag-Erling Smørgrav #endif 8701e8db6e2SBrian Feldman if (ret == -1) 8711e8db6e2SBrian Feldman status = errno_to_portable(errno); 8721e8db6e2SBrian Feldman } 873b66f2d16SKris Kennaway } 874b66f2d16SKris Kennaway send_status(id, status); 875b66f2d16SKris Kennaway } 876b66f2d16SKris Kennaway 877ae1f160dSDag-Erling Smørgrav static void 878b66f2d16SKris Kennaway process_opendir(void) 879b66f2d16SKris Kennaway { 880b66f2d16SKris Kennaway DIR *dirp = NULL; 881b66f2d16SKris Kennaway char *path; 8821e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 883b66f2d16SKris Kennaway u_int32_t id; 884b66f2d16SKris Kennaway 885b66f2d16SKris Kennaway id = get_int(); 886b66f2d16SKris Kennaway path = get_string(NULL); 887761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 888761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 889b66f2d16SKris Kennaway dirp = opendir(path); 890b66f2d16SKris Kennaway if (dirp == NULL) { 891b66f2d16SKris Kennaway status = errno_to_portable(errno); 892b66f2d16SKris Kennaway } else { 893d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, dirp); 894b66f2d16SKris Kennaway if (handle < 0) { 895b66f2d16SKris Kennaway closedir(dirp); 896b66f2d16SKris Kennaway } else { 897b66f2d16SKris Kennaway send_handle(id, handle); 8981e8db6e2SBrian Feldman status = SSH2_FX_OK; 899b66f2d16SKris Kennaway } 900b66f2d16SKris Kennaway 901b66f2d16SKris Kennaway } 9021e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 903b66f2d16SKris Kennaway send_status(id, status); 904b66f2d16SKris Kennaway xfree(path); 905b66f2d16SKris Kennaway } 906b66f2d16SKris Kennaway 907ae1f160dSDag-Erling Smørgrav static void 908b66f2d16SKris Kennaway process_readdir(void) 909b66f2d16SKris Kennaway { 910b66f2d16SKris Kennaway DIR *dirp; 911b66f2d16SKris Kennaway struct dirent *dp; 912b66f2d16SKris Kennaway char *path; 913b66f2d16SKris Kennaway int handle; 914b66f2d16SKris Kennaway u_int32_t id; 915b66f2d16SKris Kennaway 916b66f2d16SKris Kennaway id = get_int(); 917b66f2d16SKris Kennaway handle = get_handle(); 918761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 919761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 920b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 921b66f2d16SKris Kennaway path = handle_to_name(handle); 922b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 9231e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 924b66f2d16SKris Kennaway } else { 925b66f2d16SKris Kennaway struct stat st; 926761efaa7SDag-Erling Smørgrav char pathname[MAXPATHLEN]; 927b66f2d16SKris Kennaway Stat *stats; 928b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 929ee21a45fSDag-Erling Smørgrav 930761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 931b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 932b66f2d16SKris Kennaway if (count >= nstats) { 933b66f2d16SKris Kennaway nstats *= 2; 934761efaa7SDag-Erling Smørgrav stats = xrealloc(stats, nstats, sizeof(Stat)); 935b66f2d16SKris Kennaway } 936b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 937ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 938ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 939b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 940b66f2d16SKris Kennaway continue; 9411e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 942b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 943b15c8340SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); 944b66f2d16SKris Kennaway count++; 945b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 9461e8db6e2SBrian Feldman /* XXX check packet size instead */ 947b66f2d16SKris Kennaway if (count == 100) 948b66f2d16SKris Kennaway break; 949b66f2d16SKris Kennaway } 9501e8db6e2SBrian Feldman if (count > 0) { 951b66f2d16SKris Kennaway send_names(id, count, stats); 952b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 953b66f2d16SKris Kennaway xfree(stats[i].name); 954b66f2d16SKris Kennaway xfree(stats[i].long_name); 955b66f2d16SKris Kennaway } 9561e8db6e2SBrian Feldman } else { 9571e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 9581e8db6e2SBrian Feldman } 959b66f2d16SKris Kennaway xfree(stats); 960b66f2d16SKris Kennaway } 961b66f2d16SKris Kennaway } 962b66f2d16SKris Kennaway 963ae1f160dSDag-Erling Smørgrav static void 964b66f2d16SKris Kennaway process_remove(void) 965b66f2d16SKris Kennaway { 966b66f2d16SKris Kennaway char *name; 967b66f2d16SKris Kennaway u_int32_t id; 9681e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 969b66f2d16SKris Kennaway int ret; 970b66f2d16SKris Kennaway 971b66f2d16SKris Kennaway id = get_int(); 972b66f2d16SKris Kennaway name = get_string(NULL); 973761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 974761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 975b15c8340SDag-Erling Smørgrav if (readonly) 976b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 977b15c8340SDag-Erling Smørgrav else { 9781e8db6e2SBrian Feldman ret = unlink(name); 9791e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 980b15c8340SDag-Erling Smørgrav } 981b66f2d16SKris Kennaway send_status(id, status); 982b66f2d16SKris Kennaway xfree(name); 983b66f2d16SKris Kennaway } 984b66f2d16SKris Kennaway 985ae1f160dSDag-Erling Smørgrav static void 986b66f2d16SKris Kennaway process_mkdir(void) 987b66f2d16SKris Kennaway { 988b66f2d16SKris Kennaway Attrib *a; 989b66f2d16SKris Kennaway u_int32_t id; 990b66f2d16SKris Kennaway char *name; 9911e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 992b66f2d16SKris Kennaway 993b66f2d16SKris Kennaway id = get_int(); 994b66f2d16SKris Kennaway name = get_string(NULL); 995b66f2d16SKris Kennaway a = get_attrib(); 9961e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 997d4af9e69SDag-Erling Smørgrav a->perm & 07777 : 0777; 998761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 999761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1000b15c8340SDag-Erling Smørgrav if (readonly) 1001b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1002b15c8340SDag-Erling Smørgrav else { 1003b66f2d16SKris Kennaway ret = mkdir(name, mode); 10041e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1005b15c8340SDag-Erling Smørgrav } 1006b66f2d16SKris Kennaway send_status(id, status); 1007b66f2d16SKris Kennaway xfree(name); 1008b66f2d16SKris Kennaway } 1009b66f2d16SKris Kennaway 1010ae1f160dSDag-Erling Smørgrav static void 1011b66f2d16SKris Kennaway process_rmdir(void) 1012b66f2d16SKris Kennaway { 1013b66f2d16SKris Kennaway u_int32_t id; 1014b66f2d16SKris Kennaway char *name; 1015b66f2d16SKris Kennaway int ret, status; 1016b66f2d16SKris Kennaway 1017b66f2d16SKris Kennaway id = get_int(); 1018b66f2d16SKris Kennaway name = get_string(NULL); 1019761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1020761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1021b15c8340SDag-Erling Smørgrav if (readonly) 1022b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1023b15c8340SDag-Erling Smørgrav else { 1024b66f2d16SKris Kennaway ret = rmdir(name); 10251e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1026b15c8340SDag-Erling Smørgrav } 1027b66f2d16SKris Kennaway send_status(id, status); 1028b66f2d16SKris Kennaway xfree(name); 1029b66f2d16SKris Kennaway } 1030b66f2d16SKris Kennaway 1031ae1f160dSDag-Erling Smørgrav static void 1032b66f2d16SKris Kennaway process_realpath(void) 1033b66f2d16SKris Kennaway { 1034b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 1035b66f2d16SKris Kennaway u_int32_t id; 1036b66f2d16SKris Kennaway char *path; 1037b66f2d16SKris Kennaway 1038b66f2d16SKris Kennaway id = get_int(); 1039b66f2d16SKris Kennaway path = get_string(NULL); 10401e8db6e2SBrian Feldman if (path[0] == '\0') { 10411e8db6e2SBrian Feldman xfree(path); 10421e8db6e2SBrian Feldman path = xstrdup("."); 10431e8db6e2SBrian Feldman } 1044761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1045761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 1046b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 1047b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1048b66f2d16SKris Kennaway } else { 1049b66f2d16SKris Kennaway Stat s; 1050b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1051b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1052b66f2d16SKris Kennaway send_names(id, 1, &s); 1053b66f2d16SKris Kennaway } 1054b66f2d16SKris Kennaway xfree(path); 1055b66f2d16SKris Kennaway } 1056b66f2d16SKris Kennaway 1057ae1f160dSDag-Erling Smørgrav static void 1058b66f2d16SKris Kennaway process_rename(void) 1059b66f2d16SKris Kennaway { 1060b66f2d16SKris Kennaway u_int32_t id; 1061b66f2d16SKris Kennaway char *oldpath, *newpath; 1062d0c8c0bcSDag-Erling Smørgrav int status; 1063d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1064b66f2d16SKris Kennaway 1065b66f2d16SKris Kennaway id = get_int(); 1066b66f2d16SKris Kennaway oldpath = get_string(NULL); 1067b66f2d16SKris Kennaway newpath = get_string(NULL); 1068761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1069761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1070d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1071b15c8340SDag-Erling Smørgrav if (readonly) 1072b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1073b15c8340SDag-Erling Smørgrav else if (lstat(oldpath, &sb) == -1) 1074d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1075d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1076d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1077d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 10787aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1079d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1080d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1081d4af9e69SDag-Erling Smørgrav #endif 1082d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1083d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1084d74d50a8SDag-Erling Smørgrav #endif 1085d74d50a8SDag-Erling Smørgrav ) { 1086d74d50a8SDag-Erling Smørgrav struct stat st; 1087d74d50a8SDag-Erling Smørgrav 1088d74d50a8SDag-Erling Smørgrav /* 1089d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1090d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1091d74d50a8SDag-Erling Smørgrav */ 1092d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1093d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1094d74d50a8SDag-Erling Smørgrav status = 1095d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1096d74d50a8SDag-Erling Smørgrav else 1097d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1098d74d50a8SDag-Erling Smørgrav } 1099d74d50a8SDag-Erling Smørgrav } else { 1100d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1101d74d50a8SDag-Erling Smørgrav } 1102d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1103d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1104d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1105d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1106d0c8c0bcSDag-Erling Smørgrav } else 1107d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1108d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1109d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1110d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1111d0c8c0bcSDag-Erling Smørgrav else 1112d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 11131e8db6e2SBrian Feldman } 1114b66f2d16SKris Kennaway send_status(id, status); 1115b66f2d16SKris Kennaway xfree(oldpath); 1116b66f2d16SKris Kennaway xfree(newpath); 1117b66f2d16SKris Kennaway } 1118b66f2d16SKris Kennaway 1119ae1f160dSDag-Erling Smørgrav static void 11201e8db6e2SBrian Feldman process_readlink(void) 11211e8db6e2SBrian Feldman { 11221e8db6e2SBrian Feldman u_int32_t id; 1123ae1f160dSDag-Erling Smørgrav int len; 1124d74d50a8SDag-Erling Smørgrav char buf[MAXPATHLEN]; 11251e8db6e2SBrian Feldman char *path; 11261e8db6e2SBrian Feldman 11271e8db6e2SBrian Feldman id = get_int(); 11281e8db6e2SBrian Feldman path = get_string(NULL); 1129761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1130761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1131d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 11321e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 11331e8db6e2SBrian Feldman else { 11341e8db6e2SBrian Feldman Stat s; 11351e8db6e2SBrian Feldman 1136d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 11371e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1138d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 11391e8db6e2SBrian Feldman send_names(id, 1, &s); 11401e8db6e2SBrian Feldman } 11411e8db6e2SBrian Feldman xfree(path); 11421e8db6e2SBrian Feldman } 11431e8db6e2SBrian Feldman 1144ae1f160dSDag-Erling Smørgrav static void 11451e8db6e2SBrian Feldman process_symlink(void) 11461e8db6e2SBrian Feldman { 11471e8db6e2SBrian Feldman u_int32_t id; 11481e8db6e2SBrian Feldman char *oldpath, *newpath; 1149d0c8c0bcSDag-Erling Smørgrav int ret, status; 11501e8db6e2SBrian Feldman 11511e8db6e2SBrian Feldman id = get_int(); 11521e8db6e2SBrian Feldman oldpath = get_string(NULL); 11531e8db6e2SBrian Feldman newpath = get_string(NULL); 1154761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1155761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1156d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 1157b15c8340SDag-Erling Smørgrav if (readonly) 1158b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1159b15c8340SDag-Erling Smørgrav else { 11601e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 11611e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1162b15c8340SDag-Erling Smørgrav } 11631e8db6e2SBrian Feldman send_status(id, status); 11641e8db6e2SBrian Feldman xfree(oldpath); 11651e8db6e2SBrian Feldman xfree(newpath); 11661e8db6e2SBrian Feldman } 11671e8db6e2SBrian Feldman 1168ae1f160dSDag-Erling Smørgrav static void 1169d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1170d4af9e69SDag-Erling Smørgrav { 1171d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1172b15c8340SDag-Erling Smørgrav int ret, status; 1173d4af9e69SDag-Erling Smørgrav 1174d4af9e69SDag-Erling Smørgrav oldpath = get_string(NULL); 1175d4af9e69SDag-Erling Smørgrav newpath = get_string(NULL); 1176d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1177d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1178b15c8340SDag-Erling Smørgrav if (readonly) 1179b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1180b15c8340SDag-Erling Smørgrav else { 1181b15c8340SDag-Erling Smørgrav ret = rename(oldpath, newpath); 1182b15c8340SDag-Erling Smørgrav status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1183b15c8340SDag-Erling Smørgrav } 1184b15c8340SDag-Erling Smørgrav send_status(id, status); 1185d4af9e69SDag-Erling Smørgrav xfree(oldpath); 1186d4af9e69SDag-Erling Smørgrav xfree(newpath); 1187d4af9e69SDag-Erling Smørgrav } 1188d4af9e69SDag-Erling Smørgrav 1189d4af9e69SDag-Erling Smørgrav static void 1190d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1191d4af9e69SDag-Erling Smørgrav { 1192d4af9e69SDag-Erling Smørgrav char *path; 1193d4af9e69SDag-Erling Smørgrav struct statvfs st; 1194d4af9e69SDag-Erling Smørgrav 1195d4af9e69SDag-Erling Smørgrav path = get_string(NULL); 1196d4af9e69SDag-Erling Smørgrav debug3("request %u: statfs", id); 1197d4af9e69SDag-Erling Smørgrav logit("statfs \"%s\"", path); 1198d4af9e69SDag-Erling Smørgrav 1199d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1200d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1201d4af9e69SDag-Erling Smørgrav else 1202d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1203d4af9e69SDag-Erling Smørgrav xfree(path); 1204d4af9e69SDag-Erling Smørgrav } 1205d4af9e69SDag-Erling Smørgrav 1206d4af9e69SDag-Erling Smørgrav static void 1207d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1208d4af9e69SDag-Erling Smørgrav { 1209d4af9e69SDag-Erling Smørgrav int handle, fd; 1210d4af9e69SDag-Erling Smørgrav struct statvfs st; 1211d4af9e69SDag-Erling Smørgrav 1212d4af9e69SDag-Erling Smørgrav handle = get_handle(); 1213d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1214d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1215d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1216d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1217d4af9e69SDag-Erling Smørgrav return; 1218d4af9e69SDag-Erling Smørgrav } 1219d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1220d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1221d4af9e69SDag-Erling Smørgrav else 1222d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1223d4af9e69SDag-Erling Smørgrav } 1224d4af9e69SDag-Erling Smørgrav 1225d4af9e69SDag-Erling Smørgrav static void 12261e8db6e2SBrian Feldman process_extended(void) 12271e8db6e2SBrian Feldman { 12281e8db6e2SBrian Feldman u_int32_t id; 12291e8db6e2SBrian Feldman char *request; 12301e8db6e2SBrian Feldman 12311e8db6e2SBrian Feldman id = get_int(); 12321e8db6e2SBrian Feldman request = get_string(NULL); 1233d4af9e69SDag-Erling Smørgrav if (strcmp(request, "posix-rename@openssh.com") == 0) 1234d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(id); 1235d4af9e69SDag-Erling Smørgrav else if (strcmp(request, "statvfs@openssh.com") == 0) 1236d4af9e69SDag-Erling Smørgrav process_extended_statvfs(id); 1237d4af9e69SDag-Erling Smørgrav else if (strcmp(request, "fstatvfs@openssh.com") == 0) 1238d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(id); 1239d4af9e69SDag-Erling Smørgrav else 12401e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 12411e8db6e2SBrian Feldman xfree(request); 12421e8db6e2SBrian Feldman } 1243b66f2d16SKris Kennaway 1244b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1245b66f2d16SKris Kennaway 1246ae1f160dSDag-Erling Smørgrav static void 1247b66f2d16SKris Kennaway process(void) 1248b66f2d16SKris Kennaway { 12491e8db6e2SBrian Feldman u_int msg_len; 1250545d5ecaSDag-Erling Smørgrav u_int buf_len; 1251545d5ecaSDag-Erling Smørgrav u_int consumed; 12521e8db6e2SBrian Feldman u_int type; 12531e8db6e2SBrian Feldman u_char *cp; 1254b66f2d16SKris Kennaway 1255545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 1256545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1257b66f2d16SKris Kennaway return; /* Incomplete message. */ 1258ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 1259761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1260021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1261761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1262761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1263d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1264b66f2d16SKris Kennaway } 1265545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1266b66f2d16SKris Kennaway return; 1267b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 1268545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1269b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 1270b66f2d16SKris Kennaway switch (type) { 12711e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1272b66f2d16SKris Kennaway process_init(); 1273b66f2d16SKris Kennaway break; 12741e8db6e2SBrian Feldman case SSH2_FXP_OPEN: 1275b66f2d16SKris Kennaway process_open(); 1276b66f2d16SKris Kennaway break; 12771e8db6e2SBrian Feldman case SSH2_FXP_CLOSE: 1278b66f2d16SKris Kennaway process_close(); 1279b66f2d16SKris Kennaway break; 12801e8db6e2SBrian Feldman case SSH2_FXP_READ: 1281b66f2d16SKris Kennaway process_read(); 1282b66f2d16SKris Kennaway break; 12831e8db6e2SBrian Feldman case SSH2_FXP_WRITE: 1284b66f2d16SKris Kennaway process_write(); 1285b66f2d16SKris Kennaway break; 12861e8db6e2SBrian Feldman case SSH2_FXP_LSTAT: 1287b66f2d16SKris Kennaway process_lstat(); 1288b66f2d16SKris Kennaway break; 12891e8db6e2SBrian Feldman case SSH2_FXP_FSTAT: 1290b66f2d16SKris Kennaway process_fstat(); 1291b66f2d16SKris Kennaway break; 12921e8db6e2SBrian Feldman case SSH2_FXP_SETSTAT: 1293b66f2d16SKris Kennaway process_setstat(); 1294b66f2d16SKris Kennaway break; 12951e8db6e2SBrian Feldman case SSH2_FXP_FSETSTAT: 1296b66f2d16SKris Kennaway process_fsetstat(); 1297b66f2d16SKris Kennaway break; 12981e8db6e2SBrian Feldman case SSH2_FXP_OPENDIR: 1299b66f2d16SKris Kennaway process_opendir(); 1300b66f2d16SKris Kennaway break; 13011e8db6e2SBrian Feldman case SSH2_FXP_READDIR: 1302b66f2d16SKris Kennaway process_readdir(); 1303b66f2d16SKris Kennaway break; 13041e8db6e2SBrian Feldman case SSH2_FXP_REMOVE: 1305b66f2d16SKris Kennaway process_remove(); 1306b66f2d16SKris Kennaway break; 13071e8db6e2SBrian Feldman case SSH2_FXP_MKDIR: 1308b66f2d16SKris Kennaway process_mkdir(); 1309b66f2d16SKris Kennaway break; 13101e8db6e2SBrian Feldman case SSH2_FXP_RMDIR: 1311b66f2d16SKris Kennaway process_rmdir(); 1312b66f2d16SKris Kennaway break; 13131e8db6e2SBrian Feldman case SSH2_FXP_REALPATH: 1314b66f2d16SKris Kennaway process_realpath(); 1315b66f2d16SKris Kennaway break; 13161e8db6e2SBrian Feldman case SSH2_FXP_STAT: 1317b66f2d16SKris Kennaway process_stat(); 1318b66f2d16SKris Kennaway break; 13191e8db6e2SBrian Feldman case SSH2_FXP_RENAME: 1320b66f2d16SKris Kennaway process_rename(); 1321b66f2d16SKris Kennaway break; 13221e8db6e2SBrian Feldman case SSH2_FXP_READLINK: 13231e8db6e2SBrian Feldman process_readlink(); 13241e8db6e2SBrian Feldman break; 13251e8db6e2SBrian Feldman case SSH2_FXP_SYMLINK: 13261e8db6e2SBrian Feldman process_symlink(); 13271e8db6e2SBrian Feldman break; 13281e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 13291e8db6e2SBrian Feldman process_extended(); 13301e8db6e2SBrian Feldman break; 1331b66f2d16SKris Kennaway default: 1332b66f2d16SKris Kennaway error("Unknown message %d", type); 1333b66f2d16SKris Kennaway break; 1334b66f2d16SKris Kennaway } 1335545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1336d4af9e69SDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) { 1337d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1338d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1339d4af9e69SDag-Erling Smørgrav } 1340545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1341d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1342d4af9e69SDag-Erling Smørgrav error("msg_len %d < consumed %d", msg_len, consumed); 1343d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1344d4af9e69SDag-Erling Smørgrav } 1345545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1346545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1347b66f2d16SKris Kennaway } 1348b66f2d16SKris Kennaway 1349761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1350761efaa7SDag-Erling Smørgrav void 1351d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1352761efaa7SDag-Erling Smørgrav { 1353761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1354761efaa7SDag-Erling Smørgrav handle_log_exit(); 1355761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1356761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1357761efaa7SDag-Erling Smørgrav } 1358761efaa7SDag-Erling Smørgrav _exit(i); 1359761efaa7SDag-Erling Smørgrav } 1360761efaa7SDag-Erling Smørgrav 1361761efaa7SDag-Erling Smørgrav static void 1362d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1363761efaa7SDag-Erling Smørgrav { 1364761efaa7SDag-Erling Smørgrav extern char *__progname; 1365761efaa7SDag-Erling Smørgrav 1366761efaa7SDag-Erling Smørgrav fprintf(stderr, 1367b15c8340SDag-Erling Smørgrav "usage: %s [-ehR] [-f log_facility] [-l log_level] [-u umask]\n", 1368b15c8340SDag-Erling Smørgrav __progname); 1369761efaa7SDag-Erling Smørgrav exit(1); 1370761efaa7SDag-Erling Smørgrav } 1371761efaa7SDag-Erling Smørgrav 1372b66f2d16SKris Kennaway int 1373d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1374b66f2d16SKris Kennaway { 13751e8db6e2SBrian Feldman fd_set *rset, *wset; 1376761efaa7SDag-Erling Smørgrav int in, out, max, ch, skipargs = 0, log_stderr = 0; 13771e8db6e2SBrian Feldman ssize_t len, olen, set_size; 1378761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 1379d4af9e69SDag-Erling Smørgrav char *cp, buf[4*4096]; 1380b15c8340SDag-Erling Smørgrav const char *errmsg; 1381b15c8340SDag-Erling Smørgrav mode_t mask; 1382761efaa7SDag-Erling Smørgrav 1383761efaa7SDag-Erling Smørgrav extern char *optarg; 1384761efaa7SDag-Erling Smørgrav extern char *__progname; 13851e8db6e2SBrian Feldman 1386761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1387761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1388b66f2d16SKris Kennaway 1389b15c8340SDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, "f:l:u:cehR")) != -1) { 1390761efaa7SDag-Erling Smørgrav switch (ch) { 1391b15c8340SDag-Erling Smørgrav case 'R': 1392b15c8340SDag-Erling Smørgrav readonly = 1; 1393b15c8340SDag-Erling Smørgrav break; 1394761efaa7SDag-Erling Smørgrav case 'c': 1395761efaa7SDag-Erling Smørgrav /* 1396761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1397761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1398761efaa7SDag-Erling Smørgrav */ 1399761efaa7SDag-Erling Smørgrav skipargs = 1; 1400761efaa7SDag-Erling Smørgrav break; 1401761efaa7SDag-Erling Smørgrav case 'e': 1402761efaa7SDag-Erling Smørgrav log_stderr = 1; 1403761efaa7SDag-Erling Smørgrav break; 1404761efaa7SDag-Erling Smørgrav case 'l': 1405761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1406761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1407761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1408761efaa7SDag-Erling Smørgrav break; 1409761efaa7SDag-Erling Smørgrav case 'f': 1410761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1411d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1412761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1413761efaa7SDag-Erling Smørgrav break; 1414b15c8340SDag-Erling Smørgrav case 'u': 1415b15c8340SDag-Erling Smørgrav mask = (mode_t)strtonum(optarg, 0, 0777, &errmsg); 1416b15c8340SDag-Erling Smørgrav if (errmsg != NULL) 1417b15c8340SDag-Erling Smørgrav fatal("Invalid umask \"%s\": %s", 1418b15c8340SDag-Erling Smørgrav optarg, errmsg); 1419b15c8340SDag-Erling Smørgrav (void)umask(mask); 1420b15c8340SDag-Erling Smørgrav break; 1421761efaa7SDag-Erling Smørgrav case 'h': 1422761efaa7SDag-Erling Smørgrav default: 1423d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1424761efaa7SDag-Erling Smørgrav } 1425761efaa7SDag-Erling Smørgrav } 1426761efaa7SDag-Erling Smørgrav 1427761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1428761efaa7SDag-Erling Smørgrav 1429761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1430761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1431d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1432d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1433761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1434d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1435d4af9e69SDag-Erling Smørgrav } 1436761efaa7SDag-Erling Smørgrav *cp = '\0'; 1437761efaa7SDag-Erling Smørgrav } else 1438761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1439761efaa7SDag-Erling Smørgrav 1440d4af9e69SDag-Erling Smørgrav pw = pwcopy(user_pw); 1441761efaa7SDag-Erling Smørgrav 1442761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1443761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1444761efaa7SDag-Erling Smørgrav 1445b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 1446b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 1447b66f2d16SKris Kennaway 144883d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 144983d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 145083d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 145183d2307dSDag-Erling Smørgrav #endif 145283d2307dSDag-Erling Smørgrav 1453b66f2d16SKris Kennaway max = 0; 1454b66f2d16SKris Kennaway if (in > max) 1455b66f2d16SKris Kennaway max = in; 1456b66f2d16SKris Kennaway if (out > max) 1457b66f2d16SKris Kennaway max = out; 1458b66f2d16SKris Kennaway 1459b66f2d16SKris Kennaway buffer_init(&iqueue); 1460b66f2d16SKris Kennaway buffer_init(&oqueue); 1461b66f2d16SKris Kennaway 14621e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 14631e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 14641e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1465b66f2d16SKris Kennaway 14661e8db6e2SBrian Feldman for (;;) { 14671e8db6e2SBrian Feldman memset(rset, 0, set_size); 14681e8db6e2SBrian Feldman memset(wset, 0, set_size); 14691e8db6e2SBrian Feldman 1470d4af9e69SDag-Erling Smørgrav /* 1471d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1472d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1473d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1474d4af9e69SDag-Erling Smørgrav */ 1475d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&iqueue, sizeof(buf)) && 1476d4af9e69SDag-Erling Smørgrav buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 14771e8db6e2SBrian Feldman FD_SET(in, rset); 1478d4af9e69SDag-Erling Smørgrav 1479b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1480b66f2d16SKris Kennaway if (olen > 0) 14811e8db6e2SBrian Feldman FD_SET(out, wset); 1482b66f2d16SKris Kennaway 14831e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1484b66f2d16SKris Kennaway if (errno == EINTR) 1485b66f2d16SKris Kennaway continue; 1486761efaa7SDag-Erling Smørgrav error("select: %s", strerror(errno)); 1487d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1488b66f2d16SKris Kennaway } 1489b66f2d16SKris Kennaway 1490b66f2d16SKris Kennaway /* copy stdin to iqueue */ 14911e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1492b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1493b66f2d16SKris Kennaway if (len == 0) { 1494b66f2d16SKris Kennaway debug("read eof"); 1495d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 1496b66f2d16SKris Kennaway } else if (len < 0) { 1497761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1498d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1499b66f2d16SKris Kennaway } else { 1500b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1501b66f2d16SKris Kennaway } 1502b66f2d16SKris Kennaway } 1503b66f2d16SKris Kennaway /* send oqueue to stdout */ 15041e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1505b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1506b66f2d16SKris Kennaway if (len < 0) { 1507761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1508d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1509b66f2d16SKris Kennaway } else { 1510b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1511b66f2d16SKris Kennaway } 1512b66f2d16SKris Kennaway } 1513d4af9e69SDag-Erling Smørgrav 1514d4af9e69SDag-Erling Smørgrav /* 1515d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1516d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1517d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1518d4af9e69SDag-Erling Smørgrav */ 1519d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1520b66f2d16SKris Kennaway process(); 1521b66f2d16SKris Kennaway } 1522b66f2d16SKris Kennaway } 1523