10Sstevel@tonic-gate /* 2*5087Sjp161948 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3*5087Sjp161948 * 4*5087Sjp161948 * Permission to use, copy, modify, and distribute this software for any 5*5087Sjp161948 * purpose with or without fee is hereby granted, provided that the above 6*5087Sjp161948 * copyright notice and this permission notice appear in all copies. 7*5087Sjp161948 * 8*5087Sjp161948 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9*5087Sjp161948 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10*5087Sjp161948 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11*5087Sjp161948 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12*5087Sjp161948 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13*5087Sjp161948 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14*5087Sjp161948 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 150Sstevel@tonic-gate */ 160Sstevel@tonic-gate 17*5087Sjp161948 /* $OpenBSD: sftp-server.c,v 1.71 2007/01/03 07:22:36 stevesk Exp $ */ 180Sstevel@tonic-gate 190Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 200Sstevel@tonic-gate 21*5087Sjp161948 #include "includes.h" 22*5087Sjp161948 23*5087Sjp161948 #include <sys/types.h> 24*5087Sjp161948 #include <sys/param.h> 25*5087Sjp161948 #include <sys/stat.h> 26*5087Sjp161948 #ifdef HAVE_SYS_TIME_H 27*5087Sjp161948 # include <sys/time.h> 28*5087Sjp161948 #endif 29*5087Sjp161948 30*5087Sjp161948 #include <dirent.h> 31*5087Sjp161948 #include <errno.h> 32*5087Sjp161948 #include <fcntl.h> 33*5087Sjp161948 #include <pwd.h> 34*5087Sjp161948 #include <stdlib.h> 35*5087Sjp161948 #include <stdio.h> 36*5087Sjp161948 #include <string.h> 37*5087Sjp161948 #include <pwd.h> 38*5087Sjp161948 #include <time.h> 39*5087Sjp161948 #include <unistd.h> 40*5087Sjp161948 #include <stdarg.h> 41*5087Sjp161948 42*5087Sjp161948 #include "xmalloc.h" 430Sstevel@tonic-gate #include "buffer.h" 440Sstevel@tonic-gate #include "bufaux.h" 450Sstevel@tonic-gate #include "log.h" 46*5087Sjp161948 #include "misc.h" 47*5087Sjp161948 #include "uidswap.h" 480Sstevel@tonic-gate 490Sstevel@tonic-gate #include "sftp.h" 500Sstevel@tonic-gate #include "sftp-common.h" 510Sstevel@tonic-gate 520Sstevel@tonic-gate #ifdef HAVE___PROGNAME 530Sstevel@tonic-gate extern char *__progname; 540Sstevel@tonic-gate #else 550Sstevel@tonic-gate char *__progname; 560Sstevel@tonic-gate #endif 570Sstevel@tonic-gate 58*5087Sjp161948 /* helper */ 59*5087Sjp161948 #define get_int64() buffer_get_int64(&iqueue); 60*5087Sjp161948 #define get_int() buffer_get_int(&iqueue); 61*5087Sjp161948 #define get_string(lenp) buffer_get_string(&iqueue, lenp); 62*5087Sjp161948 63*5087Sjp161948 static void cleanup_exit(int i); 64*5087Sjp161948 65*5087Sjp161948 /* Our verbosity */ 66*5087Sjp161948 LogLevel log_level = SYSLOG_LEVEL_ERROR; 67*5087Sjp161948 68*5087Sjp161948 /* Our client */ 69*5087Sjp161948 struct passwd *pw = NULL; 70*5087Sjp161948 char *client_addr = NULL; 71*5087Sjp161948 720Sstevel@tonic-gate /* input and output queue */ 730Sstevel@tonic-gate Buffer iqueue; 740Sstevel@tonic-gate Buffer oqueue; 750Sstevel@tonic-gate 760Sstevel@tonic-gate /* Version of client */ 770Sstevel@tonic-gate int version; 780Sstevel@tonic-gate 79*5087Sjp161948 /* portable attributes, etc. */ 800Sstevel@tonic-gate 810Sstevel@tonic-gate typedef struct Stat Stat; 820Sstevel@tonic-gate 830Sstevel@tonic-gate struct Stat { 840Sstevel@tonic-gate char *name; 850Sstevel@tonic-gate char *long_name; 860Sstevel@tonic-gate Attrib attrib; 870Sstevel@tonic-gate }; 880Sstevel@tonic-gate 890Sstevel@tonic-gate static int 900Sstevel@tonic-gate errno_to_portable(int unixerrno) 910Sstevel@tonic-gate { 920Sstevel@tonic-gate int ret = 0; 930Sstevel@tonic-gate 940Sstevel@tonic-gate switch (unixerrno) { 950Sstevel@tonic-gate case 0: 960Sstevel@tonic-gate ret = SSH2_FX_OK; 970Sstevel@tonic-gate break; 980Sstevel@tonic-gate case ENOENT: 990Sstevel@tonic-gate case ENOTDIR: 1000Sstevel@tonic-gate case EBADF: 1010Sstevel@tonic-gate case ELOOP: 1020Sstevel@tonic-gate ret = SSH2_FX_NO_SUCH_FILE; 1030Sstevel@tonic-gate break; 1040Sstevel@tonic-gate case EPERM: 1050Sstevel@tonic-gate case EACCES: 1060Sstevel@tonic-gate case EFAULT: 1070Sstevel@tonic-gate ret = SSH2_FX_PERMISSION_DENIED; 1080Sstevel@tonic-gate break; 1090Sstevel@tonic-gate case ENAMETOOLONG: 1100Sstevel@tonic-gate case EINVAL: 1110Sstevel@tonic-gate ret = SSH2_FX_BAD_MESSAGE; 1120Sstevel@tonic-gate break; 1130Sstevel@tonic-gate default: 1140Sstevel@tonic-gate ret = SSH2_FX_FAILURE; 1150Sstevel@tonic-gate break; 1160Sstevel@tonic-gate } 1170Sstevel@tonic-gate return ret; 1180Sstevel@tonic-gate } 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate static int 1210Sstevel@tonic-gate flags_from_portable(int pflags) 1220Sstevel@tonic-gate { 1230Sstevel@tonic-gate int flags = 0; 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate if ((pflags & SSH2_FXF_READ) && 1260Sstevel@tonic-gate (pflags & SSH2_FXF_WRITE)) { 1270Sstevel@tonic-gate flags = O_RDWR; 1280Sstevel@tonic-gate } else if (pflags & SSH2_FXF_READ) { 1290Sstevel@tonic-gate flags = O_RDONLY; 1300Sstevel@tonic-gate } else if (pflags & SSH2_FXF_WRITE) { 1310Sstevel@tonic-gate flags = O_WRONLY; 1320Sstevel@tonic-gate } 1330Sstevel@tonic-gate if (pflags & SSH2_FXF_CREAT) 1340Sstevel@tonic-gate flags |= O_CREAT; 1350Sstevel@tonic-gate if (pflags & SSH2_FXF_TRUNC) 1360Sstevel@tonic-gate flags |= O_TRUNC; 1370Sstevel@tonic-gate if (pflags & SSH2_FXF_EXCL) 1380Sstevel@tonic-gate flags |= O_EXCL; 1390Sstevel@tonic-gate return flags; 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 142*5087Sjp161948 static const char * 143*5087Sjp161948 string_from_portable(int pflags) 144*5087Sjp161948 { 145*5087Sjp161948 static char ret[128]; 146*5087Sjp161948 147*5087Sjp161948 *ret = '\0'; 148*5087Sjp161948 149*5087Sjp161948 #define PAPPEND(str) { \ 150*5087Sjp161948 if (*ret != '\0') \ 151*5087Sjp161948 strlcat(ret, ",", sizeof(ret)); \ 152*5087Sjp161948 strlcat(ret, str, sizeof(ret)); \ 153*5087Sjp161948 } 154*5087Sjp161948 155*5087Sjp161948 if (pflags & SSH2_FXF_READ) 156*5087Sjp161948 PAPPEND("READ") 157*5087Sjp161948 if (pflags & SSH2_FXF_WRITE) 158*5087Sjp161948 PAPPEND("WRITE") 159*5087Sjp161948 if (pflags & SSH2_FXF_CREAT) 160*5087Sjp161948 PAPPEND("CREATE") 161*5087Sjp161948 if (pflags & SSH2_FXF_TRUNC) 162*5087Sjp161948 PAPPEND("TRUNCATE") 163*5087Sjp161948 if (pflags & SSH2_FXF_EXCL) 164*5087Sjp161948 PAPPEND("EXCL") 165*5087Sjp161948 166*5087Sjp161948 return ret; 167*5087Sjp161948 } 168*5087Sjp161948 1690Sstevel@tonic-gate static Attrib * 1700Sstevel@tonic-gate get_attrib(void) 1710Sstevel@tonic-gate { 1720Sstevel@tonic-gate return decode_attrib(&iqueue); 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate /* handle handles */ 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate typedef struct Handle Handle; 1780Sstevel@tonic-gate struct Handle { 1790Sstevel@tonic-gate int use; 1800Sstevel@tonic-gate DIR *dirp; 1810Sstevel@tonic-gate int fd; 1820Sstevel@tonic-gate char *name; 183*5087Sjp161948 u_int64_t bytes_read, bytes_write; 1840Sstevel@tonic-gate }; 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate enum { 1870Sstevel@tonic-gate HANDLE_UNUSED, 1880Sstevel@tonic-gate HANDLE_DIR, 1890Sstevel@tonic-gate HANDLE_FILE 1900Sstevel@tonic-gate }; 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate Handle handles[100]; 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate static void 1950Sstevel@tonic-gate handle_init(void) 1960Sstevel@tonic-gate { 197*5087Sjp161948 u_int i; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 2000Sstevel@tonic-gate handles[i].use = HANDLE_UNUSED; 2010Sstevel@tonic-gate } 2020Sstevel@tonic-gate 2030Sstevel@tonic-gate static int 204*5087Sjp161948 handle_new(int use, const char *name, int fd, DIR *dirp) 2050Sstevel@tonic-gate { 206*5087Sjp161948 u_int i; 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 2090Sstevel@tonic-gate if (handles[i].use == HANDLE_UNUSED) { 2100Sstevel@tonic-gate handles[i].use = use; 2110Sstevel@tonic-gate handles[i].dirp = dirp; 2120Sstevel@tonic-gate handles[i].fd = fd; 213*5087Sjp161948 handles[i].name = xstrdup(name); 214*5087Sjp161948 handles[i].bytes_read = handles[i].bytes_write = 0; 2150Sstevel@tonic-gate return i; 2160Sstevel@tonic-gate } 2170Sstevel@tonic-gate } 2180Sstevel@tonic-gate return -1; 2190Sstevel@tonic-gate } 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate static int 2220Sstevel@tonic-gate handle_is_ok(int i, int type) 2230Sstevel@tonic-gate { 224*5087Sjp161948 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) && 2250Sstevel@tonic-gate handles[i].use == type; 2260Sstevel@tonic-gate } 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate static int 2290Sstevel@tonic-gate handle_to_string(int handle, char **stringp, int *hlenp) 2300Sstevel@tonic-gate { 2310Sstevel@tonic-gate if (stringp == NULL || hlenp == NULL) 2320Sstevel@tonic-gate return -1; 2330Sstevel@tonic-gate *stringp = xmalloc(sizeof(int32_t)); 234*5087Sjp161948 put_u32(*stringp, handle); 2350Sstevel@tonic-gate *hlenp = sizeof(int32_t); 2360Sstevel@tonic-gate return 0; 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate static int 240*5087Sjp161948 handle_from_string(const char *handle, u_int hlen) 2410Sstevel@tonic-gate { 2420Sstevel@tonic-gate int val; 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate if (hlen != sizeof(int32_t)) 2450Sstevel@tonic-gate return -1; 246*5087Sjp161948 val = get_u32(handle); 2470Sstevel@tonic-gate if (handle_is_ok(val, HANDLE_FILE) || 2480Sstevel@tonic-gate handle_is_ok(val, HANDLE_DIR)) 2490Sstevel@tonic-gate return val; 2500Sstevel@tonic-gate return -1; 2510Sstevel@tonic-gate } 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate static char * 2540Sstevel@tonic-gate handle_to_name(int handle) 2550Sstevel@tonic-gate { 2560Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_DIR)|| 2570Sstevel@tonic-gate handle_is_ok(handle, HANDLE_FILE)) 2580Sstevel@tonic-gate return handles[handle].name; 2590Sstevel@tonic-gate return NULL; 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate static DIR * 2630Sstevel@tonic-gate handle_to_dir(int handle) 2640Sstevel@tonic-gate { 2650Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_DIR)) 2660Sstevel@tonic-gate return handles[handle].dirp; 2670Sstevel@tonic-gate return NULL; 2680Sstevel@tonic-gate } 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate static int 2710Sstevel@tonic-gate handle_to_fd(int handle) 2720Sstevel@tonic-gate { 2730Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_FILE)) 2740Sstevel@tonic-gate return handles[handle].fd; 2750Sstevel@tonic-gate return -1; 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate 278*5087Sjp161948 static void 279*5087Sjp161948 handle_update_read(int handle, ssize_t bytes) 280*5087Sjp161948 { 281*5087Sjp161948 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 282*5087Sjp161948 handles[handle].bytes_read += bytes; 283*5087Sjp161948 } 284*5087Sjp161948 285*5087Sjp161948 static void 286*5087Sjp161948 handle_update_write(int handle, ssize_t bytes) 287*5087Sjp161948 { 288*5087Sjp161948 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 289*5087Sjp161948 handles[handle].bytes_write += bytes; 290*5087Sjp161948 } 291*5087Sjp161948 292*5087Sjp161948 static u_int64_t 293*5087Sjp161948 handle_bytes_read(int handle) 294*5087Sjp161948 { 295*5087Sjp161948 if (handle_is_ok(handle, HANDLE_FILE)) 296*5087Sjp161948 return (handles[handle].bytes_read); 297*5087Sjp161948 return 0; 298*5087Sjp161948 } 299*5087Sjp161948 300*5087Sjp161948 static u_int64_t 301*5087Sjp161948 handle_bytes_write(int handle) 302*5087Sjp161948 { 303*5087Sjp161948 if (handle_is_ok(handle, HANDLE_FILE)) 304*5087Sjp161948 return (handles[handle].bytes_write); 305*5087Sjp161948 return 0; 306*5087Sjp161948 } 307*5087Sjp161948 3080Sstevel@tonic-gate static int 3090Sstevel@tonic-gate handle_close(int handle) 3100Sstevel@tonic-gate { 3110Sstevel@tonic-gate int ret = -1; 3120Sstevel@tonic-gate 3130Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_FILE)) { 3140Sstevel@tonic-gate ret = close(handles[handle].fd); 3150Sstevel@tonic-gate handles[handle].use = HANDLE_UNUSED; 316*5087Sjp161948 xfree(handles[handle].name); 3170Sstevel@tonic-gate } else if (handle_is_ok(handle, HANDLE_DIR)) { 3180Sstevel@tonic-gate ret = closedir(handles[handle].dirp); 3190Sstevel@tonic-gate handles[handle].use = HANDLE_UNUSED; 320*5087Sjp161948 xfree(handles[handle].name); 3210Sstevel@tonic-gate } else { 3220Sstevel@tonic-gate errno = ENOENT; 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate return ret; 3250Sstevel@tonic-gate } 3260Sstevel@tonic-gate 327*5087Sjp161948 static void 328*5087Sjp161948 handle_log_close(int handle, char *emsg) 329*5087Sjp161948 { 330*5087Sjp161948 if (handle_is_ok(handle, HANDLE_FILE)) { 331*5087Sjp161948 log("%s%sclose \"%s\" bytes read %llu written %llu", 332*5087Sjp161948 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 333*5087Sjp161948 handle_to_name(handle), 334*5087Sjp161948 (unsigned long long)handle_bytes_read(handle), 335*5087Sjp161948 (unsigned long long)handle_bytes_write(handle)); 336*5087Sjp161948 } else { 337*5087Sjp161948 log("%s%sclosedir \"%s\"", 338*5087Sjp161948 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 339*5087Sjp161948 handle_to_name(handle)); 340*5087Sjp161948 } 341*5087Sjp161948 } 342*5087Sjp161948 343*5087Sjp161948 static void 344*5087Sjp161948 handle_log_exit(void) 345*5087Sjp161948 { 346*5087Sjp161948 u_int i; 347*5087Sjp161948 348*5087Sjp161948 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 349*5087Sjp161948 if (handles[i].use != HANDLE_UNUSED) 350*5087Sjp161948 handle_log_close(i, "forced"); 351*5087Sjp161948 } 352*5087Sjp161948 3530Sstevel@tonic-gate static int 3540Sstevel@tonic-gate get_handle(void) 3550Sstevel@tonic-gate { 3560Sstevel@tonic-gate char *handle; 3570Sstevel@tonic-gate int val = -1; 3580Sstevel@tonic-gate u_int hlen; 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate handle = get_string(&hlen); 3610Sstevel@tonic-gate if (hlen < 256) 3620Sstevel@tonic-gate val = handle_from_string(handle, hlen); 3630Sstevel@tonic-gate xfree(handle); 3640Sstevel@tonic-gate return val; 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate /* send replies */ 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate static void 3700Sstevel@tonic-gate send_msg(Buffer *m) 3710Sstevel@tonic-gate { 3720Sstevel@tonic-gate int mlen = buffer_len(m); 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate buffer_put_int(&oqueue, mlen); 3750Sstevel@tonic-gate buffer_append(&oqueue, buffer_ptr(m), mlen); 3760Sstevel@tonic-gate buffer_consume(m, mlen); 3770Sstevel@tonic-gate } 3780Sstevel@tonic-gate 379*5087Sjp161948 static const char * 380*5087Sjp161948 status_to_message(u_int32_t status) 3810Sstevel@tonic-gate { 3820Sstevel@tonic-gate const char *status_messages[] = { 3830Sstevel@tonic-gate "Success", /* SSH_FX_OK */ 3840Sstevel@tonic-gate "End of file", /* SSH_FX_EOF */ 3850Sstevel@tonic-gate "No such file", /* SSH_FX_NO_SUCH_FILE */ 3860Sstevel@tonic-gate "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 3870Sstevel@tonic-gate "Failure", /* SSH_FX_FAILURE */ 3880Sstevel@tonic-gate "Bad message", /* SSH_FX_BAD_MESSAGE */ 3890Sstevel@tonic-gate "No connection", /* SSH_FX_NO_CONNECTION */ 3900Sstevel@tonic-gate "Connection lost", /* SSH_FX_CONNECTION_LOST */ 3910Sstevel@tonic-gate "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 3920Sstevel@tonic-gate "Unknown error" /* Others */ 3930Sstevel@tonic-gate }; 394*5087Sjp161948 return (status_messages[MIN(status,SSH2_FX_MAX)]); 395*5087Sjp161948 } 3960Sstevel@tonic-gate 397*5087Sjp161948 static void 398*5087Sjp161948 send_status(u_int32_t id, u_int32_t status) 399*5087Sjp161948 { 400*5087Sjp161948 Buffer msg; 401*5087Sjp161948 402*5087Sjp161948 debug3("request %u: sent status %u", id, status); 403*5087Sjp161948 if (log_level > SYSLOG_LEVEL_VERBOSE || 404*5087Sjp161948 (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 405*5087Sjp161948 log("sent status %s", status_to_message(status)); 4060Sstevel@tonic-gate buffer_init(&msg); 4070Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_STATUS); 4080Sstevel@tonic-gate buffer_put_int(&msg, id); 409*5087Sjp161948 buffer_put_int(&msg, status); 4100Sstevel@tonic-gate if (version >= 3) { 411*5087Sjp161948 buffer_put_cstring(&msg, status_to_message(status)); 4120Sstevel@tonic-gate buffer_put_cstring(&msg, ""); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate send_msg(&msg); 4150Sstevel@tonic-gate buffer_free(&msg); 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate static void 418*5087Sjp161948 send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 4190Sstevel@tonic-gate { 4200Sstevel@tonic-gate Buffer msg; 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate buffer_init(&msg); 4230Sstevel@tonic-gate buffer_put_char(&msg, type); 4240Sstevel@tonic-gate buffer_put_int(&msg, id); 4250Sstevel@tonic-gate buffer_put_string(&msg, data, dlen); 4260Sstevel@tonic-gate send_msg(&msg); 4270Sstevel@tonic-gate buffer_free(&msg); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate static void 431*5087Sjp161948 send_data(u_int32_t id, const char *data, int dlen) 4320Sstevel@tonic-gate { 433*5087Sjp161948 debug("request %u: sent data len %d", id, dlen); 4340Sstevel@tonic-gate send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 4350Sstevel@tonic-gate } 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate static void 4380Sstevel@tonic-gate send_handle(u_int32_t id, int handle) 4390Sstevel@tonic-gate { 4400Sstevel@tonic-gate char *string; 4410Sstevel@tonic-gate int hlen; 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate handle_to_string(handle, &string, &hlen); 444*5087Sjp161948 debug("request %u: sent handle handle %d", id, handle); 4450Sstevel@tonic-gate send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 4460Sstevel@tonic-gate xfree(string); 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate static void 450*5087Sjp161948 send_names(u_int32_t id, int count, const Stat *stats) 4510Sstevel@tonic-gate { 4520Sstevel@tonic-gate Buffer msg; 4530Sstevel@tonic-gate int i; 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate buffer_init(&msg); 4560Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_NAME); 4570Sstevel@tonic-gate buffer_put_int(&msg, id); 4580Sstevel@tonic-gate buffer_put_int(&msg, count); 459*5087Sjp161948 debug("request %u: sent names count %d", id, count); 4600Sstevel@tonic-gate for (i = 0; i < count; i++) { 4610Sstevel@tonic-gate buffer_put_cstring(&msg, stats[i].name); 4620Sstevel@tonic-gate buffer_put_cstring(&msg, stats[i].long_name); 4630Sstevel@tonic-gate encode_attrib(&msg, &stats[i].attrib); 4640Sstevel@tonic-gate } 4650Sstevel@tonic-gate send_msg(&msg); 4660Sstevel@tonic-gate buffer_free(&msg); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate static void 470*5087Sjp161948 send_attrib(u_int32_t id, const Attrib *a) 4710Sstevel@tonic-gate { 4720Sstevel@tonic-gate Buffer msg; 4730Sstevel@tonic-gate 474*5087Sjp161948 debug("request %u: sent attrib have 0x%x", id, a->flags); 4750Sstevel@tonic-gate buffer_init(&msg); 4760Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_ATTRS); 4770Sstevel@tonic-gate buffer_put_int(&msg, id); 4780Sstevel@tonic-gate encode_attrib(&msg, a); 4790Sstevel@tonic-gate send_msg(&msg); 4800Sstevel@tonic-gate buffer_free(&msg); 4810Sstevel@tonic-gate } 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate /* parse incoming */ 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate static void 4860Sstevel@tonic-gate process_init(void) 4870Sstevel@tonic-gate { 4880Sstevel@tonic-gate Buffer msg; 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate version = get_int(); 491*5087Sjp161948 verbose("received client version %d", version); 4920Sstevel@tonic-gate buffer_init(&msg); 4930Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_VERSION); 4940Sstevel@tonic-gate buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 4950Sstevel@tonic-gate send_msg(&msg); 4960Sstevel@tonic-gate buffer_free(&msg); 4970Sstevel@tonic-gate } 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate static void 5000Sstevel@tonic-gate process_open(void) 5010Sstevel@tonic-gate { 5020Sstevel@tonic-gate u_int32_t id, pflags; 5030Sstevel@tonic-gate Attrib *a; 5040Sstevel@tonic-gate char *name; 5050Sstevel@tonic-gate int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 5060Sstevel@tonic-gate 5070Sstevel@tonic-gate id = get_int(); 5080Sstevel@tonic-gate name = get_string(NULL); 5090Sstevel@tonic-gate pflags = get_int(); /* portable flags */ 510*5087Sjp161948 debug3("request %u: open flags %d", id, pflags); 5110Sstevel@tonic-gate a = get_attrib(); 5120Sstevel@tonic-gate flags = flags_from_portable(pflags); 5130Sstevel@tonic-gate mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 514*5087Sjp161948 log("open \"%s\" flags %s mode 0%o", 515*5087Sjp161948 name, string_from_portable(pflags), mode); 5160Sstevel@tonic-gate fd = open(name, flags, mode); 5170Sstevel@tonic-gate if (fd < 0) { 5180Sstevel@tonic-gate status = errno_to_portable(errno); 5190Sstevel@tonic-gate } else { 520*5087Sjp161948 handle = handle_new(HANDLE_FILE, name, fd, NULL); 5210Sstevel@tonic-gate if (handle < 0) { 5220Sstevel@tonic-gate close(fd); 5230Sstevel@tonic-gate } else { 5240Sstevel@tonic-gate send_handle(id, handle); 5250Sstevel@tonic-gate status = SSH2_FX_OK; 5260Sstevel@tonic-gate } 5270Sstevel@tonic-gate } 5280Sstevel@tonic-gate if (status != SSH2_FX_OK) 5290Sstevel@tonic-gate send_status(id, status); 5300Sstevel@tonic-gate xfree(name); 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate static void 5340Sstevel@tonic-gate process_close(void) 5350Sstevel@tonic-gate { 5360Sstevel@tonic-gate u_int32_t id; 5370Sstevel@tonic-gate int handle, ret, status = SSH2_FX_FAILURE; 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate id = get_int(); 5400Sstevel@tonic-gate handle = get_handle(); 541*5087Sjp161948 debug3("request %u: close handle %u", id, handle); 542*5087Sjp161948 handle_log_close(handle, NULL); 5430Sstevel@tonic-gate ret = handle_close(handle); 5440Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 5450Sstevel@tonic-gate send_status(id, status); 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate static void 5490Sstevel@tonic-gate process_read(void) 5500Sstevel@tonic-gate { 5510Sstevel@tonic-gate char buf[64*1024]; 5520Sstevel@tonic-gate u_int32_t id, len; 5530Sstevel@tonic-gate int handle, fd, ret, status = SSH2_FX_FAILURE; 5540Sstevel@tonic-gate u_int64_t off; 5550Sstevel@tonic-gate 5560Sstevel@tonic-gate id = get_int(); 5570Sstevel@tonic-gate handle = get_handle(); 5580Sstevel@tonic-gate off = get_int64(); 5590Sstevel@tonic-gate len = get_int(); 5600Sstevel@tonic-gate 561*5087Sjp161948 debug("request %u: read \"%s\" (handle %d) off %llu len %d", 562*5087Sjp161948 id, handle_to_name(handle), handle, (unsigned long long)off, len); 5630Sstevel@tonic-gate if (len > sizeof buf) { 5640Sstevel@tonic-gate len = sizeof buf; 565*5087Sjp161948 debug2("read change len %d", len); 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate fd = handle_to_fd(handle); 5680Sstevel@tonic-gate if (fd >= 0) { 5690Sstevel@tonic-gate if (lseek(fd, off, SEEK_SET) < 0) { 5700Sstevel@tonic-gate error("process_read: seek failed"); 5710Sstevel@tonic-gate status = errno_to_portable(errno); 5720Sstevel@tonic-gate } else { 5730Sstevel@tonic-gate ret = read(fd, buf, len); 5740Sstevel@tonic-gate if (ret < 0) { 5750Sstevel@tonic-gate status = errno_to_portable(errno); 5760Sstevel@tonic-gate } else if (ret == 0) { 5770Sstevel@tonic-gate status = SSH2_FX_EOF; 5780Sstevel@tonic-gate } else { 5790Sstevel@tonic-gate send_data(id, buf, ret); 5800Sstevel@tonic-gate status = SSH2_FX_OK; 581*5087Sjp161948 handle_update_read(handle, ret); 5820Sstevel@tonic-gate } 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate } 5850Sstevel@tonic-gate if (status != SSH2_FX_OK) 5860Sstevel@tonic-gate send_status(id, status); 5870Sstevel@tonic-gate } 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate static void 5900Sstevel@tonic-gate process_write(void) 5910Sstevel@tonic-gate { 5920Sstevel@tonic-gate u_int32_t id; 5930Sstevel@tonic-gate u_int64_t off; 5940Sstevel@tonic-gate u_int len; 5950Sstevel@tonic-gate int handle, fd, ret, status = SSH2_FX_FAILURE; 5960Sstevel@tonic-gate char *data; 5970Sstevel@tonic-gate 5980Sstevel@tonic-gate id = get_int(); 5990Sstevel@tonic-gate handle = get_handle(); 6000Sstevel@tonic-gate off = get_int64(); 6010Sstevel@tonic-gate data = get_string(&len); 6020Sstevel@tonic-gate 603*5087Sjp161948 debug("request %u: write \"%s\" (handle %d) off %llu len %d", 604*5087Sjp161948 id, handle_to_name(handle), handle, (unsigned long long)off, len); 6050Sstevel@tonic-gate fd = handle_to_fd(handle); 6060Sstevel@tonic-gate if (fd >= 0) { 6070Sstevel@tonic-gate if (lseek(fd, off, SEEK_SET) < 0) { 6080Sstevel@tonic-gate status = errno_to_portable(errno); 6090Sstevel@tonic-gate error("process_write: seek failed"); 6100Sstevel@tonic-gate } else { 6110Sstevel@tonic-gate /* XXX ATOMICIO ? */ 6120Sstevel@tonic-gate ret = write(fd, data, len); 613*5087Sjp161948 if (ret < 0) { 6140Sstevel@tonic-gate error("process_write: write failed"); 6150Sstevel@tonic-gate status = errno_to_portable(errno); 616*5087Sjp161948 } else if ((size_t)ret == len) { 6170Sstevel@tonic-gate status = SSH2_FX_OK; 618*5087Sjp161948 handle_update_write(handle, ret); 6190Sstevel@tonic-gate } else { 620*5087Sjp161948 debug2("nothing at all written"); 6210Sstevel@tonic-gate } 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate send_status(id, status); 6250Sstevel@tonic-gate xfree(data); 6260Sstevel@tonic-gate } 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate static void 6290Sstevel@tonic-gate process_do_stat(int do_lstat) 6300Sstevel@tonic-gate { 6310Sstevel@tonic-gate Attrib a; 6320Sstevel@tonic-gate struct stat st; 6330Sstevel@tonic-gate u_int32_t id; 6340Sstevel@tonic-gate char *name; 6350Sstevel@tonic-gate int ret, status = SSH2_FX_FAILURE; 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate id = get_int(); 6380Sstevel@tonic-gate name = get_string(NULL); 639*5087Sjp161948 debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 640*5087Sjp161948 verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 6410Sstevel@tonic-gate ret = do_lstat ? lstat(name, &st) : stat(name, &st); 6420Sstevel@tonic-gate if (ret < 0) { 6430Sstevel@tonic-gate status = errno_to_portable(errno); 6440Sstevel@tonic-gate } else { 6450Sstevel@tonic-gate stat_to_attrib(&st, &a); 6460Sstevel@tonic-gate send_attrib(id, &a); 6470Sstevel@tonic-gate status = SSH2_FX_OK; 6480Sstevel@tonic-gate } 6490Sstevel@tonic-gate if (status != SSH2_FX_OK) 6500Sstevel@tonic-gate send_status(id, status); 6510Sstevel@tonic-gate xfree(name); 6520Sstevel@tonic-gate } 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate static void 6550Sstevel@tonic-gate process_stat(void) 6560Sstevel@tonic-gate { 6570Sstevel@tonic-gate process_do_stat(0); 6580Sstevel@tonic-gate } 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate static void 6610Sstevel@tonic-gate process_lstat(void) 6620Sstevel@tonic-gate { 6630Sstevel@tonic-gate process_do_stat(1); 6640Sstevel@tonic-gate } 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate static void 6670Sstevel@tonic-gate process_fstat(void) 6680Sstevel@tonic-gate { 6690Sstevel@tonic-gate Attrib a; 6700Sstevel@tonic-gate struct stat st; 6710Sstevel@tonic-gate u_int32_t id; 6720Sstevel@tonic-gate int fd, ret, handle, status = SSH2_FX_FAILURE; 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate id = get_int(); 6750Sstevel@tonic-gate handle = get_handle(); 676*5087Sjp161948 debug("request %u: fstat \"%s\" (handle %u)", 677*5087Sjp161948 id, handle_to_name(handle), handle); 6780Sstevel@tonic-gate fd = handle_to_fd(handle); 679*5087Sjp161948 if (fd >= 0) { 6800Sstevel@tonic-gate ret = fstat(fd, &st); 6810Sstevel@tonic-gate if (ret < 0) { 6820Sstevel@tonic-gate status = errno_to_portable(errno); 6830Sstevel@tonic-gate } else { 6840Sstevel@tonic-gate stat_to_attrib(&st, &a); 6850Sstevel@tonic-gate send_attrib(id, &a); 6860Sstevel@tonic-gate status = SSH2_FX_OK; 6870Sstevel@tonic-gate } 6880Sstevel@tonic-gate } 6890Sstevel@tonic-gate if (status != SSH2_FX_OK) 6900Sstevel@tonic-gate send_status(id, status); 6910Sstevel@tonic-gate } 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate static struct timeval * 694*5087Sjp161948 attrib_to_tv(const Attrib *a) 6950Sstevel@tonic-gate { 6960Sstevel@tonic-gate static struct timeval tv[2]; 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate tv[0].tv_sec = a->atime; 6990Sstevel@tonic-gate tv[0].tv_usec = 0; 7000Sstevel@tonic-gate tv[1].tv_sec = a->mtime; 7010Sstevel@tonic-gate tv[1].tv_usec = 0; 7020Sstevel@tonic-gate return tv; 7030Sstevel@tonic-gate } 7040Sstevel@tonic-gate 7050Sstevel@tonic-gate static void 7060Sstevel@tonic-gate process_setstat(void) 7070Sstevel@tonic-gate { 7080Sstevel@tonic-gate Attrib *a; 7090Sstevel@tonic-gate u_int32_t id; 7100Sstevel@tonic-gate char *name; 7110Sstevel@tonic-gate int status = SSH2_FX_OK, ret; 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate id = get_int(); 7140Sstevel@tonic-gate name = get_string(NULL); 7150Sstevel@tonic-gate a = get_attrib(); 716*5087Sjp161948 debug("request %u: setstat name \"%s\"", id, name); 7170Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 718*5087Sjp161948 log("set \"%s\" size %llu", 719*5087Sjp161948 name, (unsigned long long)a->size); 7200Sstevel@tonic-gate ret = truncate(name, a->size); 7210Sstevel@tonic-gate if (ret == -1) 7220Sstevel@tonic-gate status = errno_to_portable(errno); 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 725*5087Sjp161948 log("set \"%s\" mode %04o", name, a->perm); 7260Sstevel@tonic-gate ret = chmod(name, a->perm & 0777); 7270Sstevel@tonic-gate if (ret == -1) 7280Sstevel@tonic-gate status = errno_to_portable(errno); 7290Sstevel@tonic-gate } 7300Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 731*5087Sjp161948 char buf[64]; 732*5087Sjp161948 time_t t = a->mtime; 733*5087Sjp161948 734*5087Sjp161948 strftime(buf, sizeof(buf), "%Y" "%m%d-%H:%M:%S", 735*5087Sjp161948 localtime(&t)); 736*5087Sjp161948 log("set \"%s\" modtime %s", name, buf); 7370Sstevel@tonic-gate ret = utimes(name, attrib_to_tv(a)); 7380Sstevel@tonic-gate if (ret == -1) 7390Sstevel@tonic-gate status = errno_to_portable(errno); 7400Sstevel@tonic-gate } 7410Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 742*5087Sjp161948 log("set \"%s\" owner %lu group %lu", name, 743*5087Sjp161948 (u_long)a->uid, (u_long)a->gid); 7440Sstevel@tonic-gate ret = chown(name, a->uid, a->gid); 7450Sstevel@tonic-gate if (ret == -1) 7460Sstevel@tonic-gate status = errno_to_portable(errno); 7470Sstevel@tonic-gate } 7480Sstevel@tonic-gate send_status(id, status); 7490Sstevel@tonic-gate xfree(name); 7500Sstevel@tonic-gate } 7510Sstevel@tonic-gate 7520Sstevel@tonic-gate static void 7530Sstevel@tonic-gate process_fsetstat(void) 7540Sstevel@tonic-gate { 7550Sstevel@tonic-gate Attrib *a; 7560Sstevel@tonic-gate u_int32_t id; 7570Sstevel@tonic-gate int handle, fd, ret; 7580Sstevel@tonic-gate int status = SSH2_FX_OK; 7590Sstevel@tonic-gate 7600Sstevel@tonic-gate id = get_int(); 7610Sstevel@tonic-gate handle = get_handle(); 7620Sstevel@tonic-gate a = get_attrib(); 763*5087Sjp161948 debug("request %u: fsetstat handle %d", id, handle); 7640Sstevel@tonic-gate fd = handle_to_fd(handle); 765*5087Sjp161948 if (fd < 0) { 7660Sstevel@tonic-gate status = SSH2_FX_FAILURE; 7670Sstevel@tonic-gate } else { 768*5087Sjp161948 char *name = handle_to_name(handle); 769*5087Sjp161948 7700Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 771*5087Sjp161948 log("set \"%s\" size %llu", 772*5087Sjp161948 name, (unsigned long long)a->size); 7730Sstevel@tonic-gate ret = ftruncate(fd, a->size); 7740Sstevel@tonic-gate if (ret == -1) 7750Sstevel@tonic-gate status = errno_to_portable(errno); 7760Sstevel@tonic-gate } 7770Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 778*5087Sjp161948 log("set \"%s\" mode %04o", name, a->perm); 7790Sstevel@tonic-gate #ifdef HAVE_FCHMOD 7800Sstevel@tonic-gate ret = fchmod(fd, a->perm & 0777); 7810Sstevel@tonic-gate #else 7820Sstevel@tonic-gate ret = chmod(name, a->perm & 0777); 7830Sstevel@tonic-gate #endif 7840Sstevel@tonic-gate if (ret == -1) 7850Sstevel@tonic-gate status = errno_to_portable(errno); 7860Sstevel@tonic-gate } 7870Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 788*5087Sjp161948 char buf[64]; 789*5087Sjp161948 time_t t = a->mtime; 790*5087Sjp161948 791*5087Sjp161948 strftime(buf, sizeof(buf), "%Y" "%m%d-%H:%M:%S", 792*5087Sjp161948 localtime(&t)); 793*5087Sjp161948 log("set \"%s\" modtime %s", name, buf); 7940Sstevel@tonic-gate #ifdef HAVE_FUTIMES 7950Sstevel@tonic-gate ret = futimes(fd, attrib_to_tv(a)); 7960Sstevel@tonic-gate #else 7970Sstevel@tonic-gate ret = utimes(name, attrib_to_tv(a)); 7980Sstevel@tonic-gate #endif 7990Sstevel@tonic-gate if (ret == -1) 8000Sstevel@tonic-gate status = errno_to_portable(errno); 8010Sstevel@tonic-gate } 8020Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 803*5087Sjp161948 log("set \"%s\" owner %lu group %lu", name, 804*5087Sjp161948 (u_long)a->uid, (u_long)a->gid); 8050Sstevel@tonic-gate #ifdef HAVE_FCHOWN 8060Sstevel@tonic-gate ret = fchown(fd, a->uid, a->gid); 8070Sstevel@tonic-gate #else 8080Sstevel@tonic-gate ret = chown(name, a->uid, a->gid); 8090Sstevel@tonic-gate #endif 8100Sstevel@tonic-gate if (ret == -1) 8110Sstevel@tonic-gate status = errno_to_portable(errno); 8120Sstevel@tonic-gate } 8130Sstevel@tonic-gate } 8140Sstevel@tonic-gate send_status(id, status); 8150Sstevel@tonic-gate } 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate static void 8180Sstevel@tonic-gate process_opendir(void) 8190Sstevel@tonic-gate { 8200Sstevel@tonic-gate DIR *dirp = NULL; 8210Sstevel@tonic-gate char *path; 8220Sstevel@tonic-gate int handle, status = SSH2_FX_FAILURE; 8230Sstevel@tonic-gate u_int32_t id; 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate id = get_int(); 8260Sstevel@tonic-gate path = get_string(NULL); 827*5087Sjp161948 debug3("request %u: opendir", id); 828*5087Sjp161948 log("opendir \"%s\"", path); 8290Sstevel@tonic-gate dirp = opendir(path); 8300Sstevel@tonic-gate if (dirp == NULL) { 8310Sstevel@tonic-gate status = errno_to_portable(errno); 8320Sstevel@tonic-gate } else { 833*5087Sjp161948 handle = handle_new(HANDLE_DIR, path, 0, dirp); 8340Sstevel@tonic-gate if (handle < 0) { 8350Sstevel@tonic-gate closedir(dirp); 8360Sstevel@tonic-gate } else { 8370Sstevel@tonic-gate send_handle(id, handle); 8380Sstevel@tonic-gate status = SSH2_FX_OK; 8390Sstevel@tonic-gate } 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate } 8420Sstevel@tonic-gate if (status != SSH2_FX_OK) 8430Sstevel@tonic-gate send_status(id, status); 8440Sstevel@tonic-gate xfree(path); 8450Sstevel@tonic-gate } 8460Sstevel@tonic-gate 8470Sstevel@tonic-gate static void 8480Sstevel@tonic-gate process_readdir(void) 8490Sstevel@tonic-gate { 8500Sstevel@tonic-gate DIR *dirp; 8510Sstevel@tonic-gate struct dirent *dp; 8520Sstevel@tonic-gate char *path; 8530Sstevel@tonic-gate int handle; 8540Sstevel@tonic-gate u_int32_t id; 8550Sstevel@tonic-gate 8560Sstevel@tonic-gate id = get_int(); 8570Sstevel@tonic-gate handle = get_handle(); 858*5087Sjp161948 debug("request %u: readdir \"%s\" (handle %d)", id, 859*5087Sjp161948 handle_to_name(handle), handle); 8600Sstevel@tonic-gate dirp = handle_to_dir(handle); 8610Sstevel@tonic-gate path = handle_to_name(handle); 8620Sstevel@tonic-gate if (dirp == NULL || path == NULL) { 8630Sstevel@tonic-gate send_status(id, SSH2_FX_FAILURE); 8640Sstevel@tonic-gate } else { 8650Sstevel@tonic-gate struct stat st; 866*5087Sjp161948 char pathname[MAXPATHLEN]; 8670Sstevel@tonic-gate Stat *stats; 8680Sstevel@tonic-gate int nstats = 10, count = 0, i; 8690Sstevel@tonic-gate 870*5087Sjp161948 stats = xcalloc(nstats, sizeof(Stat)); 8710Sstevel@tonic-gate while ((dp = readdir(dirp)) != NULL) { 8720Sstevel@tonic-gate if (count >= nstats) { 8730Sstevel@tonic-gate nstats *= 2; 8740Sstevel@tonic-gate stats = xrealloc(stats, nstats * sizeof(Stat)); 8750Sstevel@tonic-gate } 8760Sstevel@tonic-gate /* XXX OVERFLOW ? */ 8770Sstevel@tonic-gate snprintf(pathname, sizeof pathname, "%s%s%s", path, 8780Sstevel@tonic-gate strcmp(path, "/") ? "/" : "", dp->d_name); 8790Sstevel@tonic-gate if (lstat(pathname, &st) < 0) 8800Sstevel@tonic-gate continue; 8810Sstevel@tonic-gate stat_to_attrib(&st, &(stats[count].attrib)); 8820Sstevel@tonic-gate stats[count].name = xstrdup(dp->d_name); 8830Sstevel@tonic-gate stats[count].long_name = ls_file(dp->d_name, &st, 0); 8840Sstevel@tonic-gate count++; 8850Sstevel@tonic-gate /* send up to 100 entries in one message */ 8860Sstevel@tonic-gate /* XXX check packet size instead */ 8870Sstevel@tonic-gate if (count == 100) 8880Sstevel@tonic-gate break; 8890Sstevel@tonic-gate } 8900Sstevel@tonic-gate if (count > 0) { 8910Sstevel@tonic-gate send_names(id, count, stats); 8920Sstevel@tonic-gate for (i = 0; i < count; i++) { 8930Sstevel@tonic-gate xfree(stats[i].name); 8940Sstevel@tonic-gate xfree(stats[i].long_name); 8950Sstevel@tonic-gate } 8960Sstevel@tonic-gate } else { 8970Sstevel@tonic-gate send_status(id, SSH2_FX_EOF); 8980Sstevel@tonic-gate } 8990Sstevel@tonic-gate xfree(stats); 9000Sstevel@tonic-gate } 9010Sstevel@tonic-gate } 9020Sstevel@tonic-gate 9030Sstevel@tonic-gate static void 9040Sstevel@tonic-gate process_remove(void) 9050Sstevel@tonic-gate { 9060Sstevel@tonic-gate char *name; 9070Sstevel@tonic-gate u_int32_t id; 9080Sstevel@tonic-gate int status = SSH2_FX_FAILURE; 9090Sstevel@tonic-gate int ret; 9100Sstevel@tonic-gate 9110Sstevel@tonic-gate id = get_int(); 9120Sstevel@tonic-gate name = get_string(NULL); 913*5087Sjp161948 debug3("request %u: remove", id); 914*5087Sjp161948 log("remove name \"%s\"", name); 9150Sstevel@tonic-gate ret = unlink(name); 9160Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9170Sstevel@tonic-gate send_status(id, status); 9180Sstevel@tonic-gate xfree(name); 9190Sstevel@tonic-gate } 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate static void 9220Sstevel@tonic-gate process_mkdir(void) 9230Sstevel@tonic-gate { 9240Sstevel@tonic-gate Attrib *a; 9250Sstevel@tonic-gate u_int32_t id; 9260Sstevel@tonic-gate char *name; 9270Sstevel@tonic-gate int ret, mode, status = SSH2_FX_FAILURE; 9280Sstevel@tonic-gate 9290Sstevel@tonic-gate id = get_int(); 9300Sstevel@tonic-gate name = get_string(NULL); 9310Sstevel@tonic-gate a = get_attrib(); 9320Sstevel@tonic-gate mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 9330Sstevel@tonic-gate a->perm & 0777 : 0777; 934*5087Sjp161948 debug3("request %u: mkdir", id); 935*5087Sjp161948 log("mkdir name \"%s\" mode 0%o", name, mode); 9360Sstevel@tonic-gate ret = mkdir(name, mode); 9370Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9380Sstevel@tonic-gate send_status(id, status); 9390Sstevel@tonic-gate xfree(name); 9400Sstevel@tonic-gate } 9410Sstevel@tonic-gate 9420Sstevel@tonic-gate static void 9430Sstevel@tonic-gate process_rmdir(void) 9440Sstevel@tonic-gate { 9450Sstevel@tonic-gate u_int32_t id; 9460Sstevel@tonic-gate char *name; 9470Sstevel@tonic-gate int ret, status; 9480Sstevel@tonic-gate 9490Sstevel@tonic-gate id = get_int(); 9500Sstevel@tonic-gate name = get_string(NULL); 951*5087Sjp161948 debug3("request %u: rmdir", id); 952*5087Sjp161948 log("rmdir name \"%s\"", name); 9530Sstevel@tonic-gate ret = rmdir(name); 9540Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9550Sstevel@tonic-gate send_status(id, status); 9560Sstevel@tonic-gate xfree(name); 9570Sstevel@tonic-gate } 9580Sstevel@tonic-gate 9590Sstevel@tonic-gate static void 9600Sstevel@tonic-gate process_realpath(void) 9610Sstevel@tonic-gate { 9620Sstevel@tonic-gate char resolvedname[MAXPATHLEN]; 9630Sstevel@tonic-gate u_int32_t id; 9640Sstevel@tonic-gate char *path; 9650Sstevel@tonic-gate 9660Sstevel@tonic-gate id = get_int(); 9670Sstevel@tonic-gate path = get_string(NULL); 9680Sstevel@tonic-gate if (path[0] == '\0') { 9690Sstevel@tonic-gate xfree(path); 9700Sstevel@tonic-gate path = xstrdup("."); 9710Sstevel@tonic-gate } 972*5087Sjp161948 debug3("request %u: realpath", id); 973*5087Sjp161948 verbose("realpath \"%s\"", path); 9740Sstevel@tonic-gate if (realpath(path, resolvedname) == NULL) { 9750Sstevel@tonic-gate send_status(id, errno_to_portable(errno)); 9760Sstevel@tonic-gate } else { 9770Sstevel@tonic-gate Stat s; 9780Sstevel@tonic-gate attrib_clear(&s.attrib); 9790Sstevel@tonic-gate s.name = s.long_name = resolvedname; 9800Sstevel@tonic-gate send_names(id, 1, &s); 9810Sstevel@tonic-gate } 9820Sstevel@tonic-gate xfree(path); 9830Sstevel@tonic-gate } 9840Sstevel@tonic-gate 9850Sstevel@tonic-gate static void 9860Sstevel@tonic-gate process_rename(void) 9870Sstevel@tonic-gate { 9880Sstevel@tonic-gate u_int32_t id; 9890Sstevel@tonic-gate char *oldpath, *newpath; 990*5087Sjp161948 int status; 991*5087Sjp161948 struct stat sb; 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate id = get_int(); 9940Sstevel@tonic-gate oldpath = get_string(NULL); 9950Sstevel@tonic-gate newpath = get_string(NULL); 996*5087Sjp161948 debug3("request %u: rename", id); 997*5087Sjp161948 log("rename old \"%s\" new \"%s\"", oldpath, newpath); 998*5087Sjp161948 status = SSH2_FX_FAILURE; 999*5087Sjp161948 if (lstat(oldpath, &sb) == -1) 1000*5087Sjp161948 status = errno_to_portable(errno); 1001*5087Sjp161948 else if (S_ISREG(sb.st_mode)) { 1002*5087Sjp161948 /* Race-free rename of regular files */ 1003*5087Sjp161948 if (link(oldpath, newpath) == -1) { 1004*5087Sjp161948 if (errno == EOPNOTSUPP 1005*5087Sjp161948 #ifdef LINK_OPNOTSUPP_ERRNO 1006*5087Sjp161948 || errno == LINK_OPNOTSUPP_ERRNO 1007*5087Sjp161948 #endif 1008*5087Sjp161948 ) { 1009*5087Sjp161948 struct stat st; 1010*5087Sjp161948 1011*5087Sjp161948 /* 1012*5087Sjp161948 * fs doesn't support links, so fall back to 1013*5087Sjp161948 * stat+rename. This is racy. 1014*5087Sjp161948 */ 1015*5087Sjp161948 if (stat(newpath, &st) == -1) { 1016*5087Sjp161948 if (rename(oldpath, newpath) == -1) 1017*5087Sjp161948 status = 1018*5087Sjp161948 errno_to_portable(errno); 1019*5087Sjp161948 else 1020*5087Sjp161948 status = SSH2_FX_OK; 1021*5087Sjp161948 } 1022*5087Sjp161948 } else { 1023*5087Sjp161948 status = errno_to_portable(errno); 1024*5087Sjp161948 } 1025*5087Sjp161948 } else if (unlink(oldpath) == -1) { 1026*5087Sjp161948 status = errno_to_portable(errno); 1027*5087Sjp161948 /* clean spare link */ 1028*5087Sjp161948 unlink(newpath); 1029*5087Sjp161948 } else 1030*5087Sjp161948 status = SSH2_FX_OK; 1031*5087Sjp161948 } else if (stat(newpath, &sb) == -1) { 1032*5087Sjp161948 if (rename(oldpath, newpath) == -1) 1033*5087Sjp161948 status = errno_to_portable(errno); 1034*5087Sjp161948 else 1035*5087Sjp161948 status = SSH2_FX_OK; 10360Sstevel@tonic-gate } 10370Sstevel@tonic-gate send_status(id, status); 10380Sstevel@tonic-gate xfree(oldpath); 10390Sstevel@tonic-gate xfree(newpath); 10400Sstevel@tonic-gate } 10410Sstevel@tonic-gate 10420Sstevel@tonic-gate static void 10430Sstevel@tonic-gate process_readlink(void) 10440Sstevel@tonic-gate { 10450Sstevel@tonic-gate u_int32_t id; 10460Sstevel@tonic-gate int len; 1047*5087Sjp161948 char buf[MAXPATHLEN]; 10480Sstevel@tonic-gate char *path; 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate id = get_int(); 10510Sstevel@tonic-gate path = get_string(NULL); 1052*5087Sjp161948 debug3("request %u: readlink", id); 1053*5087Sjp161948 verbose("readlink \"%s\"", path); 1054*5087Sjp161948 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 10550Sstevel@tonic-gate send_status(id, errno_to_portable(errno)); 10560Sstevel@tonic-gate else { 10570Sstevel@tonic-gate Stat s; 10580Sstevel@tonic-gate 1059*5087Sjp161948 buf[len] = '\0'; 10600Sstevel@tonic-gate attrib_clear(&s.attrib); 1061*5087Sjp161948 s.name = s.long_name = buf; 10620Sstevel@tonic-gate send_names(id, 1, &s); 10630Sstevel@tonic-gate } 10640Sstevel@tonic-gate xfree(path); 10650Sstevel@tonic-gate } 10660Sstevel@tonic-gate 10670Sstevel@tonic-gate static void 10680Sstevel@tonic-gate process_symlink(void) 10690Sstevel@tonic-gate { 10700Sstevel@tonic-gate u_int32_t id; 10710Sstevel@tonic-gate char *oldpath, *newpath; 1072*5087Sjp161948 int ret, status; 10730Sstevel@tonic-gate 10740Sstevel@tonic-gate id = get_int(); 10750Sstevel@tonic-gate oldpath = get_string(NULL); 10760Sstevel@tonic-gate newpath = get_string(NULL); 1077*5087Sjp161948 debug3("request %u: symlink", id); 1078*5087Sjp161948 log("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1079*5087Sjp161948 /* this will fail if 'newpath' exists */ 1080*5087Sjp161948 ret = symlink(oldpath, newpath); 1081*5087Sjp161948 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 10820Sstevel@tonic-gate send_status(id, status); 10830Sstevel@tonic-gate xfree(oldpath); 10840Sstevel@tonic-gate xfree(newpath); 10850Sstevel@tonic-gate } 10860Sstevel@tonic-gate 10870Sstevel@tonic-gate static void 10880Sstevel@tonic-gate process_extended(void) 10890Sstevel@tonic-gate { 10900Sstevel@tonic-gate u_int32_t id; 10910Sstevel@tonic-gate char *request; 10920Sstevel@tonic-gate 10930Sstevel@tonic-gate id = get_int(); 10940Sstevel@tonic-gate request = get_string(NULL); 10950Sstevel@tonic-gate send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 10960Sstevel@tonic-gate xfree(request); 10970Sstevel@tonic-gate } 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate /* stolen from ssh-agent */ 11000Sstevel@tonic-gate 11010Sstevel@tonic-gate static void 11020Sstevel@tonic-gate process(void) 11030Sstevel@tonic-gate { 11040Sstevel@tonic-gate u_int msg_len; 11050Sstevel@tonic-gate u_int buf_len; 11060Sstevel@tonic-gate u_int consumed; 11070Sstevel@tonic-gate u_int type; 11080Sstevel@tonic-gate u_char *cp; 11090Sstevel@tonic-gate 11100Sstevel@tonic-gate buf_len = buffer_len(&iqueue); 11110Sstevel@tonic-gate if (buf_len < 5) 11120Sstevel@tonic-gate return; /* Incomplete message. */ 11130Sstevel@tonic-gate cp = buffer_ptr(&iqueue); 1114*5087Sjp161948 msg_len = get_u32(cp); 1115*5087Sjp161948 if (msg_len > SFTP_MAX_MSG_LENGTH) { 1116*5087Sjp161948 error("bad message from %s local user %s", 1117*5087Sjp161948 client_addr, pw->pw_name); 1118*5087Sjp161948 cleanup_exit(11); 11190Sstevel@tonic-gate } 11200Sstevel@tonic-gate if (buf_len < msg_len + 4) 11210Sstevel@tonic-gate return; 11220Sstevel@tonic-gate buffer_consume(&iqueue, 4); 11230Sstevel@tonic-gate buf_len -= 4; 11240Sstevel@tonic-gate type = buffer_get_char(&iqueue); 11250Sstevel@tonic-gate switch (type) { 11260Sstevel@tonic-gate case SSH2_FXP_INIT: 11270Sstevel@tonic-gate process_init(); 11280Sstevel@tonic-gate break; 11290Sstevel@tonic-gate case SSH2_FXP_OPEN: 11300Sstevel@tonic-gate process_open(); 11310Sstevel@tonic-gate break; 11320Sstevel@tonic-gate case SSH2_FXP_CLOSE: 11330Sstevel@tonic-gate process_close(); 11340Sstevel@tonic-gate break; 11350Sstevel@tonic-gate case SSH2_FXP_READ: 11360Sstevel@tonic-gate process_read(); 11370Sstevel@tonic-gate break; 11380Sstevel@tonic-gate case SSH2_FXP_WRITE: 11390Sstevel@tonic-gate process_write(); 11400Sstevel@tonic-gate break; 11410Sstevel@tonic-gate case SSH2_FXP_LSTAT: 11420Sstevel@tonic-gate process_lstat(); 11430Sstevel@tonic-gate break; 11440Sstevel@tonic-gate case SSH2_FXP_FSTAT: 11450Sstevel@tonic-gate process_fstat(); 11460Sstevel@tonic-gate break; 11470Sstevel@tonic-gate case SSH2_FXP_SETSTAT: 11480Sstevel@tonic-gate process_setstat(); 11490Sstevel@tonic-gate break; 11500Sstevel@tonic-gate case SSH2_FXP_FSETSTAT: 11510Sstevel@tonic-gate process_fsetstat(); 11520Sstevel@tonic-gate break; 11530Sstevel@tonic-gate case SSH2_FXP_OPENDIR: 11540Sstevel@tonic-gate process_opendir(); 11550Sstevel@tonic-gate break; 11560Sstevel@tonic-gate case SSH2_FXP_READDIR: 11570Sstevel@tonic-gate process_readdir(); 11580Sstevel@tonic-gate break; 11590Sstevel@tonic-gate case SSH2_FXP_REMOVE: 11600Sstevel@tonic-gate process_remove(); 11610Sstevel@tonic-gate break; 11620Sstevel@tonic-gate case SSH2_FXP_MKDIR: 11630Sstevel@tonic-gate process_mkdir(); 11640Sstevel@tonic-gate break; 11650Sstevel@tonic-gate case SSH2_FXP_RMDIR: 11660Sstevel@tonic-gate process_rmdir(); 11670Sstevel@tonic-gate break; 11680Sstevel@tonic-gate case SSH2_FXP_REALPATH: 11690Sstevel@tonic-gate process_realpath(); 11700Sstevel@tonic-gate break; 11710Sstevel@tonic-gate case SSH2_FXP_STAT: 11720Sstevel@tonic-gate process_stat(); 11730Sstevel@tonic-gate break; 11740Sstevel@tonic-gate case SSH2_FXP_RENAME: 11750Sstevel@tonic-gate process_rename(); 11760Sstevel@tonic-gate break; 11770Sstevel@tonic-gate case SSH2_FXP_READLINK: 11780Sstevel@tonic-gate process_readlink(); 11790Sstevel@tonic-gate break; 11800Sstevel@tonic-gate case SSH2_FXP_SYMLINK: 11810Sstevel@tonic-gate process_symlink(); 11820Sstevel@tonic-gate break; 11830Sstevel@tonic-gate case SSH2_FXP_EXTENDED: 11840Sstevel@tonic-gate process_extended(); 11850Sstevel@tonic-gate break; 11860Sstevel@tonic-gate default: 11870Sstevel@tonic-gate error("Unknown message %d", type); 11880Sstevel@tonic-gate break; 11890Sstevel@tonic-gate } 11900Sstevel@tonic-gate /* discard the remaining bytes from the current packet */ 11910Sstevel@tonic-gate if (buf_len < buffer_len(&iqueue)) 1192*5087Sjp161948 fatal("iqueue grew unexpectedly"); 11930Sstevel@tonic-gate consumed = buf_len - buffer_len(&iqueue); 11940Sstevel@tonic-gate if (msg_len < consumed) 11950Sstevel@tonic-gate fatal("msg_len %d < consumed %d", msg_len, consumed); 11960Sstevel@tonic-gate if (msg_len > consumed) 11970Sstevel@tonic-gate buffer_consume(&iqueue, msg_len - consumed); 11980Sstevel@tonic-gate } 11990Sstevel@tonic-gate 1200*5087Sjp161948 /* Cleanup handler that logs active handles upon normal exit */ 1201*5087Sjp161948 static void 1202*5087Sjp161948 cleanup_exit(int i) 1203*5087Sjp161948 { 1204*5087Sjp161948 if (pw != NULL && client_addr != NULL) { 1205*5087Sjp161948 handle_log_exit(); 1206*5087Sjp161948 log("session closed for local user %s from [%s]", 1207*5087Sjp161948 pw->pw_name, client_addr); 1208*5087Sjp161948 } 1209*5087Sjp161948 _exit(i); 1210*5087Sjp161948 } 1211*5087Sjp161948 1212*5087Sjp161948 static void 1213*5087Sjp161948 usage(void) 1214*5087Sjp161948 { 1215*5087Sjp161948 fprintf(stderr, 1216*5087Sjp161948 "Usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); 1217*5087Sjp161948 exit(1); 1218*5087Sjp161948 } 1219*5087Sjp161948 12200Sstevel@tonic-gate int 1221*5087Sjp161948 main(int argc, char **argv) 12220Sstevel@tonic-gate { 12230Sstevel@tonic-gate fd_set *rset, *wset; 1224*5087Sjp161948 int in, out, max, ch, skipargs = 0, log_stderr = 0; 12250Sstevel@tonic-gate ssize_t len, olen, set_size; 1226*5087Sjp161948 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 1227*5087Sjp161948 char *cp, buf[4*4096]; 12280Sstevel@tonic-gate 1229*5087Sjp161948 extern char *optarg; 12300Sstevel@tonic-gate 1231*5087Sjp161948 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1232*5087Sjp161948 sanitise_stdfd(); 1233*5087Sjp161948 1234*5087Sjp161948 __progname = get_progname(argv[0]); 12350Sstevel@tonic-gate 12360Sstevel@tonic-gate (void) g11n_setlocale(LC_ALL, ""); 12370Sstevel@tonic-gate 1238*5087Sjp161948 log_init(__progname, log_level, log_facility, log_stderr); 12390Sstevel@tonic-gate 1240*5087Sjp161948 while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { 1241*5087Sjp161948 switch (ch) { 1242*5087Sjp161948 case 'c': 1243*5087Sjp161948 /* 1244*5087Sjp161948 * Ignore all arguments if we are invoked as a 1245*5087Sjp161948 * shell using "sftp-server -c command" 1246*5087Sjp161948 */ 1247*5087Sjp161948 skipargs = 1; 1248*5087Sjp161948 break; 1249*5087Sjp161948 case 'e': 1250*5087Sjp161948 log_stderr = 1; 1251*5087Sjp161948 break; 1252*5087Sjp161948 case 'l': 1253*5087Sjp161948 log_level = log_level_number(optarg); 1254*5087Sjp161948 if (log_level == SYSLOG_LEVEL_NOT_SET) 1255*5087Sjp161948 error("Invalid log level \"%s\"", optarg); 1256*5087Sjp161948 break; 1257*5087Sjp161948 case 'f': 1258*5087Sjp161948 log_facility = log_facility_number(optarg); 1259*5087Sjp161948 if (log_facility == SYSLOG_FACILITY_NOT_SET) 1260*5087Sjp161948 error("Invalid log facility \"%s\"", optarg); 1261*5087Sjp161948 break; 1262*5087Sjp161948 case 'h': 1263*5087Sjp161948 default: 1264*5087Sjp161948 usage(); 1265*5087Sjp161948 } 1266*5087Sjp161948 } 1267*5087Sjp161948 1268*5087Sjp161948 log_init(__progname, log_level, log_facility, log_stderr); 1269*5087Sjp161948 1270*5087Sjp161948 if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1271*5087Sjp161948 client_addr = xstrdup(cp); 1272*5087Sjp161948 if ((cp = strchr(client_addr, ' ')) == NULL) 1273*5087Sjp161948 fatal("Malformed SSH_CONNECTION variable: \"%s\"", 1274*5087Sjp161948 getenv("SSH_CONNECTION")); 1275*5087Sjp161948 *cp = '\0'; 1276*5087Sjp161948 } else 1277*5087Sjp161948 client_addr = xstrdup("UNKNOWN"); 1278*5087Sjp161948 1279*5087Sjp161948 if ((pw = getpwuid(getuid())) == NULL) 1280*5087Sjp161948 fatal("No user found for uid %lu", (u_long)getuid()); 1281*5087Sjp161948 pw = pwcopy(pw); 1282*5087Sjp161948 1283*5087Sjp161948 log("session opened for local user %s from [%s]", 1284*5087Sjp161948 pw->pw_name, client_addr); 1285*5087Sjp161948 1286*5087Sjp161948 handle_init(); 12870Sstevel@tonic-gate 12880Sstevel@tonic-gate in = dup(STDIN_FILENO); 12890Sstevel@tonic-gate out = dup(STDOUT_FILENO); 12900Sstevel@tonic-gate 12910Sstevel@tonic-gate #ifdef HAVE_CYGWIN 12920Sstevel@tonic-gate setmode(in, O_BINARY); 12930Sstevel@tonic-gate setmode(out, O_BINARY); 12940Sstevel@tonic-gate #endif 12950Sstevel@tonic-gate 12960Sstevel@tonic-gate max = 0; 12970Sstevel@tonic-gate if (in > max) 12980Sstevel@tonic-gate max = in; 12990Sstevel@tonic-gate if (out > max) 13000Sstevel@tonic-gate max = out; 13010Sstevel@tonic-gate 13020Sstevel@tonic-gate buffer_init(&iqueue); 13030Sstevel@tonic-gate buffer_init(&oqueue); 13040Sstevel@tonic-gate 13050Sstevel@tonic-gate set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 13060Sstevel@tonic-gate rset = (fd_set *)xmalloc(set_size); 13070Sstevel@tonic-gate wset = (fd_set *)xmalloc(set_size); 13080Sstevel@tonic-gate 13090Sstevel@tonic-gate for (;;) { 13100Sstevel@tonic-gate memset(rset, 0, set_size); 13110Sstevel@tonic-gate memset(wset, 0, set_size); 13120Sstevel@tonic-gate 1313*5087Sjp161948 /* 1314*5087Sjp161948 * Ensure that we can read a full buffer and handle 1315*5087Sjp161948 * the worst-case length packet it can generate, 1316*5087Sjp161948 * otherwise apply backpressure by stopping reads. 1317*5087Sjp161948 */ 1318*5087Sjp161948 if (buffer_check_alloc(&iqueue, sizeof(buf)) && 1319*5087Sjp161948 buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1320*5087Sjp161948 FD_SET(in, rset); 1321*5087Sjp161948 13220Sstevel@tonic-gate olen = buffer_len(&oqueue); 13230Sstevel@tonic-gate if (olen > 0) 13240Sstevel@tonic-gate FD_SET(out, wset); 13250Sstevel@tonic-gate 13260Sstevel@tonic-gate if (select(max+1, rset, wset, NULL, NULL) < 0) { 13270Sstevel@tonic-gate if (errno == EINTR) 13280Sstevel@tonic-gate continue; 1329*5087Sjp161948 error("select: %s", strerror(errno)); 1330*5087Sjp161948 cleanup_exit(2); 13310Sstevel@tonic-gate } 13320Sstevel@tonic-gate 13330Sstevel@tonic-gate /* copy stdin to iqueue */ 13340Sstevel@tonic-gate if (FD_ISSET(in, rset)) { 13350Sstevel@tonic-gate len = read(in, buf, sizeof buf); 13360Sstevel@tonic-gate if (len == 0) { 13370Sstevel@tonic-gate debug("read eof"); 1338*5087Sjp161948 cleanup_exit(0); 13390Sstevel@tonic-gate } else if (len < 0) { 1340*5087Sjp161948 error("read: %s", strerror(errno)); 1341*5087Sjp161948 cleanup_exit(1); 13420Sstevel@tonic-gate } else { 13430Sstevel@tonic-gate buffer_append(&iqueue, buf, len); 13440Sstevel@tonic-gate } 13450Sstevel@tonic-gate } 13460Sstevel@tonic-gate /* send oqueue to stdout */ 13470Sstevel@tonic-gate if (FD_ISSET(out, wset)) { 13480Sstevel@tonic-gate len = write(out, buffer_ptr(&oqueue), olen); 13490Sstevel@tonic-gate if (len < 0) { 1350*5087Sjp161948 error("write: %s", strerror(errno)); 1351*5087Sjp161948 cleanup_exit(1); 13520Sstevel@tonic-gate } else { 13530Sstevel@tonic-gate buffer_consume(&oqueue, len); 13540Sstevel@tonic-gate } 13550Sstevel@tonic-gate } 1356*5087Sjp161948 1357*5087Sjp161948 /* 1358*5087Sjp161948 * Process requests from client if we can fit the results 1359*5087Sjp161948 * into the output buffer, otherwise stop processing input 1360*5087Sjp161948 * and let the output queue drain. 1361*5087Sjp161948 */ 1362*5087Sjp161948 if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1363*5087Sjp161948 process(); 13640Sstevel@tonic-gate } 1365*5087Sjp161948 1366*5087Sjp161948 /* NOTREACHED */ 1367*5087Sjp161948 return (0); 13680Sstevel@tonic-gate } 1369