1b66f2d16SKris Kennaway /* 2efcad6b7SDag-Erling Smørgrav * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3b66f2d16SKris Kennaway * 4efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 5efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 6efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 7b66f2d16SKris Kennaway * 8efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15b66f2d16SKris Kennaway */ 16b66f2d16SKris Kennaway #include "includes.h" 17efcad6b7SDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.45 2004/02/19 21:15:04 markus Exp $"); 18b66f2d16SKris Kennaway 19b66f2d16SKris Kennaway #include "buffer.h" 20b66f2d16SKris Kennaway #include "bufaux.h" 21b66f2d16SKris Kennaway #include "getput.h" 221e8db6e2SBrian Feldman #include "log.h" 23b66f2d16SKris Kennaway #include "xmalloc.h" 24b66f2d16SKris Kennaway 251e8db6e2SBrian Feldman #include "sftp.h" 261e8db6e2SBrian Feldman #include "sftp-common.h" 27b66f2d16SKris Kennaway 28b66f2d16SKris Kennaway /* helper */ 291e8db6e2SBrian Feldman #define get_int64() buffer_get_int64(&iqueue); 30b66f2d16SKris Kennaway #define get_int() buffer_get_int(&iqueue); 31b66f2d16SKris Kennaway #define get_string(lenp) buffer_get_string(&iqueue, lenp); 321e8db6e2SBrian Feldman #define TRACE debug 33b66f2d16SKris Kennaway 3483d2307dSDag-Erling Smørgrav #ifdef HAVE___PROGNAME 3583d2307dSDag-Erling Smørgrav extern char *__progname; 3683d2307dSDag-Erling Smørgrav #else 3783d2307dSDag-Erling Smørgrav char *__progname; 3883d2307dSDag-Erling Smørgrav #endif 3983d2307dSDag-Erling Smørgrav 40b66f2d16SKris Kennaway /* input and output queue */ 41b66f2d16SKris Kennaway Buffer iqueue; 42b66f2d16SKris Kennaway Buffer oqueue; 43b66f2d16SKris Kennaway 441e8db6e2SBrian Feldman /* Version of client */ 451e8db6e2SBrian Feldman int version; 461e8db6e2SBrian Feldman 47d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 48b66f2d16SKris Kennaway 49b66f2d16SKris Kennaway typedef struct Stat Stat; 50b66f2d16SKris Kennaway 511e8db6e2SBrian Feldman struct Stat { 52b66f2d16SKris Kennaway char *name; 53b66f2d16SKris Kennaway char *long_name; 54b66f2d16SKris Kennaway Attrib attrib; 55b66f2d16SKris Kennaway }; 56b66f2d16SKris Kennaway 57ae1f160dSDag-Erling Smørgrav static int 58b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 59b66f2d16SKris Kennaway { 60b66f2d16SKris Kennaway int ret = 0; 611e8db6e2SBrian Feldman 62b66f2d16SKris Kennaway switch (unixerrno) { 63b66f2d16SKris Kennaway case 0: 641e8db6e2SBrian Feldman ret = SSH2_FX_OK; 65b66f2d16SKris Kennaway break; 66b66f2d16SKris Kennaway case ENOENT: 67b66f2d16SKris Kennaway case ENOTDIR: 68b66f2d16SKris Kennaway case EBADF: 69b66f2d16SKris Kennaway case ELOOP: 701e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 71b66f2d16SKris Kennaway break; 72b66f2d16SKris Kennaway case EPERM: 73b66f2d16SKris Kennaway case EACCES: 74b66f2d16SKris Kennaway case EFAULT: 751e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 76b66f2d16SKris Kennaway break; 77b66f2d16SKris Kennaway case ENAMETOOLONG: 78b66f2d16SKris Kennaway case EINVAL: 791e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 80b66f2d16SKris Kennaway break; 81b66f2d16SKris Kennaway default: 821e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 83b66f2d16SKris Kennaway break; 84b66f2d16SKris Kennaway } 85b66f2d16SKris Kennaway return ret; 86b66f2d16SKris Kennaway } 87b66f2d16SKris Kennaway 88ae1f160dSDag-Erling Smørgrav static int 89b66f2d16SKris Kennaway flags_from_portable(int pflags) 90b66f2d16SKris Kennaway { 91b66f2d16SKris Kennaway int flags = 0; 921e8db6e2SBrian Feldman 931e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 941e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 95b66f2d16SKris Kennaway flags = O_RDWR; 961e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 97b66f2d16SKris Kennaway flags = O_RDONLY; 981e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 99b66f2d16SKris Kennaway flags = O_WRONLY; 100b66f2d16SKris Kennaway } 1011e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 102b66f2d16SKris Kennaway flags |= O_CREAT; 1031e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 104b66f2d16SKris Kennaway flags |= O_TRUNC; 1051e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 106b66f2d16SKris Kennaway flags |= O_EXCL; 107b66f2d16SKris Kennaway return flags; 108b66f2d16SKris Kennaway } 109b66f2d16SKris Kennaway 110ae1f160dSDag-Erling Smørgrav static Attrib * 111b66f2d16SKris Kennaway get_attrib(void) 112b66f2d16SKris Kennaway { 113b66f2d16SKris Kennaway return decode_attrib(&iqueue); 114b66f2d16SKris Kennaway } 115b66f2d16SKris Kennaway 116b66f2d16SKris Kennaway /* handle handles */ 117b66f2d16SKris Kennaway 118b66f2d16SKris Kennaway typedef struct Handle Handle; 119b66f2d16SKris Kennaway struct Handle { 120b66f2d16SKris Kennaway int use; 121b66f2d16SKris Kennaway DIR *dirp; 122b66f2d16SKris Kennaway int fd; 123b66f2d16SKris Kennaway char *name; 124b66f2d16SKris Kennaway }; 1251e8db6e2SBrian Feldman 126b66f2d16SKris Kennaway enum { 127b66f2d16SKris Kennaway HANDLE_UNUSED, 128b66f2d16SKris Kennaway HANDLE_DIR, 129b66f2d16SKris Kennaway HANDLE_FILE 130b66f2d16SKris Kennaway }; 1311e8db6e2SBrian Feldman 132b66f2d16SKris Kennaway Handle handles[100]; 133b66f2d16SKris Kennaway 134ae1f160dSDag-Erling Smørgrav static void 135b66f2d16SKris Kennaway handle_init(void) 136b66f2d16SKris Kennaway { 137b66f2d16SKris Kennaway int i; 1381e8db6e2SBrian Feldman 139b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 140b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 141b66f2d16SKris Kennaway } 142b66f2d16SKris Kennaway 143ae1f160dSDag-Erling Smørgrav static int 144efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp) 145b66f2d16SKris Kennaway { 146b66f2d16SKris Kennaway int i; 1471e8db6e2SBrian Feldman 148b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 149b66f2d16SKris Kennaway if (handles[i].use == HANDLE_UNUSED) { 150b66f2d16SKris Kennaway handles[i].use = use; 151b66f2d16SKris Kennaway handles[i].dirp = dirp; 152b66f2d16SKris Kennaway handles[i].fd = fd; 153d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 154b66f2d16SKris Kennaway return i; 155b66f2d16SKris Kennaway } 156b66f2d16SKris Kennaway } 157b66f2d16SKris Kennaway return -1; 158b66f2d16SKris Kennaway } 159b66f2d16SKris Kennaway 160ae1f160dSDag-Erling Smørgrav static int 161b66f2d16SKris Kennaway handle_is_ok(int i, int type) 162b66f2d16SKris Kennaway { 1631e8db6e2SBrian Feldman return i >= 0 && i < sizeof(handles)/sizeof(Handle) && 1641e8db6e2SBrian Feldman handles[i].use == type; 165b66f2d16SKris Kennaway } 166b66f2d16SKris Kennaway 167ae1f160dSDag-Erling Smørgrav static int 168b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 169b66f2d16SKris Kennaway { 170b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 171b66f2d16SKris Kennaway return -1; 1721e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 1731e8db6e2SBrian Feldman PUT_32BIT(*stringp, handle); 1741e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 175b66f2d16SKris Kennaway return 0; 176b66f2d16SKris Kennaway } 177b66f2d16SKris Kennaway 178ae1f160dSDag-Erling Smørgrav static int 179efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen) 180b66f2d16SKris Kennaway { 1811e8db6e2SBrian Feldman int val; 1821e8db6e2SBrian Feldman 1831e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 184b66f2d16SKris Kennaway return -1; 1851e8db6e2SBrian Feldman val = GET_32BIT(handle); 186b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 187b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 188b66f2d16SKris Kennaway return val; 189b66f2d16SKris Kennaway return -1; 190b66f2d16SKris Kennaway } 191b66f2d16SKris Kennaway 192ae1f160dSDag-Erling Smørgrav static char * 193b66f2d16SKris Kennaway handle_to_name(int handle) 194b66f2d16SKris Kennaway { 195b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 196b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 197b66f2d16SKris Kennaway return handles[handle].name; 198b66f2d16SKris Kennaway return NULL; 199b66f2d16SKris Kennaway } 200b66f2d16SKris Kennaway 201ae1f160dSDag-Erling Smørgrav static DIR * 202b66f2d16SKris Kennaway handle_to_dir(int handle) 203b66f2d16SKris Kennaway { 204b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 205b66f2d16SKris Kennaway return handles[handle].dirp; 206b66f2d16SKris Kennaway return NULL; 207b66f2d16SKris Kennaway } 208b66f2d16SKris Kennaway 209ae1f160dSDag-Erling Smørgrav static int 210b66f2d16SKris Kennaway handle_to_fd(int handle) 211b66f2d16SKris Kennaway { 212b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 213b66f2d16SKris Kennaway return handles[handle].fd; 214b66f2d16SKris Kennaway return -1; 215b66f2d16SKris Kennaway } 216b66f2d16SKris Kennaway 217ae1f160dSDag-Erling Smørgrav static int 218b66f2d16SKris Kennaway handle_close(int handle) 219b66f2d16SKris Kennaway { 220b66f2d16SKris Kennaway int ret = -1; 2211e8db6e2SBrian Feldman 222b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 223b66f2d16SKris Kennaway ret = close(handles[handle].fd); 224b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 225d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 226b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 227b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 228b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 229d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 230b66f2d16SKris Kennaway } else { 231b66f2d16SKris Kennaway errno = ENOENT; 232b66f2d16SKris Kennaway } 233b66f2d16SKris Kennaway return ret; 234b66f2d16SKris Kennaway } 235b66f2d16SKris Kennaway 236ae1f160dSDag-Erling Smørgrav static int 237b66f2d16SKris Kennaway get_handle(void) 238b66f2d16SKris Kennaway { 239b66f2d16SKris Kennaway char *handle; 2401e8db6e2SBrian Feldman int val = -1; 241b66f2d16SKris Kennaway u_int hlen; 2421e8db6e2SBrian Feldman 243b66f2d16SKris Kennaway handle = get_string(&hlen); 2441e8db6e2SBrian Feldman if (hlen < 256) 245b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 246b66f2d16SKris Kennaway xfree(handle); 247b66f2d16SKris Kennaway return val; 248b66f2d16SKris Kennaway } 249b66f2d16SKris Kennaway 250b66f2d16SKris Kennaway /* send replies */ 251b66f2d16SKris Kennaway 252ae1f160dSDag-Erling Smørgrav static void 253b66f2d16SKris Kennaway send_msg(Buffer *m) 254b66f2d16SKris Kennaway { 255b66f2d16SKris Kennaway int mlen = buffer_len(m); 2561e8db6e2SBrian Feldman 257b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 258b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 259b66f2d16SKris Kennaway buffer_consume(m, mlen); 260b66f2d16SKris Kennaway } 261b66f2d16SKris Kennaway 262ae1f160dSDag-Erling Smørgrav static void 263b66f2d16SKris Kennaway send_status(u_int32_t id, u_int32_t error) 264b66f2d16SKris Kennaway { 265b66f2d16SKris Kennaway Buffer msg; 2661e8db6e2SBrian Feldman const char *status_messages[] = { 2671e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 2681e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 2691e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 2701e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 2711e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 2721e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 2731e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 2741e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 2751e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 2761e8db6e2SBrian Feldman "Unknown error" /* Others */ 2771e8db6e2SBrian Feldman }; 2781e8db6e2SBrian Feldman 279ee21a45fSDag-Erling Smørgrav TRACE("sent status id %u error %u", id, error); 280b66f2d16SKris Kennaway buffer_init(&msg); 2811e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 282b66f2d16SKris Kennaway buffer_put_int(&msg, id); 283b66f2d16SKris Kennaway buffer_put_int(&msg, error); 2841e8db6e2SBrian Feldman if (version >= 3) { 2851e8db6e2SBrian Feldman buffer_put_cstring(&msg, 2861e8db6e2SBrian Feldman status_messages[MIN(error,SSH2_FX_MAX)]); 2871e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 2881e8db6e2SBrian Feldman } 289b66f2d16SKris Kennaway send_msg(&msg); 290b66f2d16SKris Kennaway buffer_free(&msg); 291b66f2d16SKris Kennaway } 292ae1f160dSDag-Erling Smørgrav static void 293efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 294b66f2d16SKris Kennaway { 295b66f2d16SKris Kennaway Buffer msg; 2961e8db6e2SBrian Feldman 297b66f2d16SKris Kennaway buffer_init(&msg); 298b66f2d16SKris Kennaway buffer_put_char(&msg, type); 299b66f2d16SKris Kennaway buffer_put_int(&msg, id); 300b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 301b66f2d16SKris Kennaway send_msg(&msg); 302b66f2d16SKris Kennaway buffer_free(&msg); 303b66f2d16SKris Kennaway } 304b66f2d16SKris Kennaway 305ae1f160dSDag-Erling Smørgrav static void 306efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen) 307b66f2d16SKris Kennaway { 308ee21a45fSDag-Erling Smørgrav TRACE("sent data id %u len %d", id, dlen); 3091e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 310b66f2d16SKris Kennaway } 311b66f2d16SKris Kennaway 312ae1f160dSDag-Erling Smørgrav static void 313b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 314b66f2d16SKris Kennaway { 315b66f2d16SKris Kennaway char *string; 316b66f2d16SKris Kennaway int hlen; 3171e8db6e2SBrian Feldman 318b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 319ee21a45fSDag-Erling Smørgrav TRACE("sent handle id %u handle %d", id, handle); 3201e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 321b66f2d16SKris Kennaway xfree(string); 322b66f2d16SKris Kennaway } 323b66f2d16SKris Kennaway 324ae1f160dSDag-Erling Smørgrav static void 325efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 326b66f2d16SKris Kennaway { 327b66f2d16SKris Kennaway Buffer msg; 328b66f2d16SKris Kennaway int i; 3291e8db6e2SBrian Feldman 330b66f2d16SKris Kennaway buffer_init(&msg); 3311e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 332b66f2d16SKris Kennaway buffer_put_int(&msg, id); 333b66f2d16SKris Kennaway buffer_put_int(&msg, count); 334ee21a45fSDag-Erling Smørgrav TRACE("sent names id %u count %d", id, count); 335b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 336b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 337b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 338b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 339b66f2d16SKris Kennaway } 340b66f2d16SKris Kennaway send_msg(&msg); 341b66f2d16SKris Kennaway buffer_free(&msg); 342b66f2d16SKris Kennaway } 343b66f2d16SKris Kennaway 344ae1f160dSDag-Erling Smørgrav static void 345efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 346b66f2d16SKris Kennaway { 347b66f2d16SKris Kennaway Buffer msg; 3481e8db6e2SBrian Feldman 349ee21a45fSDag-Erling Smørgrav TRACE("sent attrib id %u have 0x%x", id, a->flags); 350b66f2d16SKris Kennaway buffer_init(&msg); 3511e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 352b66f2d16SKris Kennaway buffer_put_int(&msg, id); 353b66f2d16SKris Kennaway encode_attrib(&msg, a); 354b66f2d16SKris Kennaway send_msg(&msg); 355b66f2d16SKris Kennaway buffer_free(&msg); 356b66f2d16SKris Kennaway } 357b66f2d16SKris Kennaway 358b66f2d16SKris Kennaway /* parse incoming */ 359b66f2d16SKris Kennaway 360ae1f160dSDag-Erling Smørgrav static void 361b66f2d16SKris Kennaway process_init(void) 362b66f2d16SKris Kennaway { 363b66f2d16SKris Kennaway Buffer msg; 364b66f2d16SKris Kennaway 365545d5ecaSDag-Erling Smørgrav version = get_int(); 366b66f2d16SKris Kennaway TRACE("client version %d", version); 367b66f2d16SKris Kennaway buffer_init(&msg); 3681e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 3691e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 370b66f2d16SKris Kennaway send_msg(&msg); 371b66f2d16SKris Kennaway buffer_free(&msg); 372b66f2d16SKris Kennaway } 373b66f2d16SKris Kennaway 374ae1f160dSDag-Erling Smørgrav static void 375b66f2d16SKris Kennaway process_open(void) 376b66f2d16SKris Kennaway { 377b66f2d16SKris Kennaway u_int32_t id, pflags; 378b66f2d16SKris Kennaway Attrib *a; 379b66f2d16SKris Kennaway char *name; 3801e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 381b66f2d16SKris Kennaway 382b66f2d16SKris Kennaway id = get_int(); 383b66f2d16SKris Kennaway name = get_string(NULL); 3841e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 385b66f2d16SKris Kennaway a = get_attrib(); 386b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 3871e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 388ee21a45fSDag-Erling Smørgrav TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); 389b66f2d16SKris Kennaway fd = open(name, flags, mode); 390b66f2d16SKris Kennaway if (fd < 0) { 391b66f2d16SKris Kennaway status = errno_to_portable(errno); 392b66f2d16SKris Kennaway } else { 393d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, NULL); 394b66f2d16SKris Kennaway if (handle < 0) { 395b66f2d16SKris Kennaway close(fd); 396b66f2d16SKris Kennaway } else { 397b66f2d16SKris Kennaway send_handle(id, handle); 3981e8db6e2SBrian Feldman status = SSH2_FX_OK; 399b66f2d16SKris Kennaway } 400b66f2d16SKris Kennaway } 4011e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 402b66f2d16SKris Kennaway send_status(id, status); 403b66f2d16SKris Kennaway xfree(name); 404b66f2d16SKris Kennaway } 405b66f2d16SKris Kennaway 406ae1f160dSDag-Erling Smørgrav static void 407b66f2d16SKris Kennaway process_close(void) 408b66f2d16SKris Kennaway { 409b66f2d16SKris Kennaway u_int32_t id; 4101e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 411b66f2d16SKris Kennaway 412b66f2d16SKris Kennaway id = get_int(); 413b66f2d16SKris Kennaway handle = get_handle(); 414ee21a45fSDag-Erling Smørgrav TRACE("close id %u handle %d", id, handle); 415b66f2d16SKris Kennaway ret = handle_close(handle); 4161e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 417b66f2d16SKris Kennaway send_status(id, status); 418b66f2d16SKris Kennaway } 419b66f2d16SKris Kennaway 420ae1f160dSDag-Erling Smørgrav static void 421b66f2d16SKris Kennaway process_read(void) 422b66f2d16SKris Kennaway { 423b66f2d16SKris Kennaway char buf[64*1024]; 4241e8db6e2SBrian Feldman u_int32_t id, len; 4251e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 426b66f2d16SKris Kennaway u_int64_t off; 427b66f2d16SKris Kennaway 428b66f2d16SKris Kennaway id = get_int(); 429b66f2d16SKris Kennaway handle = get_handle(); 4301e8db6e2SBrian Feldman off = get_int64(); 431b66f2d16SKris Kennaway len = get_int(); 432b66f2d16SKris Kennaway 433ee21a45fSDag-Erling Smørgrav TRACE("read id %u handle %d off %llu len %d", id, handle, 43483d2307dSDag-Erling Smørgrav (u_int64_t)off, len); 435b66f2d16SKris Kennaway if (len > sizeof buf) { 436b66f2d16SKris Kennaway len = sizeof buf; 437d95e11bfSDag-Erling Smørgrav logit("read change len %d", len); 438b66f2d16SKris Kennaway } 439b66f2d16SKris Kennaway fd = handle_to_fd(handle); 440b66f2d16SKris Kennaway if (fd >= 0) { 441b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 442b66f2d16SKris Kennaway error("process_read: seek failed"); 443b66f2d16SKris Kennaway status = errno_to_portable(errno); 444b66f2d16SKris Kennaway } else { 445b66f2d16SKris Kennaway ret = read(fd, buf, len); 446b66f2d16SKris Kennaway if (ret < 0) { 447b66f2d16SKris Kennaway status = errno_to_portable(errno); 448b66f2d16SKris Kennaway } else if (ret == 0) { 4491e8db6e2SBrian Feldman status = SSH2_FX_EOF; 450b66f2d16SKris Kennaway } else { 451b66f2d16SKris Kennaway send_data(id, buf, ret); 4521e8db6e2SBrian Feldman status = SSH2_FX_OK; 453b66f2d16SKris Kennaway } 454b66f2d16SKris Kennaway } 455b66f2d16SKris Kennaway } 4561e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 457b66f2d16SKris Kennaway send_status(id, status); 458b66f2d16SKris Kennaway } 459b66f2d16SKris Kennaway 460ae1f160dSDag-Erling Smørgrav static void 461b66f2d16SKris Kennaway process_write(void) 462b66f2d16SKris Kennaway { 4631e8db6e2SBrian Feldman u_int32_t id; 464b66f2d16SKris Kennaway u_int64_t off; 465b66f2d16SKris Kennaway u_int len; 4661e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 467b66f2d16SKris Kennaway char *data; 468b66f2d16SKris Kennaway 469b66f2d16SKris Kennaway id = get_int(); 470b66f2d16SKris Kennaway handle = get_handle(); 4711e8db6e2SBrian Feldman off = get_int64(); 472b66f2d16SKris Kennaway data = get_string(&len); 473b66f2d16SKris Kennaway 474ee21a45fSDag-Erling Smørgrav TRACE("write id %u handle %d off %llu len %d", id, handle, 47583d2307dSDag-Erling Smørgrav (u_int64_t)off, len); 476b66f2d16SKris Kennaway fd = handle_to_fd(handle); 477b66f2d16SKris Kennaway if (fd >= 0) { 478b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 479b66f2d16SKris Kennaway status = errno_to_portable(errno); 480b66f2d16SKris Kennaway error("process_write: seek failed"); 481b66f2d16SKris Kennaway } else { 482b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 483b66f2d16SKris Kennaway ret = write(fd, data, len); 484b66f2d16SKris Kennaway if (ret == -1) { 485b66f2d16SKris Kennaway error("process_write: write failed"); 486b66f2d16SKris Kennaway status = errno_to_portable(errno); 487b66f2d16SKris Kennaway } else if (ret == len) { 4881e8db6e2SBrian Feldman status = SSH2_FX_OK; 489b66f2d16SKris Kennaway } else { 490d95e11bfSDag-Erling Smørgrav logit("nothing at all written"); 491b66f2d16SKris Kennaway } 492b66f2d16SKris Kennaway } 493b66f2d16SKris Kennaway } 494b66f2d16SKris Kennaway send_status(id, status); 495b66f2d16SKris Kennaway xfree(data); 496b66f2d16SKris Kennaway } 497b66f2d16SKris Kennaway 498ae1f160dSDag-Erling Smørgrav static void 499b66f2d16SKris Kennaway process_do_stat(int do_lstat) 500b66f2d16SKris Kennaway { 5011e8db6e2SBrian Feldman Attrib a; 502b66f2d16SKris Kennaway struct stat st; 503b66f2d16SKris Kennaway u_int32_t id; 504b66f2d16SKris Kennaway char *name; 5051e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 506b66f2d16SKris Kennaway 507b66f2d16SKris Kennaway id = get_int(); 508b66f2d16SKris Kennaway name = get_string(NULL); 509ee21a45fSDag-Erling Smørgrav TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); 510b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 511b66f2d16SKris Kennaway if (ret < 0) { 512b66f2d16SKris Kennaway status = errno_to_portable(errno); 513b66f2d16SKris Kennaway } else { 5141e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5151e8db6e2SBrian Feldman send_attrib(id, &a); 5161e8db6e2SBrian Feldman status = SSH2_FX_OK; 517b66f2d16SKris Kennaway } 5181e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 519b66f2d16SKris Kennaway send_status(id, status); 520b66f2d16SKris Kennaway xfree(name); 521b66f2d16SKris Kennaway } 522b66f2d16SKris Kennaway 523ae1f160dSDag-Erling Smørgrav static void 524b66f2d16SKris Kennaway process_stat(void) 525b66f2d16SKris Kennaway { 526b66f2d16SKris Kennaway process_do_stat(0); 527b66f2d16SKris Kennaway } 528b66f2d16SKris Kennaway 529ae1f160dSDag-Erling Smørgrav static void 530b66f2d16SKris Kennaway process_lstat(void) 531b66f2d16SKris Kennaway { 532b66f2d16SKris Kennaway process_do_stat(1); 533b66f2d16SKris Kennaway } 534b66f2d16SKris Kennaway 535ae1f160dSDag-Erling Smørgrav static void 536b66f2d16SKris Kennaway process_fstat(void) 537b66f2d16SKris Kennaway { 5381e8db6e2SBrian Feldman Attrib a; 539b66f2d16SKris Kennaway struct stat st; 540b66f2d16SKris Kennaway u_int32_t id; 5411e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 542b66f2d16SKris Kennaway 543b66f2d16SKris Kennaway id = get_int(); 544b66f2d16SKris Kennaway handle = get_handle(); 545ee21a45fSDag-Erling Smørgrav TRACE("fstat id %u handle %d", id, handle); 546b66f2d16SKris Kennaway fd = handle_to_fd(handle); 547b66f2d16SKris Kennaway if (fd >= 0) { 548b66f2d16SKris Kennaway ret = fstat(fd, &st); 549b66f2d16SKris Kennaway if (ret < 0) { 550b66f2d16SKris Kennaway status = errno_to_portable(errno); 551b66f2d16SKris Kennaway } else { 5521e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5531e8db6e2SBrian Feldman send_attrib(id, &a); 5541e8db6e2SBrian Feldman status = SSH2_FX_OK; 555b66f2d16SKris Kennaway } 556b66f2d16SKris Kennaway } 5571e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 558b66f2d16SKris Kennaway send_status(id, status); 559b66f2d16SKris Kennaway } 560b66f2d16SKris Kennaway 561ae1f160dSDag-Erling Smørgrav static struct timeval * 562efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 563b66f2d16SKris Kennaway { 564b66f2d16SKris Kennaway static struct timeval tv[2]; 5651e8db6e2SBrian Feldman 566b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 567b66f2d16SKris Kennaway tv[0].tv_usec = 0; 568b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 569b66f2d16SKris Kennaway tv[1].tv_usec = 0; 570b66f2d16SKris Kennaway return tv; 571b66f2d16SKris Kennaway } 572b66f2d16SKris Kennaway 573ae1f160dSDag-Erling Smørgrav static void 574b66f2d16SKris Kennaway process_setstat(void) 575b66f2d16SKris Kennaway { 576b66f2d16SKris Kennaway Attrib *a; 577b66f2d16SKris Kennaway u_int32_t id; 578b66f2d16SKris Kennaway char *name; 579ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 580b66f2d16SKris Kennaway 581b66f2d16SKris Kennaway id = get_int(); 582b66f2d16SKris Kennaway name = get_string(NULL); 583b66f2d16SKris Kennaway a = get_attrib(); 584ee21a45fSDag-Erling Smørgrav TRACE("setstat id %u name %s", id, name); 585ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 586ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 587ae1f160dSDag-Erling Smørgrav if (ret == -1) 588ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 589ae1f160dSDag-Erling Smørgrav } 5901e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 591b66f2d16SKris Kennaway ret = chmod(name, a->perm & 0777); 592b66f2d16SKris Kennaway if (ret == -1) 593b66f2d16SKris Kennaway status = errno_to_portable(errno); 594b66f2d16SKris Kennaway } 5951e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 596b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 597b66f2d16SKris Kennaway if (ret == -1) 598b66f2d16SKris Kennaway status = errno_to_portable(errno); 599b66f2d16SKris Kennaway } 6001e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 6011e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 6021e8db6e2SBrian Feldman if (ret == -1) 6031e8db6e2SBrian Feldman status = errno_to_portable(errno); 6041e8db6e2SBrian Feldman } 605b66f2d16SKris Kennaway send_status(id, status); 606b66f2d16SKris Kennaway xfree(name); 607b66f2d16SKris Kennaway } 608b66f2d16SKris Kennaway 609ae1f160dSDag-Erling Smørgrav static void 610b66f2d16SKris Kennaway process_fsetstat(void) 611b66f2d16SKris Kennaway { 612b66f2d16SKris Kennaway Attrib *a; 613b66f2d16SKris Kennaway u_int32_t id; 614b66f2d16SKris Kennaway int handle, fd, ret; 6151e8db6e2SBrian Feldman int status = SSH2_FX_OK; 61683d2307dSDag-Erling Smørgrav char *name; 617b66f2d16SKris Kennaway 618b66f2d16SKris Kennaway id = get_int(); 619b66f2d16SKris Kennaway handle = get_handle(); 620b66f2d16SKris Kennaway a = get_attrib(); 621ee21a45fSDag-Erling Smørgrav TRACE("fsetstat id %u handle %d", id, handle); 622b66f2d16SKris Kennaway fd = handle_to_fd(handle); 62383d2307dSDag-Erling Smørgrav name = handle_to_name(handle); 62483d2307dSDag-Erling Smørgrav if (fd < 0 || name == NULL) { 6251e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 626b66f2d16SKris Kennaway } else { 627ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 628ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 629ae1f160dSDag-Erling Smørgrav if (ret == -1) 630ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 631ae1f160dSDag-Erling Smørgrav } 6321e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 63383d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 634b66f2d16SKris Kennaway ret = fchmod(fd, a->perm & 0777); 63583d2307dSDag-Erling Smørgrav #else 63683d2307dSDag-Erling Smørgrav ret = chmod(name, a->perm & 0777); 63783d2307dSDag-Erling Smørgrav #endif 638b66f2d16SKris Kennaway if (ret == -1) 639b66f2d16SKris Kennaway status = errno_to_portable(errno); 640b66f2d16SKris Kennaway } 6411e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 64283d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 643b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 64483d2307dSDag-Erling Smørgrav #else 64583d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 64683d2307dSDag-Erling Smørgrav #endif 647b66f2d16SKris Kennaway if (ret == -1) 648b66f2d16SKris Kennaway status = errno_to_portable(errno); 649b66f2d16SKris Kennaway } 6501e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 65183d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 6521e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 65383d2307dSDag-Erling Smørgrav #else 65483d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 65583d2307dSDag-Erling Smørgrav #endif 6561e8db6e2SBrian Feldman if (ret == -1) 6571e8db6e2SBrian Feldman status = errno_to_portable(errno); 6581e8db6e2SBrian Feldman } 659b66f2d16SKris Kennaway } 660b66f2d16SKris Kennaway send_status(id, status); 661b66f2d16SKris Kennaway } 662b66f2d16SKris Kennaway 663ae1f160dSDag-Erling Smørgrav static void 664b66f2d16SKris Kennaway process_opendir(void) 665b66f2d16SKris Kennaway { 666b66f2d16SKris Kennaway DIR *dirp = NULL; 667b66f2d16SKris Kennaway char *path; 6681e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 669b66f2d16SKris Kennaway u_int32_t id; 670b66f2d16SKris Kennaway 671b66f2d16SKris Kennaway id = get_int(); 672b66f2d16SKris Kennaway path = get_string(NULL); 673ee21a45fSDag-Erling Smørgrav TRACE("opendir id %u path %s", id, path); 674b66f2d16SKris Kennaway dirp = opendir(path); 675b66f2d16SKris Kennaway if (dirp == NULL) { 676b66f2d16SKris Kennaway status = errno_to_portable(errno); 677b66f2d16SKris Kennaway } else { 678d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, dirp); 679b66f2d16SKris Kennaway if (handle < 0) { 680b66f2d16SKris Kennaway closedir(dirp); 681b66f2d16SKris Kennaway } else { 682b66f2d16SKris Kennaway send_handle(id, handle); 6831e8db6e2SBrian Feldman status = SSH2_FX_OK; 684b66f2d16SKris Kennaway } 685b66f2d16SKris Kennaway 686b66f2d16SKris Kennaway } 6871e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 688b66f2d16SKris Kennaway send_status(id, status); 689b66f2d16SKris Kennaway xfree(path); 690b66f2d16SKris Kennaway } 691b66f2d16SKris Kennaway 692ae1f160dSDag-Erling Smørgrav static void 693b66f2d16SKris Kennaway process_readdir(void) 694b66f2d16SKris Kennaway { 695b66f2d16SKris Kennaway DIR *dirp; 696b66f2d16SKris Kennaway struct dirent *dp; 697b66f2d16SKris Kennaway char *path; 698b66f2d16SKris Kennaway int handle; 699b66f2d16SKris Kennaway u_int32_t id; 700b66f2d16SKris Kennaway 701b66f2d16SKris Kennaway id = get_int(); 702b66f2d16SKris Kennaway handle = get_handle(); 703ee21a45fSDag-Erling Smørgrav TRACE("readdir id %u handle %d", id, handle); 704b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 705b66f2d16SKris Kennaway path = handle_to_name(handle); 706b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 7071e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 708b66f2d16SKris Kennaway } else { 709b66f2d16SKris Kennaway struct stat st; 710b66f2d16SKris Kennaway char pathname[1024]; 711b66f2d16SKris Kennaway Stat *stats; 712b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 713ee21a45fSDag-Erling Smørgrav 714b66f2d16SKris Kennaway stats = xmalloc(nstats * sizeof(Stat)); 715b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 716b66f2d16SKris Kennaway if (count >= nstats) { 717b66f2d16SKris Kennaway nstats *= 2; 718b66f2d16SKris Kennaway stats = xrealloc(stats, nstats * sizeof(Stat)); 719b66f2d16SKris Kennaway } 720b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 721ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 722ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 723b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 724b66f2d16SKris Kennaway continue; 7251e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 726b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 7274b17dab0SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0); 728b66f2d16SKris Kennaway count++; 729b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 7301e8db6e2SBrian Feldman /* XXX check packet size instead */ 731b66f2d16SKris Kennaway if (count == 100) 732b66f2d16SKris Kennaway break; 733b66f2d16SKris Kennaway } 7341e8db6e2SBrian Feldman if (count > 0) { 735b66f2d16SKris Kennaway send_names(id, count, stats); 736b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 737b66f2d16SKris Kennaway xfree(stats[i].name); 738b66f2d16SKris Kennaway xfree(stats[i].long_name); 739b66f2d16SKris Kennaway } 7401e8db6e2SBrian Feldman } else { 7411e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 7421e8db6e2SBrian Feldman } 743b66f2d16SKris Kennaway xfree(stats); 744b66f2d16SKris Kennaway } 745b66f2d16SKris Kennaway } 746b66f2d16SKris Kennaway 747ae1f160dSDag-Erling Smørgrav static void 748b66f2d16SKris Kennaway process_remove(void) 749b66f2d16SKris Kennaway { 750b66f2d16SKris Kennaway char *name; 751b66f2d16SKris Kennaway u_int32_t id; 7521e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 753b66f2d16SKris Kennaway int ret; 754b66f2d16SKris Kennaway 755b66f2d16SKris Kennaway id = get_int(); 756b66f2d16SKris Kennaway name = get_string(NULL); 757ee21a45fSDag-Erling Smørgrav TRACE("remove id %u name %s", id, name); 7581e8db6e2SBrian Feldman ret = unlink(name); 7591e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 760b66f2d16SKris Kennaway send_status(id, status); 761b66f2d16SKris Kennaway xfree(name); 762b66f2d16SKris Kennaway } 763b66f2d16SKris Kennaway 764ae1f160dSDag-Erling Smørgrav static void 765b66f2d16SKris Kennaway process_mkdir(void) 766b66f2d16SKris Kennaway { 767b66f2d16SKris Kennaway Attrib *a; 768b66f2d16SKris Kennaway u_int32_t id; 769b66f2d16SKris Kennaway char *name; 7701e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 771b66f2d16SKris Kennaway 772b66f2d16SKris Kennaway id = get_int(); 773b66f2d16SKris Kennaway name = get_string(NULL); 774b66f2d16SKris Kennaway a = get_attrib(); 7751e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 7761e8db6e2SBrian Feldman a->perm & 0777 : 0777; 777ee21a45fSDag-Erling Smørgrav TRACE("mkdir id %u name %s mode 0%o", id, name, mode); 778b66f2d16SKris Kennaway ret = mkdir(name, mode); 7791e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 780b66f2d16SKris Kennaway send_status(id, status); 781b66f2d16SKris Kennaway xfree(name); 782b66f2d16SKris Kennaway } 783b66f2d16SKris Kennaway 784ae1f160dSDag-Erling Smørgrav static void 785b66f2d16SKris Kennaway process_rmdir(void) 786b66f2d16SKris Kennaway { 787b66f2d16SKris Kennaway u_int32_t id; 788b66f2d16SKris Kennaway char *name; 789b66f2d16SKris Kennaway int ret, status; 790b66f2d16SKris Kennaway 791b66f2d16SKris Kennaway id = get_int(); 792b66f2d16SKris Kennaway name = get_string(NULL); 793ee21a45fSDag-Erling Smørgrav TRACE("rmdir id %u name %s", id, name); 794b66f2d16SKris Kennaway ret = rmdir(name); 7951e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 796b66f2d16SKris Kennaway send_status(id, status); 797b66f2d16SKris Kennaway xfree(name); 798b66f2d16SKris Kennaway } 799b66f2d16SKris Kennaway 800ae1f160dSDag-Erling Smørgrav static void 801b66f2d16SKris Kennaway process_realpath(void) 802b66f2d16SKris Kennaway { 803b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 804b66f2d16SKris Kennaway u_int32_t id; 805b66f2d16SKris Kennaway char *path; 806b66f2d16SKris Kennaway 807b66f2d16SKris Kennaway id = get_int(); 808b66f2d16SKris Kennaway path = get_string(NULL); 8091e8db6e2SBrian Feldman if (path[0] == '\0') { 8101e8db6e2SBrian Feldman xfree(path); 8111e8db6e2SBrian Feldman path = xstrdup("."); 8121e8db6e2SBrian Feldman } 813ee21a45fSDag-Erling Smørgrav TRACE("realpath id %u path %s", id, path); 814b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 815b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 816b66f2d16SKris Kennaway } else { 817b66f2d16SKris Kennaway Stat s; 818b66f2d16SKris Kennaway attrib_clear(&s.attrib); 819b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 820b66f2d16SKris Kennaway send_names(id, 1, &s); 821b66f2d16SKris Kennaway } 822b66f2d16SKris Kennaway xfree(path); 823b66f2d16SKris Kennaway } 824b66f2d16SKris Kennaway 825ae1f160dSDag-Erling Smørgrav static void 826b66f2d16SKris Kennaway process_rename(void) 827b66f2d16SKris Kennaway { 828b66f2d16SKris Kennaway u_int32_t id; 829b66f2d16SKris Kennaway char *oldpath, *newpath; 830d0c8c0bcSDag-Erling Smørgrav int status; 831d0c8c0bcSDag-Erling Smørgrav struct stat sb; 832b66f2d16SKris Kennaway 833b66f2d16SKris Kennaway id = get_int(); 834b66f2d16SKris Kennaway oldpath = get_string(NULL); 835b66f2d16SKris Kennaway newpath = get_string(NULL); 836ee21a45fSDag-Erling Smørgrav TRACE("rename id %u old %s new %s", id, oldpath, newpath); 837d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 838d0c8c0bcSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 839d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 840d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 841d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 842d0c8c0bcSDag-Erling Smørgrav if (link(oldpath, newpath) == -1) 843d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 844d0c8c0bcSDag-Erling Smørgrav else if (unlink(oldpath) == -1) { 845d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 846d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 847d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 848d0c8c0bcSDag-Erling Smørgrav } else 849d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 850d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 851d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 852d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 853d0c8c0bcSDag-Erling Smørgrav else 854d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 8551e8db6e2SBrian Feldman } 856b66f2d16SKris Kennaway send_status(id, status); 857b66f2d16SKris Kennaway xfree(oldpath); 858b66f2d16SKris Kennaway xfree(newpath); 859b66f2d16SKris Kennaway } 860b66f2d16SKris Kennaway 861ae1f160dSDag-Erling Smørgrav static void 8621e8db6e2SBrian Feldman process_readlink(void) 8631e8db6e2SBrian Feldman { 8641e8db6e2SBrian Feldman u_int32_t id; 865ae1f160dSDag-Erling Smørgrav int len; 8661e8db6e2SBrian Feldman char link[MAXPATHLEN]; 8671e8db6e2SBrian Feldman char *path; 8681e8db6e2SBrian Feldman 8691e8db6e2SBrian Feldman id = get_int(); 8701e8db6e2SBrian Feldman path = get_string(NULL); 871ee21a45fSDag-Erling Smørgrav TRACE("readlink id %u path %s", id, path); 872ae1f160dSDag-Erling Smørgrav if ((len = readlink(path, link, sizeof(link) - 1)) == -1) 8731e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 8741e8db6e2SBrian Feldman else { 8751e8db6e2SBrian Feldman Stat s; 8761e8db6e2SBrian Feldman 877ae1f160dSDag-Erling Smørgrav link[len] = '\0'; 8781e8db6e2SBrian Feldman attrib_clear(&s.attrib); 8791e8db6e2SBrian Feldman s.name = s.long_name = link; 8801e8db6e2SBrian Feldman send_names(id, 1, &s); 8811e8db6e2SBrian Feldman } 8821e8db6e2SBrian Feldman xfree(path); 8831e8db6e2SBrian Feldman } 8841e8db6e2SBrian Feldman 885ae1f160dSDag-Erling Smørgrav static void 8861e8db6e2SBrian Feldman process_symlink(void) 8871e8db6e2SBrian Feldman { 8881e8db6e2SBrian Feldman u_int32_t id; 8891e8db6e2SBrian Feldman char *oldpath, *newpath; 890d0c8c0bcSDag-Erling Smørgrav int ret, status; 8911e8db6e2SBrian Feldman 8921e8db6e2SBrian Feldman id = get_int(); 8931e8db6e2SBrian Feldman oldpath = get_string(NULL); 8941e8db6e2SBrian Feldman newpath = get_string(NULL); 895ee21a45fSDag-Erling Smørgrav TRACE("symlink id %u old %s new %s", id, oldpath, newpath); 896d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 8971e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 8981e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 8991e8db6e2SBrian Feldman send_status(id, status); 9001e8db6e2SBrian Feldman xfree(oldpath); 9011e8db6e2SBrian Feldman xfree(newpath); 9021e8db6e2SBrian Feldman } 9031e8db6e2SBrian Feldman 904ae1f160dSDag-Erling Smørgrav static void 9051e8db6e2SBrian Feldman process_extended(void) 9061e8db6e2SBrian Feldman { 9071e8db6e2SBrian Feldman u_int32_t id; 9081e8db6e2SBrian Feldman char *request; 9091e8db6e2SBrian Feldman 9101e8db6e2SBrian Feldman id = get_int(); 9111e8db6e2SBrian Feldman request = get_string(NULL); 9121e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 9131e8db6e2SBrian Feldman xfree(request); 9141e8db6e2SBrian Feldman } 915b66f2d16SKris Kennaway 916b66f2d16SKris Kennaway /* stolen from ssh-agent */ 917b66f2d16SKris Kennaway 918ae1f160dSDag-Erling Smørgrav static void 919b66f2d16SKris Kennaway process(void) 920b66f2d16SKris Kennaway { 9211e8db6e2SBrian Feldman u_int msg_len; 922545d5ecaSDag-Erling Smørgrav u_int buf_len; 923545d5ecaSDag-Erling Smørgrav u_int consumed; 9241e8db6e2SBrian Feldman u_int type; 9251e8db6e2SBrian Feldman u_char *cp; 926b66f2d16SKris Kennaway 927545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 928545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 929b66f2d16SKris Kennaway return; /* Incomplete message. */ 930ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 931b66f2d16SKris Kennaway msg_len = GET_32BIT(cp); 932b66f2d16SKris Kennaway if (msg_len > 256 * 1024) { 933b66f2d16SKris Kennaway error("bad message "); 934b66f2d16SKris Kennaway exit(11); 935b66f2d16SKris Kennaway } 936545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 937b66f2d16SKris Kennaway return; 938b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 939545d5ecaSDag-Erling Smørgrav buf_len -= 4; 940b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 941b66f2d16SKris Kennaway switch (type) { 9421e8db6e2SBrian Feldman case SSH2_FXP_INIT: 943b66f2d16SKris Kennaway process_init(); 944b66f2d16SKris Kennaway break; 9451e8db6e2SBrian Feldman case SSH2_FXP_OPEN: 946b66f2d16SKris Kennaway process_open(); 947b66f2d16SKris Kennaway break; 9481e8db6e2SBrian Feldman case SSH2_FXP_CLOSE: 949b66f2d16SKris Kennaway process_close(); 950b66f2d16SKris Kennaway break; 9511e8db6e2SBrian Feldman case SSH2_FXP_READ: 952b66f2d16SKris Kennaway process_read(); 953b66f2d16SKris Kennaway break; 9541e8db6e2SBrian Feldman case SSH2_FXP_WRITE: 955b66f2d16SKris Kennaway process_write(); 956b66f2d16SKris Kennaway break; 9571e8db6e2SBrian Feldman case SSH2_FXP_LSTAT: 958b66f2d16SKris Kennaway process_lstat(); 959b66f2d16SKris Kennaway break; 9601e8db6e2SBrian Feldman case SSH2_FXP_FSTAT: 961b66f2d16SKris Kennaway process_fstat(); 962b66f2d16SKris Kennaway break; 9631e8db6e2SBrian Feldman case SSH2_FXP_SETSTAT: 964b66f2d16SKris Kennaway process_setstat(); 965b66f2d16SKris Kennaway break; 9661e8db6e2SBrian Feldman case SSH2_FXP_FSETSTAT: 967b66f2d16SKris Kennaway process_fsetstat(); 968b66f2d16SKris Kennaway break; 9691e8db6e2SBrian Feldman case SSH2_FXP_OPENDIR: 970b66f2d16SKris Kennaway process_opendir(); 971b66f2d16SKris Kennaway break; 9721e8db6e2SBrian Feldman case SSH2_FXP_READDIR: 973b66f2d16SKris Kennaway process_readdir(); 974b66f2d16SKris Kennaway break; 9751e8db6e2SBrian Feldman case SSH2_FXP_REMOVE: 976b66f2d16SKris Kennaway process_remove(); 977b66f2d16SKris Kennaway break; 9781e8db6e2SBrian Feldman case SSH2_FXP_MKDIR: 979b66f2d16SKris Kennaway process_mkdir(); 980b66f2d16SKris Kennaway break; 9811e8db6e2SBrian Feldman case SSH2_FXP_RMDIR: 982b66f2d16SKris Kennaway process_rmdir(); 983b66f2d16SKris Kennaway break; 9841e8db6e2SBrian Feldman case SSH2_FXP_REALPATH: 985b66f2d16SKris Kennaway process_realpath(); 986b66f2d16SKris Kennaway break; 9871e8db6e2SBrian Feldman case SSH2_FXP_STAT: 988b66f2d16SKris Kennaway process_stat(); 989b66f2d16SKris Kennaway break; 9901e8db6e2SBrian Feldman case SSH2_FXP_RENAME: 991b66f2d16SKris Kennaway process_rename(); 992b66f2d16SKris Kennaway break; 9931e8db6e2SBrian Feldman case SSH2_FXP_READLINK: 9941e8db6e2SBrian Feldman process_readlink(); 9951e8db6e2SBrian Feldman break; 9961e8db6e2SBrian Feldman case SSH2_FXP_SYMLINK: 9971e8db6e2SBrian Feldman process_symlink(); 9981e8db6e2SBrian Feldman break; 9991e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 10001e8db6e2SBrian Feldman process_extended(); 10011e8db6e2SBrian Feldman break; 1002b66f2d16SKris Kennaway default: 1003b66f2d16SKris Kennaway error("Unknown message %d", type); 1004b66f2d16SKris Kennaway break; 1005b66f2d16SKris Kennaway } 1006545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1007545d5ecaSDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) 1008545d5ecaSDag-Erling Smørgrav fatal("iqueue grows"); 1009545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1010545d5ecaSDag-Erling Smørgrav if (msg_len < consumed) 1011545d5ecaSDag-Erling Smørgrav fatal("msg_len %d < consumed %d", msg_len, consumed); 1012545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1013545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1014b66f2d16SKris Kennaway } 1015b66f2d16SKris Kennaway 1016b66f2d16SKris Kennaway int 1017b66f2d16SKris Kennaway main(int ac, char **av) 1018b66f2d16SKris Kennaway { 10191e8db6e2SBrian Feldman fd_set *rset, *wset; 1020b66f2d16SKris Kennaway int in, out, max; 10211e8db6e2SBrian Feldman ssize_t len, olen, set_size; 10221e8db6e2SBrian Feldman 10231e8db6e2SBrian Feldman /* XXX should use getopt */ 1024b66f2d16SKris Kennaway 1025d95e11bfSDag-Erling Smørgrav __progname = ssh_get_progname(av[0]); 1026b66f2d16SKris Kennaway handle_init(); 1027b66f2d16SKris Kennaway 10281e8db6e2SBrian Feldman #ifdef DEBUG_SFTP_SERVER 10291e8db6e2SBrian Feldman log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 10301e8db6e2SBrian Feldman #endif 10311e8db6e2SBrian Feldman 1032b66f2d16SKris Kennaway in = dup(STDIN_FILENO); 1033b66f2d16SKris Kennaway out = dup(STDOUT_FILENO); 1034b66f2d16SKris Kennaway 103583d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 103683d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 103783d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 103883d2307dSDag-Erling Smørgrav #endif 103983d2307dSDag-Erling Smørgrav 1040b66f2d16SKris Kennaway max = 0; 1041b66f2d16SKris Kennaway if (in > max) 1042b66f2d16SKris Kennaway max = in; 1043b66f2d16SKris Kennaway if (out > max) 1044b66f2d16SKris Kennaway max = out; 1045b66f2d16SKris Kennaway 1046b66f2d16SKris Kennaway buffer_init(&iqueue); 1047b66f2d16SKris Kennaway buffer_init(&oqueue); 1048b66f2d16SKris Kennaway 10491e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 10501e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 10511e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1052b66f2d16SKris Kennaway 10531e8db6e2SBrian Feldman for (;;) { 10541e8db6e2SBrian Feldman memset(rset, 0, set_size); 10551e8db6e2SBrian Feldman memset(wset, 0, set_size); 10561e8db6e2SBrian Feldman 10571e8db6e2SBrian Feldman FD_SET(in, rset); 1058b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1059b66f2d16SKris Kennaway if (olen > 0) 10601e8db6e2SBrian Feldman FD_SET(out, wset); 1061b66f2d16SKris Kennaway 10621e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1063b66f2d16SKris Kennaway if (errno == EINTR) 1064b66f2d16SKris Kennaway continue; 1065b66f2d16SKris Kennaway exit(2); 1066b66f2d16SKris Kennaway } 1067b66f2d16SKris Kennaway 1068b66f2d16SKris Kennaway /* copy stdin to iqueue */ 10691e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1070b66f2d16SKris Kennaway char buf[4*4096]; 1071b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1072b66f2d16SKris Kennaway if (len == 0) { 1073b66f2d16SKris Kennaway debug("read eof"); 1074b66f2d16SKris Kennaway exit(0); 1075b66f2d16SKris Kennaway } else if (len < 0) { 1076b66f2d16SKris Kennaway error("read error"); 1077b66f2d16SKris Kennaway exit(1); 1078b66f2d16SKris Kennaway } else { 1079b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1080b66f2d16SKris Kennaway } 1081b66f2d16SKris Kennaway } 1082b66f2d16SKris Kennaway /* send oqueue to stdout */ 10831e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1084b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1085b66f2d16SKris Kennaway if (len < 0) { 1086b66f2d16SKris Kennaway error("write error"); 1087b66f2d16SKris Kennaway exit(1); 1088b66f2d16SKris Kennaway } else { 1089b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1090b66f2d16SKris Kennaway } 1091b66f2d16SKris Kennaway } 1092b66f2d16SKris Kennaway /* process requests from client */ 1093b66f2d16SKris Kennaway process(); 1094b66f2d16SKris Kennaway } 1095b66f2d16SKris Kennaway } 1096