blob: e842341cb8d529b06b62665d182d2c47967fbd15 [file] [log] [blame]
Damien Miller3f941882006-03-31 23:13:02 +11001/* $OpenBSD: sftp-server.c,v 1.57 2006/03/30 09:58:16 djm Exp $ */
Damien Miller7b28dc52000-09-05 13:34:53 +11002/*
Darren Tucker37bd3662004-02-24 09:19:15 +11003 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
Damien Miller7b28dc52000-09-05 13:34:53 +11004 *
Darren Tucker37bd3662004-02-24 09:19:15 +11005 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
Damien Miller7b28dc52000-09-05 13:34:53 +11008 *
Darren Tucker37bd3662004-02-24 09:19:15 +11009 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Damien Miller7b28dc52000-09-05 13:34:53 +110016 */
17#include "includes.h"
Damien Millerf17883e2006-03-15 11:45:54 +110018
19#include <sys/types.h>
20#include <sys/stat.h>
Damien Miller88f254b2006-03-15 11:25:13 +110021
22#include <dirent.h>
Damien Miller7b28dc52000-09-05 13:34:53 +110023
Damien Miller7b28dc52000-09-05 13:34:53 +110024#include "buffer.h"
25#include "bufaux.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000026#include "log.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110027#include "xmalloc.h"
Darren Tuckerce321d82005-10-03 18:11:24 +100028#include "misc.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110029
Ben Lindstrom2f959b42001-01-11 06:20:23 +000030#include "sftp.h"
Damien Miller33804262001-02-04 23:20:18 +110031#include "sftp-common.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110032
33/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000034#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110035#define get_int() buffer_get_int(&iqueue);
36#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Ben Lindstrom2f959b42001-01-11 06:20:23 +000037#define TRACE debug
Damien Miller7b28dc52000-09-05 13:34:53 +110038
Ben Lindstrom49a79c02000-11-17 03:47:20 +000039extern char *__progname;
Ben Lindstrom49a79c02000-11-17 03:47:20 +000040
Damien Miller7b28dc52000-09-05 13:34:53 +110041/* input and output queue */
42Buffer iqueue;
43Buffer oqueue;
44
Damien Miller058316f2001-03-08 10:08:49 +110045/* Version of client */
46int version;
47
Darren Tuckera6612d42003-06-28 12:39:03 +100048/* portable attributes, etc. */
Damien Miller7b28dc52000-09-05 13:34:53 +110049
Damien Miller7b28dc52000-09-05 13:34:53 +110050typedef struct Stat Stat;
51
Damien Miller33804262001-02-04 23:20:18 +110052struct Stat {
Damien Miller7b28dc52000-09-05 13:34:53 +110053 char *name;
54 char *long_name;
55 Attrib attrib;
56};
57
Ben Lindstrombba81212001-06-25 05:01:22 +000058static int
Damien Miller7b28dc52000-09-05 13:34:53 +110059errno_to_portable(int unixerrno)
60{
61 int ret = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000062
Damien Miller7b28dc52000-09-05 13:34:53 +110063 switch (unixerrno) {
64 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000065 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110066 break;
67 case ENOENT:
68 case ENOTDIR:
69 case EBADF:
70 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000071 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110072 break;
73 case EPERM:
74 case EACCES:
75 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000076 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110077 break;
78 case ENAMETOOLONG:
79 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000080 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110081 break;
82 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000083 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +110084 break;
85 }
86 return ret;
87}
88
Ben Lindstrombba81212001-06-25 05:01:22 +000089static int
Damien Miller7b28dc52000-09-05 13:34:53 +110090flags_from_portable(int pflags)
91{
92 int flags = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000093
Ben Lindstrom36592512001-03-05 05:02:08 +000094 if ((pflags & SSH2_FXF_READ) &&
95 (pflags & SSH2_FXF_WRITE)) {
Damien Miller7b28dc52000-09-05 13:34:53 +110096 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +000097 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +110098 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +000099 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100100 flags = O_WRONLY;
101 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000102 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100103 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000104 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100105 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000106 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100107 flags |= O_EXCL;
108 return flags;
109}
110
Ben Lindstrombba81212001-06-25 05:01:22 +0000111static Attrib *
Damien Miller7b28dc52000-09-05 13:34:53 +1100112get_attrib(void)
113{
114 return decode_attrib(&iqueue);
115}
116
117/* handle handles */
118
119typedef struct Handle Handle;
120struct Handle {
121 int use;
122 DIR *dirp;
123 int fd;
124 char *name;
125};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000126
Damien Miller7b28dc52000-09-05 13:34:53 +1100127enum {
128 HANDLE_UNUSED,
129 HANDLE_DIR,
130 HANDLE_FILE
131};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000132
Damien Miller7b28dc52000-09-05 13:34:53 +1100133Handle handles[100];
134
Ben Lindstrombba81212001-06-25 05:01:22 +0000135static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100136handle_init(void)
137{
Damien Millereccb9de2005-06-17 12:59:34 +1000138 u_int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000139
Damien Miller9f0f5c62001-12-21 14:45:46 +1100140 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
Damien Miller7b28dc52000-09-05 13:34:53 +1100141 handles[i].use = HANDLE_UNUSED;
142}
143
Ben Lindstrombba81212001-06-25 05:01:22 +0000144static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100145handle_new(int use, const char *name, int fd, DIR *dirp)
Damien Miller7b28dc52000-09-05 13:34:53 +1100146{
Damien Millereccb9de2005-06-17 12:59:34 +1000147 u_int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000148
Damien Miller9f0f5c62001-12-21 14:45:46 +1100149 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100150 if (handles[i].use == HANDLE_UNUSED) {
151 handles[i].use = use;
152 handles[i].dirp = dirp;
153 handles[i].fd = fd;
Damien Miller00111382003-03-10 11:21:17 +1100154 handles[i].name = xstrdup(name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100155 return i;
156 }
157 }
158 return -1;
159}
160
Ben Lindstrombba81212001-06-25 05:01:22 +0000161static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100162handle_is_ok(int i, int type)
163{
Damien Millereccb9de2005-06-17 12:59:34 +1000164 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000165 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100166}
167
Ben Lindstrombba81212001-06-25 05:01:22 +0000168static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100169handle_to_string(int handle, char **stringp, int *hlenp)
170{
Damien Miller7b28dc52000-09-05 13:34:53 +1100171 if (stringp == NULL || hlenp == NULL)
172 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000173 *stringp = xmalloc(sizeof(int32_t));
Damien Miller3f941882006-03-31 23:13:02 +1100174 put_u32(*stringp, handle);
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000175 *hlenp = sizeof(int32_t);
Damien Miller7b28dc52000-09-05 13:34:53 +1100176 return 0;
177}
178
Ben Lindstrombba81212001-06-25 05:01:22 +0000179static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100180handle_from_string(const char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100181{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000182 int val;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000183
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000184 if (hlen != sizeof(int32_t))
Damien Miller7b28dc52000-09-05 13:34:53 +1100185 return -1;
Damien Miller3f941882006-03-31 23:13:02 +1100186 val = get_u32(handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100187 if (handle_is_ok(val, HANDLE_FILE) ||
188 handle_is_ok(val, HANDLE_DIR))
189 return val;
190 return -1;
191}
192
Ben Lindstrombba81212001-06-25 05:01:22 +0000193static char *
Damien Miller7b28dc52000-09-05 13:34:53 +1100194handle_to_name(int handle)
195{
196 if (handle_is_ok(handle, HANDLE_DIR)||
197 handle_is_ok(handle, HANDLE_FILE))
198 return handles[handle].name;
199 return NULL;
200}
201
Ben Lindstrombba81212001-06-25 05:01:22 +0000202static DIR *
Damien Miller7b28dc52000-09-05 13:34:53 +1100203handle_to_dir(int handle)
204{
205 if (handle_is_ok(handle, HANDLE_DIR))
206 return handles[handle].dirp;
207 return NULL;
208}
209
Ben Lindstrombba81212001-06-25 05:01:22 +0000210static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100211handle_to_fd(int handle)
212{
Kevin Stevesef4eea92001-02-05 12:42:17 +0000213 if (handle_is_ok(handle, HANDLE_FILE))
Damien Miller7b28dc52000-09-05 13:34:53 +1100214 return handles[handle].fd;
215 return -1;
216}
217
Ben Lindstrombba81212001-06-25 05:01:22 +0000218static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100219handle_close(int handle)
220{
221 int ret = -1;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000222
Damien Miller7b28dc52000-09-05 13:34:53 +1100223 if (handle_is_ok(handle, HANDLE_FILE)) {
224 ret = close(handles[handle].fd);
225 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100226 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100227 } else if (handle_is_ok(handle, HANDLE_DIR)) {
228 ret = closedir(handles[handle].dirp);
229 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100230 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100231 } else {
232 errno = ENOENT;
233 }
234 return ret;
235}
236
Ben Lindstrombba81212001-06-25 05:01:22 +0000237static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100238get_handle(void)
239{
240 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000241 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100242 u_int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000243
Damien Miller7b28dc52000-09-05 13:34:53 +1100244 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000245 if (hlen < 256)
246 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100247 xfree(handle);
248 return val;
249}
250
251/* send replies */
252
Ben Lindstrombba81212001-06-25 05:01:22 +0000253static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100254send_msg(Buffer *m)
255{
256 int mlen = buffer_len(m);
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000257
Damien Miller7b28dc52000-09-05 13:34:53 +1100258 buffer_put_int(&oqueue, mlen);
259 buffer_append(&oqueue, buffer_ptr(m), mlen);
260 buffer_consume(m, mlen);
261}
262
Ben Lindstrombba81212001-06-25 05:01:22 +0000263static void
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000264send_status(u_int32_t id, u_int32_t status)
Damien Miller7b28dc52000-09-05 13:34:53 +1100265{
266 Buffer msg;
Damien Miller058316f2001-03-08 10:08:49 +1100267 const char *status_messages[] = {
268 "Success", /* SSH_FX_OK */
269 "End of file", /* SSH_FX_EOF */
270 "No such file", /* SSH_FX_NO_SUCH_FILE */
271 "Permission denied", /* SSH_FX_PERMISSION_DENIED */
272 "Failure", /* SSH_FX_FAILURE */
273 "Bad message", /* SSH_FX_BAD_MESSAGE */
274 "No connection", /* SSH_FX_NO_CONNECTION */
275 "Connection lost", /* SSH_FX_CONNECTION_LOST */
276 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
277 "Unknown error" /* Others */
278 };
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000279
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000280 TRACE("sent status id %u error %u", id, status);
Damien Miller7b28dc52000-09-05 13:34:53 +1100281 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000282 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100283 buffer_put_int(&msg, id);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000284 buffer_put_int(&msg, status);
Damien Miller058316f2001-03-08 10:08:49 +1100285 if (version >= 3) {
Ben Lindstroma3700052001-04-05 23:26:32 +0000286 buffer_put_cstring(&msg,
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000287 status_messages[MIN(status,SSH2_FX_MAX)]);
Damien Miller058316f2001-03-08 10:08:49 +1100288 buffer_put_cstring(&msg, "");
289 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100290 send_msg(&msg);
291 buffer_free(&msg);
292}
Ben Lindstrombba81212001-06-25 05:01:22 +0000293static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100294send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100295{
296 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000297
Damien Miller7b28dc52000-09-05 13:34:53 +1100298 buffer_init(&msg);
299 buffer_put_char(&msg, type);
300 buffer_put_int(&msg, id);
301 buffer_put_string(&msg, data, dlen);
302 send_msg(&msg);
303 buffer_free(&msg);
304}
305
Ben Lindstrombba81212001-06-25 05:01:22 +0000306static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100307send_data(u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100308{
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000309 TRACE("sent data id %u len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000310 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100311}
312
Ben Lindstrombba81212001-06-25 05:01:22 +0000313static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100314send_handle(u_int32_t id, int handle)
315{
316 char *string;
317 int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000318
Damien Miller7b28dc52000-09-05 13:34:53 +1100319 handle_to_string(handle, &string, &hlen);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000320 TRACE("sent handle id %u handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000321 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100322 xfree(string);
323}
324
Ben Lindstrombba81212001-06-25 05:01:22 +0000325static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100326send_names(u_int32_t id, int count, const Stat *stats)
Damien Miller7b28dc52000-09-05 13:34:53 +1100327{
328 Buffer msg;
329 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000330
Damien Miller7b28dc52000-09-05 13:34:53 +1100331 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000332 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100333 buffer_put_int(&msg, id);
334 buffer_put_int(&msg, count);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000335 TRACE("sent names id %u count %d", id, count);
Damien Miller7b28dc52000-09-05 13:34:53 +1100336 for (i = 0; i < count; i++) {
337 buffer_put_cstring(&msg, stats[i].name);
338 buffer_put_cstring(&msg, stats[i].long_name);
339 encode_attrib(&msg, &stats[i].attrib);
340 }
341 send_msg(&msg);
342 buffer_free(&msg);
343}
344
Ben Lindstrombba81212001-06-25 05:01:22 +0000345static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100346send_attrib(u_int32_t id, const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100347{
348 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000349
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000350 TRACE("sent attrib id %u have 0x%x", id, a->flags);
Damien Miller7b28dc52000-09-05 13:34:53 +1100351 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000352 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100353 buffer_put_int(&msg, id);
354 encode_attrib(&msg, a);
355 send_msg(&msg);
356 buffer_free(&msg);
357}
358
359/* parse incoming */
360
Ben Lindstrombba81212001-06-25 05:01:22 +0000361static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100362process_init(void)
363{
364 Buffer msg;
Damien Miller7b28dc52000-09-05 13:34:53 +1100365
Ben Lindstrom937df1d2002-06-06 21:58:35 +0000366 version = get_int();
Damien Miller7b28dc52000-09-05 13:34:53 +1100367 TRACE("client version %d", version);
368 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000369 buffer_put_char(&msg, SSH2_FXP_VERSION);
370 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100371 send_msg(&msg);
372 buffer_free(&msg);
373}
374
Ben Lindstrombba81212001-06-25 05:01:22 +0000375static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100376process_open(void)
377{
378 u_int32_t id, pflags;
379 Attrib *a;
380 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000381 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100382
383 id = get_int();
384 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000385 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100386 a = get_attrib();
387 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000388 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000389 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100390 fd = open(name, flags, mode);
391 if (fd < 0) {
392 status = errno_to_portable(errno);
393 } else {
Damien Miller00111382003-03-10 11:21:17 +1100394 handle = handle_new(HANDLE_FILE, name, fd, NULL);
Damien Miller7b28dc52000-09-05 13:34:53 +1100395 if (handle < 0) {
396 close(fd);
397 } else {
398 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000399 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100400 }
401 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000402 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100403 send_status(id, status);
404 xfree(name);
405}
406
Ben Lindstrombba81212001-06-25 05:01:22 +0000407static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100408process_close(void)
409{
410 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000411 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100412
413 id = get_int();
414 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000415 TRACE("close id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100416 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000417 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100418 send_status(id, status);
419}
420
Ben Lindstrombba81212001-06-25 05:01:22 +0000421static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100422process_read(void)
423{
424 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000425 u_int32_t id, len;
426 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100427 u_int64_t off;
428
429 id = get_int();
430 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000431 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100432 len = get_int();
433
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000434 TRACE("read id %u handle %d off %llu len %d", id, handle,
Darren Tuckere0be3042005-11-25 14:44:55 +1100435 (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100436 if (len > sizeof buf) {
437 len = sizeof buf;
Damien Miller996acd22003-04-09 20:59:48 +1000438 logit("read change len %d", len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100439 }
440 fd = handle_to_fd(handle);
441 if (fd >= 0) {
442 if (lseek(fd, off, SEEK_SET) < 0) {
443 error("process_read: seek failed");
444 status = errno_to_portable(errno);
445 } else {
446 ret = read(fd, buf, len);
447 if (ret < 0) {
448 status = errno_to_portable(errno);
449 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000450 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100451 } else {
452 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000453 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100454 }
455 }
456 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000457 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100458 send_status(id, status);
459}
460
Ben Lindstrombba81212001-06-25 05:01:22 +0000461static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100462process_write(void)
463{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000464 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100465 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100466 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000467 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100468 char *data;
469
470 id = get_int();
471 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000472 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100473 data = get_string(&len);
474
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000475 TRACE("write id %u handle %d off %llu len %d", id, handle,
Darren Tuckere0be3042005-11-25 14:44:55 +1100476 (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100477 fd = handle_to_fd(handle);
478 if (fd >= 0) {
479 if (lseek(fd, off, SEEK_SET) < 0) {
480 status = errno_to_portable(errno);
481 error("process_write: seek failed");
482 } else {
483/* XXX ATOMICIO ? */
484 ret = write(fd, data, len);
Damien Millereccb9de2005-06-17 12:59:34 +1000485 if (ret < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100486 error("process_write: write failed");
487 status = errno_to_portable(errno);
Damien Millereccb9de2005-06-17 12:59:34 +1000488 } else if ((size_t)ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000489 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100490 } else {
Damien Miller996acd22003-04-09 20:59:48 +1000491 logit("nothing at all written");
Damien Miller7b28dc52000-09-05 13:34:53 +1100492 }
493 }
494 }
495 send_status(id, status);
496 xfree(data);
497}
498
Ben Lindstrombba81212001-06-25 05:01:22 +0000499static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100500process_do_stat(int do_lstat)
501{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000502 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100503 struct stat st;
504 u_int32_t id;
505 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000506 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100507
508 id = get_int();
509 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000510 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100511 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
512 if (ret < 0) {
513 status = errno_to_portable(errno);
514 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000515 stat_to_attrib(&st, &a);
516 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000517 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100518 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000519 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100520 send_status(id, status);
521 xfree(name);
522}
523
Ben Lindstrombba81212001-06-25 05:01:22 +0000524static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100525process_stat(void)
526{
527 process_do_stat(0);
528}
529
Ben Lindstrombba81212001-06-25 05:01:22 +0000530static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100531process_lstat(void)
532{
533 process_do_stat(1);
534}
535
Ben Lindstrombba81212001-06-25 05:01:22 +0000536static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100537process_fstat(void)
538{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000539 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100540 struct stat st;
541 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000542 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100543
544 id = get_int();
545 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000546 TRACE("fstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100547 fd = handle_to_fd(handle);
548 if (fd >= 0) {
549 ret = fstat(fd, &st);
550 if (ret < 0) {
551 status = errno_to_portable(errno);
552 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000553 stat_to_attrib(&st, &a);
554 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000555 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100556 }
557 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000558 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100559 send_status(id, status);
560}
561
Ben Lindstrombba81212001-06-25 05:01:22 +0000562static struct timeval *
Damien Millerf58b58c2003-11-17 21:18:23 +1100563attrib_to_tv(const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100564{
565 static struct timeval tv[2];
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000566
Damien Miller7b28dc52000-09-05 13:34:53 +1100567 tv[0].tv_sec = a->atime;
568 tv[0].tv_usec = 0;
569 tv[1].tv_sec = a->mtime;
570 tv[1].tv_usec = 0;
571 return tv;
572}
573
Ben Lindstrombba81212001-06-25 05:01:22 +0000574static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100575process_setstat(void)
576{
577 Attrib *a;
578 u_int32_t id;
579 char *name;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000580 int status = SSH2_FX_OK, ret;
Damien Miller7b28dc52000-09-05 13:34:53 +1100581
582 id = get_int();
583 name = get_string(NULL);
584 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000585 TRACE("setstat id %u name %s", id, name);
Damien Miller00c92172002-02-13 14:05:00 +1100586 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
587 ret = truncate(name, a->size);
588 if (ret == -1)
589 status = errno_to_portable(errno);
590 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000591 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100592 ret = chmod(name, a->perm & 0777);
593 if (ret == -1)
594 status = errno_to_portable(errno);
595 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000596 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100597 ret = utimes(name, attrib_to_tv(a));
598 if (ret == -1)
599 status = errno_to_portable(errno);
600 }
Kevin Steves8e743932001-02-05 13:24:35 +0000601 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
602 ret = chown(name, a->uid, a->gid);
603 if (ret == -1)
604 status = errno_to_portable(errno);
605 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100606 send_status(id, status);
607 xfree(name);
608}
609
Ben Lindstrombba81212001-06-25 05:01:22 +0000610static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100611process_fsetstat(void)
612{
613 Attrib *a;
614 u_int32_t id;
615 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000616 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100617 char *name;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000618
Damien Miller7b28dc52000-09-05 13:34:53 +1100619 id = get_int();
620 handle = get_handle();
621 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000622 TRACE("fsetstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100623 fd = handle_to_fd(handle);
624 name = handle_to_name(handle);
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000625 if (fd < 0 || name == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000626 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100627 } else {
Damien Miller00c92172002-02-13 14:05:00 +1100628 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
629 ret = ftruncate(fd, a->size);
630 if (ret == -1)
631 status = errno_to_portable(errno);
632 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000633 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000634#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100635 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000636#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000637 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000638#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100639 if (ret == -1)
640 status = errno_to_portable(errno);
641 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000642 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100643#ifdef HAVE_FUTIMES
644 ret = futimes(fd, attrib_to_tv(a));
645#else
646 ret = utimes(name, attrib_to_tv(a));
647#endif
648 if (ret == -1)
649 status = errno_to_portable(errno);
650 }
Kevin Steves8e743932001-02-05 13:24:35 +0000651 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000652#ifdef HAVE_FCHOWN
Kevin Steves8e743932001-02-05 13:24:35 +0000653 ret = fchown(fd, a->uid, a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000654#else
655 ret = chown(name, a->uid, a->gid);
656#endif
Kevin Steves8e743932001-02-05 13:24:35 +0000657 if (ret == -1)
658 status = errno_to_portable(errno);
659 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100660 }
661 send_status(id, status);
662}
663
Ben Lindstrombba81212001-06-25 05:01:22 +0000664static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100665process_opendir(void)
666{
667 DIR *dirp = NULL;
668 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000669 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100670 u_int32_t id;
671
672 id = get_int();
673 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000674 TRACE("opendir id %u path %s", id, path);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000675 dirp = opendir(path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100676 if (dirp == NULL) {
677 status = errno_to_portable(errno);
678 } else {
Damien Miller00111382003-03-10 11:21:17 +1100679 handle = handle_new(HANDLE_DIR, path, 0, dirp);
Damien Miller7b28dc52000-09-05 13:34:53 +1100680 if (handle < 0) {
681 closedir(dirp);
682 } else {
683 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000684 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100685 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000686
Damien Miller7b28dc52000-09-05 13:34:53 +1100687 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000688 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100689 send_status(id, status);
690 xfree(path);
691}
692
Ben Lindstrombba81212001-06-25 05:01:22 +0000693static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100694process_readdir(void)
695{
696 DIR *dirp;
697 struct dirent *dp;
698 char *path;
699 int handle;
700 u_int32_t id;
701
702 id = get_int();
703 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000704 TRACE("readdir id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100705 dirp = handle_to_dir(handle);
706 path = handle_to_name(handle);
707 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000708 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100709 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100710 struct stat st;
711 char pathname[1024];
712 Stat *stats;
713 int nstats = 10, count = 0, i;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000714
Damien Miller07d86be2006-03-26 14:19:21 +1100715 stats = xcalloc(nstats, sizeof(Stat));
Damien Miller7b28dc52000-09-05 13:34:53 +1100716 while ((dp = readdir(dirp)) != NULL) {
717 if (count >= nstats) {
718 nstats *= 2;
Damien Miller36812092006-03-26 14:22:47 +1100719 stats = xrealloc(stats, nstats, sizeof(Stat));
Damien Miller7b28dc52000-09-05 13:34:53 +1100720 }
721/* XXX OVERFLOW ? */
Ben Lindstrom95148e32001-08-06 21:30:53 +0000722 snprintf(pathname, sizeof pathname, "%s%s%s", path,
723 strcmp(path, "/") ? "/" : "", dp->d_name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100724 if (lstat(pathname, &st) < 0)
725 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000726 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100727 stats[count].name = xstrdup(dp->d_name);
Damien Millere1a49812002-09-12 09:54:25 +1000728 stats[count].long_name = ls_file(dp->d_name, &st, 0);
Damien Miller7b28dc52000-09-05 13:34:53 +1100729 count++;
730 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000731 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100732 if (count == 100)
733 break;
734 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000735 if (count > 0) {
736 send_names(id, count, stats);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100737 for (i = 0; i < count; i++) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000738 xfree(stats[i].name);
739 xfree(stats[i].long_name);
740 }
741 } else {
742 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100743 }
744 xfree(stats);
745 }
746}
747
Ben Lindstrombba81212001-06-25 05:01:22 +0000748static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100749process_remove(void)
750{
751 char *name;
752 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000753 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100754 int ret;
755
756 id = get_int();
757 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000758 TRACE("remove id %u name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000759 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000760 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100761 send_status(id, status);
762 xfree(name);
763}
764
Ben Lindstrombba81212001-06-25 05:01:22 +0000765static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100766process_mkdir(void)
767{
768 Attrib *a;
769 u_int32_t id;
770 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000771 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100772
773 id = get_int();
774 name = get_string(NULL);
775 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000776 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
777 a->perm & 0777 : 0777;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000778 TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100779 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000780 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100781 send_status(id, status);
782 xfree(name);
783}
784
Ben Lindstrombba81212001-06-25 05:01:22 +0000785static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100786process_rmdir(void)
787{
788 u_int32_t id;
789 char *name;
790 int ret, status;
791
792 id = get_int();
793 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000794 TRACE("rmdir id %u name %s", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100795 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000796 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100797 send_status(id, status);
798 xfree(name);
799}
800
Ben Lindstrombba81212001-06-25 05:01:22 +0000801static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100802process_realpath(void)
803{
804 char resolvedname[MAXPATHLEN];
805 u_int32_t id;
806 char *path;
807
808 id = get_int();
809 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000810 if (path[0] == '\0') {
811 xfree(path);
812 path = xstrdup(".");
813 }
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000814 TRACE("realpath id %u path %s", id, path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100815 if (realpath(path, resolvedname) == NULL) {
816 send_status(id, errno_to_portable(errno));
817 } else {
818 Stat s;
819 attrib_clear(&s.attrib);
820 s.name = s.long_name = resolvedname;
821 send_names(id, 1, &s);
822 }
823 xfree(path);
824}
825
Ben Lindstrombba81212001-06-25 05:01:22 +0000826static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100827process_rename(void)
828{
829 u_int32_t id;
830 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100831 int status;
Damien Millerb3207e82003-03-26 16:01:11 +1100832 struct stat sb;
Damien Miller7b28dc52000-09-05 13:34:53 +1100833
834 id = get_int();
835 oldpath = get_string(NULL);
836 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000837 TRACE("rename id %u old %s new %s", id, oldpath, newpath);
Damien Millerb3207e82003-03-26 16:01:11 +1100838 status = SSH2_FX_FAILURE;
839 if (lstat(oldpath, &sb) == -1)
Damien Miller9e51a732003-02-24 11:58:44 +1100840 status = errno_to_portable(errno);
Damien Millerb3207e82003-03-26 16:01:11 +1100841 else if (S_ISREG(sb.st_mode)) {
842 /* Race-free rename of regular files */
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000843 if (link(oldpath, newpath) == -1) {
Darren Tuckere59b5082004-06-28 16:01:19 +1000844 if (errno == EOPNOTSUPP
845#ifdef LINK_OPNOTSUPP_ERRNO
846 || errno == LINK_OPNOTSUPP_ERRNO
847#endif
848 ) {
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000849 struct stat st;
850
851 /*
852 * fs doesn't support links, so fall back to
853 * stat+rename. This is racy.
854 */
855 if (stat(newpath, &st) == -1) {
856 if (rename(oldpath, newpath) == -1)
857 status =
858 errno_to_portable(errno);
859 else
860 status = SSH2_FX_OK;
861 }
862 } else {
863 status = errno_to_portable(errno);
864 }
865 } else if (unlink(oldpath) == -1) {
Damien Millerb3207e82003-03-26 16:01:11 +1100866 status = errno_to_portable(errno);
867 /* clean spare link */
868 unlink(newpath);
869 } else
870 status = SSH2_FX_OK;
871 } else if (stat(newpath, &sb) == -1) {
872 if (rename(oldpath, newpath) == -1)
873 status = errno_to_portable(errno);
874 else
875 status = SSH2_FX_OK;
876 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100877 send_status(id, status);
878 xfree(oldpath);
879 xfree(newpath);
880}
881
Ben Lindstrombba81212001-06-25 05:01:22 +0000882static void
Damien Miller058316f2001-03-08 10:08:49 +1100883process_readlink(void)
884{
885 u_int32_t id;
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000886 int len;
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000887 char buf[MAXPATHLEN];
Damien Miller058316f2001-03-08 10:08:49 +1100888 char *path;
889
890 id = get_int();
891 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000892 TRACE("readlink id %u path %s", id, path);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000893 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
Damien Miller058316f2001-03-08 10:08:49 +1100894 send_status(id, errno_to_portable(errno));
895 else {
896 Stat s;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100897
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000898 buf[len] = '\0';
Damien Miller058316f2001-03-08 10:08:49 +1100899 attrib_clear(&s.attrib);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000900 s.name = s.long_name = buf;
Damien Miller058316f2001-03-08 10:08:49 +1100901 send_names(id, 1, &s);
902 }
903 xfree(path);
904}
905
Ben Lindstrombba81212001-06-25 05:01:22 +0000906static void
Damien Miller058316f2001-03-08 10:08:49 +1100907process_symlink(void)
908{
909 u_int32_t id;
Damien Miller058316f2001-03-08 10:08:49 +1100910 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100911 int ret, status;
Damien Miller058316f2001-03-08 10:08:49 +1100912
913 id = get_int();
914 oldpath = get_string(NULL);
915 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000916 TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
Damien Miller9e51a732003-02-24 11:58:44 +1100917 /* this will fail if 'newpath' exists */
918 ret = symlink(oldpath, newpath);
919 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller058316f2001-03-08 10:08:49 +1100920 send_status(id, status);
921 xfree(oldpath);
922 xfree(newpath);
923}
924
Ben Lindstrombba81212001-06-25 05:01:22 +0000925static void
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000926process_extended(void)
927{
928 u_int32_t id;
929 char *request;
930
931 id = get_int();
932 request = get_string(NULL);
933 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
934 xfree(request);
935}
Damien Miller7b28dc52000-09-05 13:34:53 +1100936
937/* stolen from ssh-agent */
938
Ben Lindstrombba81212001-06-25 05:01:22 +0000939static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100940process(void)
941{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000942 u_int msg_len;
Ben Lindstrom2c140472002-06-06 21:57:54 +0000943 u_int buf_len;
944 u_int consumed;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000945 u_int type;
946 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100947
Ben Lindstrom2c140472002-06-06 21:57:54 +0000948 buf_len = buffer_len(&iqueue);
949 if (buf_len < 5)
Damien Miller7b28dc52000-09-05 13:34:53 +1100950 return; /* Incomplete message. */
Damien Miller708d21c2002-01-22 23:18:15 +1100951 cp = buffer_ptr(&iqueue);
Damien Miller3f941882006-03-31 23:13:02 +1100952 msg_len = get_u32(cp);
Damien Miller54446182006-01-02 23:40:50 +1100953 if (msg_len > SFTP_MAX_MSG_LENGTH) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100954 error("bad message ");
955 exit(11);
956 }
Ben Lindstrom2c140472002-06-06 21:57:54 +0000957 if (buf_len < msg_len + 4)
Damien Miller7b28dc52000-09-05 13:34:53 +1100958 return;
959 buffer_consume(&iqueue, 4);
Ben Lindstrom2c140472002-06-06 21:57:54 +0000960 buf_len -= 4;
Damien Miller7b28dc52000-09-05 13:34:53 +1100961 type = buffer_get_char(&iqueue);
962 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000963 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100964 process_init();
965 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000966 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100967 process_open();
968 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000969 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100970 process_close();
971 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000972 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100973 process_read();
974 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000975 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100976 process_write();
977 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000978 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100979 process_lstat();
980 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000981 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100982 process_fstat();
983 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000984 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100985 process_setstat();
986 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000987 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100988 process_fsetstat();
989 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000990 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100991 process_opendir();
992 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000993 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100994 process_readdir();
995 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000996 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100997 process_remove();
998 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000999 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001000 process_mkdir();
1001 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001002 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001003 process_rmdir();
1004 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001005 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +11001006 process_realpath();
1007 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001008 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001009 process_stat();
1010 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001011 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +11001012 process_rename();
1013 break;
Damien Miller058316f2001-03-08 10:08:49 +11001014 case SSH2_FXP_READLINK:
1015 process_readlink();
1016 break;
1017 case SSH2_FXP_SYMLINK:
1018 process_symlink();
1019 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001020 case SSH2_FXP_EXTENDED:
1021 process_extended();
1022 break;
Damien Miller7b28dc52000-09-05 13:34:53 +11001023 default:
1024 error("Unknown message %d", type);
1025 break;
1026 }
Ben Lindstrom2c140472002-06-06 21:57:54 +00001027 /* discard the remaining bytes from the current packet */
1028 if (buf_len < buffer_len(&iqueue))
1029 fatal("iqueue grows");
1030 consumed = buf_len - buffer_len(&iqueue);
1031 if (msg_len < consumed)
1032 fatal("msg_len %d < consumed %d", msg_len, consumed);
1033 if (msg_len > consumed)
1034 buffer_consume(&iqueue, msg_len - consumed);
Damien Miller7b28dc52000-09-05 13:34:53 +11001035}
1036
1037int
1038main(int ac, char **av)
1039{
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001040 fd_set *rset, *wset;
Damien Miller7b28dc52000-09-05 13:34:53 +11001041 int in, out, max;
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001042 ssize_t len, olen, set_size;
Damien Miller7b28dc52000-09-05 13:34:53 +11001043
Darren Tuckerce321d82005-10-03 18:11:24 +10001044 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1045 sanitise_stdfd();
1046
Ben Lindstromc7f4ccd2001-03-15 00:09:15 +00001047 /* XXX should use getopt */
1048
Damien Miller59d3d5b2003-08-22 09:34:41 +10001049 __progname = ssh_get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +11001050 handle_init();
1051
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001052#ifdef DEBUG_SFTP_SERVER
Kevin Stevesef4eea92001-02-05 12:42:17 +00001053 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001054#endif
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001055
Damien Miller7b28dc52000-09-05 13:34:53 +11001056 in = dup(STDIN_FILENO);
1057 out = dup(STDOUT_FILENO);
1058
Damien Miller402b3312001-04-14 00:28:42 +10001059#ifdef HAVE_CYGWIN
1060 setmode(in, O_BINARY);
1061 setmode(out, O_BINARY);
1062#endif
1063
Damien Miller7b28dc52000-09-05 13:34:53 +11001064 max = 0;
1065 if (in > max)
1066 max = in;
1067 if (out > max)
1068 max = out;
1069
1070 buffer_init(&iqueue);
1071 buffer_init(&oqueue);
1072
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001073 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1074 rset = (fd_set *)xmalloc(set_size);
1075 wset = (fd_set *)xmalloc(set_size);
Damien Miller7b28dc52000-09-05 13:34:53 +11001076
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001077 for (;;) {
1078 memset(rset, 0, set_size);
1079 memset(wset, 0, set_size);
1080
1081 FD_SET(in, rset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001082 olen = buffer_len(&oqueue);
1083 if (olen > 0)
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001084 FD_SET(out, wset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001085
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001086 if (select(max+1, rset, wset, NULL, NULL) < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001087 if (errno == EINTR)
1088 continue;
1089 exit(2);
1090 }
1091
1092 /* copy stdin to iqueue */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001093 if (FD_ISSET(in, rset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001094 char buf[4*4096];
1095 len = read(in, buf, sizeof buf);
1096 if (len == 0) {
1097 debug("read eof");
1098 exit(0);
1099 } else if (len < 0) {
1100 error("read error");
1101 exit(1);
1102 } else {
1103 buffer_append(&iqueue, buf, len);
1104 }
1105 }
1106 /* send oqueue to stdout */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001107 if (FD_ISSET(out, wset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001108 len = write(out, buffer_ptr(&oqueue), olen);
1109 if (len < 0) {
1110 error("write error");
1111 exit(1);
1112 } else {
1113 buffer_consume(&oqueue, len);
1114 }
1115 }
1116 /* process requests from client */
1117 process();
1118 }
1119}