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" 17021d409fSDag-Erling Smørgrav RCSID("$OpenBSD: sftp-server.c,v 1.50 2006/01/02 01:20:31 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" 24021d409fSDag-Erling Smørgrav #include "misc.h" 25b66f2d16SKris Kennaway 261e8db6e2SBrian Feldman #include "sftp.h" 271e8db6e2SBrian Feldman #include "sftp-common.h" 28b66f2d16SKris Kennaway 29b66f2d16SKris Kennaway /* helper */ 301e8db6e2SBrian Feldman #define get_int64() buffer_get_int64(&iqueue); 31b66f2d16SKris Kennaway #define get_int() buffer_get_int(&iqueue); 32b66f2d16SKris Kennaway #define get_string(lenp) buffer_get_string(&iqueue, lenp); 331e8db6e2SBrian Feldman #define TRACE debug 34b66f2d16SKris Kennaway 3583d2307dSDag-Erling Smørgrav extern char *__progname; 3683d2307dSDag-Erling Smørgrav 37b66f2d16SKris Kennaway /* input and output queue */ 38b66f2d16SKris Kennaway Buffer iqueue; 39b66f2d16SKris Kennaway Buffer oqueue; 40b66f2d16SKris Kennaway 411e8db6e2SBrian Feldman /* Version of client */ 421e8db6e2SBrian Feldman int version; 431e8db6e2SBrian Feldman 44d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 45b66f2d16SKris Kennaway 46b66f2d16SKris Kennaway typedef struct Stat Stat; 47b66f2d16SKris Kennaway 481e8db6e2SBrian Feldman struct Stat { 49b66f2d16SKris Kennaway char *name; 50b66f2d16SKris Kennaway char *long_name; 51b66f2d16SKris Kennaway Attrib attrib; 52b66f2d16SKris Kennaway }; 53b66f2d16SKris Kennaway 54ae1f160dSDag-Erling Smørgrav static int 55b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 56b66f2d16SKris Kennaway { 57b66f2d16SKris Kennaway int ret = 0; 581e8db6e2SBrian Feldman 59b66f2d16SKris Kennaway switch (unixerrno) { 60b66f2d16SKris Kennaway case 0: 611e8db6e2SBrian Feldman ret = SSH2_FX_OK; 62b66f2d16SKris Kennaway break; 63b66f2d16SKris Kennaway case ENOENT: 64b66f2d16SKris Kennaway case ENOTDIR: 65b66f2d16SKris Kennaway case EBADF: 66b66f2d16SKris Kennaway case ELOOP: 671e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 68b66f2d16SKris Kennaway break; 69b66f2d16SKris Kennaway case EPERM: 70b66f2d16SKris Kennaway case EACCES: 71b66f2d16SKris Kennaway case EFAULT: 721e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 73b66f2d16SKris Kennaway break; 74b66f2d16SKris Kennaway case ENAMETOOLONG: 75b66f2d16SKris Kennaway case EINVAL: 761e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 77b66f2d16SKris Kennaway break; 78b66f2d16SKris Kennaway default: 791e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 80b66f2d16SKris Kennaway break; 81b66f2d16SKris Kennaway } 82b66f2d16SKris Kennaway return ret; 83b66f2d16SKris Kennaway } 84b66f2d16SKris Kennaway 85ae1f160dSDag-Erling Smørgrav static int 86b66f2d16SKris Kennaway flags_from_portable(int pflags) 87b66f2d16SKris Kennaway { 88b66f2d16SKris Kennaway int flags = 0; 891e8db6e2SBrian Feldman 901e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 911e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 92b66f2d16SKris Kennaway flags = O_RDWR; 931e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 94b66f2d16SKris Kennaway flags = O_RDONLY; 951e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 96b66f2d16SKris Kennaway flags = O_WRONLY; 97b66f2d16SKris Kennaway } 981e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 99b66f2d16SKris Kennaway flags |= O_CREAT; 1001e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 101b66f2d16SKris Kennaway flags |= O_TRUNC; 1021e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 103b66f2d16SKris Kennaway flags |= O_EXCL; 104b66f2d16SKris Kennaway return flags; 105b66f2d16SKris Kennaway } 106b66f2d16SKris Kennaway 107ae1f160dSDag-Erling Smørgrav static Attrib * 108b66f2d16SKris Kennaway get_attrib(void) 109b66f2d16SKris Kennaway { 110b66f2d16SKris Kennaway return decode_attrib(&iqueue); 111b66f2d16SKris Kennaway } 112b66f2d16SKris Kennaway 113b66f2d16SKris Kennaway /* handle handles */ 114b66f2d16SKris Kennaway 115b66f2d16SKris Kennaway typedef struct Handle Handle; 116b66f2d16SKris Kennaway struct Handle { 117b66f2d16SKris Kennaway int use; 118b66f2d16SKris Kennaway DIR *dirp; 119b66f2d16SKris Kennaway int fd; 120b66f2d16SKris Kennaway char *name; 121b66f2d16SKris Kennaway }; 1221e8db6e2SBrian Feldman 123b66f2d16SKris Kennaway enum { 124b66f2d16SKris Kennaway HANDLE_UNUSED, 125b66f2d16SKris Kennaway HANDLE_DIR, 126b66f2d16SKris Kennaway HANDLE_FILE 127b66f2d16SKris Kennaway }; 1281e8db6e2SBrian Feldman 129b66f2d16SKris Kennaway Handle handles[100]; 130b66f2d16SKris Kennaway 131ae1f160dSDag-Erling Smørgrav static void 132b66f2d16SKris Kennaway handle_init(void) 133b66f2d16SKris Kennaway { 134043840dfSDag-Erling Smørgrav u_int i; 1351e8db6e2SBrian Feldman 136b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 137b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 138b66f2d16SKris Kennaway } 139b66f2d16SKris Kennaway 140ae1f160dSDag-Erling Smørgrav static int 141efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp) 142b66f2d16SKris Kennaway { 143043840dfSDag-Erling Smørgrav u_int i; 1441e8db6e2SBrian Feldman 145b66f2d16SKris Kennaway for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 146b66f2d16SKris Kennaway if (handles[i].use == HANDLE_UNUSED) { 147b66f2d16SKris Kennaway handles[i].use = use; 148b66f2d16SKris Kennaway handles[i].dirp = dirp; 149b66f2d16SKris Kennaway handles[i].fd = fd; 150d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 151b66f2d16SKris Kennaway return i; 152b66f2d16SKris Kennaway } 153b66f2d16SKris Kennaway } 154b66f2d16SKris Kennaway return -1; 155b66f2d16SKris Kennaway } 156b66f2d16SKris Kennaway 157ae1f160dSDag-Erling Smørgrav static int 158b66f2d16SKris Kennaway handle_is_ok(int i, int type) 159b66f2d16SKris Kennaway { 160043840dfSDag-Erling Smørgrav return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) && 1611e8db6e2SBrian Feldman handles[i].use == type; 162b66f2d16SKris Kennaway } 163b66f2d16SKris Kennaway 164ae1f160dSDag-Erling Smørgrav static int 165b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 166b66f2d16SKris Kennaway { 167b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 168b66f2d16SKris Kennaway return -1; 1691e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 1701e8db6e2SBrian Feldman PUT_32BIT(*stringp, handle); 1711e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 172b66f2d16SKris Kennaway return 0; 173b66f2d16SKris Kennaway } 174b66f2d16SKris Kennaway 175ae1f160dSDag-Erling Smørgrav static int 176efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen) 177b66f2d16SKris Kennaway { 1781e8db6e2SBrian Feldman int val; 1791e8db6e2SBrian Feldman 1801e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 181b66f2d16SKris Kennaway return -1; 1821e8db6e2SBrian Feldman val = GET_32BIT(handle); 183b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 184b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 185b66f2d16SKris Kennaway return val; 186b66f2d16SKris Kennaway return -1; 187b66f2d16SKris Kennaway } 188b66f2d16SKris Kennaway 189ae1f160dSDag-Erling Smørgrav static char * 190b66f2d16SKris Kennaway handle_to_name(int handle) 191b66f2d16SKris Kennaway { 192b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 193b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 194b66f2d16SKris Kennaway return handles[handle].name; 195b66f2d16SKris Kennaway return NULL; 196b66f2d16SKris Kennaway } 197b66f2d16SKris Kennaway 198ae1f160dSDag-Erling Smørgrav static DIR * 199b66f2d16SKris Kennaway handle_to_dir(int handle) 200b66f2d16SKris Kennaway { 201b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 202b66f2d16SKris Kennaway return handles[handle].dirp; 203b66f2d16SKris Kennaway return NULL; 204b66f2d16SKris Kennaway } 205b66f2d16SKris Kennaway 206ae1f160dSDag-Erling Smørgrav static int 207b66f2d16SKris Kennaway handle_to_fd(int handle) 208b66f2d16SKris Kennaway { 209b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 210b66f2d16SKris Kennaway return handles[handle].fd; 211b66f2d16SKris Kennaway return -1; 212b66f2d16SKris Kennaway } 213b66f2d16SKris Kennaway 214ae1f160dSDag-Erling Smørgrav static int 215b66f2d16SKris Kennaway handle_close(int handle) 216b66f2d16SKris Kennaway { 217b66f2d16SKris Kennaway int ret = -1; 2181e8db6e2SBrian Feldman 219b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 220b66f2d16SKris Kennaway ret = close(handles[handle].fd); 221b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 222d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 223b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 224b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 225b66f2d16SKris Kennaway handles[handle].use = HANDLE_UNUSED; 226d0c8c0bcSDag-Erling Smørgrav xfree(handles[handle].name); 227b66f2d16SKris Kennaway } else { 228b66f2d16SKris Kennaway errno = ENOENT; 229b66f2d16SKris Kennaway } 230b66f2d16SKris Kennaway return ret; 231b66f2d16SKris Kennaway } 232b66f2d16SKris Kennaway 233ae1f160dSDag-Erling Smørgrav static int 234b66f2d16SKris Kennaway get_handle(void) 235b66f2d16SKris Kennaway { 236b66f2d16SKris Kennaway char *handle; 2371e8db6e2SBrian Feldman int val = -1; 238b66f2d16SKris Kennaway u_int hlen; 2391e8db6e2SBrian Feldman 240b66f2d16SKris Kennaway handle = get_string(&hlen); 2411e8db6e2SBrian Feldman if (hlen < 256) 242b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 243b66f2d16SKris Kennaway xfree(handle); 244b66f2d16SKris Kennaway return val; 245b66f2d16SKris Kennaway } 246b66f2d16SKris Kennaway 247b66f2d16SKris Kennaway /* send replies */ 248b66f2d16SKris Kennaway 249ae1f160dSDag-Erling Smørgrav static void 250b66f2d16SKris Kennaway send_msg(Buffer *m) 251b66f2d16SKris Kennaway { 252b66f2d16SKris Kennaway int mlen = buffer_len(m); 2531e8db6e2SBrian Feldman 254b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 255b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 256b66f2d16SKris Kennaway buffer_consume(m, mlen); 257b66f2d16SKris Kennaway } 258b66f2d16SKris Kennaway 259ae1f160dSDag-Erling Smørgrav static void 260d74d50a8SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 261b66f2d16SKris Kennaway { 262b66f2d16SKris Kennaway Buffer msg; 2631e8db6e2SBrian Feldman const char *status_messages[] = { 2641e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 2651e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 2661e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 2671e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 2681e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 2691e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 2701e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 2711e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 2721e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 2731e8db6e2SBrian Feldman "Unknown error" /* Others */ 2741e8db6e2SBrian Feldman }; 2751e8db6e2SBrian Feldman 276d74d50a8SDag-Erling Smørgrav TRACE("sent status id %u error %u", id, status); 277b66f2d16SKris Kennaway buffer_init(&msg); 2781e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 279b66f2d16SKris Kennaway buffer_put_int(&msg, id); 280d74d50a8SDag-Erling Smørgrav buffer_put_int(&msg, status); 2811e8db6e2SBrian Feldman if (version >= 3) { 2821e8db6e2SBrian Feldman buffer_put_cstring(&msg, 283d74d50a8SDag-Erling Smørgrav status_messages[MIN(status,SSH2_FX_MAX)]); 2841e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 2851e8db6e2SBrian Feldman } 286b66f2d16SKris Kennaway send_msg(&msg); 287b66f2d16SKris Kennaway buffer_free(&msg); 288b66f2d16SKris Kennaway } 289ae1f160dSDag-Erling Smørgrav static void 290efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 291b66f2d16SKris Kennaway { 292b66f2d16SKris Kennaway Buffer msg; 2931e8db6e2SBrian Feldman 294b66f2d16SKris Kennaway buffer_init(&msg); 295b66f2d16SKris Kennaway buffer_put_char(&msg, type); 296b66f2d16SKris Kennaway buffer_put_int(&msg, id); 297b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 298b66f2d16SKris Kennaway send_msg(&msg); 299b66f2d16SKris Kennaway buffer_free(&msg); 300b66f2d16SKris Kennaway } 301b66f2d16SKris Kennaway 302ae1f160dSDag-Erling Smørgrav static void 303efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen) 304b66f2d16SKris Kennaway { 305ee21a45fSDag-Erling Smørgrav TRACE("sent data id %u len %d", id, dlen); 3061e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 307b66f2d16SKris Kennaway } 308b66f2d16SKris Kennaway 309ae1f160dSDag-Erling Smørgrav static void 310b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 311b66f2d16SKris Kennaway { 312b66f2d16SKris Kennaway char *string; 313b66f2d16SKris Kennaway int hlen; 3141e8db6e2SBrian Feldman 315b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 316ee21a45fSDag-Erling Smørgrav TRACE("sent handle id %u handle %d", id, handle); 3171e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 318b66f2d16SKris Kennaway xfree(string); 319b66f2d16SKris Kennaway } 320b66f2d16SKris Kennaway 321ae1f160dSDag-Erling Smørgrav static void 322efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 323b66f2d16SKris Kennaway { 324b66f2d16SKris Kennaway Buffer msg; 325b66f2d16SKris Kennaway int i; 3261e8db6e2SBrian Feldman 327b66f2d16SKris Kennaway buffer_init(&msg); 3281e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 329b66f2d16SKris Kennaway buffer_put_int(&msg, id); 330b66f2d16SKris Kennaway buffer_put_int(&msg, count); 331ee21a45fSDag-Erling Smørgrav TRACE("sent names id %u count %d", id, count); 332b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 333b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 334b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 335b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 336b66f2d16SKris Kennaway } 337b66f2d16SKris Kennaway send_msg(&msg); 338b66f2d16SKris Kennaway buffer_free(&msg); 339b66f2d16SKris Kennaway } 340b66f2d16SKris Kennaway 341ae1f160dSDag-Erling Smørgrav static void 342efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 343b66f2d16SKris Kennaway { 344b66f2d16SKris Kennaway Buffer msg; 3451e8db6e2SBrian Feldman 346ee21a45fSDag-Erling Smørgrav TRACE("sent attrib id %u have 0x%x", id, a->flags); 347b66f2d16SKris Kennaway buffer_init(&msg); 3481e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 349b66f2d16SKris Kennaway buffer_put_int(&msg, id); 350b66f2d16SKris Kennaway encode_attrib(&msg, a); 351b66f2d16SKris Kennaway send_msg(&msg); 352b66f2d16SKris Kennaway buffer_free(&msg); 353b66f2d16SKris Kennaway } 354b66f2d16SKris Kennaway 355b66f2d16SKris Kennaway /* parse incoming */ 356b66f2d16SKris Kennaway 357ae1f160dSDag-Erling Smørgrav static void 358b66f2d16SKris Kennaway process_init(void) 359b66f2d16SKris Kennaway { 360b66f2d16SKris Kennaway Buffer msg; 361b66f2d16SKris Kennaway 362545d5ecaSDag-Erling Smørgrav version = get_int(); 363b66f2d16SKris Kennaway TRACE("client version %d", version); 364b66f2d16SKris Kennaway buffer_init(&msg); 3651e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 3661e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 367b66f2d16SKris Kennaway send_msg(&msg); 368b66f2d16SKris Kennaway buffer_free(&msg); 369b66f2d16SKris Kennaway } 370b66f2d16SKris Kennaway 371ae1f160dSDag-Erling Smørgrav static void 372b66f2d16SKris Kennaway process_open(void) 373b66f2d16SKris Kennaway { 374b66f2d16SKris Kennaway u_int32_t id, pflags; 375b66f2d16SKris Kennaway Attrib *a; 376b66f2d16SKris Kennaway char *name; 3771e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 378b66f2d16SKris Kennaway 379b66f2d16SKris Kennaway id = get_int(); 380b66f2d16SKris Kennaway name = get_string(NULL); 3811e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 382b66f2d16SKris Kennaway a = get_attrib(); 383b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 3841e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 385ee21a45fSDag-Erling Smørgrav TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); 386b66f2d16SKris Kennaway fd = open(name, flags, mode); 387b66f2d16SKris Kennaway if (fd < 0) { 388b66f2d16SKris Kennaway status = errno_to_portable(errno); 389b66f2d16SKris Kennaway } else { 390d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, NULL); 391b66f2d16SKris Kennaway if (handle < 0) { 392b66f2d16SKris Kennaway close(fd); 393b66f2d16SKris Kennaway } else { 394b66f2d16SKris Kennaway send_handle(id, handle); 3951e8db6e2SBrian Feldman status = SSH2_FX_OK; 396b66f2d16SKris Kennaway } 397b66f2d16SKris Kennaway } 3981e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 399b66f2d16SKris Kennaway send_status(id, status); 400b66f2d16SKris Kennaway xfree(name); 401b66f2d16SKris Kennaway } 402b66f2d16SKris Kennaway 403ae1f160dSDag-Erling Smørgrav static void 404b66f2d16SKris Kennaway process_close(void) 405b66f2d16SKris Kennaway { 406b66f2d16SKris Kennaway u_int32_t id; 4071e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 408b66f2d16SKris Kennaway 409b66f2d16SKris Kennaway id = get_int(); 410b66f2d16SKris Kennaway handle = get_handle(); 411ee21a45fSDag-Erling Smørgrav TRACE("close id %u handle %d", id, handle); 412b66f2d16SKris Kennaway ret = handle_close(handle); 4131e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 414b66f2d16SKris Kennaway send_status(id, status); 415b66f2d16SKris Kennaway } 416b66f2d16SKris Kennaway 417ae1f160dSDag-Erling Smørgrav static void 418b66f2d16SKris Kennaway process_read(void) 419b66f2d16SKris Kennaway { 420b66f2d16SKris Kennaway char buf[64*1024]; 4211e8db6e2SBrian Feldman u_int32_t id, len; 4221e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 423b66f2d16SKris Kennaway u_int64_t off; 424b66f2d16SKris Kennaway 425b66f2d16SKris Kennaway id = get_int(); 426b66f2d16SKris Kennaway handle = get_handle(); 4271e8db6e2SBrian Feldman off = get_int64(); 428b66f2d16SKris Kennaway len = get_int(); 429b66f2d16SKris Kennaway 430ee21a45fSDag-Erling Smørgrav TRACE("read id %u handle %d off %llu len %d", id, handle, 431021d409fSDag-Erling Smørgrav (unsigned long long)off, len); 432b66f2d16SKris Kennaway if (len > sizeof buf) { 433b66f2d16SKris Kennaway len = sizeof buf; 434d95e11bfSDag-Erling Smørgrav logit("read change len %d", len); 435b66f2d16SKris Kennaway } 436b66f2d16SKris Kennaway fd = handle_to_fd(handle); 437b66f2d16SKris Kennaway if (fd >= 0) { 438b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 439b66f2d16SKris Kennaway error("process_read: seek failed"); 440b66f2d16SKris Kennaway status = errno_to_portable(errno); 441b66f2d16SKris Kennaway } else { 442b66f2d16SKris Kennaway ret = read(fd, buf, len); 443b66f2d16SKris Kennaway if (ret < 0) { 444b66f2d16SKris Kennaway status = errno_to_portable(errno); 445b66f2d16SKris Kennaway } else if (ret == 0) { 4461e8db6e2SBrian Feldman status = SSH2_FX_EOF; 447b66f2d16SKris Kennaway } else { 448b66f2d16SKris Kennaway send_data(id, buf, ret); 4491e8db6e2SBrian Feldman status = SSH2_FX_OK; 450b66f2d16SKris Kennaway } 451b66f2d16SKris Kennaway } 452b66f2d16SKris Kennaway } 4531e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 454b66f2d16SKris Kennaway send_status(id, status); 455b66f2d16SKris Kennaway } 456b66f2d16SKris Kennaway 457ae1f160dSDag-Erling Smørgrav static void 458b66f2d16SKris Kennaway process_write(void) 459b66f2d16SKris Kennaway { 4601e8db6e2SBrian Feldman u_int32_t id; 461b66f2d16SKris Kennaway u_int64_t off; 462b66f2d16SKris Kennaway u_int len; 4631e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 464b66f2d16SKris Kennaway char *data; 465b66f2d16SKris Kennaway 466b66f2d16SKris Kennaway id = get_int(); 467b66f2d16SKris Kennaway handle = get_handle(); 4681e8db6e2SBrian Feldman off = get_int64(); 469b66f2d16SKris Kennaway data = get_string(&len); 470b66f2d16SKris Kennaway 471ee21a45fSDag-Erling Smørgrav TRACE("write id %u handle %d off %llu len %d", id, handle, 472021d409fSDag-Erling Smørgrav (unsigned long long)off, len); 473b66f2d16SKris Kennaway fd = handle_to_fd(handle); 474b66f2d16SKris Kennaway if (fd >= 0) { 475b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 476b66f2d16SKris Kennaway status = errno_to_portable(errno); 477b66f2d16SKris Kennaway error("process_write: seek failed"); 478b66f2d16SKris Kennaway } else { 479b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 480b66f2d16SKris Kennaway ret = write(fd, data, len); 481043840dfSDag-Erling Smørgrav if (ret < 0) { 482b66f2d16SKris Kennaway error("process_write: write failed"); 483b66f2d16SKris Kennaway status = errno_to_portable(errno); 484043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 4851e8db6e2SBrian Feldman status = SSH2_FX_OK; 486b66f2d16SKris Kennaway } else { 487d95e11bfSDag-Erling Smørgrav logit("nothing at all written"); 488b66f2d16SKris Kennaway } 489b66f2d16SKris Kennaway } 490b66f2d16SKris Kennaway } 491b66f2d16SKris Kennaway send_status(id, status); 492b66f2d16SKris Kennaway xfree(data); 493b66f2d16SKris Kennaway } 494b66f2d16SKris Kennaway 495ae1f160dSDag-Erling Smørgrav static void 496b66f2d16SKris Kennaway process_do_stat(int do_lstat) 497b66f2d16SKris Kennaway { 4981e8db6e2SBrian Feldman Attrib a; 499b66f2d16SKris Kennaway struct stat st; 500b66f2d16SKris Kennaway u_int32_t id; 501b66f2d16SKris Kennaway char *name; 5021e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 503b66f2d16SKris Kennaway 504b66f2d16SKris Kennaway id = get_int(); 505b66f2d16SKris Kennaway name = get_string(NULL); 506ee21a45fSDag-Erling Smørgrav TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); 507b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 508b66f2d16SKris Kennaway if (ret < 0) { 509b66f2d16SKris Kennaway status = errno_to_portable(errno); 510b66f2d16SKris Kennaway } else { 5111e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5121e8db6e2SBrian Feldman send_attrib(id, &a); 5131e8db6e2SBrian Feldman status = SSH2_FX_OK; 514b66f2d16SKris Kennaway } 5151e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 516b66f2d16SKris Kennaway send_status(id, status); 517b66f2d16SKris Kennaway xfree(name); 518b66f2d16SKris Kennaway } 519b66f2d16SKris Kennaway 520ae1f160dSDag-Erling Smørgrav static void 521b66f2d16SKris Kennaway process_stat(void) 522b66f2d16SKris Kennaway { 523b66f2d16SKris Kennaway process_do_stat(0); 524b66f2d16SKris Kennaway } 525b66f2d16SKris Kennaway 526ae1f160dSDag-Erling Smørgrav static void 527b66f2d16SKris Kennaway process_lstat(void) 528b66f2d16SKris Kennaway { 529b66f2d16SKris Kennaway process_do_stat(1); 530b66f2d16SKris Kennaway } 531b66f2d16SKris Kennaway 532ae1f160dSDag-Erling Smørgrav static void 533b66f2d16SKris Kennaway process_fstat(void) 534b66f2d16SKris Kennaway { 5351e8db6e2SBrian Feldman Attrib a; 536b66f2d16SKris Kennaway struct stat st; 537b66f2d16SKris Kennaway u_int32_t id; 5381e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 539b66f2d16SKris Kennaway 540b66f2d16SKris Kennaway id = get_int(); 541b66f2d16SKris Kennaway handle = get_handle(); 542ee21a45fSDag-Erling Smørgrav TRACE("fstat id %u handle %d", id, handle); 543b66f2d16SKris Kennaway fd = handle_to_fd(handle); 544b66f2d16SKris Kennaway if (fd >= 0) { 545b66f2d16SKris Kennaway ret = fstat(fd, &st); 546b66f2d16SKris Kennaway if (ret < 0) { 547b66f2d16SKris Kennaway status = errno_to_portable(errno); 548b66f2d16SKris Kennaway } else { 5491e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 5501e8db6e2SBrian Feldman send_attrib(id, &a); 5511e8db6e2SBrian Feldman status = SSH2_FX_OK; 552b66f2d16SKris Kennaway } 553b66f2d16SKris Kennaway } 5541e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 555b66f2d16SKris Kennaway send_status(id, status); 556b66f2d16SKris Kennaway } 557b66f2d16SKris Kennaway 558ae1f160dSDag-Erling Smørgrav static struct timeval * 559efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 560b66f2d16SKris Kennaway { 561b66f2d16SKris Kennaway static struct timeval tv[2]; 5621e8db6e2SBrian Feldman 563b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 564b66f2d16SKris Kennaway tv[0].tv_usec = 0; 565b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 566b66f2d16SKris Kennaway tv[1].tv_usec = 0; 567b66f2d16SKris Kennaway return tv; 568b66f2d16SKris Kennaway } 569b66f2d16SKris Kennaway 570ae1f160dSDag-Erling Smørgrav static void 571b66f2d16SKris Kennaway process_setstat(void) 572b66f2d16SKris Kennaway { 573b66f2d16SKris Kennaway Attrib *a; 574b66f2d16SKris Kennaway u_int32_t id; 575b66f2d16SKris Kennaway char *name; 576ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 577b66f2d16SKris Kennaway 578b66f2d16SKris Kennaway id = get_int(); 579b66f2d16SKris Kennaway name = get_string(NULL); 580b66f2d16SKris Kennaway a = get_attrib(); 581ee21a45fSDag-Erling Smørgrav TRACE("setstat id %u name %s", id, name); 582ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 583ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 584ae1f160dSDag-Erling Smørgrav if (ret == -1) 585ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 586ae1f160dSDag-Erling Smørgrav } 5871e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 588b66f2d16SKris Kennaway ret = chmod(name, a->perm & 0777); 589b66f2d16SKris Kennaway if (ret == -1) 590b66f2d16SKris Kennaway status = errno_to_portable(errno); 591b66f2d16SKris Kennaway } 5921e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 593b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 594b66f2d16SKris Kennaway if (ret == -1) 595b66f2d16SKris Kennaway status = errno_to_portable(errno); 596b66f2d16SKris Kennaway } 5971e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 5981e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 5991e8db6e2SBrian Feldman if (ret == -1) 6001e8db6e2SBrian Feldman status = errno_to_portable(errno); 6011e8db6e2SBrian Feldman } 602b66f2d16SKris Kennaway send_status(id, status); 603b66f2d16SKris Kennaway xfree(name); 604b66f2d16SKris Kennaway } 605b66f2d16SKris Kennaway 606ae1f160dSDag-Erling Smørgrav static void 607b66f2d16SKris Kennaway process_fsetstat(void) 608b66f2d16SKris Kennaway { 609b66f2d16SKris Kennaway Attrib *a; 610b66f2d16SKris Kennaway u_int32_t id; 611b66f2d16SKris Kennaway int handle, fd, ret; 6121e8db6e2SBrian Feldman int status = SSH2_FX_OK; 61383d2307dSDag-Erling Smørgrav char *name; 614b66f2d16SKris Kennaway 615b66f2d16SKris Kennaway id = get_int(); 616b66f2d16SKris Kennaway handle = get_handle(); 617b66f2d16SKris Kennaway a = get_attrib(); 618ee21a45fSDag-Erling Smørgrav TRACE("fsetstat id %u handle %d", id, handle); 619b66f2d16SKris Kennaway fd = handle_to_fd(handle); 62083d2307dSDag-Erling Smørgrav name = handle_to_name(handle); 62183d2307dSDag-Erling Smørgrav if (fd < 0 || name == NULL) { 6221e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 623b66f2d16SKris Kennaway } else { 624ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 625ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 626ae1f160dSDag-Erling Smørgrav if (ret == -1) 627ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 628ae1f160dSDag-Erling Smørgrav } 6291e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 63083d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 631b66f2d16SKris Kennaway ret = fchmod(fd, a->perm & 0777); 63283d2307dSDag-Erling Smørgrav #else 63383d2307dSDag-Erling Smørgrav ret = chmod(name, a->perm & 0777); 63483d2307dSDag-Erling Smørgrav #endif 635b66f2d16SKris Kennaway if (ret == -1) 636b66f2d16SKris Kennaway status = errno_to_portable(errno); 637b66f2d16SKris Kennaway } 6381e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 63983d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 640b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 64183d2307dSDag-Erling Smørgrav #else 64283d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 64383d2307dSDag-Erling Smørgrav #endif 644b66f2d16SKris Kennaway if (ret == -1) 645b66f2d16SKris Kennaway status = errno_to_portable(errno); 646b66f2d16SKris Kennaway } 6471e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 64883d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 6491e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 65083d2307dSDag-Erling Smørgrav #else 65183d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 65283d2307dSDag-Erling Smørgrav #endif 6531e8db6e2SBrian Feldman if (ret == -1) 6541e8db6e2SBrian Feldman status = errno_to_portable(errno); 6551e8db6e2SBrian Feldman } 656b66f2d16SKris Kennaway } 657b66f2d16SKris Kennaway send_status(id, status); 658b66f2d16SKris Kennaway } 659b66f2d16SKris Kennaway 660ae1f160dSDag-Erling Smørgrav static void 661b66f2d16SKris Kennaway process_opendir(void) 662b66f2d16SKris Kennaway { 663b66f2d16SKris Kennaway DIR *dirp = NULL; 664b66f2d16SKris Kennaway char *path; 6651e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 666b66f2d16SKris Kennaway u_int32_t id; 667b66f2d16SKris Kennaway 668b66f2d16SKris Kennaway id = get_int(); 669b66f2d16SKris Kennaway path = get_string(NULL); 670ee21a45fSDag-Erling Smørgrav TRACE("opendir id %u path %s", id, path); 671b66f2d16SKris Kennaway dirp = opendir(path); 672b66f2d16SKris Kennaway if (dirp == NULL) { 673b66f2d16SKris Kennaway status = errno_to_portable(errno); 674b66f2d16SKris Kennaway } else { 675d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, dirp); 676b66f2d16SKris Kennaway if (handle < 0) { 677b66f2d16SKris Kennaway closedir(dirp); 678b66f2d16SKris Kennaway } else { 679b66f2d16SKris Kennaway send_handle(id, handle); 6801e8db6e2SBrian Feldman status = SSH2_FX_OK; 681b66f2d16SKris Kennaway } 682b66f2d16SKris Kennaway 683b66f2d16SKris Kennaway } 6841e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 685b66f2d16SKris Kennaway send_status(id, status); 686b66f2d16SKris Kennaway xfree(path); 687b66f2d16SKris Kennaway } 688b66f2d16SKris Kennaway 689ae1f160dSDag-Erling Smørgrav static void 690b66f2d16SKris Kennaway process_readdir(void) 691b66f2d16SKris Kennaway { 692b66f2d16SKris Kennaway DIR *dirp; 693b66f2d16SKris Kennaway struct dirent *dp; 694b66f2d16SKris Kennaway char *path; 695b66f2d16SKris Kennaway int handle; 696b66f2d16SKris Kennaway u_int32_t id; 697b66f2d16SKris Kennaway 698b66f2d16SKris Kennaway id = get_int(); 699b66f2d16SKris Kennaway handle = get_handle(); 700ee21a45fSDag-Erling Smørgrav TRACE("readdir id %u handle %d", id, handle); 701b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 702b66f2d16SKris Kennaway path = handle_to_name(handle); 703b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 7041e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 705b66f2d16SKris Kennaway } else { 706b66f2d16SKris Kennaway struct stat st; 707b66f2d16SKris Kennaway char pathname[1024]; 708b66f2d16SKris Kennaway Stat *stats; 709b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 710ee21a45fSDag-Erling Smørgrav 711b66f2d16SKris Kennaway stats = xmalloc(nstats * sizeof(Stat)); 712b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 713b66f2d16SKris Kennaway if (count >= nstats) { 714b66f2d16SKris Kennaway nstats *= 2; 715b66f2d16SKris Kennaway stats = xrealloc(stats, nstats * sizeof(Stat)); 716b66f2d16SKris Kennaway } 717b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 718ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 719ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 720b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 721b66f2d16SKris Kennaway continue; 7221e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 723b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 7244b17dab0SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0); 725b66f2d16SKris Kennaway count++; 726b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 7271e8db6e2SBrian Feldman /* XXX check packet size instead */ 728b66f2d16SKris Kennaway if (count == 100) 729b66f2d16SKris Kennaway break; 730b66f2d16SKris Kennaway } 7311e8db6e2SBrian Feldman if (count > 0) { 732b66f2d16SKris Kennaway send_names(id, count, stats); 733b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 734b66f2d16SKris Kennaway xfree(stats[i].name); 735b66f2d16SKris Kennaway xfree(stats[i].long_name); 736b66f2d16SKris Kennaway } 7371e8db6e2SBrian Feldman } else { 7381e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 7391e8db6e2SBrian Feldman } 740b66f2d16SKris Kennaway xfree(stats); 741b66f2d16SKris Kennaway } 742b66f2d16SKris Kennaway } 743b66f2d16SKris Kennaway 744ae1f160dSDag-Erling Smørgrav static void 745b66f2d16SKris Kennaway process_remove(void) 746b66f2d16SKris Kennaway { 747b66f2d16SKris Kennaway char *name; 748b66f2d16SKris Kennaway u_int32_t id; 7491e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 750b66f2d16SKris Kennaway int ret; 751b66f2d16SKris Kennaway 752b66f2d16SKris Kennaway id = get_int(); 753b66f2d16SKris Kennaway name = get_string(NULL); 754ee21a45fSDag-Erling Smørgrav TRACE("remove id %u name %s", id, name); 7551e8db6e2SBrian Feldman ret = unlink(name); 7561e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 757b66f2d16SKris Kennaway send_status(id, status); 758b66f2d16SKris Kennaway xfree(name); 759b66f2d16SKris Kennaway } 760b66f2d16SKris Kennaway 761ae1f160dSDag-Erling Smørgrav static void 762b66f2d16SKris Kennaway process_mkdir(void) 763b66f2d16SKris Kennaway { 764b66f2d16SKris Kennaway Attrib *a; 765b66f2d16SKris Kennaway u_int32_t id; 766b66f2d16SKris Kennaway char *name; 7671e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 768b66f2d16SKris Kennaway 769b66f2d16SKris Kennaway id = get_int(); 770b66f2d16SKris Kennaway name = get_string(NULL); 771b66f2d16SKris Kennaway a = get_attrib(); 7721e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 7731e8db6e2SBrian Feldman a->perm & 0777 : 0777; 774ee21a45fSDag-Erling Smørgrav TRACE("mkdir id %u name %s mode 0%o", id, name, mode); 775b66f2d16SKris Kennaway ret = mkdir(name, mode); 7761e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 777b66f2d16SKris Kennaway send_status(id, status); 778b66f2d16SKris Kennaway xfree(name); 779b66f2d16SKris Kennaway } 780b66f2d16SKris Kennaway 781ae1f160dSDag-Erling Smørgrav static void 782b66f2d16SKris Kennaway process_rmdir(void) 783b66f2d16SKris Kennaway { 784b66f2d16SKris Kennaway u_int32_t id; 785b66f2d16SKris Kennaway char *name; 786b66f2d16SKris Kennaway int ret, status; 787b66f2d16SKris Kennaway 788b66f2d16SKris Kennaway id = get_int(); 789b66f2d16SKris Kennaway name = get_string(NULL); 790ee21a45fSDag-Erling Smørgrav TRACE("rmdir id %u name %s", id, name); 791b66f2d16SKris Kennaway ret = rmdir(name); 7921e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 793b66f2d16SKris Kennaway send_status(id, status); 794b66f2d16SKris Kennaway xfree(name); 795b66f2d16SKris Kennaway } 796b66f2d16SKris Kennaway 797ae1f160dSDag-Erling Smørgrav static void 798b66f2d16SKris Kennaway process_realpath(void) 799b66f2d16SKris Kennaway { 800b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 801b66f2d16SKris Kennaway u_int32_t id; 802b66f2d16SKris Kennaway char *path; 803b66f2d16SKris Kennaway 804b66f2d16SKris Kennaway id = get_int(); 805b66f2d16SKris Kennaway path = get_string(NULL); 8061e8db6e2SBrian Feldman if (path[0] == '\0') { 8071e8db6e2SBrian Feldman xfree(path); 8081e8db6e2SBrian Feldman path = xstrdup("."); 8091e8db6e2SBrian Feldman } 810ee21a45fSDag-Erling Smørgrav TRACE("realpath id %u path %s", id, path); 811b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 812b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 813b66f2d16SKris Kennaway } else { 814b66f2d16SKris Kennaway Stat s; 815b66f2d16SKris Kennaway attrib_clear(&s.attrib); 816b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 817b66f2d16SKris Kennaway send_names(id, 1, &s); 818b66f2d16SKris Kennaway } 819b66f2d16SKris Kennaway xfree(path); 820b66f2d16SKris Kennaway } 821b66f2d16SKris Kennaway 822ae1f160dSDag-Erling Smørgrav static void 823b66f2d16SKris Kennaway process_rename(void) 824b66f2d16SKris Kennaway { 825b66f2d16SKris Kennaway u_int32_t id; 826b66f2d16SKris Kennaway char *oldpath, *newpath; 827d0c8c0bcSDag-Erling Smørgrav int status; 828d0c8c0bcSDag-Erling Smørgrav struct stat sb; 829b66f2d16SKris Kennaway 830b66f2d16SKris Kennaway id = get_int(); 831b66f2d16SKris Kennaway oldpath = get_string(NULL); 832b66f2d16SKris Kennaway newpath = get_string(NULL); 833ee21a45fSDag-Erling Smørgrav TRACE("rename id %u old %s new %s", id, oldpath, newpath); 834d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 835d0c8c0bcSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 836d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 837d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 838d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 839d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 840d74d50a8SDag-Erling Smørgrav if (errno == EOPNOTSUPP 841d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 842d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 843d74d50a8SDag-Erling Smørgrav #endif 844d74d50a8SDag-Erling Smørgrav ) { 845d74d50a8SDag-Erling Smørgrav struct stat st; 846d74d50a8SDag-Erling Smørgrav 847d74d50a8SDag-Erling Smørgrav /* 848d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 849d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 850d74d50a8SDag-Erling Smørgrav */ 851d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 852d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 853d74d50a8SDag-Erling Smørgrav status = 854d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 855d74d50a8SDag-Erling Smørgrav else 856d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 857d74d50a8SDag-Erling Smørgrav } 858d74d50a8SDag-Erling Smørgrav } else { 859d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 860d74d50a8SDag-Erling Smørgrav } 861d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 862d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 863d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 864d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 865d0c8c0bcSDag-Erling Smørgrav } else 866d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 867d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 868d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 869d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 870d0c8c0bcSDag-Erling Smørgrav else 871d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 8721e8db6e2SBrian Feldman } 873b66f2d16SKris Kennaway send_status(id, status); 874b66f2d16SKris Kennaway xfree(oldpath); 875b66f2d16SKris Kennaway xfree(newpath); 876b66f2d16SKris Kennaway } 877b66f2d16SKris Kennaway 878ae1f160dSDag-Erling Smørgrav static void 8791e8db6e2SBrian Feldman process_readlink(void) 8801e8db6e2SBrian Feldman { 8811e8db6e2SBrian Feldman u_int32_t id; 882ae1f160dSDag-Erling Smørgrav int len; 883d74d50a8SDag-Erling Smørgrav char buf[MAXPATHLEN]; 8841e8db6e2SBrian Feldman char *path; 8851e8db6e2SBrian Feldman 8861e8db6e2SBrian Feldman id = get_int(); 8871e8db6e2SBrian Feldman path = get_string(NULL); 888ee21a45fSDag-Erling Smørgrav TRACE("readlink id %u path %s", id, path); 889d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 8901e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 8911e8db6e2SBrian Feldman else { 8921e8db6e2SBrian Feldman Stat s; 8931e8db6e2SBrian Feldman 894d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 8951e8db6e2SBrian Feldman attrib_clear(&s.attrib); 896d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 8971e8db6e2SBrian Feldman send_names(id, 1, &s); 8981e8db6e2SBrian Feldman } 8991e8db6e2SBrian Feldman xfree(path); 9001e8db6e2SBrian Feldman } 9011e8db6e2SBrian Feldman 902ae1f160dSDag-Erling Smørgrav static void 9031e8db6e2SBrian Feldman process_symlink(void) 9041e8db6e2SBrian Feldman { 9051e8db6e2SBrian Feldman u_int32_t id; 9061e8db6e2SBrian Feldman char *oldpath, *newpath; 907d0c8c0bcSDag-Erling Smørgrav int ret, status; 9081e8db6e2SBrian Feldman 9091e8db6e2SBrian Feldman id = get_int(); 9101e8db6e2SBrian Feldman oldpath = get_string(NULL); 9111e8db6e2SBrian Feldman newpath = get_string(NULL); 912ee21a45fSDag-Erling Smørgrav TRACE("symlink id %u old %s new %s", id, oldpath, newpath); 913d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 9141e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 9151e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9161e8db6e2SBrian Feldman send_status(id, status); 9171e8db6e2SBrian Feldman xfree(oldpath); 9181e8db6e2SBrian Feldman xfree(newpath); 9191e8db6e2SBrian Feldman } 9201e8db6e2SBrian Feldman 921ae1f160dSDag-Erling Smørgrav static void 9221e8db6e2SBrian Feldman process_extended(void) 9231e8db6e2SBrian Feldman { 9241e8db6e2SBrian Feldman u_int32_t id; 9251e8db6e2SBrian Feldman char *request; 9261e8db6e2SBrian Feldman 9271e8db6e2SBrian Feldman id = get_int(); 9281e8db6e2SBrian Feldman request = get_string(NULL); 9291e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 9301e8db6e2SBrian Feldman xfree(request); 9311e8db6e2SBrian Feldman } 932b66f2d16SKris Kennaway 933b66f2d16SKris Kennaway /* stolen from ssh-agent */ 934b66f2d16SKris Kennaway 935ae1f160dSDag-Erling Smørgrav static void 936b66f2d16SKris Kennaway process(void) 937b66f2d16SKris Kennaway { 9381e8db6e2SBrian Feldman u_int msg_len; 939545d5ecaSDag-Erling Smørgrav u_int buf_len; 940545d5ecaSDag-Erling Smørgrav u_int consumed; 9411e8db6e2SBrian Feldman u_int type; 9421e8db6e2SBrian Feldman u_char *cp; 943b66f2d16SKris Kennaway 944545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 945545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 946b66f2d16SKris Kennaway return; /* Incomplete message. */ 947ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 948b66f2d16SKris Kennaway msg_len = GET_32BIT(cp); 949021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 950b66f2d16SKris Kennaway error("bad message "); 951b66f2d16SKris Kennaway exit(11); 952b66f2d16SKris Kennaway } 953545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 954b66f2d16SKris Kennaway return; 955b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 956545d5ecaSDag-Erling Smørgrav buf_len -= 4; 957b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 958b66f2d16SKris Kennaway switch (type) { 9591e8db6e2SBrian Feldman case SSH2_FXP_INIT: 960b66f2d16SKris Kennaway process_init(); 961b66f2d16SKris Kennaway break; 9621e8db6e2SBrian Feldman case SSH2_FXP_OPEN: 963b66f2d16SKris Kennaway process_open(); 964b66f2d16SKris Kennaway break; 9651e8db6e2SBrian Feldman case SSH2_FXP_CLOSE: 966b66f2d16SKris Kennaway process_close(); 967b66f2d16SKris Kennaway break; 9681e8db6e2SBrian Feldman case SSH2_FXP_READ: 969b66f2d16SKris Kennaway process_read(); 970b66f2d16SKris Kennaway break; 9711e8db6e2SBrian Feldman case SSH2_FXP_WRITE: 972b66f2d16SKris Kennaway process_write(); 973b66f2d16SKris Kennaway break; 9741e8db6e2SBrian Feldman case SSH2_FXP_LSTAT: 975b66f2d16SKris Kennaway process_lstat(); 976b66f2d16SKris Kennaway break; 9771e8db6e2SBrian Feldman case SSH2_FXP_FSTAT: 978b66f2d16SKris Kennaway process_fstat(); 979b66f2d16SKris Kennaway break; 9801e8db6e2SBrian Feldman case SSH2_FXP_SETSTAT: 981b66f2d16SKris Kennaway process_setstat(); 982b66f2d16SKris Kennaway break; 9831e8db6e2SBrian Feldman case SSH2_FXP_FSETSTAT: 984b66f2d16SKris Kennaway process_fsetstat(); 985b66f2d16SKris Kennaway break; 9861e8db6e2SBrian Feldman case SSH2_FXP_OPENDIR: 987b66f2d16SKris Kennaway process_opendir(); 988b66f2d16SKris Kennaway break; 9891e8db6e2SBrian Feldman case SSH2_FXP_READDIR: 990b66f2d16SKris Kennaway process_readdir(); 991b66f2d16SKris Kennaway break; 9921e8db6e2SBrian Feldman case SSH2_FXP_REMOVE: 993b66f2d16SKris Kennaway process_remove(); 994b66f2d16SKris Kennaway break; 9951e8db6e2SBrian Feldman case SSH2_FXP_MKDIR: 996b66f2d16SKris Kennaway process_mkdir(); 997b66f2d16SKris Kennaway break; 9981e8db6e2SBrian Feldman case SSH2_FXP_RMDIR: 999b66f2d16SKris Kennaway process_rmdir(); 1000b66f2d16SKris Kennaway break; 10011e8db6e2SBrian Feldman case SSH2_FXP_REALPATH: 1002b66f2d16SKris Kennaway process_realpath(); 1003b66f2d16SKris Kennaway break; 10041e8db6e2SBrian Feldman case SSH2_FXP_STAT: 1005b66f2d16SKris Kennaway process_stat(); 1006b66f2d16SKris Kennaway break; 10071e8db6e2SBrian Feldman case SSH2_FXP_RENAME: 1008b66f2d16SKris Kennaway process_rename(); 1009b66f2d16SKris Kennaway break; 10101e8db6e2SBrian Feldman case SSH2_FXP_READLINK: 10111e8db6e2SBrian Feldman process_readlink(); 10121e8db6e2SBrian Feldman break; 10131e8db6e2SBrian Feldman case SSH2_FXP_SYMLINK: 10141e8db6e2SBrian Feldman process_symlink(); 10151e8db6e2SBrian Feldman break; 10161e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 10171e8db6e2SBrian Feldman process_extended(); 10181e8db6e2SBrian Feldman break; 1019b66f2d16SKris Kennaway default: 1020b66f2d16SKris Kennaway error("Unknown message %d", type); 1021b66f2d16SKris Kennaway break; 1022b66f2d16SKris Kennaway } 1023545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1024545d5ecaSDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) 1025545d5ecaSDag-Erling Smørgrav fatal("iqueue grows"); 1026545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1027545d5ecaSDag-Erling Smørgrav if (msg_len < consumed) 1028545d5ecaSDag-Erling Smørgrav fatal("msg_len %d < consumed %d", msg_len, consumed); 1029545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1030545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1031b66f2d16SKris Kennaway } 1032b66f2d16SKris Kennaway 1033b66f2d16SKris Kennaway int 1034b66f2d16SKris Kennaway main(int ac, char **av) 1035b66f2d16SKris Kennaway { 10361e8db6e2SBrian Feldman fd_set *rset, *wset; 1037b66f2d16SKris Kennaway int in, out, max; 10381e8db6e2SBrian Feldman ssize_t len, olen, set_size; 10391e8db6e2SBrian Feldman 1040021d409fSDag-Erling Smørgrav /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1041021d409fSDag-Erling Smørgrav sanitise_stdfd(); 1042021d409fSDag-Erling Smørgrav 10431e8db6e2SBrian Feldman /* XXX should use getopt */ 1044b66f2d16SKris Kennaway 1045d95e11bfSDag-Erling Smørgrav __progname = ssh_get_progname(av[0]); 1046b66f2d16SKris Kennaway handle_init(); 1047b66f2d16SKris Kennaway 10481e8db6e2SBrian Feldman #ifdef DEBUG_SFTP_SERVER 10491e8db6e2SBrian Feldman log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 10501e8db6e2SBrian Feldman #endif 10511e8db6e2SBrian Feldman 1052b66f2d16SKris Kennaway in = dup(STDIN_FILENO); 1053b66f2d16SKris Kennaway out = dup(STDOUT_FILENO); 1054b66f2d16SKris Kennaway 105583d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 105683d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 105783d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 105883d2307dSDag-Erling Smørgrav #endif 105983d2307dSDag-Erling Smørgrav 1060b66f2d16SKris Kennaway max = 0; 1061b66f2d16SKris Kennaway if (in > max) 1062b66f2d16SKris Kennaway max = in; 1063b66f2d16SKris Kennaway if (out > max) 1064b66f2d16SKris Kennaway max = out; 1065b66f2d16SKris Kennaway 1066b66f2d16SKris Kennaway buffer_init(&iqueue); 1067b66f2d16SKris Kennaway buffer_init(&oqueue); 1068b66f2d16SKris Kennaway 10691e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 10701e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 10711e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1072b66f2d16SKris Kennaway 10731e8db6e2SBrian Feldman for (;;) { 10741e8db6e2SBrian Feldman memset(rset, 0, set_size); 10751e8db6e2SBrian Feldman memset(wset, 0, set_size); 10761e8db6e2SBrian Feldman 10771e8db6e2SBrian Feldman FD_SET(in, rset); 1078b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1079b66f2d16SKris Kennaway if (olen > 0) 10801e8db6e2SBrian Feldman FD_SET(out, wset); 1081b66f2d16SKris Kennaway 10821e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1083b66f2d16SKris Kennaway if (errno == EINTR) 1084b66f2d16SKris Kennaway continue; 1085b66f2d16SKris Kennaway exit(2); 1086b66f2d16SKris Kennaway } 1087b66f2d16SKris Kennaway 1088b66f2d16SKris Kennaway /* copy stdin to iqueue */ 10891e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1090b66f2d16SKris Kennaway char buf[4*4096]; 1091b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1092b66f2d16SKris Kennaway if (len == 0) { 1093b66f2d16SKris Kennaway debug("read eof"); 1094b66f2d16SKris Kennaway exit(0); 1095b66f2d16SKris Kennaway } else if (len < 0) { 1096b66f2d16SKris Kennaway error("read error"); 1097b66f2d16SKris Kennaway exit(1); 1098b66f2d16SKris Kennaway } else { 1099b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1100b66f2d16SKris Kennaway } 1101b66f2d16SKris Kennaway } 1102b66f2d16SKris Kennaway /* send oqueue to stdout */ 11031e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1104b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1105b66f2d16SKris Kennaway if (len < 0) { 1106b66f2d16SKris Kennaway error("write error"); 1107b66f2d16SKris Kennaway exit(1); 1108b66f2d16SKris Kennaway } else { 1109b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1110b66f2d16SKris Kennaway } 1111b66f2d16SKris Kennaway } 1112b66f2d16SKris Kennaway /* process requests from client */ 1113b66f2d16SKris Kennaway process(); 1114b66f2d16SKris Kennaway } 1115b66f2d16SKris Kennaway } 1116