1*0fdf8faeSEd Maste /* $OpenBSD: sftp-server.c,v 1.148 2024/04/30 06:23:51 djm Exp $ */
2b66f2d16SKris Kennaway /*
3efcad6b7SDag-Erling Smørgrav * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
4b66f2d16SKris Kennaway *
5efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any
6efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above
7efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies.
8b66f2d16SKris Kennaway *
9efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b66f2d16SKris Kennaway */
17b66f2d16SKris Kennaway
18761efaa7SDag-Erling Smørgrav #include "includes.h"
19761efaa7SDag-Erling Smørgrav
20761efaa7SDag-Erling Smørgrav #include <sys/types.h>
21761efaa7SDag-Erling Smørgrav #include <sys/stat.h>
2219261079SEd Maste #include <sys/resource.h>
23761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H
24761efaa7SDag-Erling Smørgrav # include <sys/time.h>
25761efaa7SDag-Erling Smørgrav #endif
26d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_MOUNT_H
27d4af9e69SDag-Erling Smørgrav #include <sys/mount.h>
28d4af9e69SDag-Erling Smørgrav #endif
29d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_STATVFS_H
30d4af9e69SDag-Erling Smørgrav #include <sys/statvfs.h>
31d4af9e69SDag-Erling Smørgrav #endif
32761efaa7SDag-Erling Smørgrav
33761efaa7SDag-Erling Smørgrav #include <dirent.h>
34761efaa7SDag-Erling Smørgrav #include <errno.h>
35761efaa7SDag-Erling Smørgrav #include <fcntl.h>
361323ec57SEd Maste #ifdef HAVE_POLL_H
371323ec57SEd Maste #include <poll.h>
381323ec57SEd Maste #endif
39761efaa7SDag-Erling Smørgrav #include <pwd.h>
4038a52bd3SEd Maste #include <grp.h>
41761efaa7SDag-Erling Smørgrav #include <stdlib.h>
42761efaa7SDag-Erling Smørgrav #include <stdio.h>
43761efaa7SDag-Erling Smørgrav #include <string.h>
44761efaa7SDag-Erling Smørgrav #include <time.h>
45761efaa7SDag-Erling Smørgrav #include <unistd.h>
46761efaa7SDag-Erling Smørgrav #include <stdarg.h>
47761efaa7SDag-Erling Smørgrav
4887c1498dSEd Maste #include "atomicio.h"
49b66f2d16SKris Kennaway #include "xmalloc.h"
50bc5531deSDag-Erling Smørgrav #include "sshbuf.h"
51bc5531deSDag-Erling Smørgrav #include "ssherr.h"
52761efaa7SDag-Erling Smørgrav #include "log.h"
53021d409fSDag-Erling Smørgrav #include "misc.h"
54f7167e0eSDag-Erling Smørgrav #include "match.h"
55761efaa7SDag-Erling Smørgrav #include "uidswap.h"
56b66f2d16SKris Kennaway
571e8db6e2SBrian Feldman #include "sftp.h"
581e8db6e2SBrian Feldman #include "sftp-common.h"
59b66f2d16SKris Kennaway
6019261079SEd Maste char *sftp_realpath(const char *, char *); /* sftp-realpath.c */
6119261079SEd Maste
6219261079SEd Maste /* Maximum data read that we are willing to accept */
6319261079SEd Maste #define SFTP_MAX_READ_LENGTH (SFTP_MAX_MSG_LENGTH - 1024)
6419261079SEd Maste
65761efaa7SDag-Erling Smørgrav /* Our verbosity */
66f7167e0eSDag-Erling Smørgrav static LogLevel log_level = SYSLOG_LEVEL_ERROR;
67761efaa7SDag-Erling Smørgrav
68761efaa7SDag-Erling Smørgrav /* Our client */
69f7167e0eSDag-Erling Smørgrav static struct passwd *pw = NULL;
70f7167e0eSDag-Erling Smørgrav static char *client_addr = NULL;
7183d2307dSDag-Erling Smørgrav
72b66f2d16SKris Kennaway /* input and output queue */
73bc5531deSDag-Erling Smørgrav struct sshbuf *iqueue;
74bc5531deSDag-Erling Smørgrav struct sshbuf *oqueue;
75b66f2d16SKris Kennaway
761e8db6e2SBrian Feldman /* Version of client */
77f7167e0eSDag-Erling Smørgrav static u_int version;
78f7167e0eSDag-Erling Smørgrav
79f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_INIT received */
80f7167e0eSDag-Erling Smørgrav static int init_done;
811e8db6e2SBrian Feldman
82b15c8340SDag-Erling Smørgrav /* Disable writes */
83f7167e0eSDag-Erling Smørgrav static int readonly;
84f7167e0eSDag-Erling Smørgrav
85f7167e0eSDag-Erling Smørgrav /* Requests that are allowed/denied */
8619261079SEd Maste static char *request_allowlist, *request_denylist;
87b15c8340SDag-Erling Smørgrav
88d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */
89b66f2d16SKris Kennaway typedef struct Stat Stat;
90b66f2d16SKris Kennaway
911e8db6e2SBrian Feldman struct Stat {
92b66f2d16SKris Kennaway char *name;
93b66f2d16SKris Kennaway char *long_name;
94b66f2d16SKris Kennaway Attrib attrib;
95b66f2d16SKris Kennaway };
96b66f2d16SKris Kennaway
97f7167e0eSDag-Erling Smørgrav /* Packet handlers */
98f7167e0eSDag-Erling Smørgrav static void process_open(u_int32_t id);
99f7167e0eSDag-Erling Smørgrav static void process_close(u_int32_t id);
100f7167e0eSDag-Erling Smørgrav static void process_read(u_int32_t id);
101f7167e0eSDag-Erling Smørgrav static void process_write(u_int32_t id);
102f7167e0eSDag-Erling Smørgrav static void process_stat(u_int32_t id);
103f7167e0eSDag-Erling Smørgrav static void process_lstat(u_int32_t id);
104f7167e0eSDag-Erling Smørgrav static void process_fstat(u_int32_t id);
105f7167e0eSDag-Erling Smørgrav static void process_setstat(u_int32_t id);
106f7167e0eSDag-Erling Smørgrav static void process_fsetstat(u_int32_t id);
107f7167e0eSDag-Erling Smørgrav static void process_opendir(u_int32_t id);
108f7167e0eSDag-Erling Smørgrav static void process_readdir(u_int32_t id);
109f7167e0eSDag-Erling Smørgrav static void process_remove(u_int32_t id);
110f7167e0eSDag-Erling Smørgrav static void process_mkdir(u_int32_t id);
111f7167e0eSDag-Erling Smørgrav static void process_rmdir(u_int32_t id);
112f7167e0eSDag-Erling Smørgrav static void process_realpath(u_int32_t id);
113f7167e0eSDag-Erling Smørgrav static void process_rename(u_int32_t id);
114f7167e0eSDag-Erling Smørgrav static void process_readlink(u_int32_t id);
115f7167e0eSDag-Erling Smørgrav static void process_symlink(u_int32_t id);
116f7167e0eSDag-Erling Smørgrav static void process_extended_posix_rename(u_int32_t id);
117f7167e0eSDag-Erling Smørgrav static void process_extended_statvfs(u_int32_t id);
118f7167e0eSDag-Erling Smørgrav static void process_extended_fstatvfs(u_int32_t id);
119f7167e0eSDag-Erling Smørgrav static void process_extended_hardlink(u_int32_t id);
120f7167e0eSDag-Erling Smørgrav static void process_extended_fsync(u_int32_t id);
12119261079SEd Maste static void process_extended_lsetstat(u_int32_t id);
12219261079SEd Maste static void process_extended_limits(u_int32_t id);
12319261079SEd Maste static void process_extended_expand(u_int32_t id);
12487c1498dSEd Maste static void process_extended_copy_data(u_int32_t id);
12538a52bd3SEd Maste static void process_extended_home_directory(u_int32_t id);
12638a52bd3SEd Maste static void process_extended_get_users_groups_by_id(u_int32_t id);
127f7167e0eSDag-Erling Smørgrav static void process_extended(u_int32_t id);
128f7167e0eSDag-Erling Smørgrav
129f7167e0eSDag-Erling Smørgrav struct sftp_handler {
130f7167e0eSDag-Erling Smørgrav const char *name; /* user-visible name for fine-grained perms */
131f7167e0eSDag-Erling Smørgrav const char *ext_name; /* extended request name */
132f7167e0eSDag-Erling Smørgrav u_int type; /* packet type, for non extended packets */
133f7167e0eSDag-Erling Smørgrav void (*handler)(u_int32_t);
134f7167e0eSDag-Erling Smørgrav int does_write; /* if nonzero, banned for readonly mode */
135f7167e0eSDag-Erling Smørgrav };
136f7167e0eSDag-Erling Smørgrav
13719261079SEd Maste static const struct sftp_handler handlers[] = {
138f7167e0eSDag-Erling Smørgrav /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */
139f7167e0eSDag-Erling Smørgrav { "open", NULL, SSH2_FXP_OPEN, process_open, 0 },
140f7167e0eSDag-Erling Smørgrav { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 },
141f7167e0eSDag-Erling Smørgrav { "read", NULL, SSH2_FXP_READ, process_read, 0 },
142f7167e0eSDag-Erling Smørgrav { "write", NULL, SSH2_FXP_WRITE, process_write, 1 },
143f7167e0eSDag-Erling Smørgrav { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 },
144f7167e0eSDag-Erling Smørgrav { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 },
145f7167e0eSDag-Erling Smørgrav { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 },
146f7167e0eSDag-Erling Smørgrav { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 },
147f7167e0eSDag-Erling Smørgrav { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 },
148f7167e0eSDag-Erling Smørgrav { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 },
149f7167e0eSDag-Erling Smørgrav { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 },
150f7167e0eSDag-Erling Smørgrav { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 },
151f7167e0eSDag-Erling Smørgrav { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 },
152f7167e0eSDag-Erling Smørgrav { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 },
153f7167e0eSDag-Erling Smørgrav { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 },
154f7167e0eSDag-Erling Smørgrav { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 },
155f7167e0eSDag-Erling Smørgrav { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 },
156f7167e0eSDag-Erling Smørgrav { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 },
157f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 }
158f7167e0eSDag-Erling Smørgrav };
159f7167e0eSDag-Erling Smørgrav
160f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_EXTENDED submessages */
16119261079SEd Maste static const struct sftp_handler extended_handlers[] = {
162f7167e0eSDag-Erling Smørgrav { "posix-rename", "posix-rename@openssh.com", 0,
163f7167e0eSDag-Erling Smørgrav process_extended_posix_rename, 1 },
164f7167e0eSDag-Erling Smørgrav { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 },
165f7167e0eSDag-Erling Smørgrav { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 },
166f7167e0eSDag-Erling Smørgrav { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 },
167f7167e0eSDag-Erling Smørgrav { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 },
16819261079SEd Maste { "lsetstat", "lsetstat@openssh.com", 0, process_extended_lsetstat, 1 },
16919261079SEd Maste { "limits", "limits@openssh.com", 0, process_extended_limits, 0 },
17019261079SEd Maste { "expand-path", "expand-path@openssh.com", 0,
17119261079SEd Maste process_extended_expand, 0 },
17287c1498dSEd Maste { "copy-data", "copy-data", 0, process_extended_copy_data, 1 },
17338a52bd3SEd Maste { "home-directory", "home-directory", 0,
17438a52bd3SEd Maste process_extended_home_directory, 0 },
17538a52bd3SEd Maste { "users-groups-by-id", "users-groups-by-id@openssh.com", 0,
17638a52bd3SEd Maste process_extended_get_users_groups_by_id, 0 },
177f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 }
178f7167e0eSDag-Erling Smørgrav };
179f7167e0eSDag-Erling Smørgrav
18019261079SEd Maste static const struct sftp_handler *
extended_handler_byname(const char * name)18119261079SEd Maste extended_handler_byname(const char *name)
18219261079SEd Maste {
18319261079SEd Maste int i;
18419261079SEd Maste
18519261079SEd Maste for (i = 0; extended_handlers[i].handler != NULL; i++) {
18619261079SEd Maste if (strcmp(name, extended_handlers[i].ext_name) == 0)
18719261079SEd Maste return &extended_handlers[i];
18819261079SEd Maste }
18919261079SEd Maste return NULL;
19019261079SEd Maste }
19119261079SEd Maste
192f7167e0eSDag-Erling Smørgrav static int
request_permitted(const struct sftp_handler * h)19319261079SEd Maste request_permitted(const struct sftp_handler *h)
194f7167e0eSDag-Erling Smørgrav {
195f7167e0eSDag-Erling Smørgrav char *result;
196f7167e0eSDag-Erling Smørgrav
197f7167e0eSDag-Erling Smørgrav if (readonly && h->does_write) {
198f7167e0eSDag-Erling Smørgrav verbose("Refusing %s request in read-only mode", h->name);
199f7167e0eSDag-Erling Smørgrav return 0;
200f7167e0eSDag-Erling Smørgrav }
20119261079SEd Maste if (request_denylist != NULL &&
20219261079SEd Maste ((result = match_list(h->name, request_denylist, NULL))) != NULL) {
203f7167e0eSDag-Erling Smørgrav free(result);
20419261079SEd Maste verbose("Refusing denylisted %s request", h->name);
205f7167e0eSDag-Erling Smørgrav return 0;
206f7167e0eSDag-Erling Smørgrav }
20719261079SEd Maste if (request_allowlist != NULL &&
20819261079SEd Maste ((result = match_list(h->name, request_allowlist, NULL))) != NULL) {
209f7167e0eSDag-Erling Smørgrav free(result);
21019261079SEd Maste debug2("Permitting allowlisted %s request", h->name);
211f7167e0eSDag-Erling Smørgrav return 1;
212f7167e0eSDag-Erling Smørgrav }
21319261079SEd Maste if (request_allowlist != NULL) {
21419261079SEd Maste verbose("Refusing non-allowlisted %s request", h->name);
215f7167e0eSDag-Erling Smørgrav return 0;
216f7167e0eSDag-Erling Smørgrav }
217f7167e0eSDag-Erling Smørgrav return 1;
218f7167e0eSDag-Erling Smørgrav }
219f7167e0eSDag-Erling Smørgrav
220ae1f160dSDag-Erling Smørgrav static int
errno_to_portable(int unixerrno)221b66f2d16SKris Kennaway errno_to_portable(int unixerrno)
222b66f2d16SKris Kennaway {
223b66f2d16SKris Kennaway int ret = 0;
2241e8db6e2SBrian Feldman
225b66f2d16SKris Kennaway switch (unixerrno) {
226b66f2d16SKris Kennaway case 0:
2271e8db6e2SBrian Feldman ret = SSH2_FX_OK;
228b66f2d16SKris Kennaway break;
229b66f2d16SKris Kennaway case ENOENT:
230b66f2d16SKris Kennaway case ENOTDIR:
231b66f2d16SKris Kennaway case EBADF:
232b66f2d16SKris Kennaway case ELOOP:
2331e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE;
234b66f2d16SKris Kennaway break;
235b66f2d16SKris Kennaway case EPERM:
236b66f2d16SKris Kennaway case EACCES:
237b66f2d16SKris Kennaway case EFAULT:
2381e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED;
239b66f2d16SKris Kennaway break;
240b66f2d16SKris Kennaway case ENAMETOOLONG:
241b66f2d16SKris Kennaway case EINVAL:
2421e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE;
243b66f2d16SKris Kennaway break;
244d4af9e69SDag-Erling Smørgrav case ENOSYS:
245d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED;
246d4af9e69SDag-Erling Smørgrav break;
247b66f2d16SKris Kennaway default:
2481e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE;
249b66f2d16SKris Kennaway break;
250b66f2d16SKris Kennaway }
251b66f2d16SKris Kennaway return ret;
252b66f2d16SKris Kennaway }
253b66f2d16SKris Kennaway
254ae1f160dSDag-Erling Smørgrav static int
flags_from_portable(int pflags)255b66f2d16SKris Kennaway flags_from_portable(int pflags)
256b66f2d16SKris Kennaway {
257b66f2d16SKris Kennaway int flags = 0;
2581e8db6e2SBrian Feldman
2591e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) &&
2601e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) {
261b66f2d16SKris Kennaway flags = O_RDWR;
2621e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) {
263b66f2d16SKris Kennaway flags = O_RDONLY;
2641e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) {
265b66f2d16SKris Kennaway flags = O_WRONLY;
266b66f2d16SKris Kennaway }
267f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND)
268f7167e0eSDag-Erling Smørgrav flags |= O_APPEND;
2691e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT)
270b66f2d16SKris Kennaway flags |= O_CREAT;
2711e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC)
272b66f2d16SKris Kennaway flags |= O_TRUNC;
2731e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL)
274b66f2d16SKris Kennaway flags |= O_EXCL;
275b66f2d16SKris Kennaway return flags;
276b66f2d16SKris Kennaway }
277b66f2d16SKris Kennaway
278761efaa7SDag-Erling Smørgrav static const char *
string_from_portable(int pflags)279761efaa7SDag-Erling Smørgrav string_from_portable(int pflags)
280761efaa7SDag-Erling Smørgrav {
281761efaa7SDag-Erling Smørgrav static char ret[128];
282761efaa7SDag-Erling Smørgrav
283761efaa7SDag-Erling Smørgrav *ret = '\0';
284761efaa7SDag-Erling Smørgrav
285761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \
286761efaa7SDag-Erling Smørgrav if (*ret != '\0') \
287761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \
288761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \
289761efaa7SDag-Erling Smørgrav }
290761efaa7SDag-Erling Smørgrav
291761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ)
292761efaa7SDag-Erling Smørgrav PAPPEND("READ")
293761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE)
294761efaa7SDag-Erling Smørgrav PAPPEND("WRITE")
295f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND)
296f7167e0eSDag-Erling Smørgrav PAPPEND("APPEND")
297761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT)
298761efaa7SDag-Erling Smørgrav PAPPEND("CREATE")
299761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC)
300761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE")
301761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL)
302761efaa7SDag-Erling Smørgrav PAPPEND("EXCL")
303761efaa7SDag-Erling Smørgrav
304761efaa7SDag-Erling Smørgrav return ret;
305761efaa7SDag-Erling Smørgrav }
306761efaa7SDag-Erling Smørgrav
307b66f2d16SKris Kennaway /* handle handles */
308b66f2d16SKris Kennaway
309b66f2d16SKris Kennaway typedef struct Handle Handle;
310b66f2d16SKris Kennaway struct Handle {
311b66f2d16SKris Kennaway int use;
312b66f2d16SKris Kennaway DIR *dirp;
313b66f2d16SKris Kennaway int fd;
314f7167e0eSDag-Erling Smørgrav int flags;
315b66f2d16SKris Kennaway char *name;
316761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write;
317d4af9e69SDag-Erling Smørgrav int next_unused;
318b66f2d16SKris Kennaway };
3191e8db6e2SBrian Feldman
320b66f2d16SKris Kennaway enum {
321b66f2d16SKris Kennaway HANDLE_UNUSED,
322b66f2d16SKris Kennaway HANDLE_DIR,
323b66f2d16SKris Kennaway HANDLE_FILE
324b66f2d16SKris Kennaway };
3251e8db6e2SBrian Feldman
32619261079SEd Maste static Handle *handles = NULL;
32719261079SEd Maste static u_int num_handles = 0;
32819261079SEd Maste static int first_unused_handle = -1;
329b66f2d16SKris Kennaway
handle_unused(int i)330d4af9e69SDag-Erling Smørgrav static void handle_unused(int i)
331b66f2d16SKris Kennaway {
332b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED;
333d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle;
334d4af9e69SDag-Erling Smørgrav first_unused_handle = i;
335b66f2d16SKris Kennaway }
336b66f2d16SKris Kennaway
337ae1f160dSDag-Erling Smørgrav static int
handle_new(int use,const char * name,int fd,int flags,DIR * dirp)338f7167e0eSDag-Erling Smørgrav handle_new(int use, const char *name, int fd, int flags, DIR *dirp)
339b66f2d16SKris Kennaway {
340d4af9e69SDag-Erling Smørgrav int i;
3411e8db6e2SBrian Feldman
342d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) {
343d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles)
344d4af9e69SDag-Erling Smørgrav return -1;
345d4af9e69SDag-Erling Smørgrav num_handles++;
346557f75e5SDag-Erling Smørgrav handles = xreallocarray(handles, num_handles, sizeof(Handle));
347d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1);
348d4af9e69SDag-Erling Smørgrav }
349d4af9e69SDag-Erling Smørgrav
350d4af9e69SDag-Erling Smørgrav i = first_unused_handle;
351d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused;
352d4af9e69SDag-Erling Smørgrav
353b66f2d16SKris Kennaway handles[i].use = use;
354b66f2d16SKris Kennaway handles[i].dirp = dirp;
355b66f2d16SKris Kennaway handles[i].fd = fd;
356f7167e0eSDag-Erling Smørgrav handles[i].flags = flags;
357d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name);
358761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0;
359d4af9e69SDag-Erling Smørgrav
360b66f2d16SKris Kennaway return i;
361b66f2d16SKris Kennaway }
362b66f2d16SKris Kennaway
363ae1f160dSDag-Erling Smørgrav static int
handle_is_ok(int i,int type)364b66f2d16SKris Kennaway handle_is_ok(int i, int type)
365b66f2d16SKris Kennaway {
366d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type;
367b66f2d16SKris Kennaway }
368b66f2d16SKris Kennaway
369ae1f160dSDag-Erling Smørgrav static int
handle_to_string(int handle,u_char ** stringp,int * hlenp)370bc5531deSDag-Erling Smørgrav handle_to_string(int handle, u_char **stringp, int *hlenp)
371b66f2d16SKris Kennaway {
372b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL)
373b66f2d16SKris Kennaway return -1;
3741e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t));
375761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle);
3761e8db6e2SBrian Feldman *hlenp = sizeof(int32_t);
377b66f2d16SKris Kennaway return 0;
378b66f2d16SKris Kennaway }
379b66f2d16SKris Kennaway
380ae1f160dSDag-Erling Smørgrav static int
handle_from_string(const u_char * handle,u_int hlen)381bc5531deSDag-Erling Smørgrav handle_from_string(const u_char *handle, u_int hlen)
382b66f2d16SKris Kennaway {
3831e8db6e2SBrian Feldman int val;
3841e8db6e2SBrian Feldman
3851e8db6e2SBrian Feldman if (hlen != sizeof(int32_t))
386b66f2d16SKris Kennaway return -1;
387761efaa7SDag-Erling Smørgrav val = get_u32(handle);
388b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) ||
389b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR))
390b66f2d16SKris Kennaway return val;
391b66f2d16SKris Kennaway return -1;
392b66f2d16SKris Kennaway }
393b66f2d16SKris Kennaway
394ae1f160dSDag-Erling Smørgrav static char *
handle_to_name(int handle)395b66f2d16SKris Kennaway handle_to_name(int handle)
396b66f2d16SKris Kennaway {
397b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)||
398b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE))
399b66f2d16SKris Kennaway return handles[handle].name;
400b66f2d16SKris Kennaway return NULL;
401b66f2d16SKris Kennaway }
402b66f2d16SKris Kennaway
403ae1f160dSDag-Erling Smørgrav static DIR *
handle_to_dir(int handle)404b66f2d16SKris Kennaway handle_to_dir(int handle)
405b66f2d16SKris Kennaway {
406b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR))
407b66f2d16SKris Kennaway return handles[handle].dirp;
408b66f2d16SKris Kennaway return NULL;
409b66f2d16SKris Kennaway }
410b66f2d16SKris Kennaway
411ae1f160dSDag-Erling Smørgrav static int
handle_to_fd(int handle)412b66f2d16SKris Kennaway handle_to_fd(int handle)
413b66f2d16SKris Kennaway {
414b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE))
415b66f2d16SKris Kennaway return handles[handle].fd;
416b66f2d16SKris Kennaway return -1;
417b66f2d16SKris Kennaway }
418b66f2d16SKris Kennaway
419f7167e0eSDag-Erling Smørgrav static int
handle_to_flags(int handle)420f7167e0eSDag-Erling Smørgrav handle_to_flags(int handle)
421f7167e0eSDag-Erling Smørgrav {
422f7167e0eSDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE))
423f7167e0eSDag-Erling Smørgrav return handles[handle].flags;
424f7167e0eSDag-Erling Smørgrav return 0;
425f7167e0eSDag-Erling Smørgrav }
426f7167e0eSDag-Erling Smørgrav
427761efaa7SDag-Erling Smørgrav static void
handle_update_read(int handle,ssize_t bytes)428761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes)
429761efaa7SDag-Erling Smørgrav {
430761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
431761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes;
432761efaa7SDag-Erling Smørgrav }
433761efaa7SDag-Erling Smørgrav
434761efaa7SDag-Erling Smørgrav static void
handle_update_write(int handle,ssize_t bytes)435761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes)
436761efaa7SDag-Erling Smørgrav {
437761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
438761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes;
439761efaa7SDag-Erling Smørgrav }
440761efaa7SDag-Erling Smørgrav
441761efaa7SDag-Erling Smørgrav static u_int64_t
handle_bytes_read(int handle)442761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle)
443761efaa7SDag-Erling Smørgrav {
444761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE))
445761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read);
446761efaa7SDag-Erling Smørgrav return 0;
447761efaa7SDag-Erling Smørgrav }
448761efaa7SDag-Erling Smørgrav
449761efaa7SDag-Erling Smørgrav static u_int64_t
handle_bytes_write(int handle)450761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle)
451761efaa7SDag-Erling Smørgrav {
452761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE))
453761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write);
454761efaa7SDag-Erling Smørgrav return 0;
455761efaa7SDag-Erling Smørgrav }
456761efaa7SDag-Erling Smørgrav
457ae1f160dSDag-Erling Smørgrav static int
handle_close(int handle)458b66f2d16SKris Kennaway handle_close(int handle)
459b66f2d16SKris Kennaway {
460b66f2d16SKris Kennaway int ret = -1;
4611e8db6e2SBrian Feldman
462b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) {
463b66f2d16SKris Kennaway ret = close(handles[handle].fd);
464e4a9863fSDag-Erling Smørgrav free(handles[handle].name);
465d4af9e69SDag-Erling Smørgrav handle_unused(handle);
466b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) {
467b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp);
468e4a9863fSDag-Erling Smørgrav free(handles[handle].name);
469d4af9e69SDag-Erling Smørgrav handle_unused(handle);
470b66f2d16SKris Kennaway } else {
471b66f2d16SKris Kennaway errno = ENOENT;
472b66f2d16SKris Kennaway }
473b66f2d16SKris Kennaway return ret;
474b66f2d16SKris Kennaway }
475b66f2d16SKris Kennaway
476761efaa7SDag-Erling Smørgrav static void
handle_log_close(int handle,char * emsg)477761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg)
478761efaa7SDag-Erling Smørgrav {
479761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) {
480761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu",
481761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
482761efaa7SDag-Erling Smørgrav handle_to_name(handle),
483d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle),
484d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle));
485761efaa7SDag-Erling Smørgrav } else {
486761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"",
487761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
488761efaa7SDag-Erling Smørgrav handle_to_name(handle));
489761efaa7SDag-Erling Smørgrav }
490761efaa7SDag-Erling Smørgrav }
491761efaa7SDag-Erling Smørgrav
492761efaa7SDag-Erling Smørgrav static void
handle_log_exit(void)493761efaa7SDag-Erling Smørgrav handle_log_exit(void)
494761efaa7SDag-Erling Smørgrav {
495761efaa7SDag-Erling Smørgrav u_int i;
496761efaa7SDag-Erling Smørgrav
497d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++)
498761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED)
499761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced");
500761efaa7SDag-Erling Smørgrav }
501761efaa7SDag-Erling Smørgrav
502ae1f160dSDag-Erling Smørgrav static int
get_handle(struct sshbuf * queue,int * hp)503bc5531deSDag-Erling Smørgrav get_handle(struct sshbuf *queue, int *hp)
504b66f2d16SKris Kennaway {
505bc5531deSDag-Erling Smørgrav u_char *handle;
506bc5531deSDag-Erling Smørgrav int r;
507bc5531deSDag-Erling Smørgrav size_t hlen;
5081e8db6e2SBrian Feldman
509bc5531deSDag-Erling Smørgrav *hp = -1;
510bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0)
511bc5531deSDag-Erling Smørgrav return r;
5121e8db6e2SBrian Feldman if (hlen < 256)
513bc5531deSDag-Erling Smørgrav *hp = handle_from_string(handle, hlen);
514e4a9863fSDag-Erling Smørgrav free(handle);
515bc5531deSDag-Erling Smørgrav return 0;
516b66f2d16SKris Kennaway }
517b66f2d16SKris Kennaway
518b66f2d16SKris Kennaway /* send replies */
519b66f2d16SKris Kennaway
520ae1f160dSDag-Erling Smørgrav static void
send_msg(struct sshbuf * m)521bc5531deSDag-Erling Smørgrav send_msg(struct sshbuf *m)
522b66f2d16SKris Kennaway {
523bc5531deSDag-Erling Smørgrav int r;
5241e8db6e2SBrian Feldman
525bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(oqueue, m)) != 0)
52619261079SEd Maste fatal_fr(r, "enqueue");
527bc5531deSDag-Erling Smørgrav sshbuf_reset(m);
528b66f2d16SKris Kennaway }
529b66f2d16SKris Kennaway
530761efaa7SDag-Erling Smørgrav static const char *
status_to_message(u_int32_t status)531761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status)
532b66f2d16SKris Kennaway {
5331323ec57SEd Maste static const char * const status_messages[] = {
5341e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */
5351e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */
5361e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */
5371e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */
5381e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */
5391e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */
5401e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */
5411e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */
5421e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
5431e8db6e2SBrian Feldman "Unknown error" /* Others */
5441e8db6e2SBrian Feldman };
545ca86bcf2SDag-Erling Smørgrav return (status_messages[MINIMUM(status,SSH2_FX_MAX)]);
546761efaa7SDag-Erling Smørgrav }
5471e8db6e2SBrian Feldman
548761efaa7SDag-Erling Smørgrav static void
send_status_errmsg(u_int32_t id,u_int32_t status,const char * errmsg)5491323ec57SEd Maste send_status_errmsg(u_int32_t id, u_int32_t status, const char *errmsg)
550761efaa7SDag-Erling Smørgrav {
551bc5531deSDag-Erling Smørgrav struct sshbuf *msg;
552bc5531deSDag-Erling Smørgrav int r;
553761efaa7SDag-Erling Smørgrav
554761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status);
555761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE ||
556761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF))
557761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status));
558bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL)
55919261079SEd Maste fatal_f("sshbuf_new failed");
560bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 ||
561bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 ||
562bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, status)) != 0)
56319261079SEd Maste fatal_fr(r, "compose");
5641e8db6e2SBrian Feldman if (version >= 3) {
5651323ec57SEd Maste if ((r = sshbuf_put_cstring(msg, errmsg == NULL ?
5661323ec57SEd Maste status_to_message(status) : errmsg)) != 0 ||
567bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "")) != 0)
56819261079SEd Maste fatal_fr(r, "compose message");
5691e8db6e2SBrian Feldman }
570bc5531deSDag-Erling Smørgrav send_msg(msg);
571bc5531deSDag-Erling Smørgrav sshbuf_free(msg);
572b66f2d16SKris Kennaway }
5731323ec57SEd Maste
5741323ec57SEd Maste static void
send_status(u_int32_t id,u_int32_t status)5751323ec57SEd Maste send_status(u_int32_t id, u_int32_t status)
5761323ec57SEd Maste {
5771323ec57SEd Maste send_status_errmsg(id, status, NULL);
5781323ec57SEd Maste }
5791323ec57SEd Maste
580ae1f160dSDag-Erling Smørgrav static void
send_data_or_handle(char type,u_int32_t id,const u_char * data,int dlen)581bc5531deSDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen)
582b66f2d16SKris Kennaway {
583bc5531deSDag-Erling Smørgrav struct sshbuf *msg;
584bc5531deSDag-Erling Smørgrav int r;
5851e8db6e2SBrian Feldman
586bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL)
58719261079SEd Maste fatal_f("sshbuf_new failed");
588bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, type)) != 0 ||
589bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 ||
590bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(msg, data, dlen)) != 0)
59119261079SEd Maste fatal_fr(r, "compose");
592bc5531deSDag-Erling Smørgrav send_msg(msg);
593bc5531deSDag-Erling Smørgrav sshbuf_free(msg);
594b66f2d16SKris Kennaway }
595b66f2d16SKris Kennaway
596ae1f160dSDag-Erling Smørgrav static void
send_data(u_int32_t id,const u_char * data,int dlen)597bc5531deSDag-Erling Smørgrav send_data(u_int32_t id, const u_char *data, int dlen)
598b66f2d16SKris Kennaway {
599761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen);
6001e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
601b66f2d16SKris Kennaway }
602b66f2d16SKris Kennaway
603ae1f160dSDag-Erling Smørgrav static void
send_handle(u_int32_t id,int handle)604b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle)
605b66f2d16SKris Kennaway {
606bc5531deSDag-Erling Smørgrav u_char *string;
607b66f2d16SKris Kennaway int hlen;
6081e8db6e2SBrian Feldman
609b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen);
610535af610SEd Maste debug("request %u: sent handle %d", id, handle);
6111e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
612e4a9863fSDag-Erling Smørgrav free(string);
613b66f2d16SKris Kennaway }
614b66f2d16SKris Kennaway
615ae1f160dSDag-Erling Smørgrav static void
send_names(u_int32_t id,int count,const Stat * stats)616efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats)
617b66f2d16SKris Kennaway {
618bc5531deSDag-Erling Smørgrav struct sshbuf *msg;
619bc5531deSDag-Erling Smørgrav int i, r;
6201e8db6e2SBrian Feldman
621bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL)
62219261079SEd Maste fatal_f("sshbuf_new failed");
623bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 ||
624bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 ||
625bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, count)) != 0)
62619261079SEd Maste fatal_fr(r, "compose");
627761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count);
628b66f2d16SKris Kennaway for (i = 0; i < count; i++) {
629bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 ||
630bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 ||
631bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, &stats[i].attrib)) != 0)
63219261079SEd Maste fatal_fr(r, "compose filenames/attrib");
633b66f2d16SKris Kennaway }
634bc5531deSDag-Erling Smørgrav send_msg(msg);
635bc5531deSDag-Erling Smørgrav sshbuf_free(msg);
636b66f2d16SKris Kennaway }
637b66f2d16SKris Kennaway
638ae1f160dSDag-Erling Smørgrav static void
send_attrib(u_int32_t id,const Attrib * a)639efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a)
640b66f2d16SKris Kennaway {
641bc5531deSDag-Erling Smørgrav struct sshbuf *msg;
642bc5531deSDag-Erling Smørgrav int r;
6431e8db6e2SBrian Feldman
644761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags);
645bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL)
64619261079SEd Maste fatal_f("sshbuf_new failed");
647bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 ||
648bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 ||
649bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, a)) != 0)
65019261079SEd Maste fatal_fr(r, "compose");
651bc5531deSDag-Erling Smørgrav send_msg(msg);
652bc5531deSDag-Erling Smørgrav sshbuf_free(msg);
653b66f2d16SKris Kennaway }
654b66f2d16SKris Kennaway
655d4af9e69SDag-Erling Smørgrav static void
send_statvfs(u_int32_t id,struct statvfs * st)656d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st)
657d4af9e69SDag-Erling Smørgrav {
658bc5531deSDag-Erling Smørgrav struct sshbuf *msg;
659d4af9e69SDag-Erling Smørgrav u_int64_t flag;
660bc5531deSDag-Erling Smørgrav int r;
661d4af9e69SDag-Erling Smørgrav
662d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0;
663d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0;
664d4af9e69SDag-Erling Smørgrav
665bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL)
66619261079SEd Maste fatal_f("sshbuf_new failed");
667bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 ||
668bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 ||
669bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 ||
670bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 ||
671bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 ||
672bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 ||
673bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 ||
674bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_files)) != 0 ||
675bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 ||
676bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_favail)) != 0 ||
677bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 ||
678bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, flag)) != 0 ||
679bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_namemax)) != 0)
68019261079SEd Maste fatal_fr(r, "compose");
681bc5531deSDag-Erling Smørgrav send_msg(msg);
682bc5531deSDag-Erling Smørgrav sshbuf_free(msg);
683d4af9e69SDag-Erling Smørgrav }
684d4af9e69SDag-Erling Smørgrav
68519261079SEd Maste /*
68619261079SEd Maste * Prepare SSH2_FXP_VERSION extension advertisement for a single extension.
6871323ec57SEd Maste * The extension is checked for permission prior to advertisement.
68819261079SEd Maste */
68919261079SEd Maste static int
compose_extension(struct sshbuf * msg,const char * name,const char * ver)69019261079SEd Maste compose_extension(struct sshbuf *msg, const char *name, const char *ver)
69119261079SEd Maste {
69219261079SEd Maste int r;
69319261079SEd Maste const struct sftp_handler *exthnd;
69419261079SEd Maste
69519261079SEd Maste if ((exthnd = extended_handler_byname(name)) == NULL)
69619261079SEd Maste fatal_f("internal error: no handler for %s", name);
69719261079SEd Maste if (!request_permitted(exthnd)) {
69819261079SEd Maste debug2_f("refusing to advertise disallowed extension %s", name);
69919261079SEd Maste return 0;
70019261079SEd Maste }
70119261079SEd Maste if ((r = sshbuf_put_cstring(msg, name)) != 0 ||
70219261079SEd Maste (r = sshbuf_put_cstring(msg, ver)) != 0)
70319261079SEd Maste fatal_fr(r, "compose %s", name);
70419261079SEd Maste return 0;
70519261079SEd Maste }
70619261079SEd Maste
707b66f2d16SKris Kennaway /* parse incoming */
708b66f2d16SKris Kennaway
709ae1f160dSDag-Erling Smørgrav static void
process_init(void)710b66f2d16SKris Kennaway process_init(void)
711b66f2d16SKris Kennaway {
712bc5531deSDag-Erling Smørgrav struct sshbuf *msg;
713bc5531deSDag-Erling Smørgrav int r;
714b66f2d16SKris Kennaway
715bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &version)) != 0)
71619261079SEd Maste fatal_fr(r, "parse");
717e146993eSDag-Erling Smørgrav verbose("received client version %u", version);
718bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL)
71919261079SEd Maste fatal_f("sshbuf_new failed");
720bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 ||
72119261079SEd Maste (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0)
72219261079SEd Maste fatal_fr(r, "compose");
72319261079SEd Maste
7241323ec57SEd Maste /* extension advertisements */
72519261079SEd Maste compose_extension(msg, "posix-rename@openssh.com", "1");
72619261079SEd Maste compose_extension(msg, "statvfs@openssh.com", "2");
72719261079SEd Maste compose_extension(msg, "fstatvfs@openssh.com", "2");
72819261079SEd Maste compose_extension(msg, "hardlink@openssh.com", "1");
72919261079SEd Maste compose_extension(msg, "fsync@openssh.com", "1");
73019261079SEd Maste compose_extension(msg, "lsetstat@openssh.com", "1");
73119261079SEd Maste compose_extension(msg, "limits@openssh.com", "1");
73219261079SEd Maste compose_extension(msg, "expand-path@openssh.com", "1");
73387c1498dSEd Maste compose_extension(msg, "copy-data", "1");
73438a52bd3SEd Maste compose_extension(msg, "home-directory", "1");
73538a52bd3SEd Maste compose_extension(msg, "users-groups-by-id@openssh.com", "1");
73619261079SEd Maste
737bc5531deSDag-Erling Smørgrav send_msg(msg);
738bc5531deSDag-Erling Smørgrav sshbuf_free(msg);
739b66f2d16SKris Kennaway }
740b66f2d16SKris Kennaway
741ae1f160dSDag-Erling Smørgrav static void
process_open(u_int32_t id)742f7167e0eSDag-Erling Smørgrav process_open(u_int32_t id)
743b66f2d16SKris Kennaway {
744f7167e0eSDag-Erling Smørgrav u_int32_t pflags;
745bc5531deSDag-Erling Smørgrav Attrib a;
746b66f2d16SKris Kennaway char *name;
747bc5531deSDag-Erling Smørgrav int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE;
748b66f2d16SKris Kennaway
749bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
750bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */
751bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0)
75219261079SEd Maste fatal_fr(r, "parse");
753bc5531deSDag-Erling Smørgrav
754761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags);
755b66f2d16SKris Kennaway flags = flags_from_portable(pflags);
756bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666;
757761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o",
758761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode);
759b15c8340SDag-Erling Smørgrav if (readonly &&
7604f52dfbbSDag-Erling Smørgrav ((flags & O_ACCMODE) != O_RDONLY ||
7614f52dfbbSDag-Erling Smørgrav (flags & (O_CREAT|O_TRUNC)) != 0)) {
762f7167e0eSDag-Erling Smørgrav verbose("Refusing open request in read-only mode");
763b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED;
764f7167e0eSDag-Erling Smørgrav } else {
765b66f2d16SKris Kennaway fd = open(name, flags, mode);
76619261079SEd Maste if (fd == -1) {
767b66f2d16SKris Kennaway status = errno_to_portable(errno);
768b66f2d16SKris Kennaway } else {
769f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, flags, NULL);
770b66f2d16SKris Kennaway if (handle < 0) {
771b66f2d16SKris Kennaway close(fd);
772b66f2d16SKris Kennaway } else {
773b66f2d16SKris Kennaway send_handle(id, handle);
7741e8db6e2SBrian Feldman status = SSH2_FX_OK;
775b66f2d16SKris Kennaway }
776b66f2d16SKris Kennaway }
777b15c8340SDag-Erling Smørgrav }
7781e8db6e2SBrian Feldman if (status != SSH2_FX_OK)
779b66f2d16SKris Kennaway send_status(id, status);
780e4a9863fSDag-Erling Smørgrav free(name);
781b66f2d16SKris Kennaway }
782b66f2d16SKris Kennaway
783ae1f160dSDag-Erling Smørgrav static void
process_close(u_int32_t id)784f7167e0eSDag-Erling Smørgrav process_close(u_int32_t id)
785b66f2d16SKris Kennaway {
786bc5531deSDag-Erling Smørgrav int r, handle, ret, status = SSH2_FX_FAILURE;
787b66f2d16SKris Kennaway
788bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0)
78919261079SEd Maste fatal_fr(r, "parse");
790bc5531deSDag-Erling Smørgrav
791761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle);
792761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL);
793b66f2d16SKris Kennaway ret = handle_close(handle);
7941e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
795b66f2d16SKris Kennaway send_status(id, status);
796b66f2d16SKris Kennaway }
797b66f2d16SKris Kennaway
798ae1f160dSDag-Erling Smørgrav static void
process_read(u_int32_t id)799f7167e0eSDag-Erling Smørgrav process_read(u_int32_t id)
800b66f2d16SKris Kennaway {
80119261079SEd Maste static u_char *buf;
80219261079SEd Maste static size_t buflen;
803f7167e0eSDag-Erling Smørgrav u_int32_t len;
804bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status = SSH2_FX_FAILURE;
805b66f2d16SKris Kennaway u_int64_t off;
806b66f2d16SKris Kennaway
807bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 ||
808bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 ||
809bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &len)) != 0)
81019261079SEd Maste fatal_fr(r, "parse");
811b66f2d16SKris Kennaway
81219261079SEd Maste debug("request %u: read \"%s\" (handle %d) off %llu len %u",
813761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len);
81419261079SEd Maste if ((fd = handle_to_fd(handle)) == -1)
81519261079SEd Maste goto out;
81619261079SEd Maste if (len > SFTP_MAX_READ_LENGTH) {
81719261079SEd Maste debug2("read change len %u to %u", len, SFTP_MAX_READ_LENGTH);
81819261079SEd Maste len = SFTP_MAX_READ_LENGTH;
819b66f2d16SKris Kennaway }
82019261079SEd Maste if (len > buflen) {
82119261079SEd Maste debug3_f("allocate %zu => %u", buflen, len);
8224d3fc8b0SEd Maste if ((buf = realloc(buf, len)) == NULL)
82319261079SEd Maste fatal_f("realloc failed");
82419261079SEd Maste buflen = len;
82519261079SEd Maste }
82619261079SEd Maste if (lseek(fd, off, SEEK_SET) == -1) {
827b66f2d16SKris Kennaway status = errno_to_portable(errno);
82819261079SEd Maste error_f("seek \"%.100s\": %s", handle_to_name(handle),
82919261079SEd Maste strerror(errno));
83019261079SEd Maste goto out;
83119261079SEd Maste }
83219261079SEd Maste if (len == 0) {
83319261079SEd Maste /* weird, but not strictly disallowed */
83419261079SEd Maste ret = 0;
83519261079SEd Maste } else if ((ret = read(fd, buf, len)) == -1) {
836b66f2d16SKris Kennaway status = errno_to_portable(errno);
83719261079SEd Maste error_f("read \"%.100s\": %s", handle_to_name(handle),
83819261079SEd Maste strerror(errno));
83919261079SEd Maste goto out;
840b66f2d16SKris Kennaway } else if (ret == 0) {
8411e8db6e2SBrian Feldman status = SSH2_FX_EOF;
84219261079SEd Maste goto out;
84319261079SEd Maste }
844b66f2d16SKris Kennaway send_data(id, buf, ret);
845761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret);
84619261079SEd Maste /* success */
84719261079SEd Maste status = SSH2_FX_OK;
84819261079SEd Maste out:
8491e8db6e2SBrian Feldman if (status != SSH2_FX_OK)
850b66f2d16SKris Kennaway send_status(id, status);
851b66f2d16SKris Kennaway }
852b66f2d16SKris Kennaway
853ae1f160dSDag-Erling Smørgrav static void
process_write(u_int32_t id)854f7167e0eSDag-Erling Smørgrav process_write(u_int32_t id)
855b66f2d16SKris Kennaway {
856b66f2d16SKris Kennaway u_int64_t off;
857bc5531deSDag-Erling Smørgrav size_t len;
858bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status;
859bc5531deSDag-Erling Smørgrav u_char *data;
860b66f2d16SKris Kennaway
861bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 ||
862bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 ||
863bc5531deSDag-Erling Smørgrav (r = sshbuf_get_string(iqueue, &data, &len)) != 0)
86419261079SEd Maste fatal_fr(r, "parse");
865b66f2d16SKris Kennaway
866bc5531deSDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %zu",
867761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len);
868b66f2d16SKris Kennaway fd = handle_to_fd(handle);
869b15c8340SDag-Erling Smørgrav
870b15c8340SDag-Erling Smørgrav if (fd < 0)
871b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE;
872b15c8340SDag-Erling Smørgrav else {
873f7167e0eSDag-Erling Smørgrav if (!(handle_to_flags(handle) & O_APPEND) &&
87419261079SEd Maste lseek(fd, off, SEEK_SET) == -1) {
875b66f2d16SKris Kennaway status = errno_to_portable(errno);
87619261079SEd Maste error_f("seek \"%.100s\": %s", handle_to_name(handle),
87719261079SEd Maste strerror(errno));
878b66f2d16SKris Kennaway } else {
879b66f2d16SKris Kennaway /* XXX ATOMICIO ? */
880b66f2d16SKris Kennaway ret = write(fd, data, len);
88119261079SEd Maste if (ret == -1) {
882b66f2d16SKris Kennaway status = errno_to_portable(errno);
88319261079SEd Maste error_f("write \"%.100s\": %s",
88419261079SEd Maste handle_to_name(handle), strerror(errno));
885043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) {
8861e8db6e2SBrian Feldman status = SSH2_FX_OK;
887761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret);
888b66f2d16SKris Kennaway } else {
88919261079SEd Maste debug2_f("nothing at all written");
890b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE;
891b66f2d16SKris Kennaway }
892b66f2d16SKris Kennaway }
893b66f2d16SKris Kennaway }
894b66f2d16SKris Kennaway send_status(id, status);
895e4a9863fSDag-Erling Smørgrav free(data);
896b66f2d16SKris Kennaway }
897b66f2d16SKris Kennaway
898ae1f160dSDag-Erling Smørgrav static void
process_do_stat(u_int32_t id,int do_lstat)899f7167e0eSDag-Erling Smørgrav process_do_stat(u_int32_t id, int do_lstat)
900b66f2d16SKris Kennaway {
9011e8db6e2SBrian Feldman Attrib a;
902b66f2d16SKris Kennaway struct stat st;
903b66f2d16SKris Kennaway char *name;
904bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE;
905b66f2d16SKris Kennaway
906bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
90719261079SEd Maste fatal_fr(r, "parse");
908bc5531deSDag-Erling Smørgrav
909761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : "");
910761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
911bc5531deSDag-Erling Smørgrav r = do_lstat ? lstat(name, &st) : stat(name, &st);
91219261079SEd Maste if (r == -1) {
913b66f2d16SKris Kennaway status = errno_to_portable(errno);
914b66f2d16SKris Kennaway } else {
9151e8db6e2SBrian Feldman stat_to_attrib(&st, &a);
9161e8db6e2SBrian Feldman send_attrib(id, &a);
9171e8db6e2SBrian Feldman status = SSH2_FX_OK;
918b66f2d16SKris Kennaway }
9191e8db6e2SBrian Feldman if (status != SSH2_FX_OK)
920b66f2d16SKris Kennaway send_status(id, status);
921e4a9863fSDag-Erling Smørgrav free(name);
922b66f2d16SKris Kennaway }
923b66f2d16SKris Kennaway
924ae1f160dSDag-Erling Smørgrav static void
process_stat(u_int32_t id)925f7167e0eSDag-Erling Smørgrav process_stat(u_int32_t id)
926b66f2d16SKris Kennaway {
927f7167e0eSDag-Erling Smørgrav process_do_stat(id, 0);
928b66f2d16SKris Kennaway }
929b66f2d16SKris Kennaway
930ae1f160dSDag-Erling Smørgrav static void
process_lstat(u_int32_t id)931f7167e0eSDag-Erling Smørgrav process_lstat(u_int32_t id)
932b66f2d16SKris Kennaway {
933f7167e0eSDag-Erling Smørgrav process_do_stat(id, 1);
934b66f2d16SKris Kennaway }
935b66f2d16SKris Kennaway
936ae1f160dSDag-Erling Smørgrav static void
process_fstat(u_int32_t id)937f7167e0eSDag-Erling Smørgrav process_fstat(u_int32_t id)
938b66f2d16SKris Kennaway {
9391e8db6e2SBrian Feldman Attrib a;
940b66f2d16SKris Kennaway struct stat st;
941bc5531deSDag-Erling Smørgrav int fd, r, handle, status = SSH2_FX_FAILURE;
942b66f2d16SKris Kennaway
943bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0)
94419261079SEd Maste fatal_fr(r, "parse");
945761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)",
946761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle);
947b66f2d16SKris Kennaway fd = handle_to_fd(handle);
948b66f2d16SKris Kennaway if (fd >= 0) {
949bc5531deSDag-Erling Smørgrav r = fstat(fd, &st);
95019261079SEd Maste if (r == -1) {
951b66f2d16SKris Kennaway status = errno_to_portable(errno);
952b66f2d16SKris Kennaway } else {
9531e8db6e2SBrian Feldman stat_to_attrib(&st, &a);
9541e8db6e2SBrian Feldman send_attrib(id, &a);
9551e8db6e2SBrian Feldman status = SSH2_FX_OK;
956b66f2d16SKris Kennaway }
957b66f2d16SKris Kennaway }
9581e8db6e2SBrian Feldman if (status != SSH2_FX_OK)
959b66f2d16SKris Kennaway send_status(id, status);
960b66f2d16SKris Kennaway }
961b66f2d16SKris Kennaway
962ae1f160dSDag-Erling Smørgrav static struct timeval *
attrib_to_tv(const Attrib * a)963efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a)
964b66f2d16SKris Kennaway {
965b66f2d16SKris Kennaway static struct timeval tv[2];
9661e8db6e2SBrian Feldman
967b66f2d16SKris Kennaway tv[0].tv_sec = a->atime;
968b66f2d16SKris Kennaway tv[0].tv_usec = 0;
969b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime;
970b66f2d16SKris Kennaway tv[1].tv_usec = 0;
971b66f2d16SKris Kennaway return tv;
972b66f2d16SKris Kennaway }
973b66f2d16SKris Kennaway
97419261079SEd Maste static struct timespec *
attrib_to_ts(const Attrib * a)97519261079SEd Maste attrib_to_ts(const Attrib *a)
97619261079SEd Maste {
97719261079SEd Maste static struct timespec ts[2];
97819261079SEd Maste
97919261079SEd Maste ts[0].tv_sec = a->atime;
98019261079SEd Maste ts[0].tv_nsec = 0;
98119261079SEd Maste ts[1].tv_sec = a->mtime;
98219261079SEd Maste ts[1].tv_nsec = 0;
98319261079SEd Maste return ts;
98419261079SEd Maste }
98519261079SEd Maste
986ae1f160dSDag-Erling Smørgrav static void
process_setstat(u_int32_t id)987f7167e0eSDag-Erling Smørgrav process_setstat(u_int32_t id)
988b66f2d16SKris Kennaway {
989bc5531deSDag-Erling Smørgrav Attrib a;
990b66f2d16SKris Kennaway char *name;
991bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_OK;
992b66f2d16SKris Kennaway
993bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
994bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0)
99519261079SEd Maste fatal_fr(r, "parse");
996bc5531deSDag-Erling Smørgrav
997761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name);
998bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
999d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu",
1000bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size);
1001bc5531deSDag-Erling Smørgrav r = truncate(name, a.size);
1002bc5531deSDag-Erling Smørgrav if (r == -1)
1003ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno);
1004ae1f160dSDag-Erling Smørgrav }
1005bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1006bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm);
1007bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777);
1008bc5531deSDag-Erling Smørgrav if (r == -1)
1009b66f2d16SKris Kennaway status = errno_to_portable(errno);
1010b66f2d16SKris Kennaway }
1011bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1012761efaa7SDag-Erling Smørgrav char buf[64];
1013bc5531deSDag-Erling Smørgrav time_t t = a.mtime;
1014761efaa7SDag-Erling Smørgrav
1015761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
1016761efaa7SDag-Erling Smørgrav localtime(&t));
1017761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf);
1018bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a));
1019bc5531deSDag-Erling Smørgrav if (r == -1)
1020b66f2d16SKris Kennaway status = errno_to_portable(errno);
1021b66f2d16SKris Kennaway }
1022bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
1023761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name,
1024bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid);
1025bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid);
1026bc5531deSDag-Erling Smørgrav if (r == -1)
10271e8db6e2SBrian Feldman status = errno_to_portable(errno);
10281e8db6e2SBrian Feldman }
1029b66f2d16SKris Kennaway send_status(id, status);
1030e4a9863fSDag-Erling Smørgrav free(name);
1031b66f2d16SKris Kennaway }
1032b66f2d16SKris Kennaway
1033ae1f160dSDag-Erling Smørgrav static void
process_fsetstat(u_int32_t id)1034f7167e0eSDag-Erling Smørgrav process_fsetstat(u_int32_t id)
1035b66f2d16SKris Kennaway {
1036bc5531deSDag-Erling Smørgrav Attrib a;
1037bc5531deSDag-Erling Smørgrav int handle, fd, r;
10381e8db6e2SBrian Feldman int status = SSH2_FX_OK;
1039b66f2d16SKris Kennaway
1040bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 ||
1041bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0)
104219261079SEd Maste fatal_fr(r, "parse");
1043bc5531deSDag-Erling Smørgrav
1044761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle);
1045b66f2d16SKris Kennaway fd = handle_to_fd(handle);
1046b15c8340SDag-Erling Smørgrav if (fd < 0)
10471e8db6e2SBrian Feldman status = SSH2_FX_FAILURE;
1048b15c8340SDag-Erling Smørgrav else {
1049761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle);
1050761efaa7SDag-Erling Smørgrav
1051bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
1052d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu",
1053bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size);
1054bc5531deSDag-Erling Smørgrav r = ftruncate(fd, a.size);
1055bc5531deSDag-Erling Smørgrav if (r == -1)
1056ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno);
1057ae1f160dSDag-Erling Smørgrav }
1058bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1059bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm);
106083d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD
1061bc5531deSDag-Erling Smørgrav r = fchmod(fd, a.perm & 07777);
106283d2307dSDag-Erling Smørgrav #else
1063bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777);
106483d2307dSDag-Erling Smørgrav #endif
1065bc5531deSDag-Erling Smørgrav if (r == -1)
1066b66f2d16SKris Kennaway status = errno_to_portable(errno);
1067b66f2d16SKris Kennaway }
1068bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1069761efaa7SDag-Erling Smørgrav char buf[64];
1070bc5531deSDag-Erling Smørgrav time_t t = a.mtime;
1071761efaa7SDag-Erling Smørgrav
1072761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
1073761efaa7SDag-Erling Smørgrav localtime(&t));
1074761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf);
107583d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES
1076bc5531deSDag-Erling Smørgrav r = futimes(fd, attrib_to_tv(&a));
107783d2307dSDag-Erling Smørgrav #else
1078bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a));
107983d2307dSDag-Erling Smørgrav #endif
1080bc5531deSDag-Erling Smørgrav if (r == -1)
1081b66f2d16SKris Kennaway status = errno_to_portable(errno);
1082b66f2d16SKris Kennaway }
1083bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
1084761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name,
1085bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid);
108683d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN
1087bc5531deSDag-Erling Smørgrav r = fchown(fd, a.uid, a.gid);
108883d2307dSDag-Erling Smørgrav #else
1089bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid);
109083d2307dSDag-Erling Smørgrav #endif
1091bc5531deSDag-Erling Smørgrav if (r == -1)
10921e8db6e2SBrian Feldman status = errno_to_portable(errno);
10931e8db6e2SBrian Feldman }
1094b66f2d16SKris Kennaway }
1095b66f2d16SKris Kennaway send_status(id, status);
1096b66f2d16SKris Kennaway }
1097b66f2d16SKris Kennaway
1098ae1f160dSDag-Erling Smørgrav static void
process_opendir(u_int32_t id)1099f7167e0eSDag-Erling Smørgrav process_opendir(u_int32_t id)
1100b66f2d16SKris Kennaway {
1101b66f2d16SKris Kennaway DIR *dirp = NULL;
1102b66f2d16SKris Kennaway char *path;
1103bc5531deSDag-Erling Smørgrav int r, handle, status = SSH2_FX_FAILURE;
1104b66f2d16SKris Kennaway
1105bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
110619261079SEd Maste fatal_fr(r, "parse");
1107bc5531deSDag-Erling Smørgrav
1108761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id);
1109761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path);
1110b66f2d16SKris Kennaway dirp = opendir(path);
1111b66f2d16SKris Kennaway if (dirp == NULL) {
1112b66f2d16SKris Kennaway status = errno_to_portable(errno);
1113b66f2d16SKris Kennaway } else {
1114f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, 0, dirp);
1115b66f2d16SKris Kennaway if (handle < 0) {
1116b66f2d16SKris Kennaway closedir(dirp);
1117b66f2d16SKris Kennaway } else {
1118b66f2d16SKris Kennaway send_handle(id, handle);
11191e8db6e2SBrian Feldman status = SSH2_FX_OK;
1120b66f2d16SKris Kennaway }
1121b66f2d16SKris Kennaway
1122b66f2d16SKris Kennaway }
11231e8db6e2SBrian Feldman if (status != SSH2_FX_OK)
1124b66f2d16SKris Kennaway send_status(id, status);
1125e4a9863fSDag-Erling Smørgrav free(path);
1126b66f2d16SKris Kennaway }
1127b66f2d16SKris Kennaway
1128ae1f160dSDag-Erling Smørgrav static void
process_readdir(u_int32_t id)1129f7167e0eSDag-Erling Smørgrav process_readdir(u_int32_t id)
1130b66f2d16SKris Kennaway {
1131b66f2d16SKris Kennaway DIR *dirp;
1132b66f2d16SKris Kennaway struct dirent *dp;
1133b66f2d16SKris Kennaway char *path;
1134bc5531deSDag-Erling Smørgrav int r, handle;
1135b66f2d16SKris Kennaway
1136bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0)
113719261079SEd Maste fatal_fr(r, "parse");
1138bc5531deSDag-Erling Smørgrav
1139761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id,
1140761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle);
1141b66f2d16SKris Kennaway dirp = handle_to_dir(handle);
1142b66f2d16SKris Kennaway path = handle_to_name(handle);
1143b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) {
11441e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE);
1145b66f2d16SKris Kennaway } else {
1146b66f2d16SKris Kennaway struct stat st;
1147bc5531deSDag-Erling Smørgrav char pathname[PATH_MAX];
1148b66f2d16SKris Kennaway Stat *stats;
1149b66f2d16SKris Kennaway int nstats = 10, count = 0, i;
1150ee21a45fSDag-Erling Smørgrav
1151761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat));
1152b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) {
1153b66f2d16SKris Kennaway if (count >= nstats) {
1154b66f2d16SKris Kennaway nstats *= 2;
1155557f75e5SDag-Erling Smørgrav stats = xreallocarray(stats, nstats, sizeof(Stat));
1156b66f2d16SKris Kennaway }
1157b66f2d16SKris Kennaway /* XXX OVERFLOW ? */
1158ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path,
1159ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name);
116019261079SEd Maste if (lstat(pathname, &st) == -1)
1161b66f2d16SKris Kennaway continue;
11621e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib));
1163b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name);
116438a52bd3SEd Maste stats[count].long_name = ls_file(dp->d_name, &st,
116538a52bd3SEd Maste 0, 0, NULL, NULL);
1166b66f2d16SKris Kennaway count++;
1167b66f2d16SKris Kennaway /* send up to 100 entries in one message */
11681e8db6e2SBrian Feldman /* XXX check packet size instead */
1169b66f2d16SKris Kennaway if (count == 100)
1170b66f2d16SKris Kennaway break;
1171b66f2d16SKris Kennaway }
11721e8db6e2SBrian Feldman if (count > 0) {
1173b66f2d16SKris Kennaway send_names(id, count, stats);
1174b66f2d16SKris Kennaway for (i = 0; i < count; i++) {
1175e4a9863fSDag-Erling Smørgrav free(stats[i].name);
1176e4a9863fSDag-Erling Smørgrav free(stats[i].long_name);
1177b66f2d16SKris Kennaway }
11781e8db6e2SBrian Feldman } else {
11791e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF);
11801e8db6e2SBrian Feldman }
1181e4a9863fSDag-Erling Smørgrav free(stats);
1182b66f2d16SKris Kennaway }
1183b66f2d16SKris Kennaway }
1184b66f2d16SKris Kennaway
1185ae1f160dSDag-Erling Smørgrav static void
process_remove(u_int32_t id)1186f7167e0eSDag-Erling Smørgrav process_remove(u_int32_t id)
1187b66f2d16SKris Kennaway {
1188b66f2d16SKris Kennaway char *name;
1189bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE;
1190b66f2d16SKris Kennaway
1191bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
119219261079SEd Maste fatal_fr(r, "parse");
1193bc5531deSDag-Erling Smørgrav
1194761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id);
1195761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name);
1196bc5531deSDag-Erling Smørgrav r = unlink(name);
1197bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1198b66f2d16SKris Kennaway send_status(id, status);
1199e4a9863fSDag-Erling Smørgrav free(name);
1200b66f2d16SKris Kennaway }
1201b66f2d16SKris Kennaway
1202ae1f160dSDag-Erling Smørgrav static void
process_mkdir(u_int32_t id)1203f7167e0eSDag-Erling Smørgrav process_mkdir(u_int32_t id)
1204b66f2d16SKris Kennaway {
1205bc5531deSDag-Erling Smørgrav Attrib a;
1206b66f2d16SKris Kennaway char *name;
1207bc5531deSDag-Erling Smørgrav int r, mode, status = SSH2_FX_FAILURE;
1208b66f2d16SKris Kennaway
1209bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
1210bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0)
121119261079SEd Maste fatal_fr(r, "parse");
1212bc5531deSDag-Erling Smørgrav
1213bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
1214bc5531deSDag-Erling Smørgrav a.perm & 07777 : 0777;
1215761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id);
1216761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode);
1217bc5531deSDag-Erling Smørgrav r = mkdir(name, mode);
1218bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1219b66f2d16SKris Kennaway send_status(id, status);
1220e4a9863fSDag-Erling Smørgrav free(name);
1221b66f2d16SKris Kennaway }
1222b66f2d16SKris Kennaway
1223ae1f160dSDag-Erling Smørgrav static void
process_rmdir(u_int32_t id)1224f7167e0eSDag-Erling Smørgrav process_rmdir(u_int32_t id)
1225b66f2d16SKris Kennaway {
1226b66f2d16SKris Kennaway char *name;
1227bc5531deSDag-Erling Smørgrav int r, status;
1228b66f2d16SKris Kennaway
1229bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
123019261079SEd Maste fatal_fr(r, "parse");
1231bc5531deSDag-Erling Smørgrav
1232761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id);
1233761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name);
1234bc5531deSDag-Erling Smørgrav r = rmdir(name);
1235bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1236b66f2d16SKris Kennaway send_status(id, status);
1237e4a9863fSDag-Erling Smørgrav free(name);
1238b66f2d16SKris Kennaway }
1239b66f2d16SKris Kennaway
1240ae1f160dSDag-Erling Smørgrav static void
process_realpath(u_int32_t id)1241f7167e0eSDag-Erling Smørgrav process_realpath(u_int32_t id)
1242b66f2d16SKris Kennaway {
1243bc5531deSDag-Erling Smørgrav char resolvedname[PATH_MAX];
1244b66f2d16SKris Kennaway char *path;
1245bc5531deSDag-Erling Smørgrav int r;
1246b66f2d16SKris Kennaway
1247bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
124819261079SEd Maste fatal_fr(r, "parse");
1249bc5531deSDag-Erling Smørgrav
12501e8db6e2SBrian Feldman if (path[0] == '\0') {
1251e4a9863fSDag-Erling Smørgrav free(path);
12521e8db6e2SBrian Feldman path = xstrdup(".");
12531e8db6e2SBrian Feldman }
1254761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id);
1255761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path);
125619261079SEd Maste if (sftp_realpath(path, resolvedname) == NULL) {
1257b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno));
1258b66f2d16SKris Kennaway } else {
1259b66f2d16SKris Kennaway Stat s;
1260b66f2d16SKris Kennaway attrib_clear(&s.attrib);
1261b66f2d16SKris Kennaway s.name = s.long_name = resolvedname;
1262b66f2d16SKris Kennaway send_names(id, 1, &s);
1263b66f2d16SKris Kennaway }
1264e4a9863fSDag-Erling Smørgrav free(path);
1265b66f2d16SKris Kennaway }
1266b66f2d16SKris Kennaway
1267ae1f160dSDag-Erling Smørgrav static void
process_rename(u_int32_t id)1268f7167e0eSDag-Erling Smørgrav process_rename(u_int32_t id)
1269b66f2d16SKris Kennaway {
1270b66f2d16SKris Kennaway char *oldpath, *newpath;
1271bc5531deSDag-Erling Smørgrav int r, status;
1272d0c8c0bcSDag-Erling Smørgrav struct stat sb;
1273b66f2d16SKris Kennaway
1274bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
1275bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
127619261079SEd Maste fatal_fr(r, "parse");
1277bc5531deSDag-Erling Smørgrav
1278761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id);
1279761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
1280d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE;
1281f7167e0eSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1)
1282d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno);
1283d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) {
1284d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */
1285d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) {
12867aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS
1287d4af9e69SDag-Erling Smørgrav #ifdef EXDEV
1288d4af9e69SDag-Erling Smørgrav || errno == EXDEV
1289d4af9e69SDag-Erling Smørgrav #endif
1290d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO
1291d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO
1292d74d50a8SDag-Erling Smørgrav #endif
1293d74d50a8SDag-Erling Smørgrav ) {
1294d74d50a8SDag-Erling Smørgrav struct stat st;
1295d74d50a8SDag-Erling Smørgrav
1296d74d50a8SDag-Erling Smørgrav /*
1297d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to
1298d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy.
1299d74d50a8SDag-Erling Smørgrav */
1300d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) {
1301d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1)
1302d74d50a8SDag-Erling Smørgrav status =
1303d74d50a8SDag-Erling Smørgrav errno_to_portable(errno);
1304d74d50a8SDag-Erling Smørgrav else
1305d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK;
1306d74d50a8SDag-Erling Smørgrav }
1307d74d50a8SDag-Erling Smørgrav } else {
1308d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno);
1309d74d50a8SDag-Erling Smørgrav }
1310d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) {
1311d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno);
1312d0c8c0bcSDag-Erling Smørgrav /* clean spare link */
1313d0c8c0bcSDag-Erling Smørgrav unlink(newpath);
1314d0c8c0bcSDag-Erling Smørgrav } else
1315d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK;
1316d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) {
1317d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1)
1318d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno);
1319d0c8c0bcSDag-Erling Smørgrav else
1320d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK;
13211e8db6e2SBrian Feldman }
1322b66f2d16SKris Kennaway send_status(id, status);
1323e4a9863fSDag-Erling Smørgrav free(oldpath);
1324e4a9863fSDag-Erling Smørgrav free(newpath);
1325b66f2d16SKris Kennaway }
1326b66f2d16SKris Kennaway
1327ae1f160dSDag-Erling Smørgrav static void
process_readlink(u_int32_t id)1328f7167e0eSDag-Erling Smørgrav process_readlink(u_int32_t id)
13291e8db6e2SBrian Feldman {
1330bc5531deSDag-Erling Smørgrav int r, len;
1331bc5531deSDag-Erling Smørgrav char buf[PATH_MAX];
13321e8db6e2SBrian Feldman char *path;
13331e8db6e2SBrian Feldman
1334bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
133519261079SEd Maste fatal_fr(r, "parse");
1336bc5531deSDag-Erling Smørgrav
1337761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id);
1338761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path);
1339d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
13401e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno));
13411e8db6e2SBrian Feldman else {
13421e8db6e2SBrian Feldman Stat s;
13431e8db6e2SBrian Feldman
1344d74d50a8SDag-Erling Smørgrav buf[len] = '\0';
13451e8db6e2SBrian Feldman attrib_clear(&s.attrib);
1346d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf;
13471e8db6e2SBrian Feldman send_names(id, 1, &s);
13481e8db6e2SBrian Feldman }
1349e4a9863fSDag-Erling Smørgrav free(path);
13501e8db6e2SBrian Feldman }
13511e8db6e2SBrian Feldman
1352ae1f160dSDag-Erling Smørgrav static void
process_symlink(u_int32_t id)1353f7167e0eSDag-Erling Smørgrav process_symlink(u_int32_t id)
13541e8db6e2SBrian Feldman {
13551e8db6e2SBrian Feldman char *oldpath, *newpath;
1356bc5531deSDag-Erling Smørgrav int r, status;
13571e8db6e2SBrian Feldman
1358bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
1359bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
136019261079SEd Maste fatal_fr(r, "parse");
1361bc5531deSDag-Erling Smørgrav
1362761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id);
1363761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
1364d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */
1365bc5531deSDag-Erling Smørgrav r = symlink(oldpath, newpath);
1366bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
13671e8db6e2SBrian Feldman send_status(id, status);
1368e4a9863fSDag-Erling Smørgrav free(oldpath);
1369e4a9863fSDag-Erling Smørgrav free(newpath);
13701e8db6e2SBrian Feldman }
13711e8db6e2SBrian Feldman
1372ae1f160dSDag-Erling Smørgrav static void
process_extended_posix_rename(u_int32_t id)1373d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id)
1374d4af9e69SDag-Erling Smørgrav {
1375d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath;
1376bc5531deSDag-Erling Smørgrav int r, status;
1377d4af9e69SDag-Erling Smørgrav
1378bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
1379bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
138019261079SEd Maste fatal_fr(r, "parse");
1381bc5531deSDag-Erling Smørgrav
1382d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id);
1383d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
1384bc5531deSDag-Erling Smørgrav r = rename(oldpath, newpath);
1385bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1386b15c8340SDag-Erling Smørgrav send_status(id, status);
1387e4a9863fSDag-Erling Smørgrav free(oldpath);
1388e4a9863fSDag-Erling Smørgrav free(newpath);
1389d4af9e69SDag-Erling Smørgrav }
1390d4af9e69SDag-Erling Smørgrav
1391d4af9e69SDag-Erling Smørgrav static void
process_extended_statvfs(u_int32_t id)1392d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id)
1393d4af9e69SDag-Erling Smørgrav {
1394d4af9e69SDag-Erling Smørgrav char *path;
1395d4af9e69SDag-Erling Smørgrav struct statvfs st;
1396bc5531deSDag-Erling Smørgrav int r;
1397d4af9e69SDag-Erling Smørgrav
1398bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
139919261079SEd Maste fatal_fr(r, "parse");
1400f7167e0eSDag-Erling Smørgrav debug3("request %u: statvfs", id);
1401f7167e0eSDag-Erling Smørgrav logit("statvfs \"%s\"", path);
1402d4af9e69SDag-Erling Smørgrav
1403d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0)
1404d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno));
1405d4af9e69SDag-Erling Smørgrav else
1406d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st);
1407e4a9863fSDag-Erling Smørgrav free(path);
1408d4af9e69SDag-Erling Smørgrav }
1409d4af9e69SDag-Erling Smørgrav
1410d4af9e69SDag-Erling Smørgrav static void
process_extended_fstatvfs(u_int32_t id)1411d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id)
1412d4af9e69SDag-Erling Smørgrav {
1413bc5531deSDag-Erling Smørgrav int r, handle, fd;
1414d4af9e69SDag-Erling Smørgrav struct statvfs st;
1415d4af9e69SDag-Erling Smørgrav
1416bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0)
141719261079SEd Maste fatal_fr(r, "parse");
1418d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)",
1419d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle);
1420d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) {
1421d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE);
1422d4af9e69SDag-Erling Smørgrav return;
1423d4af9e69SDag-Erling Smørgrav }
1424d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0)
1425d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno));
1426d4af9e69SDag-Erling Smørgrav else
1427d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st);
1428d4af9e69SDag-Erling Smørgrav }
1429d4af9e69SDag-Erling Smørgrav
1430d4af9e69SDag-Erling Smørgrav static void
process_extended_hardlink(u_int32_t id)14314a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id)
14324a421b63SDag-Erling Smørgrav {
14334a421b63SDag-Erling Smørgrav char *oldpath, *newpath;
1434bc5531deSDag-Erling Smørgrav int r, status;
14354a421b63SDag-Erling Smørgrav
1436bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
1437bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
143819261079SEd Maste fatal_fr(r, "parse");
1439bc5531deSDag-Erling Smørgrav
14404a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id);
14414a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath);
1442bc5531deSDag-Erling Smørgrav r = link(oldpath, newpath);
1443bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
14444a421b63SDag-Erling Smørgrav send_status(id, status);
1445e4a9863fSDag-Erling Smørgrav free(oldpath);
1446e4a9863fSDag-Erling Smørgrav free(newpath);
14474a421b63SDag-Erling Smørgrav }
14484a421b63SDag-Erling Smørgrav
14494a421b63SDag-Erling Smørgrav static void
process_extended_fsync(u_int32_t id)1450f7167e0eSDag-Erling Smørgrav process_extended_fsync(u_int32_t id)
14511e8db6e2SBrian Feldman {
1452bc5531deSDag-Erling Smørgrav int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED;
14531e8db6e2SBrian Feldman
1454bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0)
145519261079SEd Maste fatal_fr(r, "parse");
1456f7167e0eSDag-Erling Smørgrav debug3("request %u: fsync (handle %u)", id, handle);
1457f7167e0eSDag-Erling Smørgrav verbose("fsync \"%s\"", handle_to_name(handle));
1458f7167e0eSDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0)
1459f7167e0eSDag-Erling Smørgrav status = SSH2_FX_NO_SUCH_FILE;
1460f7167e0eSDag-Erling Smørgrav else if (handle_is_ok(handle, HANDLE_FILE)) {
1461bc5531deSDag-Erling Smørgrav r = fsync(fd);
1462bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1463f7167e0eSDag-Erling Smørgrav }
1464f7167e0eSDag-Erling Smørgrav send_status(id, status);
1465f7167e0eSDag-Erling Smørgrav }
1466f7167e0eSDag-Erling Smørgrav
1467f7167e0eSDag-Erling Smørgrav static void
process_extended_lsetstat(u_int32_t id)146819261079SEd Maste process_extended_lsetstat(u_int32_t id)
146919261079SEd Maste {
147019261079SEd Maste Attrib a;
147119261079SEd Maste char *name;
147219261079SEd Maste int r, status = SSH2_FX_OK;
147319261079SEd Maste
147419261079SEd Maste if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
147519261079SEd Maste (r = decode_attrib(iqueue, &a)) != 0)
147619261079SEd Maste fatal_fr(r, "parse");
147719261079SEd Maste
147819261079SEd Maste debug("request %u: lsetstat name \"%s\"", id, name);
147919261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
148019261079SEd Maste /* nonsensical for links */
148119261079SEd Maste status = SSH2_FX_BAD_MESSAGE;
148219261079SEd Maste goto out;
148319261079SEd Maste }
148419261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
148519261079SEd Maste logit("set \"%s\" mode %04o", name, a.perm);
148619261079SEd Maste r = fchmodat(AT_FDCWD, name,
148719261079SEd Maste a.perm & 07777, AT_SYMLINK_NOFOLLOW);
148819261079SEd Maste if (r == -1)
148919261079SEd Maste status = errno_to_portable(errno);
149019261079SEd Maste }
149119261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
149219261079SEd Maste char buf[64];
149319261079SEd Maste time_t t = a.mtime;
149419261079SEd Maste
149519261079SEd Maste strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
149619261079SEd Maste localtime(&t));
149719261079SEd Maste logit("set \"%s\" modtime %s", name, buf);
149819261079SEd Maste r = utimensat(AT_FDCWD, name,
149919261079SEd Maste attrib_to_ts(&a), AT_SYMLINK_NOFOLLOW);
150019261079SEd Maste if (r == -1)
150119261079SEd Maste status = errno_to_portable(errno);
150219261079SEd Maste }
150319261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
150419261079SEd Maste logit("set \"%s\" owner %lu group %lu", name,
150519261079SEd Maste (u_long)a.uid, (u_long)a.gid);
150619261079SEd Maste r = fchownat(AT_FDCWD, name, a.uid, a.gid,
150719261079SEd Maste AT_SYMLINK_NOFOLLOW);
150819261079SEd Maste if (r == -1)
150919261079SEd Maste status = errno_to_portable(errno);
151019261079SEd Maste }
151119261079SEd Maste out:
151219261079SEd Maste send_status(id, status);
151319261079SEd Maste free(name);
151419261079SEd Maste }
151519261079SEd Maste
151619261079SEd Maste static void
process_extended_limits(u_int32_t id)151719261079SEd Maste process_extended_limits(u_int32_t id)
151819261079SEd Maste {
151919261079SEd Maste struct sshbuf *msg;
152019261079SEd Maste int r;
152119261079SEd Maste uint64_t nfiles = 0;
152219261079SEd Maste #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
152319261079SEd Maste struct rlimit rlim;
152419261079SEd Maste #endif
152519261079SEd Maste
152619261079SEd Maste debug("request %u: limits", id);
152719261079SEd Maste
152819261079SEd Maste #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
152919261079SEd Maste if (getrlimit(RLIMIT_NOFILE, &rlim) != -1 && rlim.rlim_cur > 5)
153019261079SEd Maste nfiles = rlim.rlim_cur - 5; /* stdio(3) + syslog + spare */
153119261079SEd Maste #endif
153219261079SEd Maste
153319261079SEd Maste if ((msg = sshbuf_new()) == NULL)
153419261079SEd Maste fatal_f("sshbuf_new failed");
153519261079SEd Maste if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 ||
153619261079SEd Maste (r = sshbuf_put_u32(msg, id)) != 0 ||
153719261079SEd Maste /* max-packet-length */
153819261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH)) != 0 ||
153919261079SEd Maste /* max-read-length */
154019261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_READ_LENGTH)) != 0 ||
154119261079SEd Maste /* max-write-length */
154219261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH - 1024)) != 0 ||
154319261079SEd Maste /* max-open-handles */
154419261079SEd Maste (r = sshbuf_put_u64(msg, nfiles)) != 0)
154519261079SEd Maste fatal_fr(r, "compose");
154619261079SEd Maste send_msg(msg);
154719261079SEd Maste sshbuf_free(msg);
154819261079SEd Maste }
154919261079SEd Maste
155019261079SEd Maste static void
process_extended_expand(u_int32_t id)155119261079SEd Maste process_extended_expand(u_int32_t id)
155219261079SEd Maste {
155319261079SEd Maste char cwd[PATH_MAX], resolvedname[PATH_MAX];
155419261079SEd Maste char *path, *npath;
155519261079SEd Maste int r;
155619261079SEd Maste Stat s;
155719261079SEd Maste
155819261079SEd Maste if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
155919261079SEd Maste fatal_fr(r, "parse");
156019261079SEd Maste if (getcwd(cwd, sizeof(cwd)) == NULL) {
156119261079SEd Maste send_status(id, errno_to_portable(errno));
156219261079SEd Maste goto out;
156319261079SEd Maste }
156419261079SEd Maste
156519261079SEd Maste debug3("request %u: expand, original \"%s\"", id, path);
156619261079SEd Maste if (path[0] == '\0') {
156719261079SEd Maste /* empty path */
156819261079SEd Maste free(path);
156919261079SEd Maste path = xstrdup(".");
157019261079SEd Maste } else if (*path == '~') {
157119261079SEd Maste /* ~ expand path */
157219261079SEd Maste /* Special-case for "~" and "~/" to respect homedir flag */
157319261079SEd Maste if (strcmp(path, "~") == 0) {
157419261079SEd Maste free(path);
157519261079SEd Maste path = xstrdup(cwd);
157619261079SEd Maste } else if (strncmp(path, "~/", 2) == 0) {
157719261079SEd Maste npath = xstrdup(path + 2);
157819261079SEd Maste free(path);
157919261079SEd Maste xasprintf(&path, "%s/%s", cwd, npath);
15801323ec57SEd Maste free(npath);
158119261079SEd Maste } else {
158219261079SEd Maste /* ~user expansions */
158319261079SEd Maste if (tilde_expand(path, pw->pw_uid, &npath) != 0) {
15841323ec57SEd Maste send_status_errmsg(id,
15851323ec57SEd Maste errno_to_portable(ENOENT), "no such user");
158619261079SEd Maste goto out;
158719261079SEd Maste }
158819261079SEd Maste free(path);
158919261079SEd Maste path = npath;
159019261079SEd Maste }
159119261079SEd Maste } else if (*path != '/') {
159219261079SEd Maste /* relative path */
159319261079SEd Maste xasprintf(&npath, "%s/%s", cwd, path);
159419261079SEd Maste free(path);
159519261079SEd Maste path = npath;
159619261079SEd Maste }
159719261079SEd Maste verbose("expand \"%s\"", path);
159819261079SEd Maste if (sftp_realpath(path, resolvedname) == NULL) {
159919261079SEd Maste send_status(id, errno_to_portable(errno));
160019261079SEd Maste goto out;
160119261079SEd Maste }
160219261079SEd Maste attrib_clear(&s.attrib);
160319261079SEd Maste s.name = s.long_name = resolvedname;
160419261079SEd Maste send_names(id, 1, &s);
160519261079SEd Maste out:
160619261079SEd Maste free(path);
160719261079SEd Maste }
160819261079SEd Maste
160919261079SEd Maste static void
process_extended_copy_data(u_int32_t id)161087c1498dSEd Maste process_extended_copy_data(u_int32_t id)
161187c1498dSEd Maste {
161287c1498dSEd Maste u_char buf[64*1024];
161387c1498dSEd Maste int read_handle, read_fd, write_handle, write_fd;
161487c1498dSEd Maste u_int64_t len, read_off, read_len, write_off;
161587c1498dSEd Maste int r, copy_until_eof, status = SSH2_FX_OP_UNSUPPORTED;
161687c1498dSEd Maste size_t ret;
161787c1498dSEd Maste
161887c1498dSEd Maste if ((r = get_handle(iqueue, &read_handle)) != 0 ||
161987c1498dSEd Maste (r = sshbuf_get_u64(iqueue, &read_off)) != 0 ||
162087c1498dSEd Maste (r = sshbuf_get_u64(iqueue, &read_len)) != 0 ||
162187c1498dSEd Maste (r = get_handle(iqueue, &write_handle)) != 0 ||
162287c1498dSEd Maste (r = sshbuf_get_u64(iqueue, &write_off)) != 0)
162387c1498dSEd Maste fatal("%s: buffer error: %s", __func__, ssh_err(r));
162487c1498dSEd Maste
162587c1498dSEd Maste debug("request %u: copy-data from \"%s\" (handle %d) off %llu len %llu "
162687c1498dSEd Maste "to \"%s\" (handle %d) off %llu",
162787c1498dSEd Maste id, handle_to_name(read_handle), read_handle,
162887c1498dSEd Maste (unsigned long long)read_off, (unsigned long long)read_len,
162987c1498dSEd Maste handle_to_name(write_handle), write_handle,
163087c1498dSEd Maste (unsigned long long)write_off);
163187c1498dSEd Maste
163287c1498dSEd Maste /* For read length of 0, we read until EOF. */
163387c1498dSEd Maste if (read_len == 0) {
163487c1498dSEd Maste read_len = (u_int64_t)-1 - read_off;
163587c1498dSEd Maste copy_until_eof = 1;
163687c1498dSEd Maste } else
163787c1498dSEd Maste copy_until_eof = 0;
163887c1498dSEd Maste
163987c1498dSEd Maste read_fd = handle_to_fd(read_handle);
164087c1498dSEd Maste write_fd = handle_to_fd(write_handle);
164187c1498dSEd Maste
164287c1498dSEd Maste /* Disallow reading & writing to the same handle or same path or dirs */
164387c1498dSEd Maste if (read_handle == write_handle || read_fd < 0 || write_fd < 0 ||
164487c1498dSEd Maste !strcmp(handle_to_name(read_handle), handle_to_name(write_handle))) {
164587c1498dSEd Maste status = SSH2_FX_FAILURE;
164687c1498dSEd Maste goto out;
164787c1498dSEd Maste }
164887c1498dSEd Maste
164987c1498dSEd Maste if (lseek(read_fd, read_off, SEEK_SET) < 0) {
165087c1498dSEd Maste status = errno_to_portable(errno);
165187c1498dSEd Maste error("%s: read_seek failed", __func__);
165287c1498dSEd Maste goto out;
165387c1498dSEd Maste }
165487c1498dSEd Maste
165587c1498dSEd Maste if ((handle_to_flags(write_handle) & O_APPEND) == 0 &&
165687c1498dSEd Maste lseek(write_fd, write_off, SEEK_SET) < 0) {
165787c1498dSEd Maste status = errno_to_portable(errno);
165887c1498dSEd Maste error("%s: write_seek failed", __func__);
165987c1498dSEd Maste goto out;
166087c1498dSEd Maste }
166187c1498dSEd Maste
166287c1498dSEd Maste /* Process the request in chunks. */
166387c1498dSEd Maste while (read_len > 0 || copy_until_eof) {
166487c1498dSEd Maste len = MINIMUM(sizeof(buf), read_len);
166587c1498dSEd Maste read_len -= len;
166687c1498dSEd Maste
166787c1498dSEd Maste ret = atomicio(read, read_fd, buf, len);
166887c1498dSEd Maste if (ret == 0 && errno == EPIPE) {
166987c1498dSEd Maste status = copy_until_eof ? SSH2_FX_OK : SSH2_FX_EOF;
167087c1498dSEd Maste break;
167187c1498dSEd Maste } else if (ret == 0) {
167287c1498dSEd Maste status = errno_to_portable(errno);
167387c1498dSEd Maste error("%s: read failed: %s", __func__, strerror(errno));
167487c1498dSEd Maste break;
167587c1498dSEd Maste }
167687c1498dSEd Maste len = ret;
167787c1498dSEd Maste handle_update_read(read_handle, len);
167887c1498dSEd Maste
167987c1498dSEd Maste ret = atomicio(vwrite, write_fd, buf, len);
168087c1498dSEd Maste if (ret != len) {
168187c1498dSEd Maste status = errno_to_portable(errno);
168287c1498dSEd Maste error("%s: write failed: %llu != %llu: %s", __func__,
168387c1498dSEd Maste (unsigned long long)ret, (unsigned long long)len,
168487c1498dSEd Maste strerror(errno));
168587c1498dSEd Maste break;
168687c1498dSEd Maste }
168787c1498dSEd Maste handle_update_write(write_handle, len);
168887c1498dSEd Maste }
168987c1498dSEd Maste
169087c1498dSEd Maste if (read_len == 0)
169187c1498dSEd Maste status = SSH2_FX_OK;
169287c1498dSEd Maste
169387c1498dSEd Maste out:
169487c1498dSEd Maste send_status(id, status);
169587c1498dSEd Maste }
169687c1498dSEd Maste
169787c1498dSEd Maste static void
process_extended_home_directory(u_int32_t id)169838a52bd3SEd Maste process_extended_home_directory(u_int32_t id)
169938a52bd3SEd Maste {
170038a52bd3SEd Maste char *username;
170138a52bd3SEd Maste struct passwd *user_pw;
170238a52bd3SEd Maste int r;
170338a52bd3SEd Maste Stat s;
170438a52bd3SEd Maste
170538a52bd3SEd Maste if ((r = sshbuf_get_cstring(iqueue, &username, NULL)) != 0)
170638a52bd3SEd Maste fatal_fr(r, "parse");
170738a52bd3SEd Maste
170838a52bd3SEd Maste debug3("request %u: home-directory \"%s\"", id, username);
1709*0fdf8faeSEd Maste if (username[0] == '\0') {
1710*0fdf8faeSEd Maste user_pw = pw;
1711*0fdf8faeSEd Maste } else if ((user_pw = getpwnam(username)) == NULL) {
171238a52bd3SEd Maste send_status(id, SSH2_FX_FAILURE);
171338a52bd3SEd Maste goto out;
171438a52bd3SEd Maste }
171538a52bd3SEd Maste
1716*0fdf8faeSEd Maste verbose("home-directory \"%s\"", user_pw->pw_dir);
171738a52bd3SEd Maste attrib_clear(&s.attrib);
1718*0fdf8faeSEd Maste s.name = s.long_name = user_pw->pw_dir;
171938a52bd3SEd Maste send_names(id, 1, &s);
172038a52bd3SEd Maste out:
172138a52bd3SEd Maste free(username);
172238a52bd3SEd Maste }
172338a52bd3SEd Maste
172438a52bd3SEd Maste static void
process_extended_get_users_groups_by_id(u_int32_t id)172538a52bd3SEd Maste process_extended_get_users_groups_by_id(u_int32_t id)
172638a52bd3SEd Maste {
172738a52bd3SEd Maste struct passwd *user_pw;
172838a52bd3SEd Maste struct group *gr;
172938a52bd3SEd Maste struct sshbuf *uids, *gids, *usernames, *groupnames, *msg;
173038a52bd3SEd Maste int r;
173138a52bd3SEd Maste u_int n, nusers = 0, ngroups = 0;
173238a52bd3SEd Maste const char *name;
173338a52bd3SEd Maste
173438a52bd3SEd Maste if ((usernames = sshbuf_new()) == NULL ||
173538a52bd3SEd Maste (groupnames = sshbuf_new()) == NULL ||
173638a52bd3SEd Maste (msg = sshbuf_new()) == NULL)
173738a52bd3SEd Maste fatal_f("sshbuf_new failed");
173838a52bd3SEd Maste if ((r = sshbuf_froms(iqueue, &uids)) != 0 ||
173938a52bd3SEd Maste (r = sshbuf_froms(iqueue, &gids)) != 0)
174038a52bd3SEd Maste fatal_fr(r, "parse");
174138a52bd3SEd Maste debug_f("uids len = %zu, gids len = %zu",
174238a52bd3SEd Maste sshbuf_len(uids), sshbuf_len(gids));
174338a52bd3SEd Maste while (sshbuf_len(uids) != 0) {
174438a52bd3SEd Maste if ((r = sshbuf_get_u32(uids, &n)) != 0)
174538a52bd3SEd Maste fatal_fr(r, "parse inner uid");
174638a52bd3SEd Maste user_pw = getpwuid((uid_t)n);
174738a52bd3SEd Maste name = user_pw == NULL ? "" : user_pw->pw_name;
174838a52bd3SEd Maste debug3_f("uid %u => \"%s\"", n, name);
174938a52bd3SEd Maste if ((r = sshbuf_put_cstring(usernames, name)) != 0)
1750f374ba41SEd Maste fatal_fr(r, "assemble uid reply");
175138a52bd3SEd Maste nusers++;
175238a52bd3SEd Maste }
175338a52bd3SEd Maste while (sshbuf_len(gids) != 0) {
175438a52bd3SEd Maste if ((r = sshbuf_get_u32(gids, &n)) != 0)
175538a52bd3SEd Maste fatal_fr(r, "parse inner gid");
175638a52bd3SEd Maste gr = getgrgid((gid_t)n);
175738a52bd3SEd Maste name = gr == NULL ? "" : gr->gr_name;
175838a52bd3SEd Maste debug3_f("gid %u => \"%s\"", n, name);
175938a52bd3SEd Maste if ((r = sshbuf_put_cstring(groupnames, name)) != 0)
176038a52bd3SEd Maste fatal_fr(r, "assemble gid reply");
176138a52bd3SEd Maste nusers++;
176238a52bd3SEd Maste }
176338a52bd3SEd Maste verbose("users-groups-by-id: %u users, %u groups", nusers, ngroups);
176438a52bd3SEd Maste
176538a52bd3SEd Maste if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 ||
176638a52bd3SEd Maste (r = sshbuf_put_u32(msg, id)) != 0 ||
176738a52bd3SEd Maste (r = sshbuf_put_stringb(msg, usernames)) != 0 ||
176838a52bd3SEd Maste (r = sshbuf_put_stringb(msg, groupnames)) != 0)
176938a52bd3SEd Maste fatal_fr(r, "compose");
177038a52bd3SEd Maste send_msg(msg);
177138a52bd3SEd Maste
177238a52bd3SEd Maste sshbuf_free(uids);
177338a52bd3SEd Maste sshbuf_free(gids);
177438a52bd3SEd Maste sshbuf_free(usernames);
177538a52bd3SEd Maste sshbuf_free(groupnames);
177638a52bd3SEd Maste sshbuf_free(msg);
177738a52bd3SEd Maste }
177838a52bd3SEd Maste
177938a52bd3SEd Maste static void
process_extended(u_int32_t id)1780f7167e0eSDag-Erling Smørgrav process_extended(u_int32_t id)
1781f7167e0eSDag-Erling Smørgrav {
1782f7167e0eSDag-Erling Smørgrav char *request;
178319261079SEd Maste int r;
178419261079SEd Maste const struct sftp_handler *exthand;
1785f7167e0eSDag-Erling Smørgrav
1786bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0)
178719261079SEd Maste fatal_fr(r, "parse");
178819261079SEd Maste if ((exthand = extended_handler_byname(request)) == NULL) {
1789f7167e0eSDag-Erling Smørgrav error("Unknown extended request \"%.100s\"", request);
17901e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
179119261079SEd Maste } else {
179219261079SEd Maste if (!request_permitted(exthand))
179319261079SEd Maste send_status(id, SSH2_FX_PERMISSION_DENIED);
179419261079SEd Maste else
179519261079SEd Maste exthand->handler(id);
1796f7167e0eSDag-Erling Smørgrav }
1797e4a9863fSDag-Erling Smørgrav free(request);
17981e8db6e2SBrian Feldman }
1799b66f2d16SKris Kennaway
1800b66f2d16SKris Kennaway /* stolen from ssh-agent */
1801b66f2d16SKris Kennaway
1802ae1f160dSDag-Erling Smørgrav static void
process(void)1803b66f2d16SKris Kennaway process(void)
1804b66f2d16SKris Kennaway {
1805bc5531deSDag-Erling Smørgrav u_int msg_len;
1806bc5531deSDag-Erling Smørgrav u_int buf_len;
1807bc5531deSDag-Erling Smørgrav u_int consumed;
1808bc5531deSDag-Erling Smørgrav u_char type;
1809bc5531deSDag-Erling Smørgrav const u_char *cp;
1810bc5531deSDag-Erling Smørgrav int i, r;
1811f7167e0eSDag-Erling Smørgrav u_int32_t id;
1812b66f2d16SKris Kennaway
1813bc5531deSDag-Erling Smørgrav buf_len = sshbuf_len(iqueue);
1814545d5ecaSDag-Erling Smørgrav if (buf_len < 5)
1815b66f2d16SKris Kennaway return; /* Incomplete message. */
1816bc5531deSDag-Erling Smørgrav cp = sshbuf_ptr(iqueue);
1817761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp);
1818021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) {
1819761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s",
1820761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name);
1821d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11);
1822b66f2d16SKris Kennaway }
1823545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4)
1824b66f2d16SKris Kennaway return;
1825bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(iqueue, 4)) != 0)
182619261079SEd Maste fatal_fr(r, "consume");
1827545d5ecaSDag-Erling Smørgrav buf_len -= 4;
1828bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(iqueue, &type)) != 0)
182919261079SEd Maste fatal_fr(r, "parse type");
1830f7167e0eSDag-Erling Smørgrav
1831b66f2d16SKris Kennaway switch (type) {
18321e8db6e2SBrian Feldman case SSH2_FXP_INIT:
1833b66f2d16SKris Kennaway process_init();
1834f7167e0eSDag-Erling Smørgrav init_done = 1;
18351e8db6e2SBrian Feldman break;
18361e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED:
1837f7167e0eSDag-Erling Smørgrav if (!init_done)
1838f7167e0eSDag-Erling Smørgrav fatal("Received extended request before init");
1839bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0)
184019261079SEd Maste fatal_fr(r, "parse extended ID");
1841f7167e0eSDag-Erling Smørgrav process_extended(id);
18421e8db6e2SBrian Feldman break;
1843b66f2d16SKris Kennaway default:
1844f7167e0eSDag-Erling Smørgrav if (!init_done)
1845f7167e0eSDag-Erling Smørgrav fatal("Received %u request before init", type);
1846bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0)
184719261079SEd Maste fatal_fr(r, "parse ID");
1848f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) {
1849f7167e0eSDag-Erling Smørgrav if (type == handlers[i].type) {
1850f7167e0eSDag-Erling Smørgrav if (!request_permitted(&handlers[i])) {
1851f7167e0eSDag-Erling Smørgrav send_status(id,
1852f7167e0eSDag-Erling Smørgrav SSH2_FX_PERMISSION_DENIED);
1853f7167e0eSDag-Erling Smørgrav } else {
1854f7167e0eSDag-Erling Smørgrav handlers[i].handler(id);
1855f7167e0eSDag-Erling Smørgrav }
1856b66f2d16SKris Kennaway break;
1857b66f2d16SKris Kennaway }
1858f7167e0eSDag-Erling Smørgrav }
1859f7167e0eSDag-Erling Smørgrav if (handlers[i].handler == NULL)
1860f7167e0eSDag-Erling Smørgrav error("Unknown message %u", type);
1861f7167e0eSDag-Erling Smørgrav }
1862545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */
1863bc5531deSDag-Erling Smørgrav if (buf_len < sshbuf_len(iqueue)) {
1864d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly");
1865d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255);
1866d4af9e69SDag-Erling Smørgrav }
1867bc5531deSDag-Erling Smørgrav consumed = buf_len - sshbuf_len(iqueue);
1868d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) {
1869f7167e0eSDag-Erling Smørgrav error("msg_len %u < consumed %u", msg_len, consumed);
1870d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255);
1871d4af9e69SDag-Erling Smørgrav }
1872bc5531deSDag-Erling Smørgrav if (msg_len > consumed &&
1873bc5531deSDag-Erling Smørgrav (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0)
187419261079SEd Maste fatal_fr(r, "consume");
1875b66f2d16SKris Kennaway }
1876b66f2d16SKris Kennaway
1877761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */
1878761efaa7SDag-Erling Smørgrav void
sftp_server_cleanup_exit(int i)1879d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i)
1880761efaa7SDag-Erling Smørgrav {
1881761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) {
1882761efaa7SDag-Erling Smørgrav handle_log_exit();
1883761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]",
1884761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr);
1885761efaa7SDag-Erling Smørgrav }
1886761efaa7SDag-Erling Smørgrav _exit(i);
1887761efaa7SDag-Erling Smørgrav }
1888761efaa7SDag-Erling Smørgrav
1889761efaa7SDag-Erling Smørgrav static void
sftp_server_usage(void)1890d4af9e69SDag-Erling Smørgrav sftp_server_usage(void)
1891761efaa7SDag-Erling Smørgrav {
1892761efaa7SDag-Erling Smørgrav extern char *__progname;
1893761efaa7SDag-Erling Smørgrav
1894761efaa7SDag-Erling Smørgrav fprintf(stderr,
18956888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] "
189619261079SEd Maste "[-l log_level]\n\t[-P denied_requests] "
189719261079SEd Maste "[-p allowed_requests] [-u umask]\n"
1898f7167e0eSDag-Erling Smørgrav " %s -Q protocol_feature\n",
1899f7167e0eSDag-Erling Smørgrav __progname, __progname);
1900761efaa7SDag-Erling Smørgrav exit(1);
1901761efaa7SDag-Erling Smørgrav }
1902761efaa7SDag-Erling Smørgrav
1903b66f2d16SKris Kennaway int
sftp_server_main(int argc,char ** argv,struct passwd * user_pw)1904d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw)
1905b66f2d16SKris Kennaway {
19061323ec57SEd Maste int i, r, in, out, ch, skipargs = 0, log_stderr = 0;
19071323ec57SEd Maste ssize_t len, olen;
1908761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
1909190cef3dSDag-Erling Smørgrav char *cp, *homedir = NULL, uidstr[32], buf[4*4096];
19104a421b63SDag-Erling Smørgrav long mask;
1911761efaa7SDag-Erling Smørgrav
1912761efaa7SDag-Erling Smørgrav extern char *optarg;
1913761efaa7SDag-Erling Smørgrav extern char *__progname;
19141e8db6e2SBrian Feldman
1915761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]);
1916761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr);
1917b66f2d16SKris Kennaway
19186888a9beSDag-Erling Smørgrav pw = pwcopy(user_pw);
19196888a9beSDag-Erling Smørgrav
1920f7167e0eSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv,
1921f7167e0eSDag-Erling Smørgrav "d:f:l:P:p:Q:u:cehR")) != -1) {
1922761efaa7SDag-Erling Smørgrav switch (ch) {
1923f7167e0eSDag-Erling Smørgrav case 'Q':
1924f7167e0eSDag-Erling Smørgrav if (strcasecmp(optarg, "requests") != 0) {
1925f7167e0eSDag-Erling Smørgrav fprintf(stderr, "Invalid query type\n");
1926f7167e0eSDag-Erling Smørgrav exit(1);
1927f7167e0eSDag-Erling Smørgrav }
1928f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++)
1929f7167e0eSDag-Erling Smørgrav printf("%s\n", handlers[i].name);
1930f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++)
1931f7167e0eSDag-Erling Smørgrav printf("%s\n", extended_handlers[i].name);
1932f7167e0eSDag-Erling Smørgrav exit(0);
1933f7167e0eSDag-Erling Smørgrav break;
1934b15c8340SDag-Erling Smørgrav case 'R':
1935b15c8340SDag-Erling Smørgrav readonly = 1;
1936b15c8340SDag-Erling Smørgrav break;
1937761efaa7SDag-Erling Smørgrav case 'c':
1938761efaa7SDag-Erling Smørgrav /*
1939761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a
1940761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command"
1941761efaa7SDag-Erling Smørgrav */
1942761efaa7SDag-Erling Smørgrav skipargs = 1;
1943761efaa7SDag-Erling Smørgrav break;
1944761efaa7SDag-Erling Smørgrav case 'e':
1945761efaa7SDag-Erling Smørgrav log_stderr = 1;
1946761efaa7SDag-Erling Smørgrav break;
1947761efaa7SDag-Erling Smørgrav case 'l':
1948761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg);
1949761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET)
1950761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg);
1951761efaa7SDag-Erling Smørgrav break;
1952761efaa7SDag-Erling Smørgrav case 'f':
1953761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg);
1954d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET)
1955761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg);
1956761efaa7SDag-Erling Smørgrav break;
19576888a9beSDag-Erling Smørgrav case 'd':
19586888a9beSDag-Erling Smørgrav cp = tilde_expand_filename(optarg, user_pw->pw_uid);
1959190cef3dSDag-Erling Smørgrav snprintf(uidstr, sizeof(uidstr), "%llu",
1960190cef3dSDag-Erling Smørgrav (unsigned long long)pw->pw_uid);
19616888a9beSDag-Erling Smørgrav homedir = percent_expand(cp, "d", user_pw->pw_dir,
1962190cef3dSDag-Erling Smørgrav "u", user_pw->pw_name, "U", uidstr, (char *)NULL);
19636888a9beSDag-Erling Smørgrav free(cp);
19646888a9beSDag-Erling Smørgrav break;
1965f7167e0eSDag-Erling Smørgrav case 'p':
196619261079SEd Maste if (request_allowlist != NULL)
1967f7167e0eSDag-Erling Smørgrav fatal("Permitted requests already set");
196819261079SEd Maste request_allowlist = xstrdup(optarg);
1969f7167e0eSDag-Erling Smørgrav break;
1970f7167e0eSDag-Erling Smørgrav case 'P':
197119261079SEd Maste if (request_denylist != NULL)
1972f7167e0eSDag-Erling Smørgrav fatal("Refused requests already set");
197319261079SEd Maste request_denylist = xstrdup(optarg);
1974f7167e0eSDag-Erling Smørgrav break;
1975b15c8340SDag-Erling Smørgrav case 'u':
19764a421b63SDag-Erling Smørgrav errno = 0;
19774a421b63SDag-Erling Smørgrav mask = strtol(optarg, &cp, 8);
19784a421b63SDag-Erling Smørgrav if (mask < 0 || mask > 0777 || *cp != '\0' ||
19794a421b63SDag-Erling Smørgrav cp == optarg || (mask == 0 && errno != 0))
19804a421b63SDag-Erling Smørgrav fatal("Invalid umask \"%s\"", optarg);
19814a421b63SDag-Erling Smørgrav (void)umask((mode_t)mask);
1982b15c8340SDag-Erling Smørgrav break;
1983761efaa7SDag-Erling Smørgrav case 'h':
1984761efaa7SDag-Erling Smørgrav default:
1985d4af9e69SDag-Erling Smørgrav sftp_server_usage();
1986761efaa7SDag-Erling Smørgrav }
1987761efaa7SDag-Erling Smørgrav }
1988761efaa7SDag-Erling Smørgrav
1989761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr);
1990761efaa7SDag-Erling Smørgrav
1991a0ee8cc6SDag-Erling Smørgrav /*
1992076ad2f8SDag-Erling Smørgrav * On platforms where we can, avoid making /proc/self/{mem,maps}
1993a0ee8cc6SDag-Erling Smørgrav * available to the user so that sftp access doesn't automatically
1994a0ee8cc6SDag-Erling Smørgrav * imply arbitrary code execution access that will break
1995a0ee8cc6SDag-Erling Smørgrav * restricted configurations.
1996a0ee8cc6SDag-Erling Smørgrav */
1997076ad2f8SDag-Erling Smørgrav platform_disable_tracing(1); /* strict */
1998a0ee8cc6SDag-Erling Smørgrav
1999acc1a9efSDag-Erling Smørgrav /* Drop any fine-grained privileges we don't need */
2000acc1a9efSDag-Erling Smørgrav platform_pledge_sftp_server();
2001acc1a9efSDag-Erling Smørgrav
2002761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) {
2003761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp);
2004d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) {
2005d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"",
2006761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION"));
2007d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255);
2008d4af9e69SDag-Erling Smørgrav }
2009761efaa7SDag-Erling Smørgrav *cp = '\0';
2010761efaa7SDag-Erling Smørgrav } else
2011761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN");
2012761efaa7SDag-Erling Smørgrav
2013761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]",
2014761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr);
2015761efaa7SDag-Erling Smørgrav
2016b15c8340SDag-Erling Smørgrav in = STDIN_FILENO;
2017b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO;
2018b66f2d16SKris Kennaway
201983d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN
202083d2307dSDag-Erling Smørgrav setmode(in, O_BINARY);
202183d2307dSDag-Erling Smørgrav setmode(out, O_BINARY);
202283d2307dSDag-Erling Smørgrav #endif
202383d2307dSDag-Erling Smørgrav
2024bc5531deSDag-Erling Smørgrav if ((iqueue = sshbuf_new()) == NULL)
202519261079SEd Maste fatal_f("sshbuf_new failed");
2026bc5531deSDag-Erling Smørgrav if ((oqueue = sshbuf_new()) == NULL)
202719261079SEd Maste fatal_f("sshbuf_new failed");
2028b66f2d16SKris Kennaway
20296888a9beSDag-Erling Smørgrav if (homedir != NULL) {
20306888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) {
20316888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir,
20326888a9beSDag-Erling Smørgrav strerror(errno));
20336888a9beSDag-Erling Smørgrav }
20346888a9beSDag-Erling Smørgrav }
20356888a9beSDag-Erling Smørgrav
20361e8db6e2SBrian Feldman for (;;) {
20371323ec57SEd Maste struct pollfd pfd[2];
20381323ec57SEd Maste
20391323ec57SEd Maste memset(pfd, 0, sizeof pfd);
20401323ec57SEd Maste pfd[0].fd = pfd[1].fd = -1;
20411e8db6e2SBrian Feldman
2042d4af9e69SDag-Erling Smørgrav /*
2043d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle
2044d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate,
2045d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads.
2046d4af9e69SDag-Erling Smørgrav */
2047bc5531deSDag-Erling Smørgrav if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 &&
2048bc5531deSDag-Erling Smørgrav (r = sshbuf_check_reserve(oqueue,
20491323ec57SEd Maste SFTP_MAX_MSG_LENGTH)) == 0) {
20501323ec57SEd Maste pfd[0].fd = in;
20511323ec57SEd Maste pfd[0].events = POLLIN;
20521323ec57SEd Maste }
2053bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE)
205419261079SEd Maste fatal_fr(r, "reserve");
2055d4af9e69SDag-Erling Smørgrav
2056bc5531deSDag-Erling Smørgrav olen = sshbuf_len(oqueue);
20571323ec57SEd Maste if (olen > 0) {
20581323ec57SEd Maste pfd[1].fd = out;
20591323ec57SEd Maste pfd[1].events = POLLOUT;
20601323ec57SEd Maste }
2061b66f2d16SKris Kennaway
20621323ec57SEd Maste if (poll(pfd, 2, -1) == -1) {
2063b66f2d16SKris Kennaway if (errno == EINTR)
2064b66f2d16SKris Kennaway continue;
20651323ec57SEd Maste error("poll: %s", strerror(errno));
2066d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2);
2067b66f2d16SKris Kennaway }
2068b66f2d16SKris Kennaway
2069b66f2d16SKris Kennaway /* copy stdin to iqueue */
20701323ec57SEd Maste if (pfd[0].revents & (POLLIN|POLLHUP)) {
2071b66f2d16SKris Kennaway len = read(in, buf, sizeof buf);
2072b66f2d16SKris Kennaway if (len == 0) {
2073b66f2d16SKris Kennaway debug("read eof");
2074d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0);
207519261079SEd Maste } else if (len == -1) {
20761323ec57SEd Maste if (errno != EAGAIN && errno != EINTR) {
2077761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno));
2078d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1);
20791323ec57SEd Maste }
208019261079SEd Maste } else if ((r = sshbuf_put(iqueue, buf, len)) != 0)
208119261079SEd Maste fatal_fr(r, "sshbuf_put");
2082b66f2d16SKris Kennaway }
2083b66f2d16SKris Kennaway /* send oqueue to stdout */
20841323ec57SEd Maste if (pfd[1].revents & (POLLOUT|POLLHUP)) {
2085bc5531deSDag-Erling Smørgrav len = write(out, sshbuf_ptr(oqueue), olen);
20861323ec57SEd Maste if (len == 0 || (len == -1 && errno == EPIPE)) {
20871323ec57SEd Maste debug("write eof");
20881323ec57SEd Maste sftp_server_cleanup_exit(0);
20891323ec57SEd Maste } else if (len == -1) {
20901323ec57SEd Maste sftp_server_cleanup_exit(1);
20911323ec57SEd Maste if (errno != EAGAIN && errno != EINTR) {
2092761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno));
2093d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1);
20941323ec57SEd Maste }
209519261079SEd Maste } else if ((r = sshbuf_consume(oqueue, len)) != 0)
209619261079SEd Maste fatal_fr(r, "consume");
2097b66f2d16SKris Kennaway }
2098d4af9e69SDag-Erling Smørgrav
2099d4af9e69SDag-Erling Smørgrav /*
2100d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results
2101d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input
2102d4af9e69SDag-Erling Smørgrav * and let the output queue drain.
2103d4af9e69SDag-Erling Smørgrav */
2104bc5531deSDag-Erling Smørgrav r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH);
2105bc5531deSDag-Erling Smørgrav if (r == 0)
2106b66f2d16SKris Kennaway process();
2107bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE)
210819261079SEd Maste fatal_fr(r, "reserve");
2109b66f2d16SKris Kennaway }
2110b66f2d16SKris Kennaway }
2111