Aha! At long last I've managed to reproduce the intermittent problem
[u/mdw/putty] / unix / uxsftp.c
CommitLineData
876eefd4 1/*
2 * uxsftp.c: the Unix-specific parts of PSFTP and PSCP.
3 */
4
5#include <sys/time.h>
6#include <sys/types.h>
7#include <sys/stat.h>
18609086 8#include <stdlib.h>
876eefd4 9#include <fcntl.h>
10#include <dirent.h>
11#include <unistd.h>
12#include <utime.h>
13#include <errno.h>
14#include <assert.h>
9c77ddf6 15#include <glob.h>
154c73d5 16#ifndef HAVE_NO_SYS_SELECT_H
17#include <sys/select.h>
18#endif
876eefd4 19
20#include "putty.h"
21#include "psftp.h"
0ac1920c 22#include "int64.h"
876eefd4 23
24/*
25 * In PSFTP our selects are synchronous, so these functions are
26 * empty stubs.
27 */
28int uxsel_input_add(int fd, int rwx) { return 0; }
29void uxsel_input_remove(int id) { }
30
31char *x_get_default(const char *key)
32{
33 return NULL; /* this is a stub */
34}
35
36void platform_get_x11_auth(char *display, int *protocol,
37 unsigned char *data, int *datalen)
38{
39 /* Do nothing, therefore no auth. */
40}
41
42/*
43 * Default settings that are specific to PSFTP.
44 */
45char *platform_default_s(const char *name)
46{
47 return NULL;
48}
49
50int platform_default_i(const char *name, int def)
51{
52 return def;
53}
54
55FontSpec platform_default_fontspec(const char *name)
56{
57 FontSpec ret;
58 *ret.name = '\0';
59 return ret;
60}
61
62Filename platform_default_filename(const char *name)
63{
64 Filename ret;
65 if (!strcmp(name, "LogFileName"))
66 strcpy(ret.path, "putty.log");
67 else
68 *ret.path = '\0';
69 return ret;
70}
71
c6ccd5c2 72char *get_ttymode(void *frontend, const char *mode) { return NULL; }
73
edd0cb8a 74int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
75{
76 int ret;
77 ret = cmdline_get_passwd_input(p, in, inlen);
78 if (ret == -1)
79 ret = console_get_userpass_input(p, in, inlen);
80 return ret;
81}
82
876eefd4 83/*
876eefd4 84 * Set local current directory. Returns NULL on success, or else an
85 * error message which must be freed after printing.
86 */
87char *psftp_lcd(char *dir)
88{
89 if (chdir(dir) < 0)
90 return dupprintf("%s: chdir: %s", dir, strerror(errno));
91 else
92 return NULL;
93}
94
95/*
96 * Get local current directory. Returns a string which must be
97 * freed.
98 */
99char *psftp_getcwd(void)
100{
101 char *buffer, *ret;
102 int size = 256;
103
104 buffer = snewn(size, char);
105 while (1) {
106 ret = getcwd(buffer, size);
107 if (ret != NULL)
108 return ret;
109 if (errno != ERANGE) {
110 sfree(buffer);
111 return dupprintf("[cwd unavailable: %s]", strerror(errno));
112 }
113 /*
114 * Otherwise, ERANGE was returned, meaning the buffer
115 * wasn't big enough.
116 */
117 size = size * 3 / 2;
118 buffer = sresize(buffer, size, char);
119 }
120}
121
122struct RFile {
123 int fd;
124};
125
0ac1920c 126RFile *open_existing_file(char *name, uint64 *size,
876eefd4 127 unsigned long *mtime, unsigned long *atime)
128{
129 int fd;
130 RFile *ret;
131
132 fd = open(name, O_RDONLY);
133 if (fd < 0)
134 return NULL;
135
136 ret = snew(RFile);
137 ret->fd = fd;
138
139 if (size || mtime || atime) {
140 struct stat statbuf;
141 if (fstat(fd, &statbuf) < 0) {
142 fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));
143 memset(&statbuf, 0, sizeof(statbuf));
144 }
145
0ac1920c 146 if (size) {
147 if (sizeof(statbuf.st_size) == 8) {
148 size->hi = statbuf.st_size >> 32;
149 size->lo = (long) statbuf.st_size;
150 } else {
151 *size = uint64_make(0, statbuf.st_size);
152 }
153 }
154
876eefd4 155 if (mtime)
156 *mtime = statbuf.st_mtime;
157
158 if (atime)
159 *atime = statbuf.st_atime;
160 }
161
162 return ret;
163}
164
165int read_from_file(RFile *f, void *buffer, int length)
166{
167 return read(f->fd, buffer, length);
168}
169
170void close_rfile(RFile *f)
171{
172 close(f->fd);
173 sfree(f);
174}
175
176struct WFile {
177 int fd;
178 char *name;
179};
180
181WFile *open_new_file(char *name)
182{
183 int fd;
184 WFile *ret;
185
186 fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666);
187 if (fd < 0)
188 return NULL;
189
190 ret = snew(WFile);
191 ret->fd = fd;
192 ret->name = dupstr(name);
193
194 return ret;
195}
196
0ac1920c 197
198WFile *open_existing_wfile(char *name, uint64 *size)
199{
200 int fd;
201 WFile *ret;
202
203 fd = open(name, O_APPEND | O_WRONLY);
204 if (fd < 0)
205 return NULL;
206
207 ret = snew(WFile);
208 ret->fd = fd;
209
210 if (size) {
211 struct stat statbuf;
212 if (fstat(fd, &statbuf) < 0) {
213 fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));
214 memset(&statbuf, 0, sizeof(statbuf));
215 }
216
217 if (sizeof(statbuf.st_size) == 8) {
218 size->hi = statbuf.st_size >> 32;
219 size->lo = (long) statbuf.st_size;
220 } else {
221 *size = uint64_make(0, statbuf.st_size);
222 }
223 }
224
225 return ret;
226}
227
876eefd4 228int write_to_file(WFile *f, void *buffer, int length)
229{
230 char *p = (char *)buffer;
231 int so_far = 0;
232
233 /* Keep trying until we've really written as much as we can. */
234 while (length > 0) {
235 int ret = write(f->fd, p, length);
236
237 if (ret < 0)
238 return ret;
239
240 if (ret == 0)
241 break;
242
243 p += ret;
244 length -= ret;
245 so_far += ret;
246 }
247
248 return so_far;
249}
250
251void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)
252{
253 struct utimbuf ut;
254
255 ut.actime = atime;
256 ut.modtime = mtime;
257
258 utime(f->name, &ut);
259}
260
261/* Closes and frees the WFile */
262void close_wfile(WFile *f)
263{
264 close(f->fd);
265 sfree(f->name);
266 sfree(f);
267}
268
0ac1920c 269/* Seek offset bytes through file, from whence, where whence is
270 FROM_START, FROM_CURRENT, or FROM_END */
271int seek_file(WFile *f, uint64 offset, int whence)
272{
273 off_t fileofft;
274 int lseek_whence;
275
276 if (sizeof(off_t) == 8) {
277 fileofft = ((off_t) offset.hi << 32) + offset.lo;
278 } else {
279 fileofft = offset.lo;
280 }
281
282 switch (whence) {
283 case FROM_START:
284 lseek_whence = SEEK_SET;
285 break;
286 case FROM_CURRENT:
287 lseek_whence = SEEK_CUR;
288 break;
289 case FROM_END:
290 lseek_whence = SEEK_END;
291 break;
292 default:
293 return -1;
294 }
295
296 return lseek(f->fd, fileofft, lseek_whence) >= 0 ? 0 : -1;
297}
298
299uint64 get_file_posn(WFile *f)
300{
301 off_t fileofft;
302 uint64 ret;
303
304 fileofft = lseek(f->fd, (off_t) 0, SEEK_CUR);
305
306 if (sizeof(off_t) == 8) {
307 ret.hi = fileofft >> 32;
308 ret.lo = (long) fileofft;
309 } else {
310 ret = uint64_make(0, fileofft);
311 }
312
313 return ret;
314}
315
876eefd4 316int file_type(char *name)
317{
318 struct stat statbuf;
319
320 if (stat(name, &statbuf) < 0) {
321 if (errno != ENOENT)
322 fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));
323 return FILE_TYPE_NONEXISTENT;
324 }
325
326 if (S_ISREG(statbuf.st_mode))
327 return FILE_TYPE_FILE;
328
329 if (S_ISDIR(statbuf.st_mode))
330 return FILE_TYPE_DIRECTORY;
331
332 return FILE_TYPE_WEIRD;
333}
334
335struct DirHandle {
336 DIR *dir;
337};
338
339DirHandle *open_directory(char *name)
340{
341 DIR *dir;
342 DirHandle *ret;
343
344 dir = opendir(name);
345 if (!dir)
346 return NULL;
347
348 ret = snew(DirHandle);
349 ret->dir = dir;
350 return ret;
351}
352
353char *read_filename(DirHandle *dir)
354{
355 struct dirent *de;
356
357 do {
358 de = readdir(dir->dir);
359 if (de == NULL)
360 return NULL;
361 } while ((de->d_name[0] == '.' &&
362 (de->d_name[1] == '\0' ||
363 (de->d_name[1] == '.' && de->d_name[2] == '\0'))));
364
365 return dupstr(de->d_name);
366}
367
368void close_directory(DirHandle *dir)
369{
370 closedir(dir->dir);
371 sfree(dir);
372}
373
374int test_wildcard(char *name, int cmdline)
375{
876eefd4 376 struct stat statbuf;
377
9c77ddf6 378 if (stat(name, &statbuf) == 0) {
876eefd4 379 return WCTYPE_FILENAME;
9c77ddf6 380 } else if (cmdline) {
381 /*
382 * On Unix, we never need to parse wildcards coming from
383 * the command line, because the shell will have expanded
384 * them into a filename list already.
385 */
386 return WCTYPE_NONEXISTENT;
387 } else {
388 glob_t globbed;
389 int ret = WCTYPE_NONEXISTENT;
390
391 if (glob(name, GLOB_ERR, NULL, &globbed) == 0) {
392 if (globbed.gl_pathc > 0)
393 ret = WCTYPE_WILDCARD;
394 globfree(&globbed);
395 }
396
397 return ret;
398 }
876eefd4 399}
400
401/*
9c77ddf6 402 * Actually return matching file names for a local wildcard.
876eefd4 403 */
404struct WildcardMatcher {
9c77ddf6 405 glob_t globbed;
406 int i;
876eefd4 407};
9c77ddf6 408WildcardMatcher *begin_wildcard_matching(char *name) {
409 WildcardMatcher *ret = snew(WildcardMatcher);
410
411 if (glob(name, 0, NULL, &ret->globbed) < 0) {
412 sfree(ret);
413 return NULL;
414 }
415
416 ret->i = 0;
417
418 return ret;
419}
420char *wildcard_get_filename(WildcardMatcher *dir) {
421 if (dir->i < dir->globbed.gl_pathc) {
422 return dupstr(dir->globbed.gl_pathv[dir->i++]);
423 } else
424 return NULL;
425}
426void finish_wildcard_matching(WildcardMatcher *dir) {
427 globfree(&dir->globbed);
428 sfree(dir);
429}
876eefd4 430
e9d14678 431int vet_filename(char *name)
432{
433 if (strchr(name, '/'))
434 return FALSE;
435
436 if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2])))
437 return FALSE;
438
439 return TRUE;
440}
441
876eefd4 442int create_directory(char *name)
443{
444 return mkdir(name, 0777) == 0;
445}
446
447char *dir_file_cat(char *dir, char *file)
448{
449 return dupcat(dir, "/", file, NULL);
450}
451
452/*
39934deb 453 * Do a select() between all currently active network fds and
454 * optionally stdin.
876eefd4 455 */
65857773 456static int ssh_sftp_do_select(int include_stdin, int no_fds_ok)
876eefd4 457{
458 fd_set rset, wset, xset;
459 int i, fdcount, fdsize, *fdlist;
460 int fd, fdstate, rwx, ret, maxfd;
39934deb 461 long now = GETTICKCOUNT();
876eefd4 462
463 fdlist = NULL;
464 fdcount = fdsize = 0;
465
39934deb 466 do {
876eefd4 467
39934deb 468 /* Count the currently active fds. */
469 i = 0;
470 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
471 fd = next_fd(&fdstate, &rwx)) i++;
876eefd4 472
65857773 473 if (i < 1 && !no_fds_ok)
39934deb 474 return -1; /* doom */
876eefd4 475
39934deb 476 /* Expand the fdlist buffer if necessary. */
477 if (i > fdsize) {
478 fdsize = i + 16;
479 fdlist = sresize(fdlist, fdsize, int);
480 }
876eefd4 481
39934deb 482 FD_ZERO(&rset);
483 FD_ZERO(&wset);
484 FD_ZERO(&xset);
485 maxfd = 0;
876eefd4 486
39934deb 487 /*
488 * Add all currently open fds to the select sets, and store
489 * them in fdlist as well.
490 */
491 fdcount = 0;
492 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
493 fd = next_fd(&fdstate, &rwx)) {
494 fdlist[fdcount++] = fd;
495 if (rwx & 1)
496 FD_SET_MAX(fd, maxfd, rset);
497 if (rwx & 2)
498 FD_SET_MAX(fd, maxfd, wset);
499 if (rwx & 4)
500 FD_SET_MAX(fd, maxfd, xset);
501 }
502
503 if (include_stdin)
504 FD_SET_MAX(0, maxfd, rset);
505
506 do {
507 long next, ticks;
508 struct timeval tv, *ptv;
509
510 if (run_timers(now, &next)) {
511 ticks = next - GETTICKCOUNT();
512 if (ticks <= 0)
513 ticks = 1; /* just in case */
514 tv.tv_sec = ticks / 1000;
515 tv.tv_usec = ticks % 1000 * 1000;
516 ptv = &tv;
517 } else {
518 ptv = NULL;
519 }
520 ret = select(maxfd, &rset, &wset, &xset, ptv);
521 if (ret == 0)
522 now = next;
2ac3322e 523 else {
524 long newnow = GETTICKCOUNT();
525 /*
526 * Check to see whether the system clock has
527 * changed massively during the select.
528 */
529 if (newnow - now < 0 || newnow - now > next - now) {
530 /*
531 * If so, look at the elapsed time in the
532 * select and use it to compute a new
533 * tickcount_offset.
534 */
535 long othernow = now + tv.tv_sec * 1000 + tv.tv_usec / 1000;
536 /* So we'd like GETTICKCOUNT to have returned othernow,
537 * but instead it return newnow. Hence ... */
538 tickcount_offset += othernow - newnow;
539 now = othernow;
540 } else {
541 now = newnow;
542 }
543 }
39934deb 544 } while (ret < 0 && errno != EINTR);
545 } while (ret == 0);
876eefd4 546
547 if (ret < 0) {
548 perror("select");
549 exit(1);
550 }
551
552 for (i = 0; i < fdcount; i++) {
553 fd = fdlist[i];
554 /*
555 * We must process exceptional notifications before
556 * ordinary readability ones, or we may go straight
557 * past the urgent marker.
558 */
559 if (FD_ISSET(fd, &xset))
560 select_result(fd, 4);
561 if (FD_ISSET(fd, &rset))
562 select_result(fd, 1);
563 if (FD_ISSET(fd, &wset))
564 select_result(fd, 2);
565 }
566
567 sfree(fdlist);
568
39934deb 569 return FD_ISSET(0, &rset) ? 1 : 0;
570}
571
572/*
573 * Wait for some network data and process it.
574 */
575int ssh_sftp_loop_iteration(void)
576{
65857773 577 return ssh_sftp_do_select(FALSE, FALSE);
39934deb 578}
579
580/*
581 * Read a PSFTP command line from stdin.
582 */
65857773 583char *ssh_sftp_get_cmdline(char *prompt, int no_fds_ok)
39934deb 584{
585 char *buf;
586 int buflen, bufsize, ret;
587
588 fputs(prompt, stdout);
589 fflush(stdout);
590
591 buf = NULL;
592 buflen = bufsize = 0;
593
594 while (1) {
65857773 595 ret = ssh_sftp_do_select(TRUE, no_fds_ok);
39934deb 596 if (ret < 0) {
597 printf("connection died\n");
598 return NULL; /* woop woop */
599 }
600 if (ret > 0) {
601 if (buflen >= bufsize) {
602 bufsize = buflen + 512;
603 buf = sresize(buf, bufsize, char);
604 }
605 ret = read(0, buf+buflen, 1);
606 if (ret < 0) {
607 perror("read");
608 return NULL;
609 }
610 if (ret == 0) {
611 /* eof on stdin; no error, but no answer either */
612 return NULL;
613 }
614
615 if (buf[buflen++] == '\n') {
616 /* we have a full line */
617 return buf;
618 }
619 }
620 }
876eefd4 621}
622
623/*
624 * Main program: do platform-specific initialisation and then call
625 * psftp_main().
626 */
627int main(int argc, char *argv[])
628{
629 uxsel_init();
630 return psftp_main(argc, argv);
631}