1b66f2d16SKris Kennaway /* 2ae1f160dSDag-Erling Smørgrav * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. 3b66f2d16SKris Kennaway * 4b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 5b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 6b66f2d16SKris Kennaway * are met: 7b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 8b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 9b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 10b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 11b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 12b66f2d16SKris Kennaway * 13b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23b66f2d16SKris Kennaway */ 24b66f2d16SKris Kennaway #include "includes.h" 25d95e11bfSDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.43 2003/06/25 22:39:36 miod Exp $"); 26b66f2d16SKris Kennaway 27b66f2d16SKris Kennaway #include "buffer.h" 28b66f2d16SKris Kennaway #include "bufaux.h" 29b66f2d16SKris Kennaway #include "getput.h" 301e8db6e2SBrian Feldman #include "log.h" 31b66f2d16SKris Kennaway #include "xmalloc.h" 32b66f2d16SKris Kennaway 331e8db6e2SBrian Feldman #include "sftp.h" 341e8db6e2SBrian Feldman #include "sftp-common.h" 35b66f2d16SKris Kennaway 36b66f2d16SKris Kennaway /* helper */ 371e8db6e2SBrian Feldman #define get_int64() buffer_get_int64(&iqueue); 38b66f2d16SKris Kennaway #define get_int() buffer_get_int(&iqueue); 39b66f2d16SKris Kennaway #define get_string(lenp) buffer_get_string(&iqueue, lenp); 401e8db6e2SBrian Feldman #define TRACE debug 41b66f2d16SKris Kennaway 4283d2307dSDag-Erling Smørgrav #ifdef HAVE___PROGNAME 4383d2307dSDag-Erling Smørgrav extern char *__progname; 4483d2307dSDag-Erling Smørgrav #else 4583d2307dSDag-Erling Smørgrav char *__progname; 4683d2307dSDag-Erling Smørgrav #endif 4783d2307dSDag-Erling Smørgrav 48b66f2d16SKris Kennaway /* input and output queue */ 49b66f2d16SKris Kennaway Buffer iqueue; 50b66f2d16SKris Kennaway Buffer oqueue; 51b66f2d16SKris Kennaway 521e8db6e2SBrian Feldman /* Version of client */ 531e8db6e2SBrian Feldman int version; 541e8db6e2SBrian Feldman 55d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 56b66f2d16SKris Kennaway 57b66f2d16SKris Kennaway typedef struct Stat Stat; 58b66f2d16SKris Kennaway 591e8db6e2SBrian Feldman struct Stat { 60b66f2d16SKris Kennaway char *name; 61b66f2d16SKris Kennaway char *long_name; 62b66f2d16SKris Kennaway Attrib attrib; 63b66f2d16SKris Kennaway }; 64b66f2d16SKris Kennaway 65ae1f160dSDag-Erling Smørgrav static int 66b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 67b66f2d16SKris Kennaway { 68b66f2d16SKris Kennaway int ret = 0; 691e8db6e2SBrian Feldman 70b66f2d16SKris Kennaway switch (unixerrno) { 71b66f2d16SKris Kennaway case 0: 721e8db6e2SBrian Feldman ret = SSH2_FX_OK; 73b66f2d16SKris Kennaway break; 74b66f2d16SKris Kennaway case ENOENT: 75b66f2d16SKris Kennaway case ENOTDIR: 76b66f2d16SKris Kennaway case EBADF: 77b66f2d16SKris Kennaway case ELOOP: 781e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 79b66f2d16SKris Kennaway break; 80b66f2d16SKris Kennaway case EPERM: 81b66f2d16SKris Kennaway case EACCES: 82b66f2d16SKris Kennaway case EFAULT: 831e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 84b66f2d16SKris Kennaway break; 85b66f2d16SKris Kennaway case ENAMETOOLONG: 86b66f2d16SKris Kennaway case EINVAL: 871e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 88b66f2d16SKris Kennaway break; 89b66f2d16SKris Kennaway default: 901e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 91b66f2d16SKris Kennaway break; 92b66f2d16SKris Kennaway } 93b66f2d16SKris Kennaway return ret; 94b66f2d16SKris Kennaway } 95b66f2d16SKris Kennaway 96ae1f160dSDag-Erling Smørgrav static int 97b66f2d16SKris Kennaway flags_from_portable(int pflags) 98b66f2d16SKris Kennaway { 99b66f2d16SKris Kennaway int flags = 0; 1001e8db6e2SBrian Feldman 1011e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 1021e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 103b66f2d16SKris Kennaway flags = O_RDWR; 1041e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 105b66f2d16SKris Kennaway flags = O_RDONLY; 1061e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 107b66f2d16SKris Kennaway flags = O_WRONLY; 108b66f2d16SKris Kennaway } 1091e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 110b66f2d16SKris Kennaway flags |= O_CREAT; 1111e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 112b66f2d16SKris Kennaway flags |= O_TRUNC; 1131e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 114b66f2d16SKris Kennaway flags |= O_EXCL; 115b66f2d16SKris Kennaway return flags; 116b66f2d16SKris Kennaway } 117b66f2d16SKris Kennaway 118ae1f160dSDag-Erling Smørgrav static Attrib * 119b66f2d16SKris Kennaway get_attrib(void) 120b66f2d16SKris Kennaway { 121b66f2d16SKris Kennaway return decode_attrib(&iqueue); 122b66f2d16SKris Kennaway } 123b66f2d16SKris Kennaway 124b66f2d16SKris Kennaway /* handle handles */ 125b66f2d16SKris Kennaway 126b66f2d16SKris Kennaway typedef struct Handle Handle; 127b66f2d16SKris Kennaway struct Handle { 128b66f2d16SKris Kennaway int use; 129b66f2d16SKris Kennaway DIR *dirp; 130b66f2d16SKris Kennaway int fd; 131b66f2d16SKris Kennaway char *name; 132b66f2d16SKris Kennaway }; 1331e8db6e2SBrian Feldman 134b66f2d16SKris Kennaway enum { 135b66f2d16SKris Kennaway HANDLE_UNUSED, 136b66f2d16SKris Kennaway HANDLE_DIR, 137b66f2d16SKris Kennaway HANDLE_FILE 138b66f2d16SKris Kennaway }; 1391e8db6e2SBrian Feldman 140b66f2d16SKris Kennaway Handle handles[100]; 141b66f2d16SKris Kennaway 142ae1f160dSDag-Erling Smørgrav static void 143b66f2d16SKris Kennaway handle_init(void) 144b66f2d16SKris Kennaway { 145b66f2d16SKris Kennaway int i; 1461e8db6e2SBrian Feldman 147b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 148b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 149b66f2d16SKris Kennaway } 150b66f2d16SKris Kennaway 151ae1f160dSDag-Erling Smørgrav static int 152b66f2d16SKris Kennaway handle_new(int use, char *name, int fd, DIR *dirp) 153b66f2d16SKris Kennaway { 154b66f2d16SKris Kennaway int i; 1551e8db6e2SBrian Feldman 156b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 157b66f2d16SKris Kennaway if (handles[i].use == HANDLE_UNUSED) { 158b66f2d16SKris Kennaway handles[i].use = use; 159b66f2d16SKris Kennaway handles[i].dirp = dirp; 160b66f2d16SKris Kennaway handles[i].fd = fd; 161d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 162b66f2d16SKris Kennaway return i; 163b66f2d16SKris Kennaway } 164b66f2d16SKris Kennaway } 165b66f2d16SKris Kennaway return -1; 166b66f2d16SKris Kennaway } 167b66f2d16SKris Kennaway 168ae1f160dSDag-Erling Smørgrav static int 169b66f2d16SKris Kennaway handle_is_ok(int i, int type) 170b66f2d16SKris Kennaway { 1711e8db6e2SBrian Feldman return i >= 0 && i < sizeof(handles)/sizeof(Handle) && 1721e8db6e2SBrian Feldman handles[i].use == type; 173b66f2d16SKris Kennaway } 174b66f2d16SKris Kennaway 175ae1f160dSDag-Erling Smørgrav static int 176b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 177b66f2d16SKris Kennaway { 178b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 179b66f2d16SKris Kennaway return -1; 1801e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 1811e8db6e2SBrian Feldman PUT_32BIT(*stringp, handle); 1821e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 183b66f2d16SKris Kennaway return 0; 184b66f2d16SKris Kennaway } 185b66f2d16SKris Kennaway 186ae1f160dSDag-Erling Smørgrav static int 187b66f2d16SKris Kennaway handle_from_string(char *handle, u_int hlen) 188b66f2d16SKris Kennaway { 1891e8db6e2SBrian Feldman int val; 1901e8db6e2SBrian Feldman 1911e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 192b66f2d16SKris Kennaway return -1; 1931e8db6e2SBrian Feldman val = GET_32BIT(handle); 194b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 195b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 196b66f2d16SKris Kennaway return val; 197b66f2d16SKris Kennaway return -1; 198b66f2d16SKris Kennaway } 199b66f2d16SKris Kennaway 200ae1f160dSDag-Erling Smørgrav static char * 201b66f2d16SKris Kennaway handle_to_name(int handle) 202b66f2d16SKris Kennaway { 203b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 204b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 205b66f2d16SKris Kennaway return handles[handle].name; 206b66f2d16SKris Kennaway return NULL; 207b66f2d16SKris Kennaway } 208b66f2d16SKris Kennaway 209ae1f160dSDag-Erling Smørgrav static DIR * 210b66f2d16SKris Kennaway handle_to_dir(int handle) 211b66f2d16SKris Kennaway { 212b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 213b66f2d16SKris Kennaway return handles[handle].dirp; 214b66f2d16SKris Kennaway return NULL; 215b66f2d16SKris Kennaway } 216b66f2d16SKris Kennaway 217ae1f160dSDag-Erling Smørgrav static int 218b66f2d16SKris Kennaway handle_to_fd(int handle) 219b66f2d16SKris Kennaway { 220b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 221b66f2d16SKris Kennaway return handles[handle].fd; 222b66f2d16SKris Kennaway return -1; 223b66f2d16SKris Kennaway } 224b66f2d16SKris Kennaway 225ae1f160dSDag-Erling Smørgrav static int 226b66f2d16SKris Kennaway handle_close(int handle) 227b66f2d16SKris Kennaway { 228b66f2d16SKris Kennaway int ret = -1; 2291e8db6e2SBrian Feldman 230b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 231b66f2d16SKris Kennaway ret = close(handles[handle].fd); 232b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 233d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 234b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 235b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 236b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 237d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 238b66f2d16SKris Kennaway } else { 239b66f2d16SKris Kennaway errno = ENOENT; 240b66f2d16SKris Kennaway } 241b66f2d16SKris Kennaway return ret; 242b66f2d16SKris Kennaway } 243b66f2d16SKris Kennaway 244ae1f160dSDag-Erling Smørgrav static int 245b66f2d16SKris Kennaway get_handle(void) 246b66f2d16SKris Kennaway { 247b66f2d16SKris Kennaway char *handle; 2481e8db6e2SBrian Feldman int val = -1; 249b66f2d16SKris Kennaway u_int hlen; 2501e8db6e2SBrian Feldman 251b66f2d16SKris Kennaway handle = get_string(&hlen); 2521e8db6e2SBrian Feldman if (hlen < 256) 253b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 254b66f2d16SKris Kennaway xfree(handle); 255b66f2d16SKris Kennaway return val; 256b66f2d16SKris Kennaway } 257b66f2d16SKris Kennaway 258b66f2d16SKris Kennaway /* send replies */ 259b66f2d16SKris Kennaway 260ae1f160dSDag-Erling Smørgrav static void 261b66f2d16SKris Kennaway send_msg(Buffer *m) 262b66f2d16SKris Kennaway { 263b66f2d16SKris Kennaway int mlen = buffer_len(m); 2641e8db6e2SBrian Feldman 265b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 266b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 267b66f2d16SKris Kennaway buffer_consume(m, mlen); 268b66f2d16SKris Kennaway } 269b66f2d16SKris Kennaway 270ae1f160dSDag-Erling Smørgrav static void 271b66f2d16SKris Kennaway send_status(u_int32_t id, u_int32_t error) 272b66f2d16SKris Kennaway { 273b66f2d16SKris Kennaway Buffer msg; 2741e8db6e2SBrian Feldman const char *status_messages[] = { 2751e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 2761e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 2771e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 2781e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 2791e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 2801e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 2811e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 2821e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 2831e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 2841e8db6e2SBrian Feldman "Unknown error" /* Others */ 2851e8db6e2SBrian Feldman }; 2861e8db6e2SBrian Feldman 287ee21a45fSDag-Erling Smørgrav TRACE("sent status id %u error %u", id, error); 288b66f2d16SKris Kennaway buffer_init(&msg); 2891e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 290b66f2d16SKris Kennaway buffer_put_int(&msg, id); 291b66f2d16SKris Kennaway buffer_put_int(&msg, error); 2921e8db6e2SBrian Feldman if (version >= 3) { 2931e8db6e2SBrian Feldman buffer_put_cstring(&msg, 2941e8db6e2SBrian Feldman status_messages[MIN(error,SSH2_FX_MAX)]); 2951e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 2961e8db6e2SBrian Feldman } 297b66f2d16SKris Kennaway send_msg(&msg); 298b66f2d16SKris Kennaway buffer_free(&msg); 299b66f2d16SKris Kennaway } 300ae1f160dSDag-Erling Smørgrav static void 301b66f2d16SKris Kennaway send_data_or_handle(char type, u_int32_t id, char *data, int dlen) 302b66f2d16SKris Kennaway { 303b66f2d16SKris Kennaway Buffer msg; 3041e8db6e2SBrian Feldman 305b66f2d16SKris Kennaway buffer_init(&msg); 306b66f2d16SKris Kennaway buffer_put_char(&msg, type); 307b66f2d16SKris Kennaway buffer_put_int(&msg, id); 308b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 309b66f2d16SKris Kennaway send_msg(&msg); 310b66f2d16SKris Kennaway buffer_free(&msg); 311b66f2d16SKris Kennaway } 312b66f2d16SKris Kennaway 313ae1f160dSDag-Erling Smørgrav static void 314b66f2d16SKris Kennaway send_data(u_int32_t id, char *data, int dlen) 315b66f2d16SKris Kennaway { 316ee21a45fSDag-Erling Smørgrav TRACE("sent data id %u len %d", id, dlen); 3171e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 318b66f2d16SKris Kennaway } 319b66f2d16SKris Kennaway 320ae1f160dSDag-Erling Smørgrav static void 321b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 322b66f2d16SKris Kennaway { 323b66f2d16SKris Kennaway char *string; 324b66f2d16SKris Kennaway int hlen; 3251e8db6e2SBrian Feldman 326b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 327ee21a45fSDag-Erling Smørgrav TRACE("sent handle id %u handle %d", id, handle); 3281e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 329b66f2d16SKris Kennaway xfree(string); 330b66f2d16SKris Kennaway } 331b66f2d16SKris Kennaway 332ae1f160dSDag-Erling Smørgrav static void 333b66f2d16SKris Kennaway send_names(u_int32_t id, int count, Stat *stats) 334b66f2d16SKris Kennaway { 335b66f2d16SKris Kennaway Buffer msg; 336b66f2d16SKris Kennaway int i; 3371e8db6e2SBrian Feldman 338b66f2d16SKris Kennaway buffer_init(&msg); 3391e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 340b66f2d16SKris Kennaway buffer_put_int(&msg, id); 341b66f2d16SKris Kennaway buffer_put_int(&msg, count); 342ee21a45fSDag-Erling Smørgrav TRACE("sent names id %u count %d", id, count); 343b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 344b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 345b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 346b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 347b66f2d16SKris Kennaway } 348b66f2d16SKris Kennaway send_msg(&msg); 349b66f2d16SKris Kennaway buffer_free(&msg); 350b66f2d16SKris Kennaway } 351b66f2d16SKris Kennaway 352ae1f160dSDag-Erling Smørgrav static void 353b66f2d16SKris Kennaway send_attrib(u_int32_t id, Attrib *a) 354b66f2d16SKris Kennaway { 355b66f2d16SKris Kennaway Buffer msg; 3561e8db6e2SBrian Feldman 357ee21a45fSDag-Erling Smørgrav TRACE("sent attrib id %u have 0x%x", id, a->flags); 358b66f2d16SKris Kennaway buffer_init(&msg); 3591e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 360b66f2d16SKris Kennaway buffer_put_int(&msg, id); 361b66f2d16SKris Kennaway encode_attrib(&msg, a); 362b66f2d16SKris Kennaway send_msg(&msg); 363b66f2d16SKris Kennaway buffer_free(&msg); 364b66f2d16SKris Kennaway } 365b66f2d16SKris Kennaway 366b66f2d16SKris Kennaway /* parse incoming */ 367b66f2d16SKris Kennaway 368ae1f160dSDag-Erling Smørgrav static void 369b66f2d16SKris Kennaway process_init(void) 370b66f2d16SKris Kennaway { 371b66f2d16SKris Kennaway Buffer msg; 372b66f2d16SKris Kennaway 373545d5ecaSDag-Erling Smørgrav version = get_int(); 374b66f2d16SKris Kennaway TRACE("client version %d", version); 375b66f2d16SKris Kennaway buffer_init(&msg); 3761e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 3771e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 378b66f2d16SKris Kennaway send_msg(&msg); 379b66f2d16SKris Kennaway buffer_free(&msg); 380b66f2d16SKris Kennaway } 381b66f2d16SKris Kennaway 382ae1f160dSDag-Erling Smørgrav static void 383b66f2d16SKris Kennaway process_open(void) 384b66f2d16SKris Kennaway { 385b66f2d16SKris Kennaway u_int32_t id, pflags; 386b66f2d16SKris Kennaway Attrib *a; 387b66f2d16SKris Kennaway char *name; 3881e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 389b66f2d16SKris Kennaway 390b66f2d16SKris Kennaway id = get_int(); 391b66f2d16SKris Kennaway name = get_string(NULL); 3921e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 393b66f2d16SKris Kennaway a = get_attrib(); 394b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 3951e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 396ee21a45fSDag-Erling Smørgrav TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); 397b66f2d16SKris Kennaway fd = open(name, flags, mode); 398b66f2d16SKris Kennaway if (fd < 0) { 399b66f2d16SKris Kennaway status = errno_to_portable(errno); 400b66f2d16SKris Kennaway } else { 401d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, NULL); 402b66f2d16SKris Kennaway if (handle < 0) { 403b66f2d16SKris Kennaway close(fd); 404b66f2d16SKris Kennaway } else { 405b66f2d16SKris Kennaway send_handle(id, handle); 4061e8db6e2SBrian Feldman status = SSH2_FX_OK; 407b66f2d16SKris Kennaway } 408b66f2d16SKris Kennaway } 4091e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 410b66f2d16SKris Kennaway send_status(id, status); 411b66f2d16SKris Kennaway xfree(name); 412b66f2d16SKris Kennaway } 413b66f2d16SKris Kennaway 414ae1f160dSDag-Erling Smørgrav static void 415b66f2d16SKris Kennaway process_close(void) 416b66f2d16SKris Kennaway { 417b66f2d16SKris Kennaway u_int32_t id; 4181e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 419b66f2d16SKris Kennaway 420b66f2d16SKris Kennaway id = get_int(); 421b66f2d16SKris Kennaway handle = get_handle(); 422ee21a45fSDag-Erling Smørgrav TRACE("close id %u handle %d", id, handle); 423b66f2d16SKris Kennaway ret = handle_close(handle); 4241e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 425b66f2d16SKris Kennaway send_status(id, status); 426b66f2d16SKris Kennaway } 427b66f2d16SKris Kennaway 428ae1f160dSDag-Erling Smørgrav static void 429b66f2d16SKris Kennaway process_read(void) 430b66f2d16SKris Kennaway { 431b66f2d16SKris Kennaway char buf[64*1024]; 4321e8db6e2SBrian Feldman u_int32_t id, len; 4331e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 434b66f2d16SKris Kennaway u_int64_t off; 435b66f2d16SKris Kennaway 436b66f2d16SKris Kennaway id = get_int(); 437b66f2d16SKris Kennaway handle = get_handle(); 4381e8db6e2SBrian Feldman off = get_int64(); 439b66f2d16SKris Kennaway len = get_int(); 440b66f2d16SKris Kennaway 441ee21a45fSDag-Erling Smørgrav TRACE("read id %u handle %d off %llu len %d", id, handle, 44283d2307dSDag-Erling Smørgrav (u_int64_t)off, len); 443b66f2d16SKris Kennaway if (len > sizeof buf) { 444b66f2d16SKris Kennaway len = sizeof buf; 445d95e11bfSDag-Erling Smørgrav logit("read change len %d", len); 446b66f2d16SKris Kennaway } 447b66f2d16SKris Kennaway fd = handle_to_fd(handle); 448b66f2d16SKris Kennaway if (fd >= 0) { 449b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 450b66f2d16SKris Kennaway error("process_read: seek failed"); 451b66f2d16SKris Kennaway status = errno_to_portable(errno); 452b66f2d16SKris Kennaway } else { 453b66f2d16SKris Kennaway ret = read(fd, buf, len); 454b66f2d16SKris Kennaway if (ret < 0) { 455b66f2d16SKris Kennaway status = errno_to_portable(errno); 456b66f2d16SKris Kennaway } else if (ret == 0) { 4571e8db6e2SBrian Feldman status = SSH2_FX_EOF; 458b66f2d16SKris Kennaway } else { 459b66f2d16SKris Kennaway send_data(id, buf, ret); 4601e8db6e2SBrian Feldman status = SSH2_FX_OK; 461b66f2d16SKris Kennaway } 462b66f2d16SKris Kennaway } 463b66f2d16SKris Kennaway } 4641e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 465b66f2d16SKris Kennaway send_status(id, status); 466b66f2d16SKris Kennaway } 467b66f2d16SKris Kennaway 468ae1f160dSDag-Erling Smørgrav static void 469b66f2d16SKris Kennaway process_write(void) 470b66f2d16SKris Kennaway { 4711e8db6e2SBrian Feldman u_int32_t id; 472b66f2d16SKris Kennaway u_int64_t off; 473b66f2d16SKris Kennaway u_int len; 4741e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 475b66f2d16SKris Kennaway char *data; 476b66f2d16SKris Kennaway 477b66f2d16SKris Kennaway id = get_int(); 478b66f2d16SKris Kennaway handle = get_handle(); 4791e8db6e2SBrian Feldman off = get_int64(); 480b66f2d16SKris Kennaway data = get_string(&len); 481b66f2d16SKris Kennaway 482ee21a45fSDag-Erling Smørgrav TRACE("write id %u handle %d off %llu len %d", id, handle, 48383d2307dSDag-Erling Smørgrav (u_int64_t)off, len); 484b66f2d16SKris Kennaway fd = handle_to_fd(handle); 485b66f2d16SKris Kennaway if (fd >= 0) { 486b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 487b66f2d16SKris Kennaway status = errno_to_portable(errno); 488b66f2d16SKris Kennaway error("process_write: seek failed"); 489b66f2d16SKris Kennaway } else { 490b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 491b66f2d16SKris Kennaway ret = write(fd, data, len); 492b66f2d16SKris Kennaway if (ret == -1) { 493b66f2d16SKris Kennaway error("process_write: write failed"); 494b66f2d16SKris Kennaway status = errno_to_portable(errno); 495b66f2d16SKris Kennaway } else if (ret == len) { 4961e8db6e2SBrian Feldman status = SSH2_FX_OK; 497b66f2d16SKris Kennaway } else { 498d95e11bfSDag-Erling Smørgrav logit("nothing at all written"); 499b66f2d16SKris Kennaway } 500b66f2d16SKris Kennaway } 501b66f2d16SKris Kennaway } 502b66f2d16SKris Kennaway send_status(id, status); 503b66f2d16SKris Kennaway xfree(data); 504b66f2d16SKris Kennaway } 505b66f2d16SKris Kennaway 506ae1f160dSDag-Erling Smørgrav static void 507b66f2d16SKris Kennaway process_do_stat(int do_lstat) 508b66f2d16SKris Kennaway { 5091e8db6e2SBrian Feldman Attrib a; 510b66f2d16SKris Kennaway struct stat st; 511b66f2d16SKris Kennaway u_int32_t id; 512b66f2d16SKris Kennaway char *name; 5131e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 514b66f2d16SKris Kennaway 515b66f2d16SKris Kennaway id = get_int(); 516b66f2d16SKris Kennaway name = get_string(NULL); 517ee21a45fSDag-Erling Smørgrav TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); 518b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 519b66f2d16SKris Kennaway if (ret < 0) { 520b66f2d16SKris Kennaway status = errno_to_portable(errno); 521b66f2d16SKris Kennaway } else { 5221e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5231e8db6e2SBrian Feldman send_attrib(id, &a); 5241e8db6e2SBrian Feldman status = SSH2_FX_OK; 525b66f2d16SKris Kennaway } 5261e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 527b66f2d16SKris Kennaway send_status(id, status); 528b66f2d16SKris Kennaway xfree(name); 529b66f2d16SKris Kennaway } 530b66f2d16SKris Kennaway 531ae1f160dSDag-Erling Smørgrav static void 532b66f2d16SKris Kennaway process_stat(void) 533b66f2d16SKris Kennaway { 534b66f2d16SKris Kennaway process_do_stat(0); 535b66f2d16SKris Kennaway } 536b66f2d16SKris Kennaway 537ae1f160dSDag-Erling Smørgrav static void 538b66f2d16SKris Kennaway process_lstat(void) 539b66f2d16SKris Kennaway { 540b66f2d16SKris Kennaway process_do_stat(1); 541b66f2d16SKris Kennaway } 542b66f2d16SKris Kennaway 543ae1f160dSDag-Erling Smørgrav static void 544b66f2d16SKris Kennaway process_fstat(void) 545b66f2d16SKris Kennaway { 5461e8db6e2SBrian Feldman Attrib a; 547b66f2d16SKris Kennaway struct stat st; 548b66f2d16SKris Kennaway u_int32_t id; 5491e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 550b66f2d16SKris Kennaway 551b66f2d16SKris Kennaway id = get_int(); 552b66f2d16SKris Kennaway handle = get_handle(); 553ee21a45fSDag-Erling Smørgrav TRACE("fstat id %u handle %d", id, handle); 554b66f2d16SKris Kennaway fd = handle_to_fd(handle); 555b66f2d16SKris Kennaway if (fd >= 0) { 556b66f2d16SKris Kennaway ret = fstat(fd, &st); 557b66f2d16SKris Kennaway if (ret < 0) { 558b66f2d16SKris Kennaway status = errno_to_portable(errno); 559b66f2d16SKris Kennaway } else { 5601e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5611e8db6e2SBrian Feldman send_attrib(id, &a); 5621e8db6e2SBrian Feldman status = SSH2_FX_OK; 563b66f2d16SKris Kennaway } 564b66f2d16SKris Kennaway } 5651e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 566b66f2d16SKris Kennaway send_status(id, status); 567b66f2d16SKris Kennaway } 568b66f2d16SKris Kennaway 569ae1f160dSDag-Erling Smørgrav static struct timeval * 570b66f2d16SKris Kennaway attrib_to_tv(Attrib *a) 571b66f2d16SKris Kennaway { 572b66f2d16SKris Kennaway static struct timeval tv[2]; 5731e8db6e2SBrian Feldman 574b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 575b66f2d16SKris Kennaway tv[0].tv_usec = 0; 576b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 577b66f2d16SKris Kennaway tv[1].tv_usec = 0; 578b66f2d16SKris Kennaway return tv; 579b66f2d16SKris Kennaway } 580b66f2d16SKris Kennaway 581ae1f160dSDag-Erling Smørgrav static void 582b66f2d16SKris Kennaway process_setstat(void) 583b66f2d16SKris Kennaway { 584b66f2d16SKris Kennaway Attrib *a; 585b66f2d16SKris Kennaway u_int32_t id; 586b66f2d16SKris Kennaway char *name; 587ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 588b66f2d16SKris Kennaway 589b66f2d16SKris Kennaway id = get_int(); 590b66f2d16SKris Kennaway name = get_string(NULL); 591b66f2d16SKris Kennaway a = get_attrib(); 592ee21a45fSDag-Erling Smørgrav TRACE("setstat id %u name %s", id, name); 593ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 594ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 595ae1f160dSDag-Erling Smørgrav if (ret == -1) 596ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 597ae1f160dSDag-Erling Smørgrav } 5981e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 599b66f2d16SKris Kennaway ret = chmod(name, a->perm & 0777); 600b66f2d16SKris Kennaway if (ret == -1) 601b66f2d16SKris Kennaway status = errno_to_portable(errno); 602b66f2d16SKris Kennaway } 6031e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 604b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 605b66f2d16SKris Kennaway if (ret == -1) 606b66f2d16SKris Kennaway status = errno_to_portable(errno); 607b66f2d16SKris Kennaway } 6081e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 6091e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 6101e8db6e2SBrian Feldman if (ret == -1) 6111e8db6e2SBrian Feldman status = errno_to_portable(errno); 6121e8db6e2SBrian Feldman } 613b66f2d16SKris Kennaway send_status(id, status); 614b66f2d16SKris Kennaway xfree(name); 615b66f2d16SKris Kennaway } 616b66f2d16SKris Kennaway 617ae1f160dSDag-Erling Smørgrav static void 618b66f2d16SKris Kennaway process_fsetstat(void) 619b66f2d16SKris Kennaway { 620b66f2d16SKris Kennaway Attrib *a; 621b66f2d16SKris Kennaway u_int32_t id; 622b66f2d16SKris Kennaway int handle, fd, ret; 6231e8db6e2SBrian Feldman int status = SSH2_FX_OK; 62483d2307dSDag-Erling Smørgrav char *name; 625b66f2d16SKris Kennaway 626b66f2d16SKris Kennaway id = get_int(); 627b66f2d16SKris Kennaway handle = get_handle(); 628b66f2d16SKris Kennaway a = get_attrib(); 629ee21a45fSDag-Erling Smørgrav TRACE("fsetstat id %u handle %d", id, handle); 630b66f2d16SKris Kennaway fd = handle_to_fd(handle); 63183d2307dSDag-Erling Smørgrav name = handle_to_name(handle); 63283d2307dSDag-Erling Smørgrav if (fd < 0 || name == NULL) { 6331e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 634b66f2d16SKris Kennaway } else { 635ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 636ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 637ae1f160dSDag-Erling Smørgrav if (ret == -1) 638ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 639ae1f160dSDag-Erling Smørgrav } 6401e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 64183d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 642b66f2d16SKris Kennaway ret = fchmod(fd, a->perm & 0777); 64383d2307dSDag-Erling Smørgrav #else 64483d2307dSDag-Erling Smørgrav ret = chmod(name, a->perm & 0777); 64583d2307dSDag-Erling Smørgrav #endif 646b66f2d16SKris Kennaway if (ret == -1) 647b66f2d16SKris Kennaway status = errno_to_portable(errno); 648b66f2d16SKris Kennaway } 6491e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 65083d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 651b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 65283d2307dSDag-Erling Smørgrav #else 65383d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 65483d2307dSDag-Erling Smørgrav #endif 655b66f2d16SKris Kennaway if (ret == -1) 656b66f2d16SKris Kennaway status = errno_to_portable(errno); 657b66f2d16SKris Kennaway } 6581e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 65983d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 6601e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 66183d2307dSDag-Erling Smørgrav #else 66283d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 66383d2307dSDag-Erling Smørgrav #endif 6641e8db6e2SBrian Feldman if (ret == -1) 6651e8db6e2SBrian Feldman status = errno_to_portable(errno); 6661e8db6e2SBrian Feldman } 667b66f2d16SKris Kennaway } 668b66f2d16SKris Kennaway send_status(id, status); 669b66f2d16SKris Kennaway } 670b66f2d16SKris Kennaway 671ae1f160dSDag-Erling Smørgrav static void 672b66f2d16SKris Kennaway process_opendir(void) 673b66f2d16SKris Kennaway { 674b66f2d16SKris Kennaway DIR *dirp = NULL; 675b66f2d16SKris Kennaway char *path; 6761e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 677b66f2d16SKris Kennaway u_int32_t id; 678b66f2d16SKris Kennaway 679b66f2d16SKris Kennaway id = get_int(); 680b66f2d16SKris Kennaway path = get_string(NULL); 681ee21a45fSDag-Erling Smørgrav TRACE("opendir id %u path %s", id, path); 682b66f2d16SKris Kennaway dirp = opendir(path); 683b66f2d16SKris Kennaway if (dirp == NULL) { 684b66f2d16SKris Kennaway status = errno_to_portable(errno); 685b66f2d16SKris Kennaway } else { 686d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, dirp); 687b66f2d16SKris Kennaway if (handle < 0) { 688b66f2d16SKris Kennaway closedir(dirp); 689b66f2d16SKris Kennaway } else { 690b66f2d16SKris Kennaway send_handle(id, handle); 6911e8db6e2SBrian Feldman status = SSH2_FX_OK; 692b66f2d16SKris Kennaway } 693b66f2d16SKris Kennaway 694b66f2d16SKris Kennaway } 6951e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 696b66f2d16SKris Kennaway send_status(id, status); 697b66f2d16SKris Kennaway xfree(path); 698b66f2d16SKris Kennaway } 699b66f2d16SKris Kennaway 700ae1f160dSDag-Erling Smørgrav static void 701b66f2d16SKris Kennaway process_readdir(void) 702b66f2d16SKris Kennaway { 703b66f2d16SKris Kennaway DIR *dirp; 704b66f2d16SKris Kennaway struct dirent *dp; 705b66f2d16SKris Kennaway char *path; 706b66f2d16SKris Kennaway int handle; 707b66f2d16SKris Kennaway u_int32_t id; 708b66f2d16SKris Kennaway 709b66f2d16SKris Kennaway id = get_int(); 710b66f2d16SKris Kennaway handle = get_handle(); 711ee21a45fSDag-Erling Smørgrav TRACE("readdir id %u handle %d", id, handle); 712b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 713b66f2d16SKris Kennaway path = handle_to_name(handle); 714b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 7151e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 716b66f2d16SKris Kennaway } else { 717b66f2d16SKris Kennaway struct stat st; 718b66f2d16SKris Kennaway char pathname[1024]; 719b66f2d16SKris Kennaway Stat *stats; 720b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 721ee21a45fSDag-Erling Smørgrav 722b66f2d16SKris Kennaway stats = xmalloc(nstats * sizeof(Stat)); 723b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 724b66f2d16SKris Kennaway if (count >= nstats) { 725b66f2d16SKris Kennaway nstats *= 2; 726b66f2d16SKris Kennaway stats = xrealloc(stats, nstats * sizeof(Stat)); 727b66f2d16SKris Kennaway } 728b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 729ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 730ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 731b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 732b66f2d16SKris Kennaway continue; 7331e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 734b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 7354b17dab0SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0); 736b66f2d16SKris Kennaway count++; 737b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 7381e8db6e2SBrian Feldman /* XXX check packet size instead */ 739b66f2d16SKris Kennaway if (count == 100) 740b66f2d16SKris Kennaway break; 741b66f2d16SKris Kennaway } 7421e8db6e2SBrian Feldman if (count > 0) { 743b66f2d16SKris Kennaway send_names(id, count, stats); 744b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 745b66f2d16SKris Kennaway xfree(stats[i].name); 746b66f2d16SKris Kennaway xfree(stats[i].long_name); 747b66f2d16SKris Kennaway } 7481e8db6e2SBrian Feldman } else { 7491e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 7501e8db6e2SBrian Feldman } 751b66f2d16SKris Kennaway xfree(stats); 752b66f2d16SKris Kennaway } 753b66f2d16SKris Kennaway } 754b66f2d16SKris Kennaway 755ae1f160dSDag-Erling Smørgrav static void 756b66f2d16SKris Kennaway process_remove(void) 757b66f2d16SKris Kennaway { 758b66f2d16SKris Kennaway char *name; 759b66f2d16SKris Kennaway u_int32_t id; 7601e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 761b66f2d16SKris Kennaway int ret; 762b66f2d16SKris Kennaway 763b66f2d16SKris Kennaway id = get_int(); 764b66f2d16SKris Kennaway name = get_string(NULL); 765ee21a45fSDag-Erling Smørgrav TRACE("remove id %u name %s", id, name); 7661e8db6e2SBrian Feldman ret = unlink(name); 7671e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 768b66f2d16SKris Kennaway send_status(id, status); 769b66f2d16SKris Kennaway xfree(name); 770b66f2d16SKris Kennaway } 771b66f2d16SKris Kennaway 772ae1f160dSDag-Erling Smørgrav static void 773b66f2d16SKris Kennaway process_mkdir(void) 774b66f2d16SKris Kennaway { 775b66f2d16SKris Kennaway Attrib *a; 776b66f2d16SKris Kennaway u_int32_t id; 777b66f2d16SKris Kennaway char *name; 7781e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 779b66f2d16SKris Kennaway 780b66f2d16SKris Kennaway id = get_int(); 781b66f2d16SKris Kennaway name = get_string(NULL); 782b66f2d16SKris Kennaway a = get_attrib(); 7831e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 7841e8db6e2SBrian Feldman a->perm & 0777 : 0777; 785ee21a45fSDag-Erling Smørgrav TRACE("mkdir id %u name %s mode 0%o", id, name, mode); 786b66f2d16SKris Kennaway ret = mkdir(name, mode); 7871e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 788b66f2d16SKris Kennaway send_status(id, status); 789b66f2d16SKris Kennaway xfree(name); 790b66f2d16SKris Kennaway } 791b66f2d16SKris Kennaway 792ae1f160dSDag-Erling Smørgrav static void 793b66f2d16SKris Kennaway process_rmdir(void) 794b66f2d16SKris Kennaway { 795b66f2d16SKris Kennaway u_int32_t id; 796b66f2d16SKris Kennaway char *name; 797b66f2d16SKris Kennaway int ret, status; 798b66f2d16SKris Kennaway 799b66f2d16SKris Kennaway id = get_int(); 800b66f2d16SKris Kennaway name = get_string(NULL); 801ee21a45fSDag-Erling Smørgrav TRACE("rmdir id %u name %s", id, name); 802b66f2d16SKris Kennaway ret = rmdir(name); 8031e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 804b66f2d16SKris Kennaway send_status(id, status); 805b66f2d16SKris Kennaway xfree(name); 806b66f2d16SKris Kennaway } 807b66f2d16SKris Kennaway 808ae1f160dSDag-Erling Smørgrav static void 809b66f2d16SKris Kennaway process_realpath(void) 810b66f2d16SKris Kennaway { 811b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 812b66f2d16SKris Kennaway u_int32_t id; 813b66f2d16SKris Kennaway char *path; 814b66f2d16SKris Kennaway 815b66f2d16SKris Kennaway id = get_int(); 816b66f2d16SKris Kennaway path = get_string(NULL); 8171e8db6e2SBrian Feldman if (path[0] == '\0') { 8181e8db6e2SBrian Feldman xfree(path); 8191e8db6e2SBrian Feldman path = xstrdup("."); 8201e8db6e2SBrian Feldman } 821ee21a45fSDag-Erling Smørgrav TRACE("realpath id %u path %s", id, path); 822b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 823b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 824b66f2d16SKris Kennaway } else { 825b66f2d16SKris Kennaway Stat s; 826b66f2d16SKris Kennaway attrib_clear(&s.attrib); 827b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 828b66f2d16SKris Kennaway send_names(id, 1, &s); 829b66f2d16SKris Kennaway } 830b66f2d16SKris Kennaway xfree(path); 831b66f2d16SKris Kennaway } 832b66f2d16SKris Kennaway 833ae1f160dSDag-Erling Smørgrav static void 834b66f2d16SKris Kennaway process_rename(void) 835b66f2d16SKris Kennaway { 836b66f2d16SKris Kennaway u_int32_t id; 837b66f2d16SKris Kennaway char *oldpath, *newpath; 838d0c8c0bcSDag-Erling Smørgrav int status; 839d0c8c0bcSDag-Erling Smørgrav struct stat sb; 840b66f2d16SKris Kennaway 841b66f2d16SKris Kennaway id = get_int(); 842b66f2d16SKris Kennaway oldpath = get_string(NULL); 843b66f2d16SKris Kennaway newpath = get_string(NULL); 844ee21a45fSDag-Erling Smørgrav TRACE("rename id %u old %s new %s", id, oldpath, newpath); 845d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 846d0c8c0bcSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 847d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 848d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 849d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 850d0c8c0bcSDag-Erling Smørgrav if (link(oldpath, newpath) == -1) 851d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 852d0c8c0bcSDag-Erling Smørgrav else if (unlink(oldpath) == -1) { 853d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 854d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 855d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 856d0c8c0bcSDag-Erling Smørgrav } else 857d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 858d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 859d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 860d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 861d0c8c0bcSDag-Erling Smørgrav else 862d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 8631e8db6e2SBrian Feldman } 864b66f2d16SKris Kennaway send_status(id, status); 865b66f2d16SKris Kennaway xfree(oldpath); 866b66f2d16SKris Kennaway xfree(newpath); 867b66f2d16SKris Kennaway } 868b66f2d16SKris Kennaway 869ae1f160dSDag-Erling Smørgrav static void 8701e8db6e2SBrian Feldman process_readlink(void) 8711e8db6e2SBrian Feldman { 8721e8db6e2SBrian Feldman u_int32_t id; 873ae1f160dSDag-Erling Smørgrav int len; 8741e8db6e2SBrian Feldman char link[MAXPATHLEN]; 8751e8db6e2SBrian Feldman char *path; 8761e8db6e2SBrian Feldman 8771e8db6e2SBrian Feldman id = get_int(); 8781e8db6e2SBrian Feldman path = get_string(NULL); 879ee21a45fSDag-Erling Smørgrav TRACE("readlink id %u path %s", id, path); 880ae1f160dSDag-Erling Smørgrav if ((len = readlink(path, link, sizeof(link) - 1)) == -1) 8811e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 8821e8db6e2SBrian Feldman else { 8831e8db6e2SBrian Feldman Stat s; 8841e8db6e2SBrian Feldman 885ae1f160dSDag-Erling Smørgrav link[len] = '\0'; 8861e8db6e2SBrian Feldman attrib_clear(&s.attrib); 8871e8db6e2SBrian Feldman s.name = s.long_name = link; 8881e8db6e2SBrian Feldman send_names(id, 1, &s); 8891e8db6e2SBrian Feldman } 8901e8db6e2SBrian Feldman xfree(path); 8911e8db6e2SBrian Feldman } 8921e8db6e2SBrian Feldman 893ae1f160dSDag-Erling Smørgrav static void 8941e8db6e2SBrian Feldman process_symlink(void) 8951e8db6e2SBrian Feldman { 8961e8db6e2SBrian Feldman u_int32_t id; 8971e8db6e2SBrian Feldman char *oldpath, *newpath; 898d0c8c0bcSDag-Erling Smørgrav int ret, status; 8991e8db6e2SBrian Feldman 9001e8db6e2SBrian Feldman id = get_int(); 9011e8db6e2SBrian Feldman oldpath = get_string(NULL); 9021e8db6e2SBrian Feldman newpath = get_string(NULL); 903ee21a45fSDag-Erling Smørgrav TRACE("symlink id %u old %s new %s", id, oldpath, newpath); 904d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 9051e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 9061e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9071e8db6e2SBrian Feldman send_status(id, status); 9081e8db6e2SBrian Feldman xfree(oldpath); 9091e8db6e2SBrian Feldman xfree(newpath); 9101e8db6e2SBrian Feldman } 9111e8db6e2SBrian Feldman 912ae1f160dSDag-Erling Smørgrav static void 9131e8db6e2SBrian Feldman process_extended(void) 9141e8db6e2SBrian Feldman { 9151e8db6e2SBrian Feldman u_int32_t id; 9161e8db6e2SBrian Feldman char *request; 9171e8db6e2SBrian Feldman 9181e8db6e2SBrian Feldman id = get_int(); 9191e8db6e2SBrian Feldman request = get_string(NULL); 9201e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 9211e8db6e2SBrian Feldman xfree(request); 9221e8db6e2SBrian Feldman } 923b66f2d16SKris Kennaway 924b66f2d16SKris Kennaway /* stolen from ssh-agent */ 925b66f2d16SKris Kennaway 926ae1f160dSDag-Erling Smørgrav static void 927b66f2d16SKris Kennaway process(void) 928b66f2d16SKris Kennaway { 9291e8db6e2SBrian Feldman u_int msg_len; 930545d5ecaSDag-Erling Smørgrav u_int buf_len; 931545d5ecaSDag-Erling Smørgrav u_int consumed; 9321e8db6e2SBrian Feldman u_int type; 9331e8db6e2SBrian Feldman u_char *cp; 934b66f2d16SKris Kennaway 935545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 936545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 937b66f2d16SKris Kennaway return; /* Incomplete message. */ 938ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 939b66f2d16SKris Kennaway msg_len = GET_32BIT(cp); 940b66f2d16SKris Kennaway if (msg_len > 256 * 1024) { 941b66f2d16SKris Kennaway error("bad message "); 942b66f2d16SKris Kennaway exit(11); 943b66f2d16SKris Kennaway } 944545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 945b66f2d16SKris Kennaway return; 946b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 947545d5ecaSDag-Erling Smørgrav buf_len -= 4; 948b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 949b66f2d16SKris Kennaway switch (type) { 9501e8db6e2SBrian Feldman case SSH2_FXP_INIT: 951b66f2d16SKris Kennaway process_init(); 952b66f2d16SKris Kennaway break; 9531e8db6e2SBrian Feldman case SSH2_FXP_OPEN: 954b66f2d16SKris Kennaway process_open(); 955b66f2d16SKris Kennaway break; 9561e8db6e2SBrian Feldman case SSH2_FXP_CLOSE: 957b66f2d16SKris Kennaway process_close(); 958b66f2d16SKris Kennaway break; 9591e8db6e2SBrian Feldman case SSH2_FXP_READ: 960b66f2d16SKris Kennaway process_read(); 961b66f2d16SKris Kennaway break; 9621e8db6e2SBrian Feldman case SSH2_FXP_WRITE: 963b66f2d16SKris Kennaway process_write(); 964b66f2d16SKris Kennaway break; 9651e8db6e2SBrian Feldman case SSH2_FXP_LSTAT: 966b66f2d16SKris Kennaway process_lstat(); 967b66f2d16SKris Kennaway break; 9681e8db6e2SBrian Feldman case SSH2_FXP_FSTAT: 969b66f2d16SKris Kennaway process_fstat(); 970b66f2d16SKris Kennaway break; 9711e8db6e2SBrian Feldman case SSH2_FXP_SETSTAT: 972b66f2d16SKris Kennaway process_setstat(); 973b66f2d16SKris Kennaway break; 9741e8db6e2SBrian Feldman case SSH2_FXP_FSETSTAT: 975b66f2d16SKris Kennaway process_fsetstat(); 976b66f2d16SKris Kennaway break; 9771e8db6e2SBrian Feldman case SSH2_FXP_OPENDIR: 978b66f2d16SKris Kennaway process_opendir(); 979b66f2d16SKris Kennaway break; 9801e8db6e2SBrian Feldman case SSH2_FXP_READDIR: 981b66f2d16SKris Kennaway process_readdir(); 982b66f2d16SKris Kennaway break; 9831e8db6e2SBrian Feldman case SSH2_FXP_REMOVE: 984b66f2d16SKris Kennaway process_remove(); 985b66f2d16SKris Kennaway break; 9861e8db6e2SBrian Feldman case SSH2_FXP_MKDIR: 987b66f2d16SKris Kennaway process_mkdir(); 988b66f2d16SKris Kennaway break; 9891e8db6e2SBrian Feldman case SSH2_FXP_RMDIR: 990b66f2d16SKris Kennaway process_rmdir(); 991b66f2d16SKris Kennaway break; 9921e8db6e2SBrian Feldman case SSH2_FXP_REALPATH: 993b66f2d16SKris Kennaway process_realpath(); 994b66f2d16SKris Kennaway break; 9951e8db6e2SBrian Feldman case SSH2_FXP_STAT: 996b66f2d16SKris Kennaway process_stat(); 997b66f2d16SKris Kennaway break; 9981e8db6e2SBrian Feldman case SSH2_FXP_RENAME: 999b66f2d16SKris Kennaway process_rename(); 1000b66f2d16SKris Kennaway break; 10011e8db6e2SBrian Feldman case SSH2_FXP_READLINK: 10021e8db6e2SBrian Feldman process_readlink(); 10031e8db6e2SBrian Feldman break; 10041e8db6e2SBrian Feldman case SSH2_FXP_SYMLINK: 10051e8db6e2SBrian Feldman process_symlink(); 10061e8db6e2SBrian Feldman break; 10071e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 10081e8db6e2SBrian Feldman process_extended(); 10091e8db6e2SBrian Feldman break; 1010b66f2d16SKris Kennaway default: 1011b66f2d16SKris Kennaway error("Unknown message %d", type); 1012b66f2d16SKris Kennaway break; 1013b66f2d16SKris Kennaway } 1014545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1015545d5ecaSDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) 1016545d5ecaSDag-Erling Smørgrav fatal("iqueue grows"); 1017545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1018545d5ecaSDag-Erling Smørgrav if (msg_len < consumed) 1019545d5ecaSDag-Erling Smørgrav fatal("msg_len %d < consumed %d", msg_len, consumed); 1020545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1021545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1022b66f2d16SKris Kennaway } 1023b66f2d16SKris Kennaway 1024b66f2d16SKris Kennaway int 1025b66f2d16SKris Kennaway main(int ac, char **av) 1026b66f2d16SKris Kennaway { 10271e8db6e2SBrian Feldman fd_set *rset, *wset; 1028b66f2d16SKris Kennaway int in, out, max; 10291e8db6e2SBrian Feldman ssize_t len, olen, set_size; 10301e8db6e2SBrian Feldman 10311e8db6e2SBrian Feldman /* XXX should use getopt */ 1032b66f2d16SKris Kennaway 1033d95e11bfSDag-Erling Smørgrav __progname = ssh_get_progname(av[0]); 1034b66f2d16SKris Kennaway handle_init(); 1035b66f2d16SKris Kennaway 10361e8db6e2SBrian Feldman #ifdef DEBUG_SFTP_SERVER 10371e8db6e2SBrian Feldman log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 10381e8db6e2SBrian Feldman #endif 10391e8db6e2SBrian Feldman 1040b66f2d16SKris Kennaway in = dup(STDIN_FILENO); 1041b66f2d16SKris Kennaway out = dup(STDOUT_FILENO); 1042b66f2d16SKris Kennaway 104383d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 104483d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 104583d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 104683d2307dSDag-Erling Smørgrav #endif 104783d2307dSDag-Erling Smørgrav 1048b66f2d16SKris Kennaway max = 0; 1049b66f2d16SKris Kennaway if (in > max) 1050b66f2d16SKris Kennaway max = in; 1051b66f2d16SKris Kennaway if (out > max) 1052b66f2d16SKris Kennaway max = out; 1053b66f2d16SKris Kennaway 1054b66f2d16SKris Kennaway buffer_init(&iqueue); 1055b66f2d16SKris Kennaway buffer_init(&oqueue); 1056b66f2d16SKris Kennaway 10571e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 10581e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 10591e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1060b66f2d16SKris Kennaway 10611e8db6e2SBrian Feldman for (;;) { 10621e8db6e2SBrian Feldman memset(rset, 0, set_size); 10631e8db6e2SBrian Feldman memset(wset, 0, set_size); 10641e8db6e2SBrian Feldman 10651e8db6e2SBrian Feldman FD_SET(in, rset); 1066b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1067b66f2d16SKris Kennaway if (olen > 0) 10681e8db6e2SBrian Feldman FD_SET(out, wset); 1069b66f2d16SKris Kennaway 10701e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1071b66f2d16SKris Kennaway if (errno == EINTR) 1072b66f2d16SKris Kennaway continue; 1073b66f2d16SKris Kennaway exit(2); 1074b66f2d16SKris Kennaway } 1075b66f2d16SKris Kennaway 1076b66f2d16SKris Kennaway /* copy stdin to iqueue */ 10771e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1078b66f2d16SKris Kennaway char buf[4*4096]; 1079b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1080b66f2d16SKris Kennaway if (len == 0) { 1081b66f2d16SKris Kennaway debug("read eof"); 1082b66f2d16SKris Kennaway exit(0); 1083b66f2d16SKris Kennaway } else if (len < 0) { 1084b66f2d16SKris Kennaway error("read error"); 1085b66f2d16SKris Kennaway exit(1); 1086b66f2d16SKris Kennaway } else { 1087b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1088b66f2d16SKris Kennaway } 1089b66f2d16SKris Kennaway } 1090b66f2d16SKris Kennaway /* send oqueue to stdout */ 10911e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1092b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1093b66f2d16SKris Kennaway if (len < 0) { 1094b66f2d16SKris Kennaway error("write error"); 1095b66f2d16SKris Kennaway exit(1); 1096b66f2d16SKris Kennaway } else { 1097b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1098b66f2d16SKris Kennaway } 1099b66f2d16SKris Kennaway } 1100b66f2d16SKris Kennaway /* process requests from client */ 1101b66f2d16SKris Kennaway process(); 1102b66f2d16SKris Kennaway } 1103b66f2d16SKris Kennaway } 1104