blob: 96fe1a7bbac28d7600784258a14792ddf068d5d6 [file] [log] [blame]
Damien Miller7b28dc52000-09-05 13:34:53 +11001/*
Darren Tucker37bd3662004-02-24 09:19:15 +11002 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
Damien Miller7b28dc52000-09-05 13:34:53 +11003 *
Darren Tucker37bd3662004-02-24 09:19:15 +11004 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
Damien Miller7b28dc52000-09-05 13:34:53 +11007 *
Darren Tucker37bd3662004-02-24 09:19:15 +11008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Damien Miller7b28dc52000-09-05 13:34:53 +110015 */
16#include "includes.h"
Damien Miller88f254b2006-03-15 11:25:13 +110017RCSID("$OpenBSD: sftp-server.c,v 1.51 2006/02/08 23:51:24 stevesk Exp $");
18
19#include <dirent.h>
Damien Miller7b28dc52000-09-05 13:34:53 +110020
Damien Miller7b28dc52000-09-05 13:34:53 +110021#include "buffer.h"
22#include "bufaux.h"
23#include "getput.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000024#include "log.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110025#include "xmalloc.h"
Darren Tuckerce321d82005-10-03 18:11:24 +100026#include "misc.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110027
Ben Lindstrom2f959b42001-01-11 06:20:23 +000028#include "sftp.h"
Damien Miller33804262001-02-04 23:20:18 +110029#include "sftp-common.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110030
31/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000032#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110033#define get_int() buffer_get_int(&iqueue);
34#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Ben Lindstrom2f959b42001-01-11 06:20:23 +000035#define TRACE debug
Damien Miller7b28dc52000-09-05 13:34:53 +110036
Ben Lindstrom49a79c02000-11-17 03:47:20 +000037extern char *__progname;
Ben Lindstrom49a79c02000-11-17 03:47:20 +000038
Damien Miller7b28dc52000-09-05 13:34:53 +110039/* input and output queue */
40Buffer iqueue;
41Buffer oqueue;
42
Damien Miller058316f2001-03-08 10:08:49 +110043/* Version of client */
44int version;
45
Darren Tuckera6612d42003-06-28 12:39:03 +100046/* portable attributes, etc. */
Damien Miller7b28dc52000-09-05 13:34:53 +110047
Damien Miller7b28dc52000-09-05 13:34:53 +110048typedef struct Stat Stat;
49
Damien Miller33804262001-02-04 23:20:18 +110050struct Stat {
Damien Miller7b28dc52000-09-05 13:34:53 +110051 char *name;
52 char *long_name;
53 Attrib attrib;
54};
55
Ben Lindstrombba81212001-06-25 05:01:22 +000056static int
Damien Miller7b28dc52000-09-05 13:34:53 +110057errno_to_portable(int unixerrno)
58{
59 int ret = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000060
Damien Miller7b28dc52000-09-05 13:34:53 +110061 switch (unixerrno) {
62 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000063 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110064 break;
65 case ENOENT:
66 case ENOTDIR:
67 case EBADF:
68 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000069 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110070 break;
71 case EPERM:
72 case EACCES:
73 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000074 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110075 break;
76 case ENAMETOOLONG:
77 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000078 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110079 break;
80 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000081 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +110082 break;
83 }
84 return ret;
85}
86
Ben Lindstrombba81212001-06-25 05:01:22 +000087static int
Damien Miller7b28dc52000-09-05 13:34:53 +110088flags_from_portable(int pflags)
89{
90 int flags = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000091
Ben Lindstrom36592512001-03-05 05:02:08 +000092 if ((pflags & SSH2_FXF_READ) &&
93 (pflags & SSH2_FXF_WRITE)) {
Damien Miller7b28dc52000-09-05 13:34:53 +110094 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +000095 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +110096 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +000097 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +110098 flags = O_WRONLY;
99 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000100 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100101 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000102 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100103 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000104 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100105 flags |= O_EXCL;
106 return flags;
107}
108
Ben Lindstrombba81212001-06-25 05:01:22 +0000109static Attrib *
Damien Miller7b28dc52000-09-05 13:34:53 +1100110get_attrib(void)
111{
112 return decode_attrib(&iqueue);
113}
114
115/* handle handles */
116
117typedef struct Handle Handle;
118struct Handle {
119 int use;
120 DIR *dirp;
121 int fd;
122 char *name;
123};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000124
Damien Miller7b28dc52000-09-05 13:34:53 +1100125enum {
126 HANDLE_UNUSED,
127 HANDLE_DIR,
128 HANDLE_FILE
129};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000130
Damien Miller7b28dc52000-09-05 13:34:53 +1100131Handle handles[100];
132
Ben Lindstrombba81212001-06-25 05:01:22 +0000133static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100134handle_init(void)
135{
Damien Millereccb9de2005-06-17 12:59:34 +1000136 u_int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000137
Damien Miller9f0f5c62001-12-21 14:45:46 +1100138 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
Damien Miller7b28dc52000-09-05 13:34:53 +1100139 handles[i].use = HANDLE_UNUSED;
140}
141
Ben Lindstrombba81212001-06-25 05:01:22 +0000142static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100143handle_new(int use, const char *name, int fd, DIR *dirp)
Damien Miller7b28dc52000-09-05 13:34:53 +1100144{
Damien Millereccb9de2005-06-17 12:59:34 +1000145 u_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 if (handles[i].use == HANDLE_UNUSED) {
149 handles[i].use = use;
150 handles[i].dirp = dirp;
151 handles[i].fd = fd;
Damien Miller00111382003-03-10 11:21:17 +1100152 handles[i].name = xstrdup(name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100153 return i;
154 }
155 }
156 return -1;
157}
158
Ben Lindstrombba81212001-06-25 05:01:22 +0000159static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100160handle_is_ok(int i, int type)
161{
Damien Millereccb9de2005-06-17 12:59:34 +1000162 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000163 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100164}
165
Ben Lindstrombba81212001-06-25 05:01:22 +0000166static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100167handle_to_string(int handle, char **stringp, int *hlenp)
168{
Damien Miller7b28dc52000-09-05 13:34:53 +1100169 if (stringp == NULL || hlenp == NULL)
170 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000171 *stringp = xmalloc(sizeof(int32_t));
172 PUT_32BIT(*stringp, handle);
173 *hlenp = sizeof(int32_t);
Damien Miller7b28dc52000-09-05 13:34:53 +1100174 return 0;
175}
176
Ben Lindstrombba81212001-06-25 05:01:22 +0000177static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100178handle_from_string(const char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100179{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000180 int val;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000181
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000182 if (hlen != sizeof(int32_t))
Damien Miller7b28dc52000-09-05 13:34:53 +1100183 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000184 val = GET_32BIT(handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100185 if (handle_is_ok(val, HANDLE_FILE) ||
186 handle_is_ok(val, HANDLE_DIR))
187 return val;
188 return -1;
189}
190
Ben Lindstrombba81212001-06-25 05:01:22 +0000191static char *
Damien Miller7b28dc52000-09-05 13:34:53 +1100192handle_to_name(int handle)
193{
194 if (handle_is_ok(handle, HANDLE_DIR)||
195 handle_is_ok(handle, HANDLE_FILE))
196 return handles[handle].name;
197 return NULL;
198}
199
Ben Lindstrombba81212001-06-25 05:01:22 +0000200static DIR *
Damien Miller7b28dc52000-09-05 13:34:53 +1100201handle_to_dir(int handle)
202{
203 if (handle_is_ok(handle, HANDLE_DIR))
204 return handles[handle].dirp;
205 return NULL;
206}
207
Ben Lindstrombba81212001-06-25 05:01:22 +0000208static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100209handle_to_fd(int handle)
210{
Kevin Stevesef4eea92001-02-05 12:42:17 +0000211 if (handle_is_ok(handle, HANDLE_FILE))
Damien Miller7b28dc52000-09-05 13:34:53 +1100212 return handles[handle].fd;
213 return -1;
214}
215
Ben Lindstrombba81212001-06-25 05:01:22 +0000216static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100217handle_close(int handle)
218{
219 int ret = -1;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000220
Damien Miller7b28dc52000-09-05 13:34:53 +1100221 if (handle_is_ok(handle, HANDLE_FILE)) {
222 ret = close(handles[handle].fd);
223 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100224 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100225 } else if (handle_is_ok(handle, HANDLE_DIR)) {
226 ret = closedir(handles[handle].dirp);
227 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100228 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100229 } else {
230 errno = ENOENT;
231 }
232 return ret;
233}
234
Ben Lindstrombba81212001-06-25 05:01:22 +0000235static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100236get_handle(void)
237{
238 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000239 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100240 u_int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000241
Damien Miller7b28dc52000-09-05 13:34:53 +1100242 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000243 if (hlen < 256)
244 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100245 xfree(handle);
246 return val;
247}
248
249/* send replies */
250
Ben Lindstrombba81212001-06-25 05:01:22 +0000251static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100252send_msg(Buffer *m)
253{
254 int mlen = buffer_len(m);
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000255
Damien Miller7b28dc52000-09-05 13:34:53 +1100256 buffer_put_int(&oqueue, mlen);
257 buffer_append(&oqueue, buffer_ptr(m), mlen);
258 buffer_consume(m, mlen);
259}
260
Ben Lindstrombba81212001-06-25 05:01:22 +0000261static void
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000262send_status(u_int32_t id, u_int32_t status)
Damien Miller7b28dc52000-09-05 13:34:53 +1100263{
264 Buffer msg;
Damien Miller058316f2001-03-08 10:08:49 +1100265 const char *status_messages[] = {
266 "Success", /* SSH_FX_OK */
267 "End of file", /* SSH_FX_EOF */
268 "No such file", /* SSH_FX_NO_SUCH_FILE */
269 "Permission denied", /* SSH_FX_PERMISSION_DENIED */
270 "Failure", /* SSH_FX_FAILURE */
271 "Bad message", /* SSH_FX_BAD_MESSAGE */
272 "No connection", /* SSH_FX_NO_CONNECTION */
273 "Connection lost", /* SSH_FX_CONNECTION_LOST */
274 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
275 "Unknown error" /* Others */
276 };
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000277
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000278 TRACE("sent status id %u error %u", id, status);
Damien Miller7b28dc52000-09-05 13:34:53 +1100279 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000280 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100281 buffer_put_int(&msg, id);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000282 buffer_put_int(&msg, status);
Damien Miller058316f2001-03-08 10:08:49 +1100283 if (version >= 3) {
Ben Lindstroma3700052001-04-05 23:26:32 +0000284 buffer_put_cstring(&msg,
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000285 status_messages[MIN(status,SSH2_FX_MAX)]);
Damien Miller058316f2001-03-08 10:08:49 +1100286 buffer_put_cstring(&msg, "");
287 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100288 send_msg(&msg);
289 buffer_free(&msg);
290}
Ben Lindstrombba81212001-06-25 05:01:22 +0000291static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100292send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100293{
294 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000295
Damien Miller7b28dc52000-09-05 13:34:53 +1100296 buffer_init(&msg);
297 buffer_put_char(&msg, type);
298 buffer_put_int(&msg, id);
299 buffer_put_string(&msg, data, dlen);
300 send_msg(&msg);
301 buffer_free(&msg);
302}
303
Ben Lindstrombba81212001-06-25 05:01:22 +0000304static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100305send_data(u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100306{
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000307 TRACE("sent data id %u len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000308 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100309}
310
Ben Lindstrombba81212001-06-25 05:01:22 +0000311static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100312send_handle(u_int32_t id, int handle)
313{
314 char *string;
315 int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000316
Damien Miller7b28dc52000-09-05 13:34:53 +1100317 handle_to_string(handle, &string, &hlen);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000318 TRACE("sent handle id %u handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000319 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100320 xfree(string);
321}
322
Ben Lindstrombba81212001-06-25 05:01:22 +0000323static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100324send_names(u_int32_t id, int count, const Stat *stats)
Damien Miller7b28dc52000-09-05 13:34:53 +1100325{
326 Buffer msg;
327 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000328
Damien Miller7b28dc52000-09-05 13:34:53 +1100329 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000330 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100331 buffer_put_int(&msg, id);
332 buffer_put_int(&msg, count);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000333 TRACE("sent names id %u count %d", id, count);
Damien Miller7b28dc52000-09-05 13:34:53 +1100334 for (i = 0; i < count; i++) {
335 buffer_put_cstring(&msg, stats[i].name);
336 buffer_put_cstring(&msg, stats[i].long_name);
337 encode_attrib(&msg, &stats[i].attrib);
338 }
339 send_msg(&msg);
340 buffer_free(&msg);
341}
342
Ben Lindstrombba81212001-06-25 05:01:22 +0000343static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100344send_attrib(u_int32_t id, const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100345{
346 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000347
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000348 TRACE("sent attrib id %u have 0x%x", id, a->flags);
Damien Miller7b28dc52000-09-05 13:34:53 +1100349 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000350 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100351 buffer_put_int(&msg, id);
352 encode_attrib(&msg, a);
353 send_msg(&msg);
354 buffer_free(&msg);
355}
356
357/* parse incoming */
358
Ben Lindstrombba81212001-06-25 05:01:22 +0000359static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100360process_init(void)
361{
362 Buffer msg;
Damien Miller7b28dc52000-09-05 13:34:53 +1100363
Ben Lindstrom937df1d2002-06-06 21:58:35 +0000364 version = get_int();
Damien Miller7b28dc52000-09-05 13:34:53 +1100365 TRACE("client version %d", version);
366 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000367 buffer_put_char(&msg, SSH2_FXP_VERSION);
368 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100369 send_msg(&msg);
370 buffer_free(&msg);
371}
372
Ben Lindstrombba81212001-06-25 05:01:22 +0000373static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100374process_open(void)
375{
376 u_int32_t id, pflags;
377 Attrib *a;
378 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000379 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100380
381 id = get_int();
382 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000383 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100384 a = get_attrib();
385 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000386 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000387 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100388 fd = open(name, flags, mode);
389 if (fd < 0) {
390 status = errno_to_portable(errno);
391 } else {
Damien Miller00111382003-03-10 11:21:17 +1100392 handle = handle_new(HANDLE_FILE, name, fd, NULL);
Damien Miller7b28dc52000-09-05 13:34:53 +1100393 if (handle < 0) {
394 close(fd);
395 } else {
396 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000397 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100398 }
399 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000400 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100401 send_status(id, status);
402 xfree(name);
403}
404
Ben Lindstrombba81212001-06-25 05:01:22 +0000405static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100406process_close(void)
407{
408 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000409 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100410
411 id = get_int();
412 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000413 TRACE("close id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100414 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000415 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100416 send_status(id, status);
417}
418
Ben Lindstrombba81212001-06-25 05:01:22 +0000419static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100420process_read(void)
421{
422 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000423 u_int32_t id, len;
424 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100425 u_int64_t off;
426
427 id = get_int();
428 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000429 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100430 len = get_int();
431
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000432 TRACE("read id %u handle %d off %llu len %d", id, handle,
Darren Tuckere0be3042005-11-25 14:44:55 +1100433 (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100434 if (len > sizeof buf) {
435 len = sizeof buf;
Damien Miller996acd22003-04-09 20:59:48 +1000436 logit("read change len %d", len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100437 }
438 fd = handle_to_fd(handle);
439 if (fd >= 0) {
440 if (lseek(fd, off, SEEK_SET) < 0) {
441 error("process_read: seek failed");
442 status = errno_to_portable(errno);
443 } else {
444 ret = read(fd, buf, len);
445 if (ret < 0) {
446 status = errno_to_portable(errno);
447 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000448 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100449 } else {
450 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000451 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100452 }
453 }
454 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000455 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100456 send_status(id, status);
457}
458
Ben Lindstrombba81212001-06-25 05:01:22 +0000459static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100460process_write(void)
461{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000462 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100463 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100464 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000465 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100466 char *data;
467
468 id = get_int();
469 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000470 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100471 data = get_string(&len);
472
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000473 TRACE("write id %u handle %d off %llu len %d", id, handle,
Darren Tuckere0be3042005-11-25 14:44:55 +1100474 (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100475 fd = handle_to_fd(handle);
476 if (fd >= 0) {
477 if (lseek(fd, off, SEEK_SET) < 0) {
478 status = errno_to_portable(errno);
479 error("process_write: seek failed");
480 } else {
481/* XXX ATOMICIO ? */
482 ret = write(fd, data, len);
Damien Millereccb9de2005-06-17 12:59:34 +1000483 if (ret < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100484 error("process_write: write failed");
485 status = errno_to_portable(errno);
Damien Millereccb9de2005-06-17 12:59:34 +1000486 } else if ((size_t)ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000487 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100488 } else {
Damien Miller996acd22003-04-09 20:59:48 +1000489 logit("nothing at all written");
Damien Miller7b28dc52000-09-05 13:34:53 +1100490 }
491 }
492 }
493 send_status(id, status);
494 xfree(data);
495}
496
Ben Lindstrombba81212001-06-25 05:01:22 +0000497static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100498process_do_stat(int do_lstat)
499{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000500 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100501 struct stat st;
502 u_int32_t id;
503 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000504 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100505
506 id = get_int();
507 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000508 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100509 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
510 if (ret < 0) {
511 status = errno_to_portable(errno);
512 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000513 stat_to_attrib(&st, &a);
514 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000515 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100516 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000517 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100518 send_status(id, status);
519 xfree(name);
520}
521
Ben Lindstrombba81212001-06-25 05:01:22 +0000522static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100523process_stat(void)
524{
525 process_do_stat(0);
526}
527
Ben Lindstrombba81212001-06-25 05:01:22 +0000528static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100529process_lstat(void)
530{
531 process_do_stat(1);
532}
533
Ben Lindstrombba81212001-06-25 05:01:22 +0000534static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100535process_fstat(void)
536{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000537 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100538 struct stat st;
539 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000540 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100541
542 id = get_int();
543 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000544 TRACE("fstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100545 fd = handle_to_fd(handle);
546 if (fd >= 0) {
547 ret = fstat(fd, &st);
548 if (ret < 0) {
549 status = errno_to_portable(errno);
550 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000551 stat_to_attrib(&st, &a);
552 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000553 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100554 }
555 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000556 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100557 send_status(id, status);
558}
559
Ben Lindstrombba81212001-06-25 05:01:22 +0000560static struct timeval *
Damien Millerf58b58c2003-11-17 21:18:23 +1100561attrib_to_tv(const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100562{
563 static struct timeval tv[2];
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000564
Damien Miller7b28dc52000-09-05 13:34:53 +1100565 tv[0].tv_sec = a->atime;
566 tv[0].tv_usec = 0;
567 tv[1].tv_sec = a->mtime;
568 tv[1].tv_usec = 0;
569 return tv;
570}
571
Ben Lindstrombba81212001-06-25 05:01:22 +0000572static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100573process_setstat(void)
574{
575 Attrib *a;
576 u_int32_t id;
577 char *name;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000578 int status = SSH2_FX_OK, ret;
Damien Miller7b28dc52000-09-05 13:34:53 +1100579
580 id = get_int();
581 name = get_string(NULL);
582 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000583 TRACE("setstat id %u name %s", id, name);
Damien Miller00c92172002-02-13 14:05:00 +1100584 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
585 ret = truncate(name, a->size);
586 if (ret == -1)
587 status = errno_to_portable(errno);
588 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000589 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100590 ret = chmod(name, a->perm & 0777);
591 if (ret == -1)
592 status = errno_to_portable(errno);
593 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000594 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100595 ret = utimes(name, attrib_to_tv(a));
596 if (ret == -1)
597 status = errno_to_portable(errno);
598 }
Kevin Steves8e743932001-02-05 13:24:35 +0000599 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
600 ret = chown(name, a->uid, a->gid);
601 if (ret == -1)
602 status = errno_to_portable(errno);
603 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100604 send_status(id, status);
605 xfree(name);
606}
607
Ben Lindstrombba81212001-06-25 05:01:22 +0000608static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100609process_fsetstat(void)
610{
611 Attrib *a;
612 u_int32_t id;
613 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000614 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100615 char *name;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000616
Damien Miller7b28dc52000-09-05 13:34:53 +1100617 id = get_int();
618 handle = get_handle();
619 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000620 TRACE("fsetstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100621 fd = handle_to_fd(handle);
622 name = handle_to_name(handle);
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000623 if (fd < 0 || name == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000624 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100625 } else {
Damien Miller00c92172002-02-13 14:05:00 +1100626 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
627 ret = ftruncate(fd, a->size);
628 if (ret == -1)
629 status = errno_to_portable(errno);
630 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000631 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000632#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100633 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000634#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000635 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000636#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100637 if (ret == -1)
638 status = errno_to_portable(errno);
639 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000640 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100641#ifdef HAVE_FUTIMES
642 ret = futimes(fd, attrib_to_tv(a));
643#else
644 ret = utimes(name, attrib_to_tv(a));
645#endif
646 if (ret == -1)
647 status = errno_to_portable(errno);
648 }
Kevin Steves8e743932001-02-05 13:24:35 +0000649 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000650#ifdef HAVE_FCHOWN
Kevin Steves8e743932001-02-05 13:24:35 +0000651 ret = fchown(fd, a->uid, a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000652#else
653 ret = chown(name, a->uid, a->gid);
654#endif
Kevin Steves8e743932001-02-05 13:24:35 +0000655 if (ret == -1)
656 status = errno_to_portable(errno);
657 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100658 }
659 send_status(id, status);
660}
661
Ben Lindstrombba81212001-06-25 05:01:22 +0000662static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100663process_opendir(void)
664{
665 DIR *dirp = NULL;
666 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000667 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100668 u_int32_t id;
669
670 id = get_int();
671 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000672 TRACE("opendir id %u path %s", id, path);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000673 dirp = opendir(path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100674 if (dirp == NULL) {
675 status = errno_to_portable(errno);
676 } else {
Damien Miller00111382003-03-10 11:21:17 +1100677 handle = handle_new(HANDLE_DIR, path, 0, dirp);
Damien Miller7b28dc52000-09-05 13:34:53 +1100678 if (handle < 0) {
679 closedir(dirp);
680 } else {
681 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000682 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100683 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000684
Damien Miller7b28dc52000-09-05 13:34:53 +1100685 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000686 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100687 send_status(id, status);
688 xfree(path);
689}
690
Ben Lindstrombba81212001-06-25 05:01:22 +0000691static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100692process_readdir(void)
693{
694 DIR *dirp;
695 struct dirent *dp;
696 char *path;
697 int handle;
698 u_int32_t id;
699
700 id = get_int();
701 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000702 TRACE("readdir id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100703 dirp = handle_to_dir(handle);
704 path = handle_to_name(handle);
705 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000706 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100707 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100708 struct stat st;
709 char pathname[1024];
710 Stat *stats;
711 int nstats = 10, count = 0, i;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000712
Damien Miller7b28dc52000-09-05 13:34:53 +1100713 stats = xmalloc(nstats * sizeof(Stat));
714 while ((dp = readdir(dirp)) != NULL) {
715 if (count >= nstats) {
716 nstats *= 2;
717 stats = xrealloc(stats, nstats * sizeof(Stat));
718 }
719/* XXX OVERFLOW ? */
Ben Lindstrom95148e32001-08-06 21:30:53 +0000720 snprintf(pathname, sizeof pathname, "%s%s%s", path,
721 strcmp(path, "/") ? "/" : "", dp->d_name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100722 if (lstat(pathname, &st) < 0)
723 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000724 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100725 stats[count].name = xstrdup(dp->d_name);
Damien Millere1a49812002-09-12 09:54:25 +1000726 stats[count].long_name = ls_file(dp->d_name, &st, 0);
Damien Miller7b28dc52000-09-05 13:34:53 +1100727 count++;
728 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000729 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100730 if (count == 100)
731 break;
732 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000733 if (count > 0) {
734 send_names(id, count, stats);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100735 for (i = 0; i < count; i++) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000736 xfree(stats[i].name);
737 xfree(stats[i].long_name);
738 }
739 } else {
740 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100741 }
742 xfree(stats);
743 }
744}
745
Ben Lindstrombba81212001-06-25 05:01:22 +0000746static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100747process_remove(void)
748{
749 char *name;
750 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000751 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100752 int ret;
753
754 id = get_int();
755 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000756 TRACE("remove id %u name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000757 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000758 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100759 send_status(id, status);
760 xfree(name);
761}
762
Ben Lindstrombba81212001-06-25 05:01:22 +0000763static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100764process_mkdir(void)
765{
766 Attrib *a;
767 u_int32_t id;
768 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000769 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100770
771 id = get_int();
772 name = get_string(NULL);
773 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000774 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
775 a->perm & 0777 : 0777;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000776 TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100777 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000778 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100779 send_status(id, status);
780 xfree(name);
781}
782
Ben Lindstrombba81212001-06-25 05:01:22 +0000783static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100784process_rmdir(void)
785{
786 u_int32_t id;
787 char *name;
788 int ret, status;
789
790 id = get_int();
791 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000792 TRACE("rmdir id %u name %s", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100793 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000794 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100795 send_status(id, status);
796 xfree(name);
797}
798
Ben Lindstrombba81212001-06-25 05:01:22 +0000799static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100800process_realpath(void)
801{
802 char resolvedname[MAXPATHLEN];
803 u_int32_t id;
804 char *path;
805
806 id = get_int();
807 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000808 if (path[0] == '\0') {
809 xfree(path);
810 path = xstrdup(".");
811 }
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000812 TRACE("realpath id %u path %s", id, path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100813 if (realpath(path, resolvedname) == NULL) {
814 send_status(id, errno_to_portable(errno));
815 } else {
816 Stat s;
817 attrib_clear(&s.attrib);
818 s.name = s.long_name = resolvedname;
819 send_names(id, 1, &s);
820 }
821 xfree(path);
822}
823
Ben Lindstrombba81212001-06-25 05:01:22 +0000824static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100825process_rename(void)
826{
827 u_int32_t id;
828 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100829 int status;
Damien Millerb3207e82003-03-26 16:01:11 +1100830 struct stat sb;
Damien Miller7b28dc52000-09-05 13:34:53 +1100831
832 id = get_int();
833 oldpath = get_string(NULL);
834 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000835 TRACE("rename id %u old %s new %s", id, oldpath, newpath);
Damien Millerb3207e82003-03-26 16:01:11 +1100836 status = SSH2_FX_FAILURE;
837 if (lstat(oldpath, &sb) == -1)
Damien Miller9e51a732003-02-24 11:58:44 +1100838 status = errno_to_portable(errno);
Damien Millerb3207e82003-03-26 16:01:11 +1100839 else if (S_ISREG(sb.st_mode)) {
840 /* Race-free rename of regular files */
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000841 if (link(oldpath, newpath) == -1) {
Darren Tuckere59b5082004-06-28 16:01:19 +1000842 if (errno == EOPNOTSUPP
843#ifdef LINK_OPNOTSUPP_ERRNO
844 || errno == LINK_OPNOTSUPP_ERRNO
845#endif
846 ) {
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000847 struct stat st;
848
849 /*
850 * fs doesn't support links, so fall back to
851 * stat+rename. This is racy.
852 */
853 if (stat(newpath, &st) == -1) {
854 if (rename(oldpath, newpath) == -1)
855 status =
856 errno_to_portable(errno);
857 else
858 status = SSH2_FX_OK;
859 }
860 } else {
861 status = errno_to_portable(errno);
862 }
863 } else if (unlink(oldpath) == -1) {
Damien Millerb3207e82003-03-26 16:01:11 +1100864 status = errno_to_portable(errno);
865 /* clean spare link */
866 unlink(newpath);
867 } else
868 status = SSH2_FX_OK;
869 } else if (stat(newpath, &sb) == -1) {
870 if (rename(oldpath, newpath) == -1)
871 status = errno_to_portable(errno);
872 else
873 status = SSH2_FX_OK;
874 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100875 send_status(id, status);
876 xfree(oldpath);
877 xfree(newpath);
878}
879
Ben Lindstrombba81212001-06-25 05:01:22 +0000880static void
Damien Miller058316f2001-03-08 10:08:49 +1100881process_readlink(void)
882{
883 u_int32_t id;
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000884 int len;
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000885 char buf[MAXPATHLEN];
Damien Miller058316f2001-03-08 10:08:49 +1100886 char *path;
887
888 id = get_int();
889 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000890 TRACE("readlink id %u path %s", id, path);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000891 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
Damien Miller058316f2001-03-08 10:08:49 +1100892 send_status(id, errno_to_portable(errno));
893 else {
894 Stat s;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100895
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000896 buf[len] = '\0';
Damien Miller058316f2001-03-08 10:08:49 +1100897 attrib_clear(&s.attrib);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000898 s.name = s.long_name = buf;
Damien Miller058316f2001-03-08 10:08:49 +1100899 send_names(id, 1, &s);
900 }
901 xfree(path);
902}
903
Ben Lindstrombba81212001-06-25 05:01:22 +0000904static void
Damien Miller058316f2001-03-08 10:08:49 +1100905process_symlink(void)
906{
907 u_int32_t id;
Damien Miller058316f2001-03-08 10:08:49 +1100908 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100909 int ret, status;
Damien Miller058316f2001-03-08 10:08:49 +1100910
911 id = get_int();
912 oldpath = get_string(NULL);
913 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000914 TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
Damien Miller9e51a732003-02-24 11:58:44 +1100915 /* this will fail if 'newpath' exists */
916 ret = symlink(oldpath, newpath);
917 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller058316f2001-03-08 10:08:49 +1100918 send_status(id, status);
919 xfree(oldpath);
920 xfree(newpath);
921}
922
Ben Lindstrombba81212001-06-25 05:01:22 +0000923static void
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000924process_extended(void)
925{
926 u_int32_t id;
927 char *request;
928
929 id = get_int();
930 request = get_string(NULL);
931 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
932 xfree(request);
933}
Damien Miller7b28dc52000-09-05 13:34:53 +1100934
935/* stolen from ssh-agent */
936
Ben Lindstrombba81212001-06-25 05:01:22 +0000937static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100938process(void)
939{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000940 u_int msg_len;
Ben Lindstrom2c140472002-06-06 21:57:54 +0000941 u_int buf_len;
942 u_int consumed;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000943 u_int type;
944 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100945
Ben Lindstrom2c140472002-06-06 21:57:54 +0000946 buf_len = buffer_len(&iqueue);
947 if (buf_len < 5)
Damien Miller7b28dc52000-09-05 13:34:53 +1100948 return; /* Incomplete message. */
Damien Miller708d21c2002-01-22 23:18:15 +1100949 cp = buffer_ptr(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +1100950 msg_len = GET_32BIT(cp);
Damien Miller54446182006-01-02 23:40:50 +1100951 if (msg_len > SFTP_MAX_MSG_LENGTH) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100952 error("bad message ");
953 exit(11);
954 }
Ben Lindstrom2c140472002-06-06 21:57:54 +0000955 if (buf_len < msg_len + 4)
Damien Miller7b28dc52000-09-05 13:34:53 +1100956 return;
957 buffer_consume(&iqueue, 4);
Ben Lindstrom2c140472002-06-06 21:57:54 +0000958 buf_len -= 4;
Damien Miller7b28dc52000-09-05 13:34:53 +1100959 type = buffer_get_char(&iqueue);
960 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000961 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100962 process_init();
963 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000964 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100965 process_open();
966 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000967 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100968 process_close();
969 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000970 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100971 process_read();
972 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000973 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100974 process_write();
975 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000976 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100977 process_lstat();
978 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000979 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100980 process_fstat();
981 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000982 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100983 process_setstat();
984 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000985 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100986 process_fsetstat();
987 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000988 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100989 process_opendir();
990 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000991 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100992 process_readdir();
993 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000994 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100995 process_remove();
996 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000997 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100998 process_mkdir();
999 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001000 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001001 process_rmdir();
1002 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001003 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +11001004 process_realpath();
1005 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001006 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001007 process_stat();
1008 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001009 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +11001010 process_rename();
1011 break;
Damien Miller058316f2001-03-08 10:08:49 +11001012 case SSH2_FXP_READLINK:
1013 process_readlink();
1014 break;
1015 case SSH2_FXP_SYMLINK:
1016 process_symlink();
1017 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001018 case SSH2_FXP_EXTENDED:
1019 process_extended();
1020 break;
Damien Miller7b28dc52000-09-05 13:34:53 +11001021 default:
1022 error("Unknown message %d", type);
1023 break;
1024 }
Ben Lindstrom2c140472002-06-06 21:57:54 +00001025 /* discard the remaining bytes from the current packet */
1026 if (buf_len < buffer_len(&iqueue))
1027 fatal("iqueue grows");
1028 consumed = buf_len - buffer_len(&iqueue);
1029 if (msg_len < consumed)
1030 fatal("msg_len %d < consumed %d", msg_len, consumed);
1031 if (msg_len > consumed)
1032 buffer_consume(&iqueue, msg_len - consumed);
Damien Miller7b28dc52000-09-05 13:34:53 +11001033}
1034
1035int
1036main(int ac, char **av)
1037{
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001038 fd_set *rset, *wset;
Damien Miller7b28dc52000-09-05 13:34:53 +11001039 int in, out, max;
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001040 ssize_t len, olen, set_size;
Damien Miller7b28dc52000-09-05 13:34:53 +11001041
Darren Tuckerce321d82005-10-03 18:11:24 +10001042 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1043 sanitise_stdfd();
1044
Ben Lindstromc7f4ccd2001-03-15 00:09:15 +00001045 /* XXX should use getopt */
1046
Damien Miller59d3d5b2003-08-22 09:34:41 +10001047 __progname = ssh_get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +11001048 handle_init();
1049
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001050#ifdef DEBUG_SFTP_SERVER
Kevin Stevesef4eea92001-02-05 12:42:17 +00001051 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001052#endif
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001053
Damien Miller7b28dc52000-09-05 13:34:53 +11001054 in = dup(STDIN_FILENO);
1055 out = dup(STDOUT_FILENO);
1056
Damien Miller402b3312001-04-14 00:28:42 +10001057#ifdef HAVE_CYGWIN
1058 setmode(in, O_BINARY);
1059 setmode(out, O_BINARY);
1060#endif
1061
Damien Miller7b28dc52000-09-05 13:34:53 +11001062 max = 0;
1063 if (in > max)
1064 max = in;
1065 if (out > max)
1066 max = out;
1067
1068 buffer_init(&iqueue);
1069 buffer_init(&oqueue);
1070
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001071 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1072 rset = (fd_set *)xmalloc(set_size);
1073 wset = (fd_set *)xmalloc(set_size);
Damien Miller7b28dc52000-09-05 13:34:53 +11001074
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001075 for (;;) {
1076 memset(rset, 0, set_size);
1077 memset(wset, 0, set_size);
1078
1079 FD_SET(in, rset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001080 olen = buffer_len(&oqueue);
1081 if (olen > 0)
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001082 FD_SET(out, wset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001083
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001084 if (select(max+1, rset, wset, NULL, NULL) < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001085 if (errno == EINTR)
1086 continue;
1087 exit(2);
1088 }
1089
1090 /* copy stdin to iqueue */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001091 if (FD_ISSET(in, rset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001092 char buf[4*4096];
1093 len = read(in, buf, sizeof buf);
1094 if (len == 0) {
1095 debug("read eof");
1096 exit(0);
1097 } else if (len < 0) {
1098 error("read error");
1099 exit(1);
1100 } else {
1101 buffer_append(&iqueue, buf, len);
1102 }
1103 }
1104 /* send oqueue to stdout */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001105 if (FD_ISSET(out, wset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001106 len = write(out, buffer_ptr(&oqueue), olen);
1107 if (len < 0) {
1108 error("write error");
1109 exit(1);
1110 } else {
1111 buffer_consume(&oqueue, len);
1112 }
1113 }
1114 /* process requests from client */
1115 process();
1116 }
1117}