blob: 9166853ed7fb143174b3fc0cc6621aae3ebe4de8 [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"
Darren Tuckera6612d42003-06-28 12:39:03 +100025RCSID("$OpenBSD: sftp-server.c,v 1.43 2003/06/25 22:39:36 miod 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
Darren Tuckera6612d42003-06-28 12:39:03 +100055/* portable attributes, etc. */
Damien Miller7b28dc52000-09-05 13:34:53 +110056
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;
Damien Miller00111382003-03-10 11:21:17 +1100161 handles[i].name = xstrdup(name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100162 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;
Damien Miller00111382003-03-10 11:21:17 +1100233 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100234 } else if (handle_is_ok(handle, HANDLE_DIR)) {
235 ret = closedir(handles[handle].dirp);
236 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100237 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100238 } else {
239 errno = ENOENT;
240 }
241 return ret;
242}
243
Ben Lindstrombba81212001-06-25 05:01:22 +0000244static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100245get_handle(void)
246{
247 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000248 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100249 u_int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000250
Damien Miller7b28dc52000-09-05 13:34:53 +1100251 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000252 if (hlen < 256)
253 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100254 xfree(handle);
255 return val;
256}
257
258/* send replies */
259
Ben Lindstrombba81212001-06-25 05:01:22 +0000260static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100261send_msg(Buffer *m)
262{
263 int mlen = buffer_len(m);
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000264
Damien Miller7b28dc52000-09-05 13:34:53 +1100265 buffer_put_int(&oqueue, mlen);
266 buffer_append(&oqueue, buffer_ptr(m), mlen);
267 buffer_consume(m, mlen);
268}
269
Ben Lindstrombba81212001-06-25 05:01:22 +0000270static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100271send_status(u_int32_t id, u_int32_t error)
272{
273 Buffer msg;
Damien Miller058316f2001-03-08 10:08:49 +1100274 const char *status_messages[] = {
275 "Success", /* SSH_FX_OK */
276 "End of file", /* SSH_FX_EOF */
277 "No such file", /* SSH_FX_NO_SUCH_FILE */
278 "Permission denied", /* SSH_FX_PERMISSION_DENIED */
279 "Failure", /* SSH_FX_FAILURE */
280 "Bad message", /* SSH_FX_BAD_MESSAGE */
281 "No connection", /* SSH_FX_NO_CONNECTION */
282 "Connection lost", /* SSH_FX_CONNECTION_LOST */
283 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
284 "Unknown error" /* Others */
285 };
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000286
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000287 TRACE("sent status id %u error %u", id, error);
Damien Miller7b28dc52000-09-05 13:34:53 +1100288 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000289 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100290 buffer_put_int(&msg, id);
291 buffer_put_int(&msg, error);
Damien Miller058316f2001-03-08 10:08:49 +1100292 if (version >= 3) {
Ben Lindstroma3700052001-04-05 23:26:32 +0000293 buffer_put_cstring(&msg,
Damien Miller058316f2001-03-08 10:08:49 +1100294 status_messages[MIN(error,SSH2_FX_MAX)]);
295 buffer_put_cstring(&msg, "");
296 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100297 send_msg(&msg);
298 buffer_free(&msg);
299}
Ben Lindstrombba81212001-06-25 05:01:22 +0000300static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100301send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
302{
303 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000304
Damien Miller7b28dc52000-09-05 13:34:53 +1100305 buffer_init(&msg);
306 buffer_put_char(&msg, type);
307 buffer_put_int(&msg, id);
308 buffer_put_string(&msg, data, dlen);
309 send_msg(&msg);
310 buffer_free(&msg);
311}
312
Ben Lindstrombba81212001-06-25 05:01:22 +0000313static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100314send_data(u_int32_t id, char *data, int dlen)
315{
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000316 TRACE("sent data id %u len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000317 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100318}
319
Ben Lindstrombba81212001-06-25 05:01:22 +0000320static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100321send_handle(u_int32_t id, int handle)
322{
323 char *string;
324 int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000325
Damien Miller7b28dc52000-09-05 13:34:53 +1100326 handle_to_string(handle, &string, &hlen);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000327 TRACE("sent handle id %u handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000328 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100329 xfree(string);
330}
331
Ben Lindstrombba81212001-06-25 05:01:22 +0000332static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100333send_names(u_int32_t id, int count, Stat *stats)
334{
335 Buffer msg;
336 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000337
Damien Miller7b28dc52000-09-05 13:34:53 +1100338 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000339 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100340 buffer_put_int(&msg, id);
341 buffer_put_int(&msg, count);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000342 TRACE("sent names id %u count %d", id, count);
Damien Miller7b28dc52000-09-05 13:34:53 +1100343 for (i = 0; i < count; i++) {
344 buffer_put_cstring(&msg, stats[i].name);
345 buffer_put_cstring(&msg, stats[i].long_name);
346 encode_attrib(&msg, &stats[i].attrib);
347 }
348 send_msg(&msg);
349 buffer_free(&msg);
350}
351
Ben Lindstrombba81212001-06-25 05:01:22 +0000352static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100353send_attrib(u_int32_t id, Attrib *a)
354{
355 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000356
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000357 TRACE("sent attrib id %u have 0x%x", id, a->flags);
Damien Miller7b28dc52000-09-05 13:34:53 +1100358 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000359 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100360 buffer_put_int(&msg, id);
361 encode_attrib(&msg, a);
362 send_msg(&msg);
363 buffer_free(&msg);
364}
365
366/* parse incoming */
367
Ben Lindstrombba81212001-06-25 05:01:22 +0000368static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100369process_init(void)
370{
371 Buffer msg;
Damien Miller7b28dc52000-09-05 13:34:53 +1100372
Ben Lindstrom937df1d2002-06-06 21:58:35 +0000373 version = get_int();
Damien Miller7b28dc52000-09-05 13:34:53 +1100374 TRACE("client version %d", version);
375 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000376 buffer_put_char(&msg, SSH2_FXP_VERSION);
377 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100378 send_msg(&msg);
379 buffer_free(&msg);
380}
381
Ben Lindstrombba81212001-06-25 05:01:22 +0000382static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100383process_open(void)
384{
385 u_int32_t id, pflags;
386 Attrib *a;
387 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000388 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100389
390 id = get_int();
391 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000392 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100393 a = get_attrib();
394 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000395 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000396 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100397 fd = open(name, flags, mode);
398 if (fd < 0) {
399 status = errno_to_portable(errno);
400 } else {
Damien Miller00111382003-03-10 11:21:17 +1100401 handle = handle_new(HANDLE_FILE, name, fd, NULL);
Damien Miller7b28dc52000-09-05 13:34:53 +1100402 if (handle < 0) {
403 close(fd);
404 } else {
405 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000406 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100407 }
408 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000409 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100410 send_status(id, status);
411 xfree(name);
412}
413
Ben Lindstrombba81212001-06-25 05:01:22 +0000414static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100415process_close(void)
416{
417 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000418 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100419
420 id = get_int();
421 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000422 TRACE("close id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100423 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000424 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100425 send_status(id, status);
426}
427
Ben Lindstrombba81212001-06-25 05:01:22 +0000428static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100429process_read(void)
430{
431 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000432 u_int32_t id, len;
433 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100434 u_int64_t off;
435
436 id = get_int();
437 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000438 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100439 len = get_int();
440
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000441 TRACE("read id %u handle %d off %llu len %d", id, handle,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000442 (u_int64_t)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100443 if (len > sizeof buf) {
444 len = sizeof buf;
Damien Miller996acd22003-04-09 20:59:48 +1000445 logit("read change len %d", len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100446 }
447 fd = handle_to_fd(handle);
448 if (fd >= 0) {
449 if (lseek(fd, off, SEEK_SET) < 0) {
450 error("process_read: seek failed");
451 status = errno_to_portable(errno);
452 } else {
453 ret = read(fd, buf, len);
454 if (ret < 0) {
455 status = errno_to_portable(errno);
456 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000457 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100458 } else {
459 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000460 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100461 }
462 }
463 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000464 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100465 send_status(id, status);
466}
467
Ben Lindstrombba81212001-06-25 05:01:22 +0000468static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100469process_write(void)
470{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000471 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100472 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100473 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000474 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100475 char *data;
476
477 id = get_int();
478 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000479 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100480 data = get_string(&len);
481
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000482 TRACE("write id %u handle %d off %llu len %d", id, handle,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000483 (u_int64_t)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100484 fd = handle_to_fd(handle);
485 if (fd >= 0) {
486 if (lseek(fd, off, SEEK_SET) < 0) {
487 status = errno_to_portable(errno);
488 error("process_write: seek failed");
489 } else {
490/* XXX ATOMICIO ? */
491 ret = write(fd, data, len);
492 if (ret == -1) {
493 error("process_write: write failed");
494 status = errno_to_portable(errno);
495 } else if (ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000496 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100497 } else {
Damien Miller996acd22003-04-09 20:59:48 +1000498 logit("nothing at all written");
Damien Miller7b28dc52000-09-05 13:34:53 +1100499 }
500 }
501 }
502 send_status(id, status);
503 xfree(data);
504}
505
Ben Lindstrombba81212001-06-25 05:01:22 +0000506static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100507process_do_stat(int do_lstat)
508{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000509 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100510 struct stat st;
511 u_int32_t id;
512 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000513 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100514
515 id = get_int();
516 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000517 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100518 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
519 if (ret < 0) {
520 status = errno_to_portable(errno);
521 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000522 stat_to_attrib(&st, &a);
523 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000524 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100525 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000526 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100527 send_status(id, status);
528 xfree(name);
529}
530
Ben Lindstrombba81212001-06-25 05:01:22 +0000531static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100532process_stat(void)
533{
534 process_do_stat(0);
535}
536
Ben Lindstrombba81212001-06-25 05:01:22 +0000537static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100538process_lstat(void)
539{
540 process_do_stat(1);
541}
542
Ben Lindstrombba81212001-06-25 05:01:22 +0000543static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100544process_fstat(void)
545{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000546 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100547 struct stat st;
548 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000549 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100550
551 id = get_int();
552 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000553 TRACE("fstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100554 fd = handle_to_fd(handle);
555 if (fd >= 0) {
556 ret = fstat(fd, &st);
557 if (ret < 0) {
558 status = errno_to_portable(errno);
559 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000560 stat_to_attrib(&st, &a);
561 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000562 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100563 }
564 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000565 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100566 send_status(id, status);
567}
568
Ben Lindstrombba81212001-06-25 05:01:22 +0000569static struct timeval *
Damien Miller7b28dc52000-09-05 13:34:53 +1100570attrib_to_tv(Attrib *a)
571{
572 static struct timeval tv[2];
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000573
Damien Miller7b28dc52000-09-05 13:34:53 +1100574 tv[0].tv_sec = a->atime;
575 tv[0].tv_usec = 0;
576 tv[1].tv_sec = a->mtime;
577 tv[1].tv_usec = 0;
578 return tv;
579}
580
Ben Lindstrombba81212001-06-25 05:01:22 +0000581static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100582process_setstat(void)
583{
584 Attrib *a;
585 u_int32_t id;
586 char *name;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000587 int status = SSH2_FX_OK, ret;
Damien Miller7b28dc52000-09-05 13:34:53 +1100588
589 id = get_int();
590 name = get_string(NULL);
591 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000592 TRACE("setstat id %u name %s", id, name);
Damien Miller00c92172002-02-13 14:05:00 +1100593 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
594 ret = truncate(name, a->size);
595 if (ret == -1)
596 status = errno_to_portable(errno);
597 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000598 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100599 ret = chmod(name, a->perm & 0777);
600 if (ret == -1)
601 status = errno_to_portable(errno);
602 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000603 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100604 ret = utimes(name, attrib_to_tv(a));
605 if (ret == -1)
606 status = errno_to_portable(errno);
607 }
Kevin Steves8e743932001-02-05 13:24:35 +0000608 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
609 ret = chown(name, a->uid, a->gid);
610 if (ret == -1)
611 status = errno_to_portable(errno);
612 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100613 send_status(id, status);
614 xfree(name);
615}
616
Ben Lindstrombba81212001-06-25 05:01:22 +0000617static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100618process_fsetstat(void)
619{
620 Attrib *a;
621 u_int32_t id;
622 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000623 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100624 char *name;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000625
Damien Miller7b28dc52000-09-05 13:34:53 +1100626 id = get_int();
627 handle = get_handle();
628 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000629 TRACE("fsetstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100630 fd = handle_to_fd(handle);
631 name = handle_to_name(handle);
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000632 if (fd < 0 || name == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000633 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100634 } else {
Damien Miller00c92172002-02-13 14:05:00 +1100635 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
636 ret = ftruncate(fd, a->size);
637 if (ret == -1)
638 status = errno_to_portable(errno);
639 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000640 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000641#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100642 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000643#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000644 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000645#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100646 if (ret == -1)
647 status = errno_to_portable(errno);
648 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000649 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100650#ifdef HAVE_FUTIMES
651 ret = futimes(fd, attrib_to_tv(a));
652#else
653 ret = utimes(name, attrib_to_tv(a));
654#endif
655 if (ret == -1)
656 status = errno_to_portable(errno);
657 }
Kevin Steves8e743932001-02-05 13:24:35 +0000658 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000659#ifdef HAVE_FCHOWN
Kevin Steves8e743932001-02-05 13:24:35 +0000660 ret = fchown(fd, a->uid, a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000661#else
662 ret = chown(name, a->uid, a->gid);
663#endif
Kevin Steves8e743932001-02-05 13:24:35 +0000664 if (ret == -1)
665 status = errno_to_portable(errno);
666 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100667 }
668 send_status(id, status);
669}
670
Ben Lindstrombba81212001-06-25 05:01:22 +0000671static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100672process_opendir(void)
673{
674 DIR *dirp = NULL;
675 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000676 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100677 u_int32_t id;
678
679 id = get_int();
680 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000681 TRACE("opendir id %u path %s", id, path);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000682 dirp = opendir(path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100683 if (dirp == NULL) {
684 status = errno_to_portable(errno);
685 } else {
Damien Miller00111382003-03-10 11:21:17 +1100686 handle = handle_new(HANDLE_DIR, path, 0, dirp);
Damien Miller7b28dc52000-09-05 13:34:53 +1100687 if (handle < 0) {
688 closedir(dirp);
689 } else {
690 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000691 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100692 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000693
Damien Miller7b28dc52000-09-05 13:34:53 +1100694 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000695 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100696 send_status(id, status);
697 xfree(path);
698}
699
Ben Lindstrombba81212001-06-25 05:01:22 +0000700static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100701process_readdir(void)
702{
703 DIR *dirp;
704 struct dirent *dp;
705 char *path;
706 int handle;
707 u_int32_t id;
708
709 id = get_int();
710 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000711 TRACE("readdir id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100712 dirp = handle_to_dir(handle);
713 path = handle_to_name(handle);
714 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000715 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100716 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100717 struct stat st;
718 char pathname[1024];
719 Stat *stats;
720 int nstats = 10, count = 0, i;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000721
Damien Miller7b28dc52000-09-05 13:34:53 +1100722 stats = xmalloc(nstats * sizeof(Stat));
723 while ((dp = readdir(dirp)) != NULL) {
724 if (count >= nstats) {
725 nstats *= 2;
726 stats = xrealloc(stats, nstats * sizeof(Stat));
727 }
728/* XXX OVERFLOW ? */
Ben Lindstrom95148e32001-08-06 21:30:53 +0000729 snprintf(pathname, sizeof pathname, "%s%s%s", path,
730 strcmp(path, "/") ? "/" : "", dp->d_name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100731 if (lstat(pathname, &st) < 0)
732 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000733 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100734 stats[count].name = xstrdup(dp->d_name);
Damien Millere1a49812002-09-12 09:54:25 +1000735 stats[count].long_name = ls_file(dp->d_name, &st, 0);
Damien Miller7b28dc52000-09-05 13:34:53 +1100736 count++;
737 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000738 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100739 if (count == 100)
740 break;
741 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000742 if (count > 0) {
743 send_names(id, count, stats);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100744 for (i = 0; i < count; i++) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000745 xfree(stats[i].name);
746 xfree(stats[i].long_name);
747 }
748 } else {
749 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100750 }
751 xfree(stats);
752 }
753}
754
Ben Lindstrombba81212001-06-25 05:01:22 +0000755static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100756process_remove(void)
757{
758 char *name;
759 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000760 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100761 int ret;
762
763 id = get_int();
764 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000765 TRACE("remove id %u name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000766 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000767 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100768 send_status(id, status);
769 xfree(name);
770}
771
Ben Lindstrombba81212001-06-25 05:01:22 +0000772static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100773process_mkdir(void)
774{
775 Attrib *a;
776 u_int32_t id;
777 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000778 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100779
780 id = get_int();
781 name = get_string(NULL);
782 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000783 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
784 a->perm & 0777 : 0777;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000785 TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100786 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000787 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100788 send_status(id, status);
789 xfree(name);
790}
791
Ben Lindstrombba81212001-06-25 05:01:22 +0000792static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100793process_rmdir(void)
794{
795 u_int32_t id;
796 char *name;
797 int ret, status;
798
799 id = get_int();
800 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000801 TRACE("rmdir id %u name %s", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100802 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000803 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100804 send_status(id, status);
805 xfree(name);
806}
807
Ben Lindstrombba81212001-06-25 05:01:22 +0000808static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100809process_realpath(void)
810{
811 char resolvedname[MAXPATHLEN];
812 u_int32_t id;
813 char *path;
814
815 id = get_int();
816 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000817 if (path[0] == '\0') {
818 xfree(path);
819 path = xstrdup(".");
820 }
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000821 TRACE("realpath id %u path %s", id, path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100822 if (realpath(path, resolvedname) == NULL) {
823 send_status(id, errno_to_portable(errno));
824 } else {
825 Stat s;
826 attrib_clear(&s.attrib);
827 s.name = s.long_name = resolvedname;
828 send_names(id, 1, &s);
829 }
830 xfree(path);
831}
832
Ben Lindstrombba81212001-06-25 05:01:22 +0000833static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100834process_rename(void)
835{
836 u_int32_t id;
837 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100838 int status;
Damien Millerb3207e82003-03-26 16:01:11 +1100839 struct stat sb;
Damien Miller7b28dc52000-09-05 13:34:53 +1100840
841 id = get_int();
842 oldpath = get_string(NULL);
843 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000844 TRACE("rename id %u old %s new %s", id, oldpath, newpath);
Damien Millerb3207e82003-03-26 16:01:11 +1100845 status = SSH2_FX_FAILURE;
846 if (lstat(oldpath, &sb) == -1)
Damien Miller9e51a732003-02-24 11:58:44 +1100847 status = errno_to_portable(errno);
Damien Millerb3207e82003-03-26 16:01:11 +1100848 else if (S_ISREG(sb.st_mode)) {
849 /* Race-free rename of regular files */
850 if (link(oldpath, newpath) == -1)
851 status = errno_to_portable(errno);
852 else if (unlink(oldpath) == -1) {
853 status = errno_to_portable(errno);
854 /* clean spare link */
855 unlink(newpath);
856 } else
857 status = SSH2_FX_OK;
858 } else if (stat(newpath, &sb) == -1) {
859 if (rename(oldpath, newpath) == -1)
860 status = errno_to_portable(errno);
861 else
862 status = SSH2_FX_OK;
863 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100864 send_status(id, status);
865 xfree(oldpath);
866 xfree(newpath);
867}
868
Ben Lindstrombba81212001-06-25 05:01:22 +0000869static void
Damien Miller058316f2001-03-08 10:08:49 +1100870process_readlink(void)
871{
872 u_int32_t id;
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000873 int len;
Damien Miller058316f2001-03-08 10:08:49 +1100874 char link[MAXPATHLEN];
875 char *path;
876
877 id = get_int();
878 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000879 TRACE("readlink id %u path %s", id, path);
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000880 if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
Damien Miller058316f2001-03-08 10:08:49 +1100881 send_status(id, errno_to_portable(errno));
882 else {
883 Stat s;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100884
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000885 link[len] = '\0';
Damien Miller058316f2001-03-08 10:08:49 +1100886 attrib_clear(&s.attrib);
887 s.name = s.long_name = link;
888 send_names(id, 1, &s);
889 }
890 xfree(path);
891}
892
Ben Lindstrombba81212001-06-25 05:01:22 +0000893static void
Damien Miller058316f2001-03-08 10:08:49 +1100894process_symlink(void)
895{
896 u_int32_t id;
Damien Miller058316f2001-03-08 10:08:49 +1100897 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100898 int ret, status;
Damien Miller058316f2001-03-08 10:08:49 +1100899
900 id = get_int();
901 oldpath = get_string(NULL);
902 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000903 TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
Damien Miller9e51a732003-02-24 11:58:44 +1100904 /* this will fail if 'newpath' exists */
905 ret = symlink(oldpath, newpath);
906 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller058316f2001-03-08 10:08:49 +1100907 send_status(id, status);
908 xfree(oldpath);
909 xfree(newpath);
910}
911
Ben Lindstrombba81212001-06-25 05:01:22 +0000912static void
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000913process_extended(void)
914{
915 u_int32_t id;
916 char *request;
917
918 id = get_int();
919 request = get_string(NULL);
920 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
921 xfree(request);
922}
Damien Miller7b28dc52000-09-05 13:34:53 +1100923
924/* stolen from ssh-agent */
925
Ben Lindstrombba81212001-06-25 05:01:22 +0000926static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100927process(void)
928{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000929 u_int msg_len;
Ben Lindstrom2c140472002-06-06 21:57:54 +0000930 u_int buf_len;
931 u_int consumed;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000932 u_int type;
933 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100934
Ben Lindstrom2c140472002-06-06 21:57:54 +0000935 buf_len = buffer_len(&iqueue);
936 if (buf_len < 5)
Damien Miller7b28dc52000-09-05 13:34:53 +1100937 return; /* Incomplete message. */
Damien Miller708d21c2002-01-22 23:18:15 +1100938 cp = buffer_ptr(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +1100939 msg_len = GET_32BIT(cp);
940 if (msg_len > 256 * 1024) {
941 error("bad message ");
942 exit(11);
943 }
Ben Lindstrom2c140472002-06-06 21:57:54 +0000944 if (buf_len < msg_len + 4)
Damien Miller7b28dc52000-09-05 13:34:53 +1100945 return;
946 buffer_consume(&iqueue, 4);
Ben Lindstrom2c140472002-06-06 21:57:54 +0000947 buf_len -= 4;
Damien Miller7b28dc52000-09-05 13:34:53 +1100948 type = buffer_get_char(&iqueue);
949 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000950 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100951 process_init();
952 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000953 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100954 process_open();
955 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000956 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100957 process_close();
958 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000959 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100960 process_read();
961 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000962 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100963 process_write();
964 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000965 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100966 process_lstat();
967 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000968 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100969 process_fstat();
970 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000971 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100972 process_setstat();
973 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000974 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100975 process_fsetstat();
976 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000977 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100978 process_opendir();
979 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000980 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100981 process_readdir();
982 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000983 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100984 process_remove();
985 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000986 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100987 process_mkdir();
988 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000989 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100990 process_rmdir();
991 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000992 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +1100993 process_realpath();
994 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000995 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100996 process_stat();
997 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000998 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +1100999 process_rename();
1000 break;
Damien Miller058316f2001-03-08 10:08:49 +11001001 case SSH2_FXP_READLINK:
1002 process_readlink();
1003 break;
1004 case SSH2_FXP_SYMLINK:
1005 process_symlink();
1006 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001007 case SSH2_FXP_EXTENDED:
1008 process_extended();
1009 break;
Damien Miller7b28dc52000-09-05 13:34:53 +11001010 default:
1011 error("Unknown message %d", type);
1012 break;
1013 }
Ben Lindstrom2c140472002-06-06 21:57:54 +00001014 /* discard the remaining bytes from the current packet */
1015 if (buf_len < buffer_len(&iqueue))
1016 fatal("iqueue grows");
1017 consumed = buf_len - buffer_len(&iqueue);
1018 if (msg_len < consumed)
1019 fatal("msg_len %d < consumed %d", msg_len, consumed);
1020 if (msg_len > consumed)
1021 buffer_consume(&iqueue, msg_len - consumed);
Damien Miller7b28dc52000-09-05 13:34:53 +11001022}
1023
1024int
1025main(int ac, char **av)
1026{
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001027 fd_set *rset, *wset;
Damien Miller7b28dc52000-09-05 13:34:53 +11001028 int in, out, max;
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001029 ssize_t len, olen, set_size;
Damien Miller7b28dc52000-09-05 13:34:53 +11001030
Ben Lindstromc7f4ccd2001-03-15 00:09:15 +00001031 /* XXX should use getopt */
1032
Damien Miller59d3d5b2003-08-22 09:34:41 +10001033 __progname = ssh_get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +11001034 handle_init();
1035
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001036#ifdef DEBUG_SFTP_SERVER
Kevin Stevesef4eea92001-02-05 12:42:17 +00001037 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001038#endif
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001039
Damien Miller7b28dc52000-09-05 13:34:53 +11001040 in = dup(STDIN_FILENO);
1041 out = dup(STDOUT_FILENO);
1042
Damien Miller402b3312001-04-14 00:28:42 +10001043#ifdef HAVE_CYGWIN
1044 setmode(in, O_BINARY);
1045 setmode(out, O_BINARY);
1046#endif
1047
Damien Miller7b28dc52000-09-05 13:34:53 +11001048 max = 0;
1049 if (in > max)
1050 max = in;
1051 if (out > max)
1052 max = out;
1053
1054 buffer_init(&iqueue);
1055 buffer_init(&oqueue);
1056
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001057 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1058 rset = (fd_set *)xmalloc(set_size);
1059 wset = (fd_set *)xmalloc(set_size);
Damien Miller7b28dc52000-09-05 13:34:53 +11001060
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001061 for (;;) {
1062 memset(rset, 0, set_size);
1063 memset(wset, 0, set_size);
1064
1065 FD_SET(in, rset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001066 olen = buffer_len(&oqueue);
1067 if (olen > 0)
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001068 FD_SET(out, wset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001069
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001070 if (select(max+1, rset, wset, NULL, NULL) < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001071 if (errno == EINTR)
1072 continue;
1073 exit(2);
1074 }
1075
1076 /* copy stdin to iqueue */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001077 if (FD_ISSET(in, rset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001078 char buf[4*4096];
1079 len = read(in, buf, sizeof buf);
1080 if (len == 0) {
1081 debug("read eof");
1082 exit(0);
1083 } else if (len < 0) {
1084 error("read error");
1085 exit(1);
1086 } else {
1087 buffer_append(&iqueue, buf, len);
1088 }
1089 }
1090 /* send oqueue to stdout */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001091 if (FD_ISSET(out, wset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001092 len = write(out, buffer_ptr(&oqueue), olen);
1093 if (len < 0) {
1094 error("write error");
1095 exit(1);
1096 } else {
1097 buffer_consume(&oqueue, len);
1098 }
1099 }
1100 /* process requests from client */
1101 process();
1102 }
1103}