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" 17043840dfSDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.48 2005/06/17 02:44:33 djm 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 extern char *__progname; 3583d2307dSDag-Erling Smørgrav 36b66f2d16SKris Kennaway /* input and output queue */ 37b66f2d16SKris Kennaway Buffer iqueue; 38b66f2d16SKris Kennaway Buffer oqueue; 39b66f2d16SKris Kennaway 401e8db6e2SBrian Feldman /* Version of client */ 411e8db6e2SBrian Feldman int version; 421e8db6e2SBrian Feldman 43d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 44b66f2d16SKris Kennaway 45b66f2d16SKris Kennaway typedef struct Stat Stat; 46b66f2d16SKris Kennaway 471e8db6e2SBrian Feldman struct Stat { 48b66f2d16SKris Kennaway char *name; 49b66f2d16SKris Kennaway char *long_name; 50b66f2d16SKris Kennaway Attrib attrib; 51b66f2d16SKris Kennaway }; 52b66f2d16SKris Kennaway 53ae1f160dSDag-Erling Smørgrav static int 54b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 55b66f2d16SKris Kennaway { 56b66f2d16SKris Kennaway int ret = 0; 571e8db6e2SBrian Feldman 58b66f2d16SKris Kennaway switch (unixerrno) { 59b66f2d16SKris Kennaway case 0: 601e8db6e2SBrian Feldman ret = SSH2_FX_OK; 61b66f2d16SKris Kennaway break; 62b66f2d16SKris Kennaway case ENOENT: 63b66f2d16SKris Kennaway case ENOTDIR: 64b66f2d16SKris Kennaway case EBADF: 65b66f2d16SKris Kennaway case ELOOP: 661e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 67b66f2d16SKris Kennaway break; 68b66f2d16SKris Kennaway case EPERM: 69b66f2d16SKris Kennaway case EACCES: 70b66f2d16SKris Kennaway case EFAULT: 711e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 72b66f2d16SKris Kennaway break; 73b66f2d16SKris Kennaway case ENAMETOOLONG: 74b66f2d16SKris Kennaway case EINVAL: 751e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 76b66f2d16SKris Kennaway break; 77b66f2d16SKris Kennaway default: 781e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 79b66f2d16SKris Kennaway break; 80b66f2d16SKris Kennaway } 81b66f2d16SKris Kennaway return ret; 82b66f2d16SKris Kennaway } 83b66f2d16SKris Kennaway 84ae1f160dSDag-Erling Smørgrav static int 85b66f2d16SKris Kennaway flags_from_portable(int pflags) 86b66f2d16SKris Kennaway { 87b66f2d16SKris Kennaway int flags = 0; 881e8db6e2SBrian Feldman 891e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 901e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 91b66f2d16SKris Kennaway flags = O_RDWR; 921e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 93b66f2d16SKris Kennaway flags = O_RDONLY; 941e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 95b66f2d16SKris Kennaway flags = O_WRONLY; 96b66f2d16SKris Kennaway } 971e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 98b66f2d16SKris Kennaway flags |= O_CREAT; 991e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 100b66f2d16SKris Kennaway flags |= O_TRUNC; 1011e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 102b66f2d16SKris Kennaway flags |= O_EXCL; 103b66f2d16SKris Kennaway return flags; 104b66f2d16SKris Kennaway } 105b66f2d16SKris Kennaway 106ae1f160dSDag-Erling Smørgrav static Attrib * 107b66f2d16SKris Kennaway get_attrib(void) 108b66f2d16SKris Kennaway { 109b66f2d16SKris Kennaway return decode_attrib(&iqueue); 110b66f2d16SKris Kennaway } 111b66f2d16SKris Kennaway 112b66f2d16SKris Kennaway /* handle handles */ 113b66f2d16SKris Kennaway 114b66f2d16SKris Kennaway typedef struct Handle Handle; 115b66f2d16SKris Kennaway struct Handle { 116b66f2d16SKris Kennaway int use; 117b66f2d16SKris Kennaway DIR *dirp; 118b66f2d16SKris Kennaway int fd; 119b66f2d16SKris Kennaway char *name; 120b66f2d16SKris Kennaway }; 1211e8db6e2SBrian Feldman 122b66f2d16SKris Kennaway enum { 123b66f2d16SKris Kennaway HANDLE_UNUSED, 124b66f2d16SKris Kennaway HANDLE_DIR, 125b66f2d16SKris Kennaway HANDLE_FILE 126b66f2d16SKris Kennaway }; 1271e8db6e2SBrian Feldman 128b66f2d16SKris Kennaway Handle handles[100]; 129b66f2d16SKris Kennaway 130ae1f160dSDag-Erling Smørgrav static void 131b66f2d16SKris Kennaway handle_init(void) 132b66f2d16SKris Kennaway { 133043840dfSDag-Erling Smørgrav u_int i; 1341e8db6e2SBrian Feldman 135b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 136b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 137b66f2d16SKris Kennaway } 138b66f2d16SKris Kennaway 139ae1f160dSDag-Erling Smørgrav static int 140efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp) 141b66f2d16SKris Kennaway { 142043840dfSDag-Erling Smørgrav u_int i; 1431e8db6e2SBrian Feldman 144b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 145b66f2d16SKris Kennaway if (handles[i].use == HANDLE_UNUSED) { 146b66f2d16SKris Kennaway handles[i].use = use; 147b66f2d16SKris Kennaway handles[i].dirp = dirp; 148b66f2d16SKris Kennaway handles[i].fd = fd; 149d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 150b66f2d16SKris Kennaway return i; 151b66f2d16SKris Kennaway } 152b66f2d16SKris Kennaway } 153b66f2d16SKris Kennaway return -1; 154b66f2d16SKris Kennaway } 155b66f2d16SKris Kennaway 156ae1f160dSDag-Erling Smørgrav static int 157b66f2d16SKris Kennaway handle_is_ok(int i, int type) 158b66f2d16SKris Kennaway { 159043840dfSDag-Erling Smørgrav return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) && 1601e8db6e2SBrian Feldman handles[i].use == type; 161b66f2d16SKris Kennaway } 162b66f2d16SKris Kennaway 163ae1f160dSDag-Erling Smørgrav static int 164b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 165b66f2d16SKris Kennaway { 166b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 167b66f2d16SKris Kennaway return -1; 1681e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 1691e8db6e2SBrian Feldman PUT_32BIT(*stringp, handle); 1701e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 171b66f2d16SKris Kennaway return 0; 172b66f2d16SKris Kennaway } 173b66f2d16SKris Kennaway 174ae1f160dSDag-Erling Smørgrav static int 175efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen) 176b66f2d16SKris Kennaway { 1771e8db6e2SBrian Feldman int val; 1781e8db6e2SBrian Feldman 1791e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 180b66f2d16SKris Kennaway return -1; 1811e8db6e2SBrian Feldman val = GET_32BIT(handle); 182b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 183b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 184b66f2d16SKris Kennaway return val; 185b66f2d16SKris Kennaway return -1; 186b66f2d16SKris Kennaway } 187b66f2d16SKris Kennaway 188ae1f160dSDag-Erling Smørgrav static char * 189b66f2d16SKris Kennaway handle_to_name(int handle) 190b66f2d16SKris Kennaway { 191b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 192b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 193b66f2d16SKris Kennaway return handles[handle].name; 194b66f2d16SKris Kennaway return NULL; 195b66f2d16SKris Kennaway } 196b66f2d16SKris Kennaway 197ae1f160dSDag-Erling Smørgrav static DIR * 198b66f2d16SKris Kennaway handle_to_dir(int handle) 199b66f2d16SKris Kennaway { 200b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 201b66f2d16SKris Kennaway return handles[handle].dirp; 202b66f2d16SKris Kennaway return NULL; 203b66f2d16SKris Kennaway } 204b66f2d16SKris Kennaway 205ae1f160dSDag-Erling Smørgrav static int 206b66f2d16SKris Kennaway handle_to_fd(int handle) 207b66f2d16SKris Kennaway { 208b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 209b66f2d16SKris Kennaway return handles[handle].fd; 210b66f2d16SKris Kennaway return -1; 211b66f2d16SKris Kennaway } 212b66f2d16SKris Kennaway 213ae1f160dSDag-Erling Smørgrav static int 214b66f2d16SKris Kennaway handle_close(int handle) 215b66f2d16SKris Kennaway { 216b66f2d16SKris Kennaway int ret = -1; 2171e8db6e2SBrian Feldman 218b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 219b66f2d16SKris Kennaway ret = close(handles[handle].fd); 220b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 221d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 222b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 223b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 224b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 225d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 226b66f2d16SKris Kennaway } else { 227b66f2d16SKris Kennaway errno = ENOENT; 228b66f2d16SKris Kennaway } 229b66f2d16SKris Kennaway return ret; 230b66f2d16SKris Kennaway } 231b66f2d16SKris Kennaway 232ae1f160dSDag-Erling Smørgrav static int 233b66f2d16SKris Kennaway get_handle(void) 234b66f2d16SKris Kennaway { 235b66f2d16SKris Kennaway char *handle; 2361e8db6e2SBrian Feldman int val = -1; 237b66f2d16SKris Kennaway u_int hlen; 2381e8db6e2SBrian Feldman 239b66f2d16SKris Kennaway handle = get_string(&hlen); 2401e8db6e2SBrian Feldman if (hlen < 256) 241b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 242b66f2d16SKris Kennaway xfree(handle); 243b66f2d16SKris Kennaway return val; 244b66f2d16SKris Kennaway } 245b66f2d16SKris Kennaway 246b66f2d16SKris Kennaway /* send replies */ 247b66f2d16SKris Kennaway 248ae1f160dSDag-Erling Smørgrav static void 249b66f2d16SKris Kennaway send_msg(Buffer *m) 250b66f2d16SKris Kennaway { 251b66f2d16SKris Kennaway int mlen = buffer_len(m); 2521e8db6e2SBrian Feldman 253b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 254b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 255b66f2d16SKris Kennaway buffer_consume(m, mlen); 256b66f2d16SKris Kennaway } 257b66f2d16SKris Kennaway 258ae1f160dSDag-Erling Smørgrav static void 259d74d50a8SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 260b66f2d16SKris Kennaway { 261b66f2d16SKris Kennaway Buffer msg; 2621e8db6e2SBrian Feldman const char *status_messages[] = { 2631e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 2641e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 2651e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 2661e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 2671e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 2681e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 2691e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 2701e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 2711e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 2721e8db6e2SBrian Feldman "Unknown error" /* Others */ 2731e8db6e2SBrian Feldman }; 2741e8db6e2SBrian Feldman 275d74d50a8SDag-Erling Smørgrav TRACE("sent status id %u error %u", id, status); 276b66f2d16SKris Kennaway buffer_init(&msg); 2771e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 278b66f2d16SKris Kennaway buffer_put_int(&msg, id); 279d74d50a8SDag-Erling Smørgrav buffer_put_int(&msg, status); 2801e8db6e2SBrian Feldman if (version >= 3) { 2811e8db6e2SBrian Feldman buffer_put_cstring(&msg, 282d74d50a8SDag-Erling Smørgrav status_messages[MIN(status,SSH2_FX_MAX)]); 2831e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 2841e8db6e2SBrian Feldman } 285b66f2d16SKris Kennaway send_msg(&msg); 286b66f2d16SKris Kennaway buffer_free(&msg); 287b66f2d16SKris Kennaway } 288ae1f160dSDag-Erling Smørgrav static void 289efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 290b66f2d16SKris Kennaway { 291b66f2d16SKris Kennaway Buffer msg; 2921e8db6e2SBrian Feldman 293b66f2d16SKris Kennaway buffer_init(&msg); 294b66f2d16SKris Kennaway buffer_put_char(&msg, type); 295b66f2d16SKris Kennaway buffer_put_int(&msg, id); 296b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 297b66f2d16SKris Kennaway send_msg(&msg); 298b66f2d16SKris Kennaway buffer_free(&msg); 299b66f2d16SKris Kennaway } 300b66f2d16SKris Kennaway 301ae1f160dSDag-Erling Smørgrav static void 302efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen) 303b66f2d16SKris Kennaway { 304ee21a45fSDag-Erling Smørgrav TRACE("sent data id %u len %d", id, dlen); 3051e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 306b66f2d16SKris Kennaway } 307b66f2d16SKris Kennaway 308ae1f160dSDag-Erling Smørgrav static void 309b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 310b66f2d16SKris Kennaway { 311b66f2d16SKris Kennaway char *string; 312b66f2d16SKris Kennaway int hlen; 3131e8db6e2SBrian Feldman 314b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 315ee21a45fSDag-Erling Smørgrav TRACE("sent handle id %u handle %d", id, handle); 3161e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 317b66f2d16SKris Kennaway xfree(string); 318b66f2d16SKris Kennaway } 319b66f2d16SKris Kennaway 320ae1f160dSDag-Erling Smørgrav static void 321efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 322b66f2d16SKris Kennaway { 323b66f2d16SKris Kennaway Buffer msg; 324b66f2d16SKris Kennaway int i; 3251e8db6e2SBrian Feldman 326b66f2d16SKris Kennaway buffer_init(&msg); 3271e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 328b66f2d16SKris Kennaway buffer_put_int(&msg, id); 329b66f2d16SKris Kennaway buffer_put_int(&msg, count); 330ee21a45fSDag-Erling Smørgrav TRACE("sent names id %u count %d", id, count); 331b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 332b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 333b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 334b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 335b66f2d16SKris Kennaway } 336b66f2d16SKris Kennaway send_msg(&msg); 337b66f2d16SKris Kennaway buffer_free(&msg); 338b66f2d16SKris Kennaway } 339b66f2d16SKris Kennaway 340ae1f160dSDag-Erling Smørgrav static void 341efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 342b66f2d16SKris Kennaway { 343b66f2d16SKris Kennaway Buffer msg; 3441e8db6e2SBrian Feldman 345ee21a45fSDag-Erling Smørgrav TRACE("sent attrib id %u have 0x%x", id, a->flags); 346b66f2d16SKris Kennaway buffer_init(&msg); 3471e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 348b66f2d16SKris Kennaway buffer_put_int(&msg, id); 349b66f2d16SKris Kennaway encode_attrib(&msg, a); 350b66f2d16SKris Kennaway send_msg(&msg); 351b66f2d16SKris Kennaway buffer_free(&msg); 352b66f2d16SKris Kennaway } 353b66f2d16SKris Kennaway 354b66f2d16SKris Kennaway /* parse incoming */ 355b66f2d16SKris Kennaway 356ae1f160dSDag-Erling Smørgrav static void 357b66f2d16SKris Kennaway process_init(void) 358b66f2d16SKris Kennaway { 359b66f2d16SKris Kennaway Buffer msg; 360b66f2d16SKris Kennaway 361545d5ecaSDag-Erling Smørgrav version = get_int(); 362b66f2d16SKris Kennaway TRACE("client version %d", version); 363b66f2d16SKris Kennaway buffer_init(&msg); 3641e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 3651e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 366b66f2d16SKris Kennaway send_msg(&msg); 367b66f2d16SKris Kennaway buffer_free(&msg); 368b66f2d16SKris Kennaway } 369b66f2d16SKris Kennaway 370ae1f160dSDag-Erling Smørgrav static void 371b66f2d16SKris Kennaway process_open(void) 372b66f2d16SKris Kennaway { 373b66f2d16SKris Kennaway u_int32_t id, pflags; 374b66f2d16SKris Kennaway Attrib *a; 375b66f2d16SKris Kennaway char *name; 3761e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 377b66f2d16SKris Kennaway 378b66f2d16SKris Kennaway id = get_int(); 379b66f2d16SKris Kennaway name = get_string(NULL); 3801e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 381b66f2d16SKris Kennaway a = get_attrib(); 382b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 3831e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 384ee21a45fSDag-Erling Smørgrav TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); 385b66f2d16SKris Kennaway fd = open(name, flags, mode); 386b66f2d16SKris Kennaway if (fd < 0) { 387b66f2d16SKris Kennaway status = errno_to_portable(errno); 388b66f2d16SKris Kennaway } else { 389d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, NULL); 390b66f2d16SKris Kennaway if (handle < 0) { 391b66f2d16SKris Kennaway close(fd); 392b66f2d16SKris Kennaway } else { 393b66f2d16SKris Kennaway send_handle(id, handle); 3941e8db6e2SBrian Feldman status = SSH2_FX_OK; 395b66f2d16SKris Kennaway } 396b66f2d16SKris Kennaway } 3971e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 398b66f2d16SKris Kennaway send_status(id, status); 399b66f2d16SKris Kennaway xfree(name); 400b66f2d16SKris Kennaway } 401b66f2d16SKris Kennaway 402ae1f160dSDag-Erling Smørgrav static void 403b66f2d16SKris Kennaway process_close(void) 404b66f2d16SKris Kennaway { 405b66f2d16SKris Kennaway u_int32_t id; 4061e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 407b66f2d16SKris Kennaway 408b66f2d16SKris Kennaway id = get_int(); 409b66f2d16SKris Kennaway handle = get_handle(); 410ee21a45fSDag-Erling Smørgrav TRACE("close id %u handle %d", id, handle); 411b66f2d16SKris Kennaway ret = handle_close(handle); 4121e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 413b66f2d16SKris Kennaway send_status(id, status); 414b66f2d16SKris Kennaway } 415b66f2d16SKris Kennaway 416ae1f160dSDag-Erling Smørgrav static void 417b66f2d16SKris Kennaway process_read(void) 418b66f2d16SKris Kennaway { 419b66f2d16SKris Kennaway char buf[64*1024]; 4201e8db6e2SBrian Feldman u_int32_t id, len; 4211e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 422b66f2d16SKris Kennaway u_int64_t off; 423b66f2d16SKris Kennaway 424b66f2d16SKris Kennaway id = get_int(); 425b66f2d16SKris Kennaway handle = get_handle(); 4261e8db6e2SBrian Feldman off = get_int64(); 427b66f2d16SKris Kennaway len = get_int(); 428b66f2d16SKris Kennaway 429ee21a45fSDag-Erling Smørgrav TRACE("read id %u handle %d off %llu len %d", id, handle, 43083d2307dSDag-Erling Smørgrav (u_int64_t)off, len); 431b66f2d16SKris Kennaway if (len > sizeof buf) { 432b66f2d16SKris Kennaway len = sizeof buf; 433d95e11bfSDag-Erling Smørgrav logit("read change len %d", len); 434b66f2d16SKris Kennaway } 435b66f2d16SKris Kennaway fd = handle_to_fd(handle); 436b66f2d16SKris Kennaway if (fd >= 0) { 437b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 438b66f2d16SKris Kennaway error("process_read: seek failed"); 439b66f2d16SKris Kennaway status = errno_to_portable(errno); 440b66f2d16SKris Kennaway } else { 441b66f2d16SKris Kennaway ret = read(fd, buf, len); 442b66f2d16SKris Kennaway if (ret < 0) { 443b66f2d16SKris Kennaway status = errno_to_portable(errno); 444b66f2d16SKris Kennaway } else if (ret == 0) { 4451e8db6e2SBrian Feldman status = SSH2_FX_EOF; 446b66f2d16SKris Kennaway } else { 447b66f2d16SKris Kennaway send_data(id, buf, ret); 4481e8db6e2SBrian Feldman status = SSH2_FX_OK; 449b66f2d16SKris Kennaway } 450b66f2d16SKris Kennaway } 451b66f2d16SKris Kennaway } 4521e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 453b66f2d16SKris Kennaway send_status(id, status); 454b66f2d16SKris Kennaway } 455b66f2d16SKris Kennaway 456ae1f160dSDag-Erling Smørgrav static void 457b66f2d16SKris Kennaway process_write(void) 458b66f2d16SKris Kennaway { 4591e8db6e2SBrian Feldman u_int32_t id; 460b66f2d16SKris Kennaway u_int64_t off; 461b66f2d16SKris Kennaway u_int len; 4621e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 463b66f2d16SKris Kennaway char *data; 464b66f2d16SKris Kennaway 465b66f2d16SKris Kennaway id = get_int(); 466b66f2d16SKris Kennaway handle = get_handle(); 4671e8db6e2SBrian Feldman off = get_int64(); 468b66f2d16SKris Kennaway data = get_string(&len); 469b66f2d16SKris Kennaway 470ee21a45fSDag-Erling Smørgrav TRACE("write id %u handle %d off %llu len %d", id, handle, 47183d2307dSDag-Erling Smørgrav (u_int64_t)off, len); 472b66f2d16SKris Kennaway fd = handle_to_fd(handle); 473b66f2d16SKris Kennaway if (fd >= 0) { 474b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 475b66f2d16SKris Kennaway status = errno_to_portable(errno); 476b66f2d16SKris Kennaway error("process_write: seek failed"); 477b66f2d16SKris Kennaway } else { 478b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 479b66f2d16SKris Kennaway ret = write(fd, data, len); 480043840dfSDag-Erling Smørgrav if (ret < 0) { 481b66f2d16SKris Kennaway error("process_write: write failed"); 482b66f2d16SKris Kennaway status = errno_to_portable(errno); 483043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 4841e8db6e2SBrian Feldman status = SSH2_FX_OK; 485b66f2d16SKris Kennaway } else { 486d95e11bfSDag-Erling Smørgrav logit("nothing at all written"); 487b66f2d16SKris Kennaway } 488b66f2d16SKris Kennaway } 489b66f2d16SKris Kennaway } 490b66f2d16SKris Kennaway send_status(id, status); 491b66f2d16SKris Kennaway xfree(data); 492b66f2d16SKris Kennaway } 493b66f2d16SKris Kennaway 494ae1f160dSDag-Erling Smørgrav static void 495b66f2d16SKris Kennaway process_do_stat(int do_lstat) 496b66f2d16SKris Kennaway { 4971e8db6e2SBrian Feldman Attrib a; 498b66f2d16SKris Kennaway struct stat st; 499b66f2d16SKris Kennaway u_int32_t id; 500b66f2d16SKris Kennaway char *name; 5011e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 502b66f2d16SKris Kennaway 503b66f2d16SKris Kennaway id = get_int(); 504b66f2d16SKris Kennaway name = get_string(NULL); 505ee21a45fSDag-Erling Smørgrav TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); 506b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 507b66f2d16SKris Kennaway if (ret < 0) { 508b66f2d16SKris Kennaway status = errno_to_portable(errno); 509b66f2d16SKris Kennaway } else { 5101e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5111e8db6e2SBrian Feldman send_attrib(id, &a); 5121e8db6e2SBrian Feldman status = SSH2_FX_OK; 513b66f2d16SKris Kennaway } 5141e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 515b66f2d16SKris Kennaway send_status(id, status); 516b66f2d16SKris Kennaway xfree(name); 517b66f2d16SKris Kennaway } 518b66f2d16SKris Kennaway 519ae1f160dSDag-Erling Smørgrav static void 520b66f2d16SKris Kennaway process_stat(void) 521b66f2d16SKris Kennaway { 522b66f2d16SKris Kennaway process_do_stat(0); 523b66f2d16SKris Kennaway } 524b66f2d16SKris Kennaway 525ae1f160dSDag-Erling Smørgrav static void 526b66f2d16SKris Kennaway process_lstat(void) 527b66f2d16SKris Kennaway { 528b66f2d16SKris Kennaway process_do_stat(1); 529b66f2d16SKris Kennaway } 530b66f2d16SKris Kennaway 531ae1f160dSDag-Erling Smørgrav static void 532b66f2d16SKris Kennaway process_fstat(void) 533b66f2d16SKris Kennaway { 5341e8db6e2SBrian Feldman Attrib a; 535b66f2d16SKris Kennaway struct stat st; 536b66f2d16SKris Kennaway u_int32_t id; 5371e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 538b66f2d16SKris Kennaway 539b66f2d16SKris Kennaway id = get_int(); 540b66f2d16SKris Kennaway handle = get_handle(); 541ee21a45fSDag-Erling Smørgrav TRACE("fstat id %u handle %d", id, handle); 542b66f2d16SKris Kennaway fd = handle_to_fd(handle); 543b66f2d16SKris Kennaway if (fd >= 0) { 544b66f2d16SKris Kennaway ret = fstat(fd, &st); 545b66f2d16SKris Kennaway if (ret < 0) { 546b66f2d16SKris Kennaway status = errno_to_portable(errno); 547b66f2d16SKris Kennaway } else { 5481e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5491e8db6e2SBrian Feldman send_attrib(id, &a); 5501e8db6e2SBrian Feldman status = SSH2_FX_OK; 551b66f2d16SKris Kennaway } 552b66f2d16SKris Kennaway } 5531e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 554b66f2d16SKris Kennaway send_status(id, status); 555b66f2d16SKris Kennaway } 556b66f2d16SKris Kennaway 557ae1f160dSDag-Erling Smørgrav static struct timeval * 558efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 559b66f2d16SKris Kennaway { 560b66f2d16SKris Kennaway static struct timeval tv[2]; 5611e8db6e2SBrian Feldman 562b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 563b66f2d16SKris Kennaway tv[0].tv_usec = 0; 564b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 565b66f2d16SKris Kennaway tv[1].tv_usec = 0; 566b66f2d16SKris Kennaway return tv; 567b66f2d16SKris Kennaway } 568b66f2d16SKris Kennaway 569ae1f160dSDag-Erling Smørgrav static void 570b66f2d16SKris Kennaway process_setstat(void) 571b66f2d16SKris Kennaway { 572b66f2d16SKris Kennaway Attrib *a; 573b66f2d16SKris Kennaway u_int32_t id; 574b66f2d16SKris Kennaway char *name; 575ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 576b66f2d16SKris Kennaway 577b66f2d16SKris Kennaway id = get_int(); 578b66f2d16SKris Kennaway name = get_string(NULL); 579b66f2d16SKris Kennaway a = get_attrib(); 580ee21a45fSDag-Erling Smørgrav TRACE("setstat id %u name %s", id, name); 581ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 582ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 583ae1f160dSDag-Erling Smørgrav if (ret == -1) 584ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 585ae1f160dSDag-Erling Smørgrav } 5861e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 587b66f2d16SKris Kennaway ret = chmod(name, a->perm & 0777); 588b66f2d16SKris Kennaway if (ret == -1) 589b66f2d16SKris Kennaway status = errno_to_portable(errno); 590b66f2d16SKris Kennaway } 5911e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 592b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 593b66f2d16SKris Kennaway if (ret == -1) 594b66f2d16SKris Kennaway status = errno_to_portable(errno); 595b66f2d16SKris Kennaway } 5961e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 5971e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 5981e8db6e2SBrian Feldman if (ret == -1) 5991e8db6e2SBrian Feldman status = errno_to_portable(errno); 6001e8db6e2SBrian Feldman } 601b66f2d16SKris Kennaway send_status(id, status); 602b66f2d16SKris Kennaway xfree(name); 603b66f2d16SKris Kennaway } 604b66f2d16SKris Kennaway 605ae1f160dSDag-Erling Smørgrav static void 606b66f2d16SKris Kennaway process_fsetstat(void) 607b66f2d16SKris Kennaway { 608b66f2d16SKris Kennaway Attrib *a; 609b66f2d16SKris Kennaway u_int32_t id; 610b66f2d16SKris Kennaway int handle, fd, ret; 6111e8db6e2SBrian Feldman int status = SSH2_FX_OK; 61283d2307dSDag-Erling Smørgrav char *name; 613b66f2d16SKris Kennaway 614b66f2d16SKris Kennaway id = get_int(); 615b66f2d16SKris Kennaway handle = get_handle(); 616b66f2d16SKris Kennaway a = get_attrib(); 617ee21a45fSDag-Erling Smørgrav TRACE("fsetstat id %u handle %d", id, handle); 618b66f2d16SKris Kennaway fd = handle_to_fd(handle); 61983d2307dSDag-Erling Smørgrav name = handle_to_name(handle); 62083d2307dSDag-Erling Smørgrav if (fd < 0 || name == NULL) { 6211e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 622b66f2d16SKris Kennaway } else { 623ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 624ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 625ae1f160dSDag-Erling Smørgrav if (ret == -1) 626ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 627ae1f160dSDag-Erling Smørgrav } 6281e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 62983d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 630b66f2d16SKris Kennaway ret = fchmod(fd, a->perm & 0777); 63183d2307dSDag-Erling Smørgrav #else 63283d2307dSDag-Erling Smørgrav ret = chmod(name, a->perm & 0777); 63383d2307dSDag-Erling Smørgrav #endif 634b66f2d16SKris Kennaway if (ret == -1) 635b66f2d16SKris Kennaway status = errno_to_portable(errno); 636b66f2d16SKris Kennaway } 6371e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 63883d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 639b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 64083d2307dSDag-Erling Smørgrav #else 64183d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 64283d2307dSDag-Erling Smørgrav #endif 643b66f2d16SKris Kennaway if (ret == -1) 644b66f2d16SKris Kennaway status = errno_to_portable(errno); 645b66f2d16SKris Kennaway } 6461e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 64783d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 6481e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 64983d2307dSDag-Erling Smørgrav #else 65083d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 65183d2307dSDag-Erling Smørgrav #endif 6521e8db6e2SBrian Feldman if (ret == -1) 6531e8db6e2SBrian Feldman status = errno_to_portable(errno); 6541e8db6e2SBrian Feldman } 655b66f2d16SKris Kennaway } 656b66f2d16SKris Kennaway send_status(id, status); 657b66f2d16SKris Kennaway } 658b66f2d16SKris Kennaway 659ae1f160dSDag-Erling Smørgrav static void 660b66f2d16SKris Kennaway process_opendir(void) 661b66f2d16SKris Kennaway { 662b66f2d16SKris Kennaway DIR *dirp = NULL; 663b66f2d16SKris Kennaway char *path; 6641e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 665b66f2d16SKris Kennaway u_int32_t id; 666b66f2d16SKris Kennaway 667b66f2d16SKris Kennaway id = get_int(); 668b66f2d16SKris Kennaway path = get_string(NULL); 669ee21a45fSDag-Erling Smørgrav TRACE("opendir id %u path %s", id, path); 670b66f2d16SKris Kennaway dirp = opendir(path); 671b66f2d16SKris Kennaway if (dirp == NULL) { 672b66f2d16SKris Kennaway status = errno_to_portable(errno); 673b66f2d16SKris Kennaway } else { 674d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, dirp); 675b66f2d16SKris Kennaway if (handle < 0) { 676b66f2d16SKris Kennaway closedir(dirp); 677b66f2d16SKris Kennaway } else { 678b66f2d16SKris Kennaway send_handle(id, handle); 6791e8db6e2SBrian Feldman status = SSH2_FX_OK; 680b66f2d16SKris Kennaway } 681b66f2d16SKris Kennaway 682b66f2d16SKris Kennaway } 6831e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 684b66f2d16SKris Kennaway send_status(id, status); 685b66f2d16SKris Kennaway xfree(path); 686b66f2d16SKris Kennaway } 687b66f2d16SKris Kennaway 688ae1f160dSDag-Erling Smørgrav static void 689b66f2d16SKris Kennaway process_readdir(void) 690b66f2d16SKris Kennaway { 691b66f2d16SKris Kennaway DIR *dirp; 692b66f2d16SKris Kennaway struct dirent *dp; 693b66f2d16SKris Kennaway char *path; 694b66f2d16SKris Kennaway int handle; 695b66f2d16SKris Kennaway u_int32_t id; 696b66f2d16SKris Kennaway 697b66f2d16SKris Kennaway id = get_int(); 698b66f2d16SKris Kennaway handle = get_handle(); 699ee21a45fSDag-Erling Smørgrav TRACE("readdir id %u handle %d", id, handle); 700b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 701b66f2d16SKris Kennaway path = handle_to_name(handle); 702b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 7031e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 704b66f2d16SKris Kennaway } else { 705b66f2d16SKris Kennaway struct stat st; 706b66f2d16SKris Kennaway char pathname[1024]; 707b66f2d16SKris Kennaway Stat *stats; 708b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 709ee21a45fSDag-Erling Smørgrav 710b66f2d16SKris Kennaway stats = xmalloc(nstats * sizeof(Stat)); 711b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 712b66f2d16SKris Kennaway if (count >= nstats) { 713b66f2d16SKris Kennaway nstats *= 2; 714b66f2d16SKris Kennaway stats = xrealloc(stats, nstats * sizeof(Stat)); 715b66f2d16SKris Kennaway } 716b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 717ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 718ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 719b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 720b66f2d16SKris Kennaway continue; 7211e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 722b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 7234b17dab0SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0); 724b66f2d16SKris Kennaway count++; 725b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 7261e8db6e2SBrian Feldman /* XXX check packet size instead */ 727b66f2d16SKris Kennaway if (count == 100) 728b66f2d16SKris Kennaway break; 729b66f2d16SKris Kennaway } 7301e8db6e2SBrian Feldman if (count > 0) { 731b66f2d16SKris Kennaway send_names(id, count, stats); 732b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 733b66f2d16SKris Kennaway xfree(stats[i].name); 734b66f2d16SKris Kennaway xfree(stats[i].long_name); 735b66f2d16SKris Kennaway } 7361e8db6e2SBrian Feldman } else { 7371e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 7381e8db6e2SBrian Feldman } 739b66f2d16SKris Kennaway xfree(stats); 740b66f2d16SKris Kennaway } 741b66f2d16SKris Kennaway } 742b66f2d16SKris Kennaway 743ae1f160dSDag-Erling Smørgrav static void 744b66f2d16SKris Kennaway process_remove(void) 745b66f2d16SKris Kennaway { 746b66f2d16SKris Kennaway char *name; 747b66f2d16SKris Kennaway u_int32_t id; 7481e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 749b66f2d16SKris Kennaway int ret; 750b66f2d16SKris Kennaway 751b66f2d16SKris Kennaway id = get_int(); 752b66f2d16SKris Kennaway name = get_string(NULL); 753ee21a45fSDag-Erling Smørgrav TRACE("remove id %u name %s", id, name); 7541e8db6e2SBrian Feldman ret = unlink(name); 7551e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 756b66f2d16SKris Kennaway send_status(id, status); 757b66f2d16SKris Kennaway xfree(name); 758b66f2d16SKris Kennaway } 759b66f2d16SKris Kennaway 760ae1f160dSDag-Erling Smørgrav static void 761b66f2d16SKris Kennaway process_mkdir(void) 762b66f2d16SKris Kennaway { 763b66f2d16SKris Kennaway Attrib *a; 764b66f2d16SKris Kennaway u_int32_t id; 765b66f2d16SKris Kennaway char *name; 7661e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 767b66f2d16SKris Kennaway 768b66f2d16SKris Kennaway id = get_int(); 769b66f2d16SKris Kennaway name = get_string(NULL); 770b66f2d16SKris Kennaway a = get_attrib(); 7711e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 7721e8db6e2SBrian Feldman a->perm & 0777 : 0777; 773ee21a45fSDag-Erling Smørgrav TRACE("mkdir id %u name %s mode 0%o", id, name, mode); 774b66f2d16SKris Kennaway ret = mkdir(name, mode); 7751e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 776b66f2d16SKris Kennaway send_status(id, status); 777b66f2d16SKris Kennaway xfree(name); 778b66f2d16SKris Kennaway } 779b66f2d16SKris Kennaway 780ae1f160dSDag-Erling Smørgrav static void 781b66f2d16SKris Kennaway process_rmdir(void) 782b66f2d16SKris Kennaway { 783b66f2d16SKris Kennaway u_int32_t id; 784b66f2d16SKris Kennaway char *name; 785b66f2d16SKris Kennaway int ret, status; 786b66f2d16SKris Kennaway 787b66f2d16SKris Kennaway id = get_int(); 788b66f2d16SKris Kennaway name = get_string(NULL); 789ee21a45fSDag-Erling Smørgrav TRACE("rmdir id %u name %s", id, name); 790b66f2d16SKris Kennaway ret = rmdir(name); 7911e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 792b66f2d16SKris Kennaway send_status(id, status); 793b66f2d16SKris Kennaway xfree(name); 794b66f2d16SKris Kennaway } 795b66f2d16SKris Kennaway 796ae1f160dSDag-Erling Smørgrav static void 797b66f2d16SKris Kennaway process_realpath(void) 798b66f2d16SKris Kennaway { 799b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 800b66f2d16SKris Kennaway u_int32_t id; 801b66f2d16SKris Kennaway char *path; 802b66f2d16SKris Kennaway 803b66f2d16SKris Kennaway id = get_int(); 804b66f2d16SKris Kennaway path = get_string(NULL); 8051e8db6e2SBrian Feldman if (path[0] == '\0') { 8061e8db6e2SBrian Feldman xfree(path); 8071e8db6e2SBrian Feldman path = xstrdup("."); 8081e8db6e2SBrian Feldman } 809ee21a45fSDag-Erling Smørgrav TRACE("realpath id %u path %s", id, path); 810b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 811b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 812b66f2d16SKris Kennaway } else { 813b66f2d16SKris Kennaway Stat s; 814b66f2d16SKris Kennaway attrib_clear(&s.attrib); 815b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 816b66f2d16SKris Kennaway send_names(id, 1, &s); 817b66f2d16SKris Kennaway } 818b66f2d16SKris Kennaway xfree(path); 819b66f2d16SKris Kennaway } 820b66f2d16SKris Kennaway 821ae1f160dSDag-Erling Smørgrav static void 822b66f2d16SKris Kennaway process_rename(void) 823b66f2d16SKris Kennaway { 824b66f2d16SKris Kennaway u_int32_t id; 825b66f2d16SKris Kennaway char *oldpath, *newpath; 826d0c8c0bcSDag-Erling Smørgrav int status; 827d0c8c0bcSDag-Erling Smørgrav struct stat sb; 828b66f2d16SKris Kennaway 829b66f2d16SKris Kennaway id = get_int(); 830b66f2d16SKris Kennaway oldpath = get_string(NULL); 831b66f2d16SKris Kennaway newpath = get_string(NULL); 832ee21a45fSDag-Erling Smørgrav TRACE("rename id %u old %s new %s", id, oldpath, newpath); 833d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 834d0c8c0bcSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 835d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 836d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 837d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 838d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 839d74d50a8SDag-Erling Smørgrav if (errno == EOPNOTSUPP 840d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 841d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 842d74d50a8SDag-Erling Smørgrav #endif 843d74d50a8SDag-Erling Smørgrav ) { 844d74d50a8SDag-Erling Smørgrav struct stat st; 845d74d50a8SDag-Erling Smørgrav 846d74d50a8SDag-Erling Smørgrav /* 847d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 848d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 849d74d50a8SDag-Erling Smørgrav */ 850d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 851d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 852d74d50a8SDag-Erling Smørgrav status = 853d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 854d74d50a8SDag-Erling Smørgrav else 855d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 856d74d50a8SDag-Erling Smørgrav } 857d74d50a8SDag-Erling Smørgrav } else { 858d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 859d74d50a8SDag-Erling Smørgrav } 860d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 861d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 862d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 863d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 864d0c8c0bcSDag-Erling Smørgrav } else 865d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 866d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 867d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 868d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 869d0c8c0bcSDag-Erling Smørgrav else 870d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 8711e8db6e2SBrian Feldman } 872b66f2d16SKris Kennaway send_status(id, status); 873b66f2d16SKris Kennaway xfree(oldpath); 874b66f2d16SKris Kennaway xfree(newpath); 875b66f2d16SKris Kennaway } 876b66f2d16SKris Kennaway 877ae1f160dSDag-Erling Smørgrav static void 8781e8db6e2SBrian Feldman process_readlink(void) 8791e8db6e2SBrian Feldman { 8801e8db6e2SBrian Feldman u_int32_t id; 881ae1f160dSDag-Erling Smørgrav int len; 882d74d50a8SDag-Erling Smørgrav char buf[MAXPATHLEN]; 8831e8db6e2SBrian Feldman char *path; 8841e8db6e2SBrian Feldman 8851e8db6e2SBrian Feldman id = get_int(); 8861e8db6e2SBrian Feldman path = get_string(NULL); 887ee21a45fSDag-Erling Smørgrav TRACE("readlink id %u path %s", id, path); 888d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 8891e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 8901e8db6e2SBrian Feldman else { 8911e8db6e2SBrian Feldman Stat s; 8921e8db6e2SBrian Feldman 893d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 8941e8db6e2SBrian Feldman attrib_clear(&s.attrib); 895d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 8961e8db6e2SBrian Feldman send_names(id, 1, &s); 8971e8db6e2SBrian Feldman } 8981e8db6e2SBrian Feldman xfree(path); 8991e8db6e2SBrian Feldman } 9001e8db6e2SBrian Feldman 901ae1f160dSDag-Erling Smørgrav static void 9021e8db6e2SBrian Feldman process_symlink(void) 9031e8db6e2SBrian Feldman { 9041e8db6e2SBrian Feldman u_int32_t id; 9051e8db6e2SBrian Feldman char *oldpath, *newpath; 906d0c8c0bcSDag-Erling Smørgrav int ret, status; 9071e8db6e2SBrian Feldman 9081e8db6e2SBrian Feldman id = get_int(); 9091e8db6e2SBrian Feldman oldpath = get_string(NULL); 9101e8db6e2SBrian Feldman newpath = get_string(NULL); 911ee21a45fSDag-Erling Smørgrav TRACE("symlink id %u old %s new %s", id, oldpath, newpath); 912d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 9131e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 9141e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9151e8db6e2SBrian Feldman send_status(id, status); 9161e8db6e2SBrian Feldman xfree(oldpath); 9171e8db6e2SBrian Feldman xfree(newpath); 9181e8db6e2SBrian Feldman } 9191e8db6e2SBrian Feldman 920ae1f160dSDag-Erling Smørgrav static void 9211e8db6e2SBrian Feldman process_extended(void) 9221e8db6e2SBrian Feldman { 9231e8db6e2SBrian Feldman u_int32_t id; 9241e8db6e2SBrian Feldman char *request; 9251e8db6e2SBrian Feldman 9261e8db6e2SBrian Feldman id = get_int(); 9271e8db6e2SBrian Feldman request = get_string(NULL); 9281e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 9291e8db6e2SBrian Feldman xfree(request); 9301e8db6e2SBrian Feldman } 931b66f2d16SKris Kennaway 932b66f2d16SKris Kennaway /* stolen from ssh-agent */ 933b66f2d16SKris Kennaway 934ae1f160dSDag-Erling Smørgrav static void 935b66f2d16SKris Kennaway process(void) 936b66f2d16SKris Kennaway { 9371e8db6e2SBrian Feldman u_int msg_len; 938545d5ecaSDag-Erling Smørgrav u_int buf_len; 939545d5ecaSDag-Erling Smørgrav u_int consumed; 9401e8db6e2SBrian Feldman u_int type; 9411e8db6e2SBrian Feldman u_char *cp; 942b66f2d16SKris Kennaway 943545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 944545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 945b66f2d16SKris Kennaway return; /* Incomplete message. */ 946ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 947b66f2d16SKris Kennaway msg_len = GET_32BIT(cp); 948b66f2d16SKris Kennaway if (msg_len > 256 * 1024) { 949b66f2d16SKris Kennaway error("bad message "); 950b66f2d16SKris Kennaway exit(11); 951b66f2d16SKris Kennaway } 952545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 953b66f2d16SKris Kennaway return; 954b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 955545d5ecaSDag-Erling Smørgrav buf_len -= 4; 956b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 957b66f2d16SKris Kennaway switch (type) { 9581e8db6e2SBrian Feldman case SSH2_FXP_INIT: 959b66f2d16SKris Kennaway process_init(); 960b66f2d16SKris Kennaway break; 9611e8db6e2SBrian Feldman case SSH2_FXP_OPEN: 962b66f2d16SKris Kennaway process_open(); 963b66f2d16SKris Kennaway break; 9641e8db6e2SBrian Feldman case SSH2_FXP_CLOSE: 965b66f2d16SKris Kennaway process_close(); 966b66f2d16SKris Kennaway break; 9671e8db6e2SBrian Feldman case SSH2_FXP_READ: 968b66f2d16SKris Kennaway process_read(); 969b66f2d16SKris Kennaway break; 9701e8db6e2SBrian Feldman case SSH2_FXP_WRITE: 971b66f2d16SKris Kennaway process_write(); 972b66f2d16SKris Kennaway break; 9731e8db6e2SBrian Feldman case SSH2_FXP_LSTAT: 974b66f2d16SKris Kennaway process_lstat(); 975b66f2d16SKris Kennaway break; 9761e8db6e2SBrian Feldman case SSH2_FXP_FSTAT: 977b66f2d16SKris Kennaway process_fstat(); 978b66f2d16SKris Kennaway break; 9791e8db6e2SBrian Feldman case SSH2_FXP_SETSTAT: 980b66f2d16SKris Kennaway process_setstat(); 981b66f2d16SKris Kennaway break; 9821e8db6e2SBrian Feldman case SSH2_FXP_FSETSTAT: 983b66f2d16SKris Kennaway process_fsetstat(); 984b66f2d16SKris Kennaway break; 9851e8db6e2SBrian Feldman case SSH2_FXP_OPENDIR: 986b66f2d16SKris Kennaway process_opendir(); 987b66f2d16SKris Kennaway break; 9881e8db6e2SBrian Feldman case SSH2_FXP_READDIR: 989b66f2d16SKris Kennaway process_readdir(); 990b66f2d16SKris Kennaway break; 9911e8db6e2SBrian Feldman case SSH2_FXP_REMOVE: 992b66f2d16SKris Kennaway process_remove(); 993b66f2d16SKris Kennaway break; 9941e8db6e2SBrian Feldman case SSH2_FXP_MKDIR: 995b66f2d16SKris Kennaway process_mkdir(); 996b66f2d16SKris Kennaway break; 9971e8db6e2SBrian Feldman case SSH2_FXP_RMDIR: 998b66f2d16SKris Kennaway process_rmdir(); 999b66f2d16SKris Kennaway break; 10001e8db6e2SBrian Feldman case SSH2_FXP_REALPATH: 1001b66f2d16SKris Kennaway process_realpath(); 1002b66f2d16SKris Kennaway break; 10031e8db6e2SBrian Feldman case SSH2_FXP_STAT: 1004b66f2d16SKris Kennaway process_stat(); 1005b66f2d16SKris Kennaway break; 10061e8db6e2SBrian Feldman case SSH2_FXP_RENAME: 1007b66f2d16SKris Kennaway process_rename(); 1008b66f2d16SKris Kennaway break; 10091e8db6e2SBrian Feldman case SSH2_FXP_READLINK: 10101e8db6e2SBrian Feldman process_readlink(); 10111e8db6e2SBrian Feldman break; 10121e8db6e2SBrian Feldman case SSH2_FXP_SYMLINK: 10131e8db6e2SBrian Feldman process_symlink(); 10141e8db6e2SBrian Feldman break; 10151e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 10161e8db6e2SBrian Feldman process_extended(); 10171e8db6e2SBrian Feldman break; 1018b66f2d16SKris Kennaway default: 1019b66f2d16SKris Kennaway error("Unknown message %d", type); 1020b66f2d16SKris Kennaway break; 1021b66f2d16SKris Kennaway } 1022545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1023545d5ecaSDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) 1024545d5ecaSDag-Erling Smørgrav fatal("iqueue grows"); 1025545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1026545d5ecaSDag-Erling Smørgrav if (msg_len < consumed) 1027545d5ecaSDag-Erling Smørgrav fatal("msg_len %d < consumed %d", msg_len, consumed); 1028545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1029545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1030b66f2d16SKris Kennaway } 1031b66f2d16SKris Kennaway 1032b66f2d16SKris Kennaway int 1033b66f2d16SKris Kennaway main(int ac, char **av) 1034b66f2d16SKris Kennaway { 10351e8db6e2SBrian Feldman fd_set *rset, *wset; 1036b66f2d16SKris Kennaway int in, out, max; 10371e8db6e2SBrian Feldman ssize_t len, olen, set_size; 10381e8db6e2SBrian Feldman 10391e8db6e2SBrian Feldman /* XXX should use getopt */ 1040b66f2d16SKris Kennaway 1041d95e11bfSDag-Erling Smørgrav __progname = ssh_get_progname(av[0]); 1042b66f2d16SKris Kennaway handle_init(); 1043b66f2d16SKris Kennaway 10441e8db6e2SBrian Feldman #ifdef DEBUG_SFTP_SERVER 10451e8db6e2SBrian Feldman log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 10461e8db6e2SBrian Feldman #endif 10471e8db6e2SBrian Feldman 1048b66f2d16SKris Kennaway in = dup(STDIN_FILENO); 1049b66f2d16SKris Kennaway out = dup(STDOUT_FILENO); 1050b66f2d16SKris Kennaway 105183d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 105283d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 105383d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 105483d2307dSDag-Erling Smørgrav #endif 105583d2307dSDag-Erling Smørgrav 1056b66f2d16SKris Kennaway max = 0; 1057b66f2d16SKris Kennaway if (in > max) 1058b66f2d16SKris Kennaway max = in; 1059b66f2d16SKris Kennaway if (out > max) 1060b66f2d16SKris Kennaway max = out; 1061b66f2d16SKris Kennaway 1062b66f2d16SKris Kennaway buffer_init(&iqueue); 1063b66f2d16SKris Kennaway buffer_init(&oqueue); 1064b66f2d16SKris Kennaway 10651e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 10661e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 10671e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1068b66f2d16SKris Kennaway 10691e8db6e2SBrian Feldman for (;;) { 10701e8db6e2SBrian Feldman memset(rset, 0, set_size); 10711e8db6e2SBrian Feldman memset(wset, 0, set_size); 10721e8db6e2SBrian Feldman 10731e8db6e2SBrian Feldman FD_SET(in, rset); 1074b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1075b66f2d16SKris Kennaway if (olen > 0) 10761e8db6e2SBrian Feldman FD_SET(out, wset); 1077b66f2d16SKris Kennaway 10781e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1079b66f2d16SKris Kennaway if (errno == EINTR) 1080b66f2d16SKris Kennaway continue; 1081b66f2d16SKris Kennaway exit(2); 1082b66f2d16SKris Kennaway } 1083b66f2d16SKris Kennaway 1084b66f2d16SKris Kennaway /* copy stdin to iqueue */ 10851e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1086b66f2d16SKris Kennaway char buf[4*4096]; 1087b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1088b66f2d16SKris Kennaway if (len == 0) { 1089b66f2d16SKris Kennaway debug("read eof"); 1090b66f2d16SKris Kennaway exit(0); 1091b66f2d16SKris Kennaway } else if (len < 0) { 1092b66f2d16SKris Kennaway error("read error"); 1093b66f2d16SKris Kennaway exit(1); 1094b66f2d16SKris Kennaway } else { 1095b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1096b66f2d16SKris Kennaway } 1097b66f2d16SKris Kennaway } 1098b66f2d16SKris Kennaway /* send oqueue to stdout */ 10991e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1100b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1101b66f2d16SKris Kennaway if (len < 0) { 1102b66f2d16SKris Kennaway error("write error"); 1103b66f2d16SKris Kennaway exit(1); 1104b66f2d16SKris Kennaway } else { 1105b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1106b66f2d16SKris Kennaway } 1107b66f2d16SKris Kennaway } 1108b66f2d16SKris Kennaway /* process requests from client */ 1109b66f2d16SKris Kennaway process(); 1110b66f2d16SKris Kennaway } 1111b66f2d16SKris Kennaway } 1112