blob: 4eb31d94e2ebad34315ca7d4ea6221d9db181e68 [file] [log] [blame]
Damien Miller7b28dc52000-09-05 13:34:53 +11001/*
Damien Miller00c92172002-02-13 14:05:00 +11002 * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
Damien Miller7b28dc52000-09-05 13:34:53 +11003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
Damien Miller7b28dc52000-09-05 13:34:53 +110012 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24#include "includes.h"
Damien Miller9e51a732003-02-24 11:58:44 +110025RCSID("$OpenBSD: sftp-server.c,v 1.39 2003/02/06 09:29:18 markus Exp $");
Damien Miller7b28dc52000-09-05 13:34:53 +110026
Damien Miller7b28dc52000-09-05 13:34:53 +110027#include "buffer.h"
28#include "bufaux.h"
29#include "getput.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000030#include "log.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110031#include "xmalloc.h"
32
Ben Lindstrom2f959b42001-01-11 06:20:23 +000033#include "sftp.h"
Damien Miller33804262001-02-04 23:20:18 +110034#include "sftp-common.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110035
36/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000037#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110038#define get_int() buffer_get_int(&iqueue);
39#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Ben Lindstrom2f959b42001-01-11 06:20:23 +000040#define TRACE debug
Damien Miller7b28dc52000-09-05 13:34:53 +110041
Ben Lindstrom49a79c02000-11-17 03:47:20 +000042#ifdef HAVE___PROGNAME
43extern char *__progname;
44#else
45char *__progname;
46#endif
47
Damien Miller7b28dc52000-09-05 13:34:53 +110048/* input and output queue */
49Buffer iqueue;
50Buffer oqueue;
51
Damien Miller058316f2001-03-08 10:08:49 +110052/* Version of client */
53int version;
54
Damien Miller7b28dc52000-09-05 13:34:53 +110055/* portable attibutes, etc. */
56
Damien Miller7b28dc52000-09-05 13:34:53 +110057typedef struct Stat Stat;
58
Damien Miller33804262001-02-04 23:20:18 +110059struct Stat {
Damien Miller7b28dc52000-09-05 13:34:53 +110060 char *name;
61 char *long_name;
62 Attrib attrib;
63};
64
Ben Lindstrombba81212001-06-25 05:01:22 +000065static int
Damien Miller7b28dc52000-09-05 13:34:53 +110066errno_to_portable(int unixerrno)
67{
68 int ret = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000069
Damien Miller7b28dc52000-09-05 13:34:53 +110070 switch (unixerrno) {
71 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000072 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110073 break;
74 case ENOENT:
75 case ENOTDIR:
76 case EBADF:
77 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000078 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110079 break;
80 case EPERM:
81 case EACCES:
82 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000083 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110084 break;
85 case ENAMETOOLONG:
86 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000087 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110088 break;
89 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000090 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +110091 break;
92 }
93 return ret;
94}
95
Ben Lindstrombba81212001-06-25 05:01:22 +000096static int
Damien Miller7b28dc52000-09-05 13:34:53 +110097flags_from_portable(int pflags)
98{
99 int flags = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000100
Ben Lindstrom36592512001-03-05 05:02:08 +0000101 if ((pflags & SSH2_FXF_READ) &&
102 (pflags & SSH2_FXF_WRITE)) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100103 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000104 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100105 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000106 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100107 flags = O_WRONLY;
108 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000109 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100110 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000111 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100112 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000113 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100114 flags |= O_EXCL;
115 return flags;
116}
117
Ben Lindstrombba81212001-06-25 05:01:22 +0000118static Attrib *
Damien Miller7b28dc52000-09-05 13:34:53 +1100119get_attrib(void)
120{
121 return decode_attrib(&iqueue);
122}
123
124/* handle handles */
125
126typedef struct Handle Handle;
127struct Handle {
128 int use;
129 DIR *dirp;
130 int fd;
131 char *name;
132};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000133
Damien Miller7b28dc52000-09-05 13:34:53 +1100134enum {
135 HANDLE_UNUSED,
136 HANDLE_DIR,
137 HANDLE_FILE
138};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000139
Damien Miller7b28dc52000-09-05 13:34:53 +1100140Handle handles[100];
141
Ben Lindstrombba81212001-06-25 05:01:22 +0000142static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100143handle_init(void)
144{
145 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000146
Damien Miller9f0f5c62001-12-21 14:45:46 +1100147 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
Damien Miller7b28dc52000-09-05 13:34:53 +1100148 handles[i].use = HANDLE_UNUSED;
149}
150
Ben Lindstrombba81212001-06-25 05:01:22 +0000151static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100152handle_new(int use, char *name, int fd, DIR *dirp)
153{
154 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000155
Damien Miller9f0f5c62001-12-21 14:45:46 +1100156 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100157 if (handles[i].use == HANDLE_UNUSED) {
158 handles[i].use = use;
159 handles[i].dirp = dirp;
160 handles[i].fd = fd;
161 handles[i].name = name;
162 return i;
163 }
164 }
165 return -1;
166}
167
Ben Lindstrombba81212001-06-25 05:01:22 +0000168static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100169handle_is_ok(int i, int type)
170{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000171 return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
172 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100173}
174
Ben Lindstrombba81212001-06-25 05:01:22 +0000175static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100176handle_to_string(int handle, char **stringp, int *hlenp)
177{
Damien Miller7b28dc52000-09-05 13:34:53 +1100178 if (stringp == NULL || hlenp == NULL)
179 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000180 *stringp = xmalloc(sizeof(int32_t));
181 PUT_32BIT(*stringp, handle);
182 *hlenp = sizeof(int32_t);
Damien Miller7b28dc52000-09-05 13:34:53 +1100183 return 0;
184}
185
Ben Lindstrombba81212001-06-25 05:01:22 +0000186static int
Damien Millere4340be2000-09-16 13:29:08 +1100187handle_from_string(char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100188{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000189 int val;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000190
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000191 if (hlen != sizeof(int32_t))
Damien Miller7b28dc52000-09-05 13:34:53 +1100192 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000193 val = GET_32BIT(handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100194 if (handle_is_ok(val, HANDLE_FILE) ||
195 handle_is_ok(val, HANDLE_DIR))
196 return val;
197 return -1;
198}
199
Ben Lindstrombba81212001-06-25 05:01:22 +0000200static char *
Damien Miller7b28dc52000-09-05 13:34:53 +1100201handle_to_name(int handle)
202{
203 if (handle_is_ok(handle, HANDLE_DIR)||
204 handle_is_ok(handle, HANDLE_FILE))
205 return handles[handle].name;
206 return NULL;
207}
208
Ben Lindstrombba81212001-06-25 05:01:22 +0000209static DIR *
Damien Miller7b28dc52000-09-05 13:34:53 +1100210handle_to_dir(int handle)
211{
212 if (handle_is_ok(handle, HANDLE_DIR))
213 return handles[handle].dirp;
214 return NULL;
215}
216
Ben Lindstrombba81212001-06-25 05:01:22 +0000217static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100218handle_to_fd(int handle)
219{
Kevin Stevesef4eea92001-02-05 12:42:17 +0000220 if (handle_is_ok(handle, HANDLE_FILE))
Damien Miller7b28dc52000-09-05 13:34:53 +1100221 return handles[handle].fd;
222 return -1;
223}
224
Ben Lindstrombba81212001-06-25 05:01:22 +0000225static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100226handle_close(int handle)
227{
228 int ret = -1;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000229
Damien Miller7b28dc52000-09-05 13:34:53 +1100230 if (handle_is_ok(handle, HANDLE_FILE)) {
231 ret = close(handles[handle].fd);
232 handles[handle].use = HANDLE_UNUSED;
233 } else if (handle_is_ok(handle, HANDLE_DIR)) {
234 ret = closedir(handles[handle].dirp);
235 handles[handle].use = HANDLE_UNUSED;
236 } else {
237 errno = ENOENT;
238 }
239 return ret;
240}
241
Ben Lindstrombba81212001-06-25 05:01:22 +0000242static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100243get_handle(void)
244{
245 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000246 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100247 u_int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000248
Damien Miller7b28dc52000-09-05 13:34:53 +1100249 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000250 if (hlen < 256)
251 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100252 xfree(handle);
253 return val;
254}
255
256/* send replies */
257
Ben Lindstrombba81212001-06-25 05:01:22 +0000258static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100259send_msg(Buffer *m)
260{
261 int mlen = buffer_len(m);
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000262
Damien Miller7b28dc52000-09-05 13:34:53 +1100263 buffer_put_int(&oqueue, mlen);
264 buffer_append(&oqueue, buffer_ptr(m), mlen);
265 buffer_consume(m, mlen);
266}
267
Ben Lindstrombba81212001-06-25 05:01:22 +0000268static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100269send_status(u_int32_t id, u_int32_t error)
270{
271 Buffer msg;
Damien Miller058316f2001-03-08 10:08:49 +1100272 const char *status_messages[] = {
273 "Success", /* SSH_FX_OK */
274 "End of file", /* SSH_FX_EOF */
275 "No such file", /* SSH_FX_NO_SUCH_FILE */
276 "Permission denied", /* SSH_FX_PERMISSION_DENIED */
277 "Failure", /* SSH_FX_FAILURE */
278 "Bad message", /* SSH_FX_BAD_MESSAGE */
279 "No connection", /* SSH_FX_NO_CONNECTION */
280 "Connection lost", /* SSH_FX_CONNECTION_LOST */
281 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
282 "Unknown error" /* Others */
283 };
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000284
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000285 TRACE("sent status id %u error %u", id, error);
Damien Miller7b28dc52000-09-05 13:34:53 +1100286 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000287 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100288 buffer_put_int(&msg, id);
289 buffer_put_int(&msg, error);
Damien Miller058316f2001-03-08 10:08:49 +1100290 if (version >= 3) {
Ben Lindstroma3700052001-04-05 23:26:32 +0000291 buffer_put_cstring(&msg,
Damien Miller058316f2001-03-08 10:08:49 +1100292 status_messages[MIN(error,SSH2_FX_MAX)]);
293 buffer_put_cstring(&msg, "");
294 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100295 send_msg(&msg);
296 buffer_free(&msg);
297}
Ben Lindstrombba81212001-06-25 05:01:22 +0000298static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100299send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
300{
301 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000302
Damien Miller7b28dc52000-09-05 13:34:53 +1100303 buffer_init(&msg);
304 buffer_put_char(&msg, type);
305 buffer_put_int(&msg, id);
306 buffer_put_string(&msg, data, dlen);
307 send_msg(&msg);
308 buffer_free(&msg);
309}
310
Ben Lindstrombba81212001-06-25 05:01:22 +0000311static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100312send_data(u_int32_t id, char *data, int dlen)
313{
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000314 TRACE("sent data id %u len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000315 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100316}
317
Ben Lindstrombba81212001-06-25 05:01:22 +0000318static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100319send_handle(u_int32_t id, int handle)
320{
321 char *string;
322 int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000323
Damien Miller7b28dc52000-09-05 13:34:53 +1100324 handle_to_string(handle, &string, &hlen);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000325 TRACE("sent handle id %u handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000326 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100327 xfree(string);
328}
329
Ben Lindstrombba81212001-06-25 05:01:22 +0000330static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100331send_names(u_int32_t id, int count, Stat *stats)
332{
333 Buffer msg;
334 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000335
Damien Miller7b28dc52000-09-05 13:34:53 +1100336 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000337 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100338 buffer_put_int(&msg, id);
339 buffer_put_int(&msg, count);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000340 TRACE("sent names id %u count %d", id, count);
Damien Miller7b28dc52000-09-05 13:34:53 +1100341 for (i = 0; i < count; i++) {
342 buffer_put_cstring(&msg, stats[i].name);
343 buffer_put_cstring(&msg, stats[i].long_name);
344 encode_attrib(&msg, &stats[i].attrib);
345 }
346 send_msg(&msg);
347 buffer_free(&msg);
348}
349
Ben Lindstrombba81212001-06-25 05:01:22 +0000350static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100351send_attrib(u_int32_t id, Attrib *a)
352{
353 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000354
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000355 TRACE("sent attrib id %u have 0x%x", id, a->flags);
Damien Miller7b28dc52000-09-05 13:34:53 +1100356 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000357 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100358 buffer_put_int(&msg, id);
359 encode_attrib(&msg, a);
360 send_msg(&msg);
361 buffer_free(&msg);
362}
363
364/* parse incoming */
365
Ben Lindstrombba81212001-06-25 05:01:22 +0000366static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100367process_init(void)
368{
369 Buffer msg;
Damien Miller7b28dc52000-09-05 13:34:53 +1100370
Ben Lindstrom937df1d2002-06-06 21:58:35 +0000371 version = get_int();
Damien Miller7b28dc52000-09-05 13:34:53 +1100372 TRACE("client version %d", version);
373 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000374 buffer_put_char(&msg, SSH2_FXP_VERSION);
375 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100376 send_msg(&msg);
377 buffer_free(&msg);
378}
379
Ben Lindstrombba81212001-06-25 05:01:22 +0000380static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100381process_open(void)
382{
383 u_int32_t id, pflags;
384 Attrib *a;
385 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000386 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100387
388 id = get_int();
389 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000390 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100391 a = get_attrib();
392 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000393 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000394 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100395 fd = open(name, flags, mode);
396 if (fd < 0) {
397 status = errno_to_portable(errno);
398 } else {
399 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
400 if (handle < 0) {
401 close(fd);
402 } else {
403 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000404 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100405 }
406 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000407 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100408 send_status(id, status);
409 xfree(name);
410}
411
Ben Lindstrombba81212001-06-25 05:01:22 +0000412static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100413process_close(void)
414{
415 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000416 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100417
418 id = get_int();
419 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000420 TRACE("close id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100421 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000422 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100423 send_status(id, status);
424}
425
Ben Lindstrombba81212001-06-25 05:01:22 +0000426static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100427process_read(void)
428{
429 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000430 u_int32_t id, len;
431 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100432 u_int64_t off;
433
434 id = get_int();
435 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000436 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100437 len = get_int();
438
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000439 TRACE("read id %u handle %d off %llu len %d", id, handle,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000440 (u_int64_t)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100441 if (len > sizeof buf) {
442 len = sizeof buf;
443 log("read change len %d", len);
444 }
445 fd = handle_to_fd(handle);
446 if (fd >= 0) {
447 if (lseek(fd, off, SEEK_SET) < 0) {
448 error("process_read: seek failed");
449 status = errno_to_portable(errno);
450 } else {
451 ret = read(fd, buf, len);
452 if (ret < 0) {
453 status = errno_to_portable(errno);
454 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000455 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100456 } else {
457 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000458 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100459 }
460 }
461 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000462 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100463 send_status(id, status);
464}
465
Ben Lindstrombba81212001-06-25 05:01:22 +0000466static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100467process_write(void)
468{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000469 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100470 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100471 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000472 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100473 char *data;
474
475 id = get_int();
476 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000477 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100478 data = get_string(&len);
479
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000480 TRACE("write id %u handle %d off %llu len %d", id, handle,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000481 (u_int64_t)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100482 fd = handle_to_fd(handle);
483 if (fd >= 0) {
484 if (lseek(fd, off, SEEK_SET) < 0) {
485 status = errno_to_portable(errno);
486 error("process_write: seek failed");
487 } else {
488/* XXX ATOMICIO ? */
489 ret = write(fd, data, len);
490 if (ret == -1) {
491 error("process_write: write failed");
492 status = errno_to_portable(errno);
493 } else if (ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000494 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100495 } else {
496 log("nothing at all written");
497 }
498 }
499 }
500 send_status(id, status);
501 xfree(data);
502}
503
Ben Lindstrombba81212001-06-25 05:01:22 +0000504static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100505process_do_stat(int do_lstat)
506{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000507 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100508 struct stat st;
509 u_int32_t id;
510 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000511 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100512
513 id = get_int();
514 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000515 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100516 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
517 if (ret < 0) {
518 status = errno_to_portable(errno);
519 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000520 stat_to_attrib(&st, &a);
521 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000522 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100523 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000524 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100525 send_status(id, status);
526 xfree(name);
527}
528
Ben Lindstrombba81212001-06-25 05:01:22 +0000529static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100530process_stat(void)
531{
532 process_do_stat(0);
533}
534
Ben Lindstrombba81212001-06-25 05:01:22 +0000535static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100536process_lstat(void)
537{
538 process_do_stat(1);
539}
540
Ben Lindstrombba81212001-06-25 05:01:22 +0000541static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100542process_fstat(void)
543{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000544 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100545 struct stat st;
546 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000547 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100548
549 id = get_int();
550 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000551 TRACE("fstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100552 fd = handle_to_fd(handle);
553 if (fd >= 0) {
554 ret = fstat(fd, &st);
555 if (ret < 0) {
556 status = errno_to_portable(errno);
557 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000558 stat_to_attrib(&st, &a);
559 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000560 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100561 }
562 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000563 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100564 send_status(id, status);
565}
566
Ben Lindstrombba81212001-06-25 05:01:22 +0000567static struct timeval *
Damien Miller7b28dc52000-09-05 13:34:53 +1100568attrib_to_tv(Attrib *a)
569{
570 static struct timeval tv[2];
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000571
Damien Miller7b28dc52000-09-05 13:34:53 +1100572 tv[0].tv_sec = a->atime;
573 tv[0].tv_usec = 0;
574 tv[1].tv_sec = a->mtime;
575 tv[1].tv_usec = 0;
576 return tv;
577}
578
Ben Lindstrombba81212001-06-25 05:01:22 +0000579static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100580process_setstat(void)
581{
582 Attrib *a;
583 u_int32_t id;
584 char *name;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000585 int status = SSH2_FX_OK, ret;
Damien Miller7b28dc52000-09-05 13:34:53 +1100586
587 id = get_int();
588 name = get_string(NULL);
589 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000590 TRACE("setstat id %u name %s", id, name);
Damien Miller00c92172002-02-13 14:05:00 +1100591 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
592 ret = truncate(name, a->size);
593 if (ret == -1)
594 status = errno_to_portable(errno);
595 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000596 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100597 ret = chmod(name, a->perm & 0777);
598 if (ret == -1)
599 status = errno_to_portable(errno);
600 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000601 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100602 ret = utimes(name, attrib_to_tv(a));
603 if (ret == -1)
604 status = errno_to_portable(errno);
605 }
Kevin Steves8e743932001-02-05 13:24:35 +0000606 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
607 ret = chown(name, a->uid, a->gid);
608 if (ret == -1)
609 status = errno_to_portable(errno);
610 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100611 send_status(id, status);
612 xfree(name);
613}
614
Ben Lindstrombba81212001-06-25 05:01:22 +0000615static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100616process_fsetstat(void)
617{
618 Attrib *a;
619 u_int32_t id;
620 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000621 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100622 char *name;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000623
Damien Miller7b28dc52000-09-05 13:34:53 +1100624 id = get_int();
625 handle = get_handle();
626 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000627 TRACE("fsetstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100628 fd = handle_to_fd(handle);
629 name = handle_to_name(handle);
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000630 if (fd < 0 || name == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000631 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100632 } else {
Damien Miller00c92172002-02-13 14:05:00 +1100633 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
634 ret = ftruncate(fd, a->size);
635 if (ret == -1)
636 status = errno_to_portable(errno);
637 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000638 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000639#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100640 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000641#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000642 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000643#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100644 if (ret == -1)
645 status = errno_to_portable(errno);
646 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000647 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100648#ifdef HAVE_FUTIMES
649 ret = futimes(fd, attrib_to_tv(a));
650#else
651 ret = utimes(name, attrib_to_tv(a));
652#endif
653 if (ret == -1)
654 status = errno_to_portable(errno);
655 }
Kevin Steves8e743932001-02-05 13:24:35 +0000656 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000657#ifdef HAVE_FCHOWN
Kevin Steves8e743932001-02-05 13:24:35 +0000658 ret = fchown(fd, a->uid, a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000659#else
660 ret = chown(name, a->uid, a->gid);
661#endif
Kevin Steves8e743932001-02-05 13:24:35 +0000662 if (ret == -1)
663 status = errno_to_portable(errno);
664 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100665 }
666 send_status(id, status);
667}
668
Ben Lindstrombba81212001-06-25 05:01:22 +0000669static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100670process_opendir(void)
671{
672 DIR *dirp = NULL;
673 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000674 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100675 u_int32_t id;
676
677 id = get_int();
678 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000679 TRACE("opendir id %u path %s", id, path);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000680 dirp = opendir(path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100681 if (dirp == NULL) {
682 status = errno_to_portable(errno);
683 } else {
684 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
685 if (handle < 0) {
686 closedir(dirp);
687 } else {
688 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000689 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100690 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000691
Damien Miller7b28dc52000-09-05 13:34:53 +1100692 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000693 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100694 send_status(id, status);
695 xfree(path);
696}
697
Ben Lindstrombba81212001-06-25 05:01:22 +0000698static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100699process_readdir(void)
700{
701 DIR *dirp;
702 struct dirent *dp;
703 char *path;
704 int handle;
705 u_int32_t id;
706
707 id = get_int();
708 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000709 TRACE("readdir id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100710 dirp = handle_to_dir(handle);
711 path = handle_to_name(handle);
712 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000713 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100714 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100715 struct stat st;
716 char pathname[1024];
717 Stat *stats;
718 int nstats = 10, count = 0, i;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000719
Damien Miller7b28dc52000-09-05 13:34:53 +1100720 stats = xmalloc(nstats * sizeof(Stat));
721 while ((dp = readdir(dirp)) != NULL) {
722 if (count >= nstats) {
723 nstats *= 2;
724 stats = xrealloc(stats, nstats * sizeof(Stat));
725 }
726/* XXX OVERFLOW ? */
Ben Lindstrom95148e32001-08-06 21:30:53 +0000727 snprintf(pathname, sizeof pathname, "%s%s%s", path,
728 strcmp(path, "/") ? "/" : "", dp->d_name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100729 if (lstat(pathname, &st) < 0)
730 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000731 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100732 stats[count].name = xstrdup(dp->d_name);
Damien Millere1a49812002-09-12 09:54:25 +1000733 stats[count].long_name = ls_file(dp->d_name, &st, 0);
Damien Miller7b28dc52000-09-05 13:34:53 +1100734 count++;
735 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000736 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100737 if (count == 100)
738 break;
739 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000740 if (count > 0) {
741 send_names(id, count, stats);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100742 for (i = 0; i < count; i++) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000743 xfree(stats[i].name);
744 xfree(stats[i].long_name);
745 }
746 } else {
747 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100748 }
749 xfree(stats);
750 }
751}
752
Ben Lindstrombba81212001-06-25 05:01:22 +0000753static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100754process_remove(void)
755{
756 char *name;
757 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000758 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100759 int ret;
760
761 id = get_int();
762 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000763 TRACE("remove id %u name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000764 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000765 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100766 send_status(id, status);
767 xfree(name);
768}
769
Ben Lindstrombba81212001-06-25 05:01:22 +0000770static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100771process_mkdir(void)
772{
773 Attrib *a;
774 u_int32_t id;
775 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000776 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100777
778 id = get_int();
779 name = get_string(NULL);
780 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000781 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
782 a->perm & 0777 : 0777;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000783 TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100784 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000785 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100786 send_status(id, status);
787 xfree(name);
788}
789
Ben Lindstrombba81212001-06-25 05:01:22 +0000790static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100791process_rmdir(void)
792{
793 u_int32_t id;
794 char *name;
795 int ret, status;
796
797 id = get_int();
798 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000799 TRACE("rmdir id %u name %s", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100800 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000801 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100802 send_status(id, status);
803 xfree(name);
804}
805
Ben Lindstrombba81212001-06-25 05:01:22 +0000806static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100807process_realpath(void)
808{
809 char resolvedname[MAXPATHLEN];
810 u_int32_t id;
811 char *path;
812
813 id = get_int();
814 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000815 if (path[0] == '\0') {
816 xfree(path);
817 path = xstrdup(".");
818 }
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000819 TRACE("realpath id %u path %s", id, path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100820 if (realpath(path, resolvedname) == NULL) {
821 send_status(id, errno_to_portable(errno));
822 } else {
823 Stat s;
824 attrib_clear(&s.attrib);
825 s.name = s.long_name = resolvedname;
826 send_names(id, 1, &s);
827 }
828 xfree(path);
829}
830
Ben Lindstrombba81212001-06-25 05:01:22 +0000831static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100832process_rename(void)
833{
834 u_int32_t id;
835 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100836 int status;
Damien Miller7b28dc52000-09-05 13:34:53 +1100837
838 id = get_int();
839 oldpath = get_string(NULL);
840 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000841 TRACE("rename id %u old %s new %s", id, oldpath, newpath);
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000842 /* fail if 'newpath' exists */
Damien Miller9e51a732003-02-24 11:58:44 +1100843 if (link(oldpath, newpath) == -1)
844 status = errno_to_portable(errno);
845 else if (unlink(oldpath) == -1) {
846 status = errno_to_portable(errno);
847 /* clean spare link */
848 unlink(newpath);
849 } else
850 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100851 send_status(id, status);
852 xfree(oldpath);
853 xfree(newpath);
854}
855
Ben Lindstrombba81212001-06-25 05:01:22 +0000856static void
Damien Miller058316f2001-03-08 10:08:49 +1100857process_readlink(void)
858{
859 u_int32_t id;
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000860 int len;
Damien Miller058316f2001-03-08 10:08:49 +1100861 char link[MAXPATHLEN];
862 char *path;
863
864 id = get_int();
865 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000866 TRACE("readlink id %u path %s", id, path);
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000867 if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
Damien Miller058316f2001-03-08 10:08:49 +1100868 send_status(id, errno_to_portable(errno));
869 else {
870 Stat s;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100871
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000872 link[len] = '\0';
Damien Miller058316f2001-03-08 10:08:49 +1100873 attrib_clear(&s.attrib);
874 s.name = s.long_name = link;
875 send_names(id, 1, &s);
876 }
877 xfree(path);
878}
879
Ben Lindstrombba81212001-06-25 05:01:22 +0000880static void
Damien Miller058316f2001-03-08 10:08:49 +1100881process_symlink(void)
882{
883 u_int32_t id;
Damien Miller058316f2001-03-08 10:08:49 +1100884 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100885 int ret, status;
Damien Miller058316f2001-03-08 10:08:49 +1100886
887 id = get_int();
888 oldpath = get_string(NULL);
889 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000890 TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
Damien Miller9e51a732003-02-24 11:58:44 +1100891 /* this will fail if 'newpath' exists */
892 ret = symlink(oldpath, newpath);
893 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller058316f2001-03-08 10:08:49 +1100894 send_status(id, status);
895 xfree(oldpath);
896 xfree(newpath);
897}
898
Ben Lindstrombba81212001-06-25 05:01:22 +0000899static void
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000900process_extended(void)
901{
902 u_int32_t id;
903 char *request;
904
905 id = get_int();
906 request = get_string(NULL);
907 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
908 xfree(request);
909}
Damien Miller7b28dc52000-09-05 13:34:53 +1100910
911/* stolen from ssh-agent */
912
Ben Lindstrombba81212001-06-25 05:01:22 +0000913static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100914process(void)
915{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000916 u_int msg_len;
Ben Lindstrom2c140472002-06-06 21:57:54 +0000917 u_int buf_len;
918 u_int consumed;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000919 u_int type;
920 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100921
Ben Lindstrom2c140472002-06-06 21:57:54 +0000922 buf_len = buffer_len(&iqueue);
923 if (buf_len < 5)
Damien Miller7b28dc52000-09-05 13:34:53 +1100924 return; /* Incomplete message. */
Damien Miller708d21c2002-01-22 23:18:15 +1100925 cp = buffer_ptr(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +1100926 msg_len = GET_32BIT(cp);
927 if (msg_len > 256 * 1024) {
928 error("bad message ");
929 exit(11);
930 }
Ben Lindstrom2c140472002-06-06 21:57:54 +0000931 if (buf_len < msg_len + 4)
Damien Miller7b28dc52000-09-05 13:34:53 +1100932 return;
933 buffer_consume(&iqueue, 4);
Ben Lindstrom2c140472002-06-06 21:57:54 +0000934 buf_len -= 4;
Damien Miller7b28dc52000-09-05 13:34:53 +1100935 type = buffer_get_char(&iqueue);
936 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000937 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100938 process_init();
939 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000940 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100941 process_open();
942 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000943 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100944 process_close();
945 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000946 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100947 process_read();
948 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000949 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100950 process_write();
951 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000952 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100953 process_lstat();
954 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000955 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100956 process_fstat();
957 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000958 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100959 process_setstat();
960 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000961 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100962 process_fsetstat();
963 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000964 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100965 process_opendir();
966 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000967 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100968 process_readdir();
969 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000970 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100971 process_remove();
972 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000973 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100974 process_mkdir();
975 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000976 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100977 process_rmdir();
978 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000979 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +1100980 process_realpath();
981 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000982 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100983 process_stat();
984 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000985 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +1100986 process_rename();
987 break;
Damien Miller058316f2001-03-08 10:08:49 +1100988 case SSH2_FXP_READLINK:
989 process_readlink();
990 break;
991 case SSH2_FXP_SYMLINK:
992 process_symlink();
993 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000994 case SSH2_FXP_EXTENDED:
995 process_extended();
996 break;
Damien Miller7b28dc52000-09-05 13:34:53 +1100997 default:
998 error("Unknown message %d", type);
999 break;
1000 }
Ben Lindstrom2c140472002-06-06 21:57:54 +00001001 /* discard the remaining bytes from the current packet */
1002 if (buf_len < buffer_len(&iqueue))
1003 fatal("iqueue grows");
1004 consumed = buf_len - buffer_len(&iqueue);
1005 if (msg_len < consumed)
1006 fatal("msg_len %d < consumed %d", msg_len, consumed);
1007 if (msg_len > consumed)
1008 buffer_consume(&iqueue, msg_len - consumed);
Damien Miller7b28dc52000-09-05 13:34:53 +11001009}
1010
1011int
1012main(int ac, char **av)
1013{
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001014 fd_set *rset, *wset;
Damien Miller7b28dc52000-09-05 13:34:53 +11001015 int in, out, max;
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001016 ssize_t len, olen, set_size;
Damien Miller7b28dc52000-09-05 13:34:53 +11001017
Ben Lindstromc7f4ccd2001-03-15 00:09:15 +00001018 /* XXX should use getopt */
1019
Ben Lindstrom49a79c02000-11-17 03:47:20 +00001020 __progname = get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +11001021 handle_init();
1022
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001023#ifdef DEBUG_SFTP_SERVER
Kevin Stevesef4eea92001-02-05 12:42:17 +00001024 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001025#endif
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001026
Damien Miller7b28dc52000-09-05 13:34:53 +11001027 in = dup(STDIN_FILENO);
1028 out = dup(STDOUT_FILENO);
1029
Damien Miller402b3312001-04-14 00:28:42 +10001030#ifdef HAVE_CYGWIN
1031 setmode(in, O_BINARY);
1032 setmode(out, O_BINARY);
1033#endif
1034
Damien Miller7b28dc52000-09-05 13:34:53 +11001035 max = 0;
1036 if (in > max)
1037 max = in;
1038 if (out > max)
1039 max = out;
1040
1041 buffer_init(&iqueue);
1042 buffer_init(&oqueue);
1043
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001044 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1045 rset = (fd_set *)xmalloc(set_size);
1046 wset = (fd_set *)xmalloc(set_size);
Damien Miller7b28dc52000-09-05 13:34:53 +11001047
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001048 for (;;) {
1049 memset(rset, 0, set_size);
1050 memset(wset, 0, set_size);
1051
1052 FD_SET(in, rset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001053 olen = buffer_len(&oqueue);
1054 if (olen > 0)
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001055 FD_SET(out, wset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001056
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001057 if (select(max+1, rset, wset, NULL, NULL) < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001058 if (errno == EINTR)
1059 continue;
1060 exit(2);
1061 }
1062
1063 /* copy stdin to iqueue */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001064 if (FD_ISSET(in, rset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001065 char buf[4*4096];
1066 len = read(in, buf, sizeof buf);
1067 if (len == 0) {
1068 debug("read eof");
1069 exit(0);
1070 } else if (len < 0) {
1071 error("read error");
1072 exit(1);
1073 } else {
1074 buffer_append(&iqueue, buf, len);
1075 }
1076 }
1077 /* send oqueue to stdout */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001078 if (FD_ISSET(out, wset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001079 len = write(out, buffer_ptr(&oqueue), olen);
1080 if (len < 0) {
1081 error("write error");
1082 exit(1);
1083 } else {
1084 buffer_consume(&oqueue, len);
1085 }
1086 }
1087 /* process requests from client */
1088 process();
1089 }
1090}