blob: e58aa59c8c6d3e1b224fa6203acc386539c22238 [file] [log] [blame]
Damien Miller57c30112006-03-26 14:24:48 +11001/* $OpenBSD: sftp-server.c,v 1.56 2006/03/25 13:17:02 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"
26#include "getput.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000027#include "log.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110028#include "xmalloc.h"
Darren Tuckerce321d82005-10-03 18:11:24 +100029#include "misc.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110030
Ben Lindstrom2f959b42001-01-11 06:20:23 +000031#include "sftp.h"
Damien Miller33804262001-02-04 23:20:18 +110032#include "sftp-common.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110033
34/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000035#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110036#define get_int() buffer_get_int(&iqueue);
37#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Ben Lindstrom2f959b42001-01-11 06:20:23 +000038#define TRACE debug
Damien Miller7b28dc52000-09-05 13:34:53 +110039
Ben Lindstrom49a79c02000-11-17 03:47:20 +000040extern char *__progname;
Ben Lindstrom49a79c02000-11-17 03:47:20 +000041
Damien Miller7b28dc52000-09-05 13:34:53 +110042/* input and output queue */
43Buffer iqueue;
44Buffer oqueue;
45
Damien Miller058316f2001-03-08 10:08:49 +110046/* Version of client */
47int version;
48
Darren Tuckera6612d42003-06-28 12:39:03 +100049/* portable attributes, etc. */
Damien Miller7b28dc52000-09-05 13:34:53 +110050
Damien Miller7b28dc52000-09-05 13:34:53 +110051typedef struct Stat Stat;
52
Damien Miller33804262001-02-04 23:20:18 +110053struct Stat {
Damien Miller7b28dc52000-09-05 13:34:53 +110054 char *name;
55 char *long_name;
56 Attrib attrib;
57};
58
Ben Lindstrombba81212001-06-25 05:01:22 +000059static int
Damien Miller7b28dc52000-09-05 13:34:53 +110060errno_to_portable(int unixerrno)
61{
62 int ret = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000063
Damien Miller7b28dc52000-09-05 13:34:53 +110064 switch (unixerrno) {
65 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000066 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110067 break;
68 case ENOENT:
69 case ENOTDIR:
70 case EBADF:
71 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000072 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110073 break;
74 case EPERM:
75 case EACCES:
76 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000077 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110078 break;
79 case ENAMETOOLONG:
80 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000081 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110082 break;
83 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000084 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +110085 break;
86 }
87 return ret;
88}
89
Ben Lindstrombba81212001-06-25 05:01:22 +000090static int
Damien Miller7b28dc52000-09-05 13:34:53 +110091flags_from_portable(int pflags)
92{
93 int flags = 0;
Ben Lindstrom1addabd2001-03-05 07:09:11 +000094
Ben Lindstrom36592512001-03-05 05:02:08 +000095 if ((pflags & SSH2_FXF_READ) &&
96 (pflags & SSH2_FXF_WRITE)) {
Damien Miller7b28dc52000-09-05 13:34:53 +110097 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +000098 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +110099 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000100 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100101 flags = O_WRONLY;
102 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000103 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100104 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000105 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100106 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000107 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100108 flags |= O_EXCL;
109 return flags;
110}
111
Ben Lindstrombba81212001-06-25 05:01:22 +0000112static Attrib *
Damien Miller7b28dc52000-09-05 13:34:53 +1100113get_attrib(void)
114{
115 return decode_attrib(&iqueue);
116}
117
118/* handle handles */
119
120typedef struct Handle Handle;
121struct Handle {
122 int use;
123 DIR *dirp;
124 int fd;
125 char *name;
126};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000127
Damien Miller7b28dc52000-09-05 13:34:53 +1100128enum {
129 HANDLE_UNUSED,
130 HANDLE_DIR,
131 HANDLE_FILE
132};
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000133
Damien Miller7b28dc52000-09-05 13:34:53 +1100134Handle handles[100];
135
Ben Lindstrombba81212001-06-25 05:01:22 +0000136static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100137handle_init(void)
138{
Damien Millereccb9de2005-06-17 12:59:34 +1000139 u_int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000140
Damien Miller9f0f5c62001-12-21 14:45:46 +1100141 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
Damien Miller7b28dc52000-09-05 13:34:53 +1100142 handles[i].use = HANDLE_UNUSED;
143}
144
Ben Lindstrombba81212001-06-25 05:01:22 +0000145static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100146handle_new(int use, const char *name, int fd, DIR *dirp)
Damien Miller7b28dc52000-09-05 13:34:53 +1100147{
Damien Millereccb9de2005-06-17 12:59:34 +1000148 u_int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000149
Damien Miller9f0f5c62001-12-21 14:45:46 +1100150 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100151 if (handles[i].use == HANDLE_UNUSED) {
152 handles[i].use = use;
153 handles[i].dirp = dirp;
154 handles[i].fd = fd;
Damien Miller00111382003-03-10 11:21:17 +1100155 handles[i].name = xstrdup(name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100156 return i;
157 }
158 }
159 return -1;
160}
161
Ben Lindstrombba81212001-06-25 05:01:22 +0000162static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100163handle_is_ok(int i, int type)
164{
Damien Millereccb9de2005-06-17 12:59:34 +1000165 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000166 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100167}
168
Ben Lindstrombba81212001-06-25 05:01:22 +0000169static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100170handle_to_string(int handle, char **stringp, int *hlenp)
171{
Damien Miller7b28dc52000-09-05 13:34:53 +1100172 if (stringp == NULL || hlenp == NULL)
173 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000174 *stringp = xmalloc(sizeof(int32_t));
175 PUT_32BIT(*stringp, handle);
176 *hlenp = sizeof(int32_t);
Damien Miller7b28dc52000-09-05 13:34:53 +1100177 return 0;
178}
179
Ben Lindstrombba81212001-06-25 05:01:22 +0000180static int
Damien Millerf58b58c2003-11-17 21:18:23 +1100181handle_from_string(const char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100182{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000183 int val;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000184
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000185 if (hlen != sizeof(int32_t))
Damien Miller7b28dc52000-09-05 13:34:53 +1100186 return -1;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000187 val = GET_32BIT(handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100188 if (handle_is_ok(val, HANDLE_FILE) ||
189 handle_is_ok(val, HANDLE_DIR))
190 return val;
191 return -1;
192}
193
Ben Lindstrombba81212001-06-25 05:01:22 +0000194static char *
Damien Miller7b28dc52000-09-05 13:34:53 +1100195handle_to_name(int handle)
196{
197 if (handle_is_ok(handle, HANDLE_DIR)||
198 handle_is_ok(handle, HANDLE_FILE))
199 return handles[handle].name;
200 return NULL;
201}
202
Ben Lindstrombba81212001-06-25 05:01:22 +0000203static DIR *
Damien Miller7b28dc52000-09-05 13:34:53 +1100204handle_to_dir(int handle)
205{
206 if (handle_is_ok(handle, HANDLE_DIR))
207 return handles[handle].dirp;
208 return NULL;
209}
210
Ben Lindstrombba81212001-06-25 05:01:22 +0000211static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100212handle_to_fd(int handle)
213{
Kevin Stevesef4eea92001-02-05 12:42:17 +0000214 if (handle_is_ok(handle, HANDLE_FILE))
Damien Miller7b28dc52000-09-05 13:34:53 +1100215 return handles[handle].fd;
216 return -1;
217}
218
Ben Lindstrombba81212001-06-25 05:01:22 +0000219static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100220handle_close(int handle)
221{
222 int ret = -1;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000223
Damien Miller7b28dc52000-09-05 13:34:53 +1100224 if (handle_is_ok(handle, HANDLE_FILE)) {
225 ret = close(handles[handle].fd);
226 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100227 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100228 } else if (handle_is_ok(handle, HANDLE_DIR)) {
229 ret = closedir(handles[handle].dirp);
230 handles[handle].use = HANDLE_UNUSED;
Damien Miller00111382003-03-10 11:21:17 +1100231 xfree(handles[handle].name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100232 } else {
233 errno = ENOENT;
234 }
235 return ret;
236}
237
Ben Lindstrombba81212001-06-25 05:01:22 +0000238static int
Damien Miller7b28dc52000-09-05 13:34:53 +1100239get_handle(void)
240{
241 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000242 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100243 u_int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000244
Damien Miller7b28dc52000-09-05 13:34:53 +1100245 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000246 if (hlen < 256)
247 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100248 xfree(handle);
249 return val;
250}
251
252/* send replies */
253
Ben Lindstrombba81212001-06-25 05:01:22 +0000254static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100255send_msg(Buffer *m)
256{
257 int mlen = buffer_len(m);
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000258
Damien Miller7b28dc52000-09-05 13:34:53 +1100259 buffer_put_int(&oqueue, mlen);
260 buffer_append(&oqueue, buffer_ptr(m), mlen);
261 buffer_consume(m, mlen);
262}
263
Ben Lindstrombba81212001-06-25 05:01:22 +0000264static void
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000265send_status(u_int32_t id, u_int32_t status)
Damien Miller7b28dc52000-09-05 13:34:53 +1100266{
267 Buffer msg;
Damien Miller058316f2001-03-08 10:08:49 +1100268 const char *status_messages[] = {
269 "Success", /* SSH_FX_OK */
270 "End of file", /* SSH_FX_EOF */
271 "No such file", /* SSH_FX_NO_SUCH_FILE */
272 "Permission denied", /* SSH_FX_PERMISSION_DENIED */
273 "Failure", /* SSH_FX_FAILURE */
274 "Bad message", /* SSH_FX_BAD_MESSAGE */
275 "No connection", /* SSH_FX_NO_CONNECTION */
276 "Connection lost", /* SSH_FX_CONNECTION_LOST */
277 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
278 "Unknown error" /* Others */
279 };
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000280
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000281 TRACE("sent status id %u error %u", id, status);
Damien Miller7b28dc52000-09-05 13:34:53 +1100282 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000283 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100284 buffer_put_int(&msg, id);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000285 buffer_put_int(&msg, status);
Damien Miller058316f2001-03-08 10:08:49 +1100286 if (version >= 3) {
Ben Lindstroma3700052001-04-05 23:26:32 +0000287 buffer_put_cstring(&msg,
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000288 status_messages[MIN(status,SSH2_FX_MAX)]);
Damien Miller058316f2001-03-08 10:08:49 +1100289 buffer_put_cstring(&msg, "");
290 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100291 send_msg(&msg);
292 buffer_free(&msg);
293}
Ben Lindstrombba81212001-06-25 05:01:22 +0000294static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100295send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100296{
297 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000298
Damien Miller7b28dc52000-09-05 13:34:53 +1100299 buffer_init(&msg);
300 buffer_put_char(&msg, type);
301 buffer_put_int(&msg, id);
302 buffer_put_string(&msg, data, dlen);
303 send_msg(&msg);
304 buffer_free(&msg);
305}
306
Ben Lindstrombba81212001-06-25 05:01:22 +0000307static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100308send_data(u_int32_t id, const char *data, int dlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100309{
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000310 TRACE("sent data id %u len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000311 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100312}
313
Ben Lindstrombba81212001-06-25 05:01:22 +0000314static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100315send_handle(u_int32_t id, int handle)
316{
317 char *string;
318 int hlen;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000319
Damien Miller7b28dc52000-09-05 13:34:53 +1100320 handle_to_string(handle, &string, &hlen);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000321 TRACE("sent handle id %u handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000322 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100323 xfree(string);
324}
325
Ben Lindstrombba81212001-06-25 05:01:22 +0000326static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100327send_names(u_int32_t id, int count, const Stat *stats)
Damien Miller7b28dc52000-09-05 13:34:53 +1100328{
329 Buffer msg;
330 int i;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000331
Damien Miller7b28dc52000-09-05 13:34:53 +1100332 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000333 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100334 buffer_put_int(&msg, id);
335 buffer_put_int(&msg, count);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000336 TRACE("sent names id %u count %d", id, count);
Damien Miller7b28dc52000-09-05 13:34:53 +1100337 for (i = 0; i < count; i++) {
338 buffer_put_cstring(&msg, stats[i].name);
339 buffer_put_cstring(&msg, stats[i].long_name);
340 encode_attrib(&msg, &stats[i].attrib);
341 }
342 send_msg(&msg);
343 buffer_free(&msg);
344}
345
Ben Lindstrombba81212001-06-25 05:01:22 +0000346static void
Damien Millerf58b58c2003-11-17 21:18:23 +1100347send_attrib(u_int32_t id, const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100348{
349 Buffer msg;
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000350
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000351 TRACE("sent attrib id %u have 0x%x", id, a->flags);
Damien Miller7b28dc52000-09-05 13:34:53 +1100352 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000353 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100354 buffer_put_int(&msg, id);
355 encode_attrib(&msg, a);
356 send_msg(&msg);
357 buffer_free(&msg);
358}
359
360/* parse incoming */
361
Ben Lindstrombba81212001-06-25 05:01:22 +0000362static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100363process_init(void)
364{
365 Buffer msg;
Damien Miller7b28dc52000-09-05 13:34:53 +1100366
Ben Lindstrom937df1d2002-06-06 21:58:35 +0000367 version = get_int();
Damien Miller7b28dc52000-09-05 13:34:53 +1100368 TRACE("client version %d", version);
369 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000370 buffer_put_char(&msg, SSH2_FXP_VERSION);
371 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100372 send_msg(&msg);
373 buffer_free(&msg);
374}
375
Ben Lindstrombba81212001-06-25 05:01:22 +0000376static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100377process_open(void)
378{
379 u_int32_t id, pflags;
380 Attrib *a;
381 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000382 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100383
384 id = get_int();
385 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000386 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100387 a = get_attrib();
388 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000389 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000390 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100391 fd = open(name, flags, mode);
392 if (fd < 0) {
393 status = errno_to_portable(errno);
394 } else {
Damien Miller00111382003-03-10 11:21:17 +1100395 handle = handle_new(HANDLE_FILE, name, fd, NULL);
Damien Miller7b28dc52000-09-05 13:34:53 +1100396 if (handle < 0) {
397 close(fd);
398 } else {
399 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000400 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100401 }
402 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000403 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100404 send_status(id, status);
405 xfree(name);
406}
407
Ben Lindstrombba81212001-06-25 05:01:22 +0000408static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100409process_close(void)
410{
411 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000412 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100413
414 id = get_int();
415 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000416 TRACE("close id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100417 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000418 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100419 send_status(id, status);
420}
421
Ben Lindstrombba81212001-06-25 05:01:22 +0000422static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100423process_read(void)
424{
425 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000426 u_int32_t id, len;
427 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100428 u_int64_t off;
429
430 id = get_int();
431 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000432 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100433 len = get_int();
434
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000435 TRACE("read id %u handle %d off %llu len %d", id, handle,
Darren Tuckere0be3042005-11-25 14:44:55 +1100436 (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100437 if (len > sizeof buf) {
438 len = sizeof buf;
Damien Miller996acd22003-04-09 20:59:48 +1000439 logit("read change len %d", len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100440 }
441 fd = handle_to_fd(handle);
442 if (fd >= 0) {
443 if (lseek(fd, off, SEEK_SET) < 0) {
444 error("process_read: seek failed");
445 status = errno_to_portable(errno);
446 } else {
447 ret = read(fd, buf, len);
448 if (ret < 0) {
449 status = errno_to_portable(errno);
450 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000451 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100452 } else {
453 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000454 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100455 }
456 }
457 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000458 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100459 send_status(id, status);
460}
461
Ben Lindstrombba81212001-06-25 05:01:22 +0000462static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100463process_write(void)
464{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000465 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100466 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100467 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000468 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100469 char *data;
470
471 id = get_int();
472 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000473 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100474 data = get_string(&len);
475
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000476 TRACE("write id %u handle %d off %llu len %d", id, handle,
Darren Tuckere0be3042005-11-25 14:44:55 +1100477 (unsigned long long)off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100478 fd = handle_to_fd(handle);
479 if (fd >= 0) {
480 if (lseek(fd, off, SEEK_SET) < 0) {
481 status = errno_to_portable(errno);
482 error("process_write: seek failed");
483 } else {
484/* XXX ATOMICIO ? */
485 ret = write(fd, data, len);
Damien Millereccb9de2005-06-17 12:59:34 +1000486 if (ret < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100487 error("process_write: write failed");
488 status = errno_to_portable(errno);
Damien Millereccb9de2005-06-17 12:59:34 +1000489 } else if ((size_t)ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000490 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100491 } else {
Damien Miller996acd22003-04-09 20:59:48 +1000492 logit("nothing at all written");
Damien Miller7b28dc52000-09-05 13:34:53 +1100493 }
494 }
495 }
496 send_status(id, status);
497 xfree(data);
498}
499
Ben Lindstrombba81212001-06-25 05:01:22 +0000500static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100501process_do_stat(int do_lstat)
502{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000503 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100504 struct stat st;
505 u_int32_t id;
506 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000507 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100508
509 id = get_int();
510 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000511 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100512 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
513 if (ret < 0) {
514 status = errno_to_portable(errno);
515 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000516 stat_to_attrib(&st, &a);
517 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000518 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100519 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000520 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100521 send_status(id, status);
522 xfree(name);
523}
524
Ben Lindstrombba81212001-06-25 05:01:22 +0000525static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100526process_stat(void)
527{
528 process_do_stat(0);
529}
530
Ben Lindstrombba81212001-06-25 05:01:22 +0000531static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100532process_lstat(void)
533{
534 process_do_stat(1);
535}
536
Ben Lindstrombba81212001-06-25 05:01:22 +0000537static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100538process_fstat(void)
539{
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000540 Attrib a;
Damien Miller7b28dc52000-09-05 13:34:53 +1100541 struct stat st;
542 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000543 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100544
545 id = get_int();
546 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000547 TRACE("fstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100548 fd = handle_to_fd(handle);
549 if (fd >= 0) {
550 ret = fstat(fd, &st);
551 if (ret < 0) {
552 status = errno_to_portable(errno);
553 } else {
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000554 stat_to_attrib(&st, &a);
555 send_attrib(id, &a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000556 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100557 }
558 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000559 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100560 send_status(id, status);
561}
562
Ben Lindstrombba81212001-06-25 05:01:22 +0000563static struct timeval *
Damien Millerf58b58c2003-11-17 21:18:23 +1100564attrib_to_tv(const Attrib *a)
Damien Miller7b28dc52000-09-05 13:34:53 +1100565{
566 static struct timeval tv[2];
Ben Lindstrom1addabd2001-03-05 07:09:11 +0000567
Damien Miller7b28dc52000-09-05 13:34:53 +1100568 tv[0].tv_sec = a->atime;
569 tv[0].tv_usec = 0;
570 tv[1].tv_sec = a->mtime;
571 tv[1].tv_usec = 0;
572 return tv;
573}
574
Ben Lindstrombba81212001-06-25 05:01:22 +0000575static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100576process_setstat(void)
577{
578 Attrib *a;
579 u_int32_t id;
580 char *name;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000581 int status = SSH2_FX_OK, ret;
Damien Miller7b28dc52000-09-05 13:34:53 +1100582
583 id = get_int();
584 name = get_string(NULL);
585 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000586 TRACE("setstat id %u name %s", id, name);
Damien Miller00c92172002-02-13 14:05:00 +1100587 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
588 ret = truncate(name, a->size);
589 if (ret == -1)
590 status = errno_to_portable(errno);
591 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000592 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100593 ret = chmod(name, a->perm & 0777);
594 if (ret == -1)
595 status = errno_to_portable(errno);
596 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000597 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100598 ret = utimes(name, attrib_to_tv(a));
599 if (ret == -1)
600 status = errno_to_portable(errno);
601 }
Kevin Steves8e743932001-02-05 13:24:35 +0000602 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
603 ret = chown(name, a->uid, a->gid);
604 if (ret == -1)
605 status = errno_to_portable(errno);
606 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100607 send_status(id, status);
608 xfree(name);
609}
610
Ben Lindstrombba81212001-06-25 05:01:22 +0000611static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100612process_fsetstat(void)
613{
614 Attrib *a;
615 u_int32_t id;
616 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000617 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100618 char *name;
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000619
Damien Miller7b28dc52000-09-05 13:34:53 +1100620 id = get_int();
621 handle = get_handle();
622 a = get_attrib();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000623 TRACE("fsetstat id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100624 fd = handle_to_fd(handle);
625 name = handle_to_name(handle);
Kevin Stevesf7ffab32001-01-24 20:11:06 +0000626 if (fd < 0 || name == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000627 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100628 } else {
Damien Miller00c92172002-02-13 14:05:00 +1100629 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
630 ret = ftruncate(fd, a->size);
631 if (ret == -1)
632 status = errno_to_portable(errno);
633 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000634 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000635#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100636 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000637#else
Kevin Stevesb6b37ba2001-01-24 20:01:44 +0000638 ret = chmod(name, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000639#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100640 if (ret == -1)
641 status = errno_to_portable(errno);
642 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000643 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100644#ifdef HAVE_FUTIMES
645 ret = futimes(fd, attrib_to_tv(a));
646#else
647 ret = utimes(name, attrib_to_tv(a));
648#endif
649 if (ret == -1)
650 status = errno_to_portable(errno);
651 }
Kevin Steves8e743932001-02-05 13:24:35 +0000652 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000653#ifdef HAVE_FCHOWN
Kevin Steves8e743932001-02-05 13:24:35 +0000654 ret = fchown(fd, a->uid, a->gid);
Ben Lindstrom34bb0c72001-02-13 02:40:56 +0000655#else
656 ret = chown(name, a->uid, a->gid);
657#endif
Kevin Steves8e743932001-02-05 13:24:35 +0000658 if (ret == -1)
659 status = errno_to_portable(errno);
660 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100661 }
662 send_status(id, status);
663}
664
Ben Lindstrombba81212001-06-25 05:01:22 +0000665static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100666process_opendir(void)
667{
668 DIR *dirp = NULL;
669 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000670 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100671 u_int32_t id;
672
673 id = get_int();
674 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000675 TRACE("opendir id %u path %s", id, path);
Kevin Stevesef4eea92001-02-05 12:42:17 +0000676 dirp = opendir(path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100677 if (dirp == NULL) {
678 status = errno_to_portable(errno);
679 } else {
Damien Miller00111382003-03-10 11:21:17 +1100680 handle = handle_new(HANDLE_DIR, path, 0, dirp);
Damien Miller7b28dc52000-09-05 13:34:53 +1100681 if (handle < 0) {
682 closedir(dirp);
683 } else {
684 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000685 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100686 }
Kevin Stevesef4eea92001-02-05 12:42:17 +0000687
Damien Miller7b28dc52000-09-05 13:34:53 +1100688 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000689 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100690 send_status(id, status);
691 xfree(path);
692}
693
Ben Lindstrombba81212001-06-25 05:01:22 +0000694static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100695process_readdir(void)
696{
697 DIR *dirp;
698 struct dirent *dp;
699 char *path;
700 int handle;
701 u_int32_t id;
702
703 id = get_int();
704 handle = get_handle();
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000705 TRACE("readdir id %u handle %d", id, handle);
Damien Miller7b28dc52000-09-05 13:34:53 +1100706 dirp = handle_to_dir(handle);
707 path = handle_to_name(handle);
708 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000709 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100710 } else {
Damien Miller7b28dc52000-09-05 13:34:53 +1100711 struct stat st;
712 char pathname[1024];
713 Stat *stats;
714 int nstats = 10, count = 0, i;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000715
Damien Miller07d86be2006-03-26 14:19:21 +1100716 stats = xcalloc(nstats, sizeof(Stat));
Damien Miller7b28dc52000-09-05 13:34:53 +1100717 while ((dp = readdir(dirp)) != NULL) {
718 if (count >= nstats) {
719 nstats *= 2;
Damien Miller36812092006-03-26 14:22:47 +1100720 stats = xrealloc(stats, nstats, sizeof(Stat));
Damien Miller7b28dc52000-09-05 13:34:53 +1100721 }
722/* XXX OVERFLOW ? */
Ben Lindstrom95148e32001-08-06 21:30:53 +0000723 snprintf(pathname, sizeof pathname, "%s%s%s", path,
724 strcmp(path, "/") ? "/" : "", dp->d_name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100725 if (lstat(pathname, &st) < 0)
726 continue;
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000727 stat_to_attrib(&st, &(stats[count].attrib));
Damien Miller7b28dc52000-09-05 13:34:53 +1100728 stats[count].name = xstrdup(dp->d_name);
Damien Millere1a49812002-09-12 09:54:25 +1000729 stats[count].long_name = ls_file(dp->d_name, &st, 0);
Damien Miller7b28dc52000-09-05 13:34:53 +1100730 count++;
731 /* send up to 100 entries in one message */
Ben Lindstrombf555ba2001-01-18 02:04:35 +0000732 /* XXX check packet size instead */
Damien Miller7b28dc52000-09-05 13:34:53 +1100733 if (count == 100)
734 break;
735 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000736 if (count > 0) {
737 send_names(id, count, stats);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100738 for (i = 0; i < count; i++) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000739 xfree(stats[i].name);
740 xfree(stats[i].long_name);
741 }
742 } else {
743 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100744 }
745 xfree(stats);
746 }
747}
748
Ben Lindstrombba81212001-06-25 05:01:22 +0000749static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100750process_remove(void)
751{
752 char *name;
753 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000754 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100755 int ret;
756
757 id = get_int();
758 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000759 TRACE("remove id %u name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000760 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000761 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100762 send_status(id, status);
763 xfree(name);
764}
765
Ben Lindstrombba81212001-06-25 05:01:22 +0000766static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100767process_mkdir(void)
768{
769 Attrib *a;
770 u_int32_t id;
771 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000772 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100773
774 id = get_int();
775 name = get_string(NULL);
776 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000777 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
778 a->perm & 0777 : 0777;
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000779 TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
Damien Miller7b28dc52000-09-05 13:34:53 +1100780 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000781 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100782 send_status(id, status);
783 xfree(name);
784}
785
Ben Lindstrombba81212001-06-25 05:01:22 +0000786static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100787process_rmdir(void)
788{
789 u_int32_t id;
790 char *name;
791 int ret, status;
792
793 id = get_int();
794 name = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000795 TRACE("rmdir id %u name %s", id, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100796 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000797 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100798 send_status(id, status);
799 xfree(name);
800}
801
Ben Lindstrombba81212001-06-25 05:01:22 +0000802static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100803process_realpath(void)
804{
805 char resolvedname[MAXPATHLEN];
806 u_int32_t id;
807 char *path;
808
809 id = get_int();
810 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000811 if (path[0] == '\0') {
812 xfree(path);
813 path = xstrdup(".");
814 }
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000815 TRACE("realpath id %u path %s", id, path);
Damien Miller7b28dc52000-09-05 13:34:53 +1100816 if (realpath(path, resolvedname) == NULL) {
817 send_status(id, errno_to_portable(errno));
818 } else {
819 Stat s;
820 attrib_clear(&s.attrib);
821 s.name = s.long_name = resolvedname;
822 send_names(id, 1, &s);
823 }
824 xfree(path);
825}
826
Ben Lindstrombba81212001-06-25 05:01:22 +0000827static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100828process_rename(void)
829{
830 u_int32_t id;
831 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100832 int status;
Damien Millerb3207e82003-03-26 16:01:11 +1100833 struct stat sb;
Damien Miller7b28dc52000-09-05 13:34:53 +1100834
835 id = get_int();
836 oldpath = get_string(NULL);
837 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000838 TRACE("rename id %u old %s new %s", id, oldpath, newpath);
Damien Millerb3207e82003-03-26 16:01:11 +1100839 status = SSH2_FX_FAILURE;
840 if (lstat(oldpath, &sb) == -1)
Damien Miller9e51a732003-02-24 11:58:44 +1100841 status = errno_to_portable(errno);
Damien Millerb3207e82003-03-26 16:01:11 +1100842 else if (S_ISREG(sb.st_mode)) {
843 /* Race-free rename of regular files */
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000844 if (link(oldpath, newpath) == -1) {
Darren Tuckere59b5082004-06-28 16:01:19 +1000845 if (errno == EOPNOTSUPP
846#ifdef LINK_OPNOTSUPP_ERRNO
847 || errno == LINK_OPNOTSUPP_ERRNO
848#endif
849 ) {
Darren Tuckeraedc1d62004-06-25 17:06:02 +1000850 struct stat st;
851
852 /*
853 * fs doesn't support links, so fall back to
854 * stat+rename. This is racy.
855 */
856 if (stat(newpath, &st) == -1) {
857 if (rename(oldpath, newpath) == -1)
858 status =
859 errno_to_portable(errno);
860 else
861 status = SSH2_FX_OK;
862 }
863 } else {
864 status = errno_to_portable(errno);
865 }
866 } else if (unlink(oldpath) == -1) {
Damien Millerb3207e82003-03-26 16:01:11 +1100867 status = errno_to_portable(errno);
868 /* clean spare link */
869 unlink(newpath);
870 } else
871 status = SSH2_FX_OK;
872 } else if (stat(newpath, &sb) == -1) {
873 if (rename(oldpath, newpath) == -1)
874 status = errno_to_portable(errno);
875 else
876 status = SSH2_FX_OK;
877 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100878 send_status(id, status);
879 xfree(oldpath);
880 xfree(newpath);
881}
882
Ben Lindstrombba81212001-06-25 05:01:22 +0000883static void
Damien Miller058316f2001-03-08 10:08:49 +1100884process_readlink(void)
885{
886 u_int32_t id;
Ben Lindstromabbb73d2001-05-17 03:14:57 +0000887 int len;
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000888 char buf[MAXPATHLEN];
Damien Miller058316f2001-03-08 10:08:49 +1100889 char *path;
890
891 id = get_int();
892 path = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000893 TRACE("readlink id %u path %s", id, path);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000894 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
Damien Miller058316f2001-03-08 10:08:49 +1100895 send_status(id, errno_to_portable(errno));
896 else {
897 Stat s;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100898
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000899 buf[len] = '\0';
Damien Miller058316f2001-03-08 10:08:49 +1100900 attrib_clear(&s.attrib);
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000901 s.name = s.long_name = buf;
Damien Miller058316f2001-03-08 10:08:49 +1100902 send_names(id, 1, &s);
903 }
904 xfree(path);
905}
906
Ben Lindstrombba81212001-06-25 05:01:22 +0000907static void
Damien Miller058316f2001-03-08 10:08:49 +1100908process_symlink(void)
909{
910 u_int32_t id;
Damien Miller058316f2001-03-08 10:08:49 +1100911 char *oldpath, *newpath;
Damien Miller9e51a732003-02-24 11:58:44 +1100912 int ret, status;
Damien Miller058316f2001-03-08 10:08:49 +1100913
914 id = get_int();
915 oldpath = get_string(NULL);
916 newpath = get_string(NULL);
Ben Lindstromb1f483f2002-06-23 21:27:18 +0000917 TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
Damien Miller9e51a732003-02-24 11:58:44 +1100918 /* this will fail if 'newpath' exists */
919 ret = symlink(oldpath, newpath);
920 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller058316f2001-03-08 10:08:49 +1100921 send_status(id, status);
922 xfree(oldpath);
923 xfree(newpath);
924}
925
Ben Lindstrombba81212001-06-25 05:01:22 +0000926static void
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000927process_extended(void)
928{
929 u_int32_t id;
930 char *request;
931
932 id = get_int();
933 request = get_string(NULL);
934 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
935 xfree(request);
936}
Damien Miller7b28dc52000-09-05 13:34:53 +1100937
938/* stolen from ssh-agent */
939
Ben Lindstrombba81212001-06-25 05:01:22 +0000940static void
Damien Miller7b28dc52000-09-05 13:34:53 +1100941process(void)
942{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000943 u_int msg_len;
Ben Lindstrom2c140472002-06-06 21:57:54 +0000944 u_int buf_len;
945 u_int consumed;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000946 u_int type;
947 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100948
Ben Lindstrom2c140472002-06-06 21:57:54 +0000949 buf_len = buffer_len(&iqueue);
950 if (buf_len < 5)
Damien Miller7b28dc52000-09-05 13:34:53 +1100951 return; /* Incomplete message. */
Damien Miller708d21c2002-01-22 23:18:15 +1100952 cp = buffer_ptr(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +1100953 msg_len = GET_32BIT(cp);
Damien Miller54446182006-01-02 23:40:50 +1100954 if (msg_len > SFTP_MAX_MSG_LENGTH) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100955 error("bad message ");
956 exit(11);
957 }
Ben Lindstrom2c140472002-06-06 21:57:54 +0000958 if (buf_len < msg_len + 4)
Damien Miller7b28dc52000-09-05 13:34:53 +1100959 return;
960 buffer_consume(&iqueue, 4);
Ben Lindstrom2c140472002-06-06 21:57:54 +0000961 buf_len -= 4;
Damien Miller7b28dc52000-09-05 13:34:53 +1100962 type = buffer_get_char(&iqueue);
963 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000964 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100965 process_init();
966 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000967 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100968 process_open();
969 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000970 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100971 process_close();
972 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000973 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100974 process_read();
975 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000976 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100977 process_write();
978 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000979 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100980 process_lstat();
981 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000982 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100983 process_fstat();
984 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000985 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100986 process_setstat();
987 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000988 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100989 process_fsetstat();
990 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000991 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100992 process_opendir();
993 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000994 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100995 process_readdir();
996 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000997 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100998 process_remove();
999 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001000 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001001 process_mkdir();
1002 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001003 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +11001004 process_rmdir();
1005 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001006 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +11001007 process_realpath();
1008 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001009 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +11001010 process_stat();
1011 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001012 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +11001013 process_rename();
1014 break;
Damien Miller058316f2001-03-08 10:08:49 +11001015 case SSH2_FXP_READLINK:
1016 process_readlink();
1017 break;
1018 case SSH2_FXP_SYMLINK:
1019 process_symlink();
1020 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001021 case SSH2_FXP_EXTENDED:
1022 process_extended();
1023 break;
Damien Miller7b28dc52000-09-05 13:34:53 +11001024 default:
1025 error("Unknown message %d", type);
1026 break;
1027 }
Ben Lindstrom2c140472002-06-06 21:57:54 +00001028 /* discard the remaining bytes from the current packet */
1029 if (buf_len < buffer_len(&iqueue))
1030 fatal("iqueue grows");
1031 consumed = buf_len - buffer_len(&iqueue);
1032 if (msg_len < consumed)
1033 fatal("msg_len %d < consumed %d", msg_len, consumed);
1034 if (msg_len > consumed)
1035 buffer_consume(&iqueue, msg_len - consumed);
Damien Miller7b28dc52000-09-05 13:34:53 +11001036}
1037
1038int
1039main(int ac, char **av)
1040{
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001041 fd_set *rset, *wset;
Damien Miller7b28dc52000-09-05 13:34:53 +11001042 int in, out, max;
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001043 ssize_t len, olen, set_size;
Damien Miller7b28dc52000-09-05 13:34:53 +11001044
Darren Tuckerce321d82005-10-03 18:11:24 +10001045 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1046 sanitise_stdfd();
1047
Ben Lindstromc7f4ccd2001-03-15 00:09:15 +00001048 /* XXX should use getopt */
1049
Damien Miller59d3d5b2003-08-22 09:34:41 +10001050 __progname = ssh_get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +11001051 handle_init();
1052
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001053#ifdef DEBUG_SFTP_SERVER
Kevin Stevesef4eea92001-02-05 12:42:17 +00001054 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrombf555ba2001-01-18 02:04:35 +00001055#endif
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001056
Damien Miller7b28dc52000-09-05 13:34:53 +11001057 in = dup(STDIN_FILENO);
1058 out = dup(STDOUT_FILENO);
1059
Damien Miller402b3312001-04-14 00:28:42 +10001060#ifdef HAVE_CYGWIN
1061 setmode(in, O_BINARY);
1062 setmode(out, O_BINARY);
1063#endif
1064
Damien Miller7b28dc52000-09-05 13:34:53 +11001065 max = 0;
1066 if (in > max)
1067 max = in;
1068 if (out > max)
1069 max = out;
1070
1071 buffer_init(&iqueue);
1072 buffer_init(&oqueue);
1073
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001074 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1075 rset = (fd_set *)xmalloc(set_size);
1076 wset = (fd_set *)xmalloc(set_size);
Damien Miller7b28dc52000-09-05 13:34:53 +11001077
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001078 for (;;) {
1079 memset(rset, 0, set_size);
1080 memset(wset, 0, set_size);
1081
1082 FD_SET(in, rset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001083 olen = buffer_len(&oqueue);
1084 if (olen > 0)
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001085 FD_SET(out, wset);
Damien Miller7b28dc52000-09-05 13:34:53 +11001086
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001087 if (select(max+1, rset, wset, NULL, NULL) < 0) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001088 if (errno == EINTR)
1089 continue;
1090 exit(2);
1091 }
1092
1093 /* copy stdin to iqueue */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001094 if (FD_ISSET(in, rset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001095 char buf[4*4096];
1096 len = read(in, buf, sizeof buf);
1097 if (len == 0) {
1098 debug("read eof");
1099 exit(0);
1100 } else if (len < 0) {
1101 error("read error");
1102 exit(1);
1103 } else {
1104 buffer_append(&iqueue, buf, len);
1105 }
1106 }
1107 /* send oqueue to stdout */
Ben Lindstromcb80bdf2001-03-05 07:06:12 +00001108 if (FD_ISSET(out, wset)) {
Damien Miller7b28dc52000-09-05 13:34:53 +11001109 len = write(out, buffer_ptr(&oqueue), olen);
1110 if (len < 0) {
1111 error("write error");
1112 exit(1);
1113 } else {
1114 buffer_consume(&oqueue, len);
1115 }
1116 }
1117 /* process requests from client */
1118 process();
1119 }
1120}