1 /* pfs-sftp.c:
2  *
3  ****************************************************************
4  * Copyright (C) 2002 - 2004 Scott Parish
5  * Copyright (C) 2003 Tom Lord
6  *
7  * See the file "COPYING" for further information about
8  * the copyright and warranty status of this work.
9  */
10 
11 
12 #include <string.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include "config-options.h"
16 #include "hackerlab/arrays/ar.h"
17 #include "hackerlab/bugs/panic.h"
18 #include "hackerlab/machine/types.h"
19 #include "hackerlab/mem/alloc-limits.h"
20 #include "hackerlab/mem/mem.h"
21 #include "hackerlab/char/str.h"
22 #include "hackerlab/fmt/cvt.h"
23 #include "hackerlab/fs/file-names.h"
24 #include "hackerlab/os/errno-to-string.h"
25 #include "hackerlab/vu/safe.h"
26 #include "tla/libfsutils/tmp-files.h"
27 #include "tla/libfsutils/file-contents.h"
28 #include "tla/libarch/archives.h"
29 #include "tla/libarch/exec.h"
30 #include "tla/libarch/pfs-sftp.h"
31 #include "tla/libarch/pfs-sftp-version.h"
32 #include "tla/libarch/sftp.h"
33 
34 
35 
36 
37 #undef FIXME
38 /* TODO: look through ssh/tcp/ethernet specks to find an optimal size */
39 #define SFTP_RWSIZE 16384
40 
41 /* PipeLine-REQuests: num r/w requests open at once for get() and put() */
42 #define MAX_PL_REQS 16
43 
44 #undef MIN
45 #define MIN(A,B) ((A) < (B) ? (A) : (B))
46 #define LISTING_FILE ".listing"
47 
48 
49 #define WRITE_UINT4(str, ui) do { \
50   (str)[0] = (ui) >> 24; \
51   (str)[1] = (ui) >> 16; \
52   (str)[2] = (ui) >> 8; \
53   (str)[3] = (ui); } while (0)
54 
55 #define WRITE_UINT8(str, ui) do { \
56   (str)[0] = (ui) >> 56; \
57   (str)[1] = (ui) >> 48; \
58   (str)[2] = (ui) >> 40; \
59   (str)[3] = (ui) >> 32; \
60   (str)[4] = (ui) >> 24; \
61   (str)[5] = (ui) >> 16; \
62   (str)[6] = (ui) >> 8; \
63   (str)[7] = (ui); } while (0)
64 
65 #define READ_UINT4(str) ( \
66   ((t_uint32)(unsigned char)((str)[0]) << 24) | \
67   ((t_uint32)(unsigned char)((str)[1]) << 16) | \
68   ((t_uint32)(unsigned char)((str)[2]) << 8) | \
69   ((t_uint32)(unsigned char)((str)[3])) )
70 
71 #define READ_UINT8(str) ( \
72   ((unsigned long long int)(unsigned char)((str)[0]) << 56) | \
73   ((unsigned long long int)(unsigned char)((str)[1]) << 48) | \
74   ((unsigned long long int)(unsigned char)((str)[2]) << 40) | \
75   ((unsigned long long int)(unsigned char)((str)[3]) << 32) | \
76   ((unsigned long long int)(unsigned char)((str)[4]) << 24) | \
77   ((unsigned long long int)(unsigned char)((str)[5]) << 16) | \
78   ((unsigned long long int)(unsigned char)((str)[6]) << 8) | \
79   ((unsigned long long int)(unsigned char)((str)[7])) )
80 
81 
82 struct arch_pfs_sftp_session
83 {
84   struct arch_pfs_session pfs;
85 
86   char * cwd;
87   t_uint32 request_id;
88   t_uint32 plreqs;
89 
90   int in_fd;
91   int out_fd;
92 };
93 
94 
95 struct sftp_attrs
96 {
97   t_uint32 flags;
98   unsigned long long int size;
99   t_uint32 uid;
100   t_uint32 gid;
101   t_uint32 perms;
102   t_uint32 atime;
103   t_uint32 mtime;
104   unsigned char *extended;
105 };
106 
107 
108 
109 
110 
111 
112 /* __STDC__ prototypes for static functions */
113 static int sftp_write_pkt (struct arch_pfs_sftp_session * p,
114                            int soft_errors,
115                            const char *fmt, ...);
116 static int sftp_export_attrs (struct sftp_attrs *a,
117                               char ** pkt,
118                               int * len);
119 static void sftp_client_do_init (struct arch_pfs_sftp_session * p,
120                                  int soft_errors);
121 static int sftp_server_response (struct arch_pfs_sftp_session * p,
122                                  char ** pkt,
123                                  int * pkt_len,
124                                  int expected_response,
125                                  int soft_errors);
126 static int sftp_decode_status (char * pkt,
127                                int pkt_len,
128                                int i,
129                                int soft_errors);
130 static t_uchar * sftp_abs_path (const t_uchar * cwd,
131                                 const t_uchar *path);
132 static int sftp_get (struct arch_pfs_sftp_session * p,
133                      int data_fd,
134                      const t_uchar * path,
135                      int soft_errors);
136 static int pfs_get_file (struct arch_pfs_session * p,
137                          int out_fd,
138                          const t_uchar * path,
139                          int soft_errors);
140 static t_uchar * pfs_file_contents (struct arch_pfs_session * p,
141                                     const t_uchar * path,
142                                     int soft_errors);
143 static rel_table pfs_directory_files (struct arch_pfs_session * p,
144                                       const t_uchar * path,
145                                       int soft_errors);
146 static int sftp_import_attrs (struct sftp_attrs *a,
147                               char * pkt,
148                               unsigned int pkt_len,
149                               int soft_errors);
150 static int pfs_put_file (struct arch_pfs_session *p,
151                          const t_uchar * path,
152                          mode_t perms,
153                          int data_fd,
154                          int soft_errors);
155 static int pfs_mkdir (struct arch_pfs_session * p,
156                       const t_uchar * path,
157                       mode_t mode,
158                       int soft_errors);
159 static int pfs_file_exists (struct arch_pfs_session * p,
160                             const t_uchar * path);
161 static int pfs_is_dir (struct arch_pfs_session * p,
162                        const t_uchar * path);
163 static int pfs_rename (struct arch_pfs_session * p,
164                        t_uchar ** errstr,
165                        const t_uchar * from,
166                        const t_uchar * to,
167                        int soft_errors);
168 static int pfs_rmdir (struct arch_pfs_session * p,
169                       const t_uchar * path,
170                       int soft_errors);
171 static int pfs_rm (struct arch_pfs_session * p,
172                    const t_uchar * path,
173                    int soft_errors);
174 static t_uchar * dirfold (t_uchar *dir);
175 
176 
177 
178 struct arch_pfs_vtable sftp_pfs_fns =
179   {
180     pfs_file_exists,
181     pfs_is_dir,
182 
183     pfs_file_contents,
184     pfs_get_file,
185     pfs_directory_files,
186 
187     pfs_put_file,
188 
189     pfs_mkdir,
190     pfs_rename,
191 
192     pfs_rmdir,
193     pfs_rm,
194   };
195 
196 
197 
198 
199 int
arch_pfs_sftp_supported_protocol(const t_uchar * uri)200 arch_pfs_sftp_supported_protocol (const t_uchar * uri)
201 {
202   if (!str_cmp_prefix ("sftp:", uri))
203     return 1;
204   else
205     return 0;
206 }
207 
208 
209 static int
sftp_write_pkt(struct arch_pfs_sftp_session * p,int soft_errors,const char * fmt,...)210 sftp_write_pkt (struct arch_pfs_sftp_session * p,
211                 int soft_errors,
212                 const char *fmt, ...)
213 {
214   va_list ap;
215   struct sftp_attrs *attrs;
216   char *buf;
217   int i;
218   int errn = 0;
219   unsigned int len, s_len, ret;
220   t_uint32 l;
221   unsigned long long ll;
222   char *s;
223   unsigned char c;
224 
225   va_start (ap, fmt);
226 
227   len = 4;
228   buf = lim_malloc (0, len);
229   i = 4;
230 
231   while (*fmt)
232     switch (*fmt++)
233       {
234         case '8': /* unsigned long long */
235           ll = va_arg (ap, unsigned long long);
236           len += sizeof (ll);
237           buf = lim_realloc (0, buf, len);
238           WRITE_UINT8 (buf + i, ll);
239           i += sizeof (ll);
240           break;
241 
242         case '4': /* t_uint32 */
243           l = va_arg (ap, t_uint32);
244           len += sizeof (l);
245           buf = lim_realloc (0, buf, len);
246           WRITE_UINT4 (buf + i, l);
247           i += sizeof (l);
248           break;
249 
250         case '1': /* int */
251           c = va_arg (ap, int);
252           len += sizeof (c);
253           buf = lim_realloc (0, buf, len);
254           buf[i] = c;
255           i += sizeof (c);
256           break;
257 
258         case 's': /* char*,size_t  (IE: <int32:length> <string>) */
259           s = va_arg (ap, char *);
260           l = va_arg (ap, size_t);
261           len += sizeof (l) + l;
262           buf = lim_realloc (0, buf, len);
263           WRITE_UINT4 (buf + i, l);
264           i += sizeof (l);
265           mem_move (buf + i, s, l);
266           i += l;
267           break;
268 
269         case 'a': /* struct sftp_attrs* */
270             attrs = va_arg (ap, struct sftp_attrs *);
271           sftp_export_attrs (attrs, &s, &s_len);
272           len += s_len;
273           buf = lim_realloc (0, buf, len);
274           mem_move (buf + i, s, s_len);
275           i += s_len;
276           break;
277 
278         default:
279           if (!soft_errors)
280             {
281               safe_printfmt (2, "Invalid fmt to sftp_write_pkt!\n");
282               exit (2);
283             }
284           else
285             return -1;
286       }
287 
288   c = va_arg (ap, int);
289 
290   WRITE_UINT4 (buf, len - sizeof(len));
291   ret = vu_write_retry (&errn, p->out_fd, buf, len);
292 
293   lim_free (0, buf);
294   va_end (ap);
295 
296   return ret;
297 }
298 
299 
300 
301 /* take the structure and create an sftp attr string, returns size of string */
302 static int
sftp_export_attrs(struct sftp_attrs * a,char ** pkt,int * len)303 sftp_export_attrs (struct sftp_attrs *a,
304                    char ** pkt,
305                    int * len)
306 {
307   int i = 0;
308 
309   *len = sizeof (a->flags)
310     + (a->flags & SSH_FILEXFER_ATTR_SIZE ? sizeof (a->size) : 0)
311     + (a->flags & SSH_FILEXFER_ATTR_UIDGID ? sizeof (a->uid)*2 : 0)
312     + (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS ? sizeof (a->perms) : 0)
313     + (a->flags & SSH_FILEXFER_ATTR_ACMODTIME ? sizeof (a->atime)*2 : 0);
314 
315   *pkt = lim_malloc (0, *len);
316 
317   WRITE_UINT4 (*pkt, a->flags);
318   i += 4;
319 
320   if (a->flags & SSH_FILEXFER_ATTR_SIZE)
321     {
322       WRITE_UINT8 (*pkt + i, a->size);
323       i += 8;
324     }
325 
326   if (a->flags & SSH_FILEXFER_ATTR_UIDGID)
327     {
328       WRITE_UINT4 (*pkt + i, a->uid);
329       i += 4;
330       WRITE_UINT4 (*pkt + i, a->gid);
331       i += 4;
332     }
333 
334   if (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS)
335     {
336       WRITE_UINT4 (*pkt + i, a->perms);
337       i += 4;
338     }
339 
340   if (a->flags & SSH_FILEXFER_ATTR_ACMODTIME)
341     {
342       WRITE_UINT4 (*pkt + i, a->atime);
343       i += 4;
344       WRITE_UINT4 (*pkt + i, a->mtime);
345       i += 4;
346     }
347 
348   return *len;
349 }
350 
351 
352 
353 
354 
355 struct arch_pfs_session *
arch_pfs_sftp_connect(const t_uchar * uri)356 arch_pfs_sftp_connect (const t_uchar * uri)
357 {
358   struct arch_pfs_sftp_session * answer = 0;
359   t_uchar * hostname = 0;
360   t_uchar * port = 0;
361   t_uchar * user = 0;
362   t_uchar ** ssh_argv;
363   int subproc;
364   int pin[2], pout[2];
365   int c_in_fd, c_out_fd;
366   enum arch_ssh_type ssh_type = arch_pfs_sftp_ssh_type ();
367 
368   answer = (struct arch_pfs_sftp_session *)lim_malloc (0, sizeof (*answer));
369   mem_set0 ((t_uchar *)answer, sizeof (*answer));
370   answer->pfs.vtable = &sftp_pfs_fns;
371 
372   if (arch_pfs_sftp_parse_uri (&user, &hostname, &port, &answer->cwd, uri)!=0)
373     {
374       safe_printfmt (2, "Invalid sftp URL\n");
375       /*safe_printfmt (2, "Invalid sftp URL: %s\n", uri);
376        * can't use URI (may be mangled by arch_pfs_pfs_make_archive, etc */
377       exit (2);
378     }
379 
380   ssh_argv = 0;
381   *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = arch_pfs_sftp_ssh_binary (ssh_type);
382 
383   if (port)
384     {
385       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-p";
386       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = port;
387     }
388 
389   if (user)
390     {
391       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-l";
392       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = user;
393     }
394 
395   if (ssh_type == ssh_type_lsh)
396     {
397       /* `--no-x11-forward' is the default with `lshc', so we don't need to
398 	 make it explicit.  Furthermore, it is not supported by `lshg', so
399 	 we'd better not use it so that both `lshc' and `lshg' can be used
400 	 interchangeably.  */
401 
402       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "--subsystem=sftp";
403       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = hostname;
404       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = 0;
405     }
406   else if (ssh_type == ssh_type_openssh)
407     {
408       /* From OpenSSH' ssh(1), when using `-s', "The subsystem is specified
409 	 as the remote command".  IOW, we are to perform an invokation along
410 	 the lines of:
411 
412 	   $ ssh -l USER -2 -s REMOTE-HOST sftp
413 
414          We assume the use of `ssh', not `sftp'.  */
415 
416       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-oFallBackToRsh=no";
417       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-oForwardX11=no";
418       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-oForwardAgent=no";
419       /*  *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-oClearAllForwardings=yes"; */
420       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-2";
421       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-s";
422       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = hostname;
423       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "sftp";
424       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = 0;
425     }
426   else if (ssh_type == ssh_type_fsecure3)
427     {
428       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-x";
429       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-a";
430       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "-s";
431       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = "sftp";
432       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = hostname;
433       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar *)) = 0;
434     }
435   else if (ssh_type == ssh_type_psftp)
436     {
437       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar*)) = "-2";
438       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar*)) = hostname;
439       *(t_uchar **)ar_push ((void **)&ssh_argv, 0, sizeof (t_uchar*)) = 0;
440     }
441   else
442     panic ("undefined SSH implementation type");
443 
444 
445   if ((pipe (pin) == -1) || (pipe (pout) == -1))
446     panic ("Couldn't create pipe(s)");
447 
448   answer->out_fd = pout[1];
449   answer->in_fd = pin[0];
450   c_in_fd = pout[0];
451   c_out_fd = pin[1];
452 
453   subproc = fork ();
454 
455   if (subproc < 0)
456     panic ("unable to fork");
457 
458   if (subproc == 0)
459     {
460       if ((dup2 (c_in_fd, STDIN_FILENO) == -1) ||
461           (dup2 (c_out_fd, STDOUT_FILENO) == -1))
462         {
463           safe_printfmt (2, "dup2: %s\n", errno_to_string (errno));
464           exit (1);
465         }
466 
467       close (answer->in_fd);
468       close (answer->out_fd);
469       close (c_in_fd);
470       close (c_out_fd);
471       arch_util_execvp (ssh_argv[0], ssh_argv);
472       safe_printfmt (2, "exec: %s: %s\n", ssh_argv[0], errno_to_string (errno));
473       exit (1);
474     }
475 
476   close (c_in_fd);
477   close (c_out_fd);
478 
479   sftp_client_do_init (answer, 0);
480   if (!pfs_file_exists((struct arch_pfs_session *)answer, answer->cwd))
481     {
482       safe_printfmt (2, "No such file or directory: %s\n", answer->cwd);
483       exit (2);
484     }
485 
486   lim_free (0, hostname);
487   lim_free (0, user);
488   lim_free (0, port);
489 
490   return (struct arch_pfs_session *)answer;
491 }
492 
493 
494 
495 
496 static void
sftp_client_do_init(struct arch_pfs_sftp_session * p,int soft_errors)497 sftp_client_do_init (struct arch_pfs_sftp_session * p,
498                      int soft_errors)
499 {
500   int pkt_len;
501   char *pkt;
502 
503   sftp_write_pkt (p, soft_errors, "14", SSH_FXP_INIT,
504 		  (t_uint32)SFTP_VERSION, soft_errors);
505   sftp_server_response (p, &pkt, &pkt_len, SSH_FXP_VERSION, soft_errors);
506 
507   lim_free (0, pkt);
508 }
509 
510 
511 
512 
513 static int
sftp_server_response(struct arch_pfs_sftp_session * p,char ** pkt,int * pkt_len,int expected_response,int soft_errors)514 sftp_server_response (struct arch_pfs_sftp_session * p,
515                       char ** pkt,
516                       int * pkt_len,
517                       int expected_response,
518                       int soft_errors)
519 {
520   int i, errn;
521   unsigned int len, read_len, total_read, response;
522   unsigned char *pl;
523   unsigned char buf[4];
524 
525   errn = 0;
526 
527   read_len = vu_read_retry (&errn, p->in_fd, buf, 4);
528 
529   if (read_len < 4)
530     {
531       if (!soft_errors)
532         {
533           if (errn)
534             safe_printfmt (2, "Error reading from server: %s\n", errno_to_string (errn));
535           else
536             safe_printfmt (2, "Error reading from server\n");
537           exit (2);
538         }
539       else
540         return -1;
541     }
542 
543   *pkt_len = len = READ_UINT4 (buf);
544 
545   *pkt = pl = lim_malloc (0, len);
546 
547   total_read = 0;
548   while (len)
549     {
550       read_len = vu_read_retry (&errn, p->in_fd, pl + total_read,
551                                 MIN(len, BUFSIZ));
552       if (read_len <= 0 || read_len > len)
553         {
554           if (!soft_errors)
555             {
556               if (errn)
557                 safe_printfmt (2, "Error reading from server: %s\n", errno_to_string (errn));
558               else
559                 safe_printfmt (2, "Error reading from server.\n");
560               exit (2);
561             }
562           else
563             return -1;
564         }
565       total_read += read_len;
566       len -= read_len;
567     }
568 
569   i = 0;
570   response = pl[i++];
571 
572   if (response != SSH_FXP_VERSION)
573     {
574       int rq_id_offset = 0; /* request_id offset caused by pipe-lining */
575       if (p->plreqs)
576 	rq_id_offset = p->plreqs - 1;
577 
578       if (READ_UINT4 (pl + i) != (p->request_id - rq_id_offset))
579         {
580           if (!soft_errors)
581             {
582               lim_free (0, pl);
583               safe_printfmt (2, "Error: server returned wrong request id\n");
584               exit(2);
585             }
586           else
587             return -1;
588         }
589       i += 4;
590     }
591 
592   if (response != expected_response)
593     {
594       if (response == SSH_FXP_STATUS)
595         {
596           return sftp_decode_status (pl, *pkt_len, i, soft_errors);
597         }
598 
599 
600       if (!soft_errors)
601         {
602           safe_printfmt (2, "Error: server returned wrong response\n");
603           exit (2);
604         }
605       else
606         lim_free (0, pl);
607     }
608 
609   return i;
610 }
611 
612 
613 
614 
615 static int
sftp_decode_status(char * pkt,int pkt_len,int i,int soft_errors)616 sftp_decode_status (char * pkt,
617                     int pkt_len,
618                     int i,
619                     int soft_errors)
620 {
621   int code, err_len;
622   const char *err_msg;
623 
624   code = READ_UINT4 (pkt + i);
625   i += 4;
626 
627   if (code == SSH_FX_OK || code == SSH_FX_EOF)
628     return 0;
629 
630   if ( i == pkt_len )
631     {
632       switch (code)
633         {
634           case SSH_FX_NO_SUCH_FILE:
635             err_msg = "no such file or directory"; break;
636 
637           case SSH_FX_PERMISSION_DENIED:
638             err_msg = "permission denied"; break;
639 
640           case SSH_FX_FAILURE:
641             err_msg = "failure"; break;
642 
643           case SSH_FX_BAD_MESSAGE:
644             err_msg = "bad message"; break;
645 
646           case SSH_FX_NO_CONNECTION:
647             err_msg = "no connection"; break;
648 
649           case SSH_FX_CONNECTION_LOST:
650             err_msg = "connection lost"; break;
651 
652           case SSH_FX_OP_UNSUPPORTED:
653             err_msg = "operation not supported"; break;
654 
655           default:
656             err_msg = "???"; break;
657         }
658       err_len = strlen(err_msg);
659     }
660   else if ( pkt_len - i < 4 ||
661             pkt_len - i < (err_len = READ_UINT4 (pkt + i)) + 4 )
662     {
663       if (!soft_errors)
664         {
665           safe_printfmt (2, "sftp_decode_status: Packet too short(1): possibly garbage from server?\n");
666           exit (2);
667         }
668       else
669         return -1;
670     }
671   else
672     {
673       i += 4;
674       err_msg = pkt + i;
675     }
676 
677   if (!soft_errors)
678     {
679       safe_printfmt (2, "sftp status: %.*s\n", err_len, err_msg);
680       exit (2);
681     }
682   else
683     return -1;
684 
685 }
686 
687 
688 
689 static t_uchar *
sftp_abs_path(const t_uchar * cwd,const t_uchar * path)690 sftp_abs_path (const t_uchar * cwd,
691                const t_uchar *path)
692 {
693   t_uchar * ap;
694   if (path[0] != '/')
695     ap = str_alloc_cat_many (0, cwd, "/", path, str_end);
696   else
697     ap = str_save (0, path);
698 
699   return dirfold (ap);
700 }
701 
702 
703 
704 static int
sftp_get(struct arch_pfs_sftp_session * p,int data_fd,const t_uchar * path,int soft_errors)705 sftp_get (struct arch_pfs_sftp_session * p,
706           int data_fd,
707           const t_uchar * path,
708           int soft_errors)
709 {
710   struct sftp_attrs attrs;
711   unsigned int pkt_len;
712   char *pkt, *file;
713   int ret, handle_len, i;
714   unsigned long long total_read;
715   char *handle;
716   int len, errn, eof, numpkts;
717 
718   file = sftp_abs_path (p->cwd, path);
719 
720   attrs.flags = 0;
721   ret = sftp_write_pkt (p, soft_errors,
722                         "14s4a", SSH_FXP_OPEN, ++p->request_id,
723                         file, str_length (file),
724 			(t_uint32)SSH_FXF_READ, &attrs);
725   lim_free (0, file);
726 
727   if (0 > ret)
728     return -1;
729 
730   if (0 > (i = sftp_server_response (p, &pkt, &pkt_len, SSH_FXP_HANDLE,
731                                      soft_errors)))
732     return -1;
733 
734   handle_len = READ_UINT4 (pkt + i);
735   i += 4;
736   if (handle_len > pkt_len - i)
737     {
738      if (!soft_errors)
739         {
740           safe_printfmt(2, "sftp_client_get: Packet too short(2): did we get garbage from server?\n");
741           exit (2);
742         }
743       else
744         lim_free (0, pkt);
745         return -1;
746     }
747   handle = lim_malloc (0, handle_len);
748   mem_move (handle, pkt + i, handle_len);
749 
750   lim_free (0, pkt);
751 
752   total_read = 0;
753   eof = 0;
754   numpkts = 0;
755   while (1)
756     {
757       for (; ! eof && p->plreqs <= MIN(MAX_PL_REQS, numpkts + 1); p->plreqs++)
758 	{
759 	  ret = sftp_write_pkt (p, soft_errors, "14s84", SSH_FXP_READ,
760 				++p->request_id, handle, (size_t)handle_len,
761 				total_read + SFTP_RWSIZE * p->plreqs,
762 				(t_uint32)SFTP_RWSIZE);
763 	  if (0 > ret)
764 	    {
765 	      lim_free (0, handle);
766 	      return -1;
767 	    }
768 	}
769 
770       if (0 > (i = sftp_server_response (p, &pkt, &pkt_len, SSH_FXP_DATA,
771                                          soft_errors)))
772         {
773           lim_free (0, handle);
774           return -1;
775         }
776 
777       p->plreqs--;
778       numpkts++;
779 
780       if (i == 0)
781         {
782 	  if (p->plreqs)
783 	    {
784 	      eof = 1;
785 	      continue;
786 	    }
787 
788           lim_free (0, pkt);
789           break;
790         }
791 
792       if (eof)
793 	{
794 	  safe_printfmt(2, "EOF already reached, but the server sent data!\n");
795 	  exit(2);
796 	}
797 
798       len = READ_UINT4 (pkt + i);
799       i += 4;
800       if (len > pkt_len - i)
801         {
802           if (!soft_errors) {
803             safe_printfmt (2, "sftp_client_get: Packet too short(3): did we get garbage from server?\n");
804             exit (2);
805           }
806           else {
807             lim_free (0, handle);
808             lim_free (0, pkt);
809             return -1;
810           }
811         }
812 
813       if (0 > vu_write_retry (&errn, data_fd, pkt + i, len))
814         {
815           if (! soft_errors) {
816             safe_printfmt(2, "Error while writing: %s\n", errno_to_string (errn));
817             exit (2);
818           }
819           else
820             return -1;
821         }
822       i += len;
823       total_read += len;
824 
825       lim_free (0, pkt);
826     }
827 
828   if (p->plreqs)
829     {
830       safe_printfmt (2, "sftp_client_get: Requests still in pipe-line!\n");
831       exit (2);
832     }
833 
834   if (0 > sftp_write_pkt (p, soft_errors,
835                           "14s", SSH_FXP_CLOSE, ++p->request_id,
836                           handle, (size_t)handle_len))
837     return -1;
838 
839   lim_free (0, handle);
840 
841   if (0 > (i = sftp_server_response (p, &pkt, &pkt_len, SSH_FXP_STATUS,
842                                      soft_errors)))
843     return -1;
844 
845   if (i >= 0)
846     lim_free (0, pkt);
847 
848   return i;
849 }
850 
851 
852 
853 
854 static int
pfs_get_file(struct arch_pfs_session * p,int out_fd,const t_uchar * path,int soft_errors)855 pfs_get_file (struct arch_pfs_session * p,
856               int out_fd,
857               const t_uchar * path,
858               int soft_errors)
859 {
860   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
861 
862   return sftp_get (pfs, out_fd, path, soft_errors);
863 }
864 
865 
866 static t_uchar *
pfs_file_contents(struct arch_pfs_session * p,const t_uchar * path,int soft_errors)867 pfs_file_contents (struct arch_pfs_session * p,
868                    const t_uchar * path,
869                    int soft_errors)
870 {
871   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
872   t_uchar * tmp_path = 0;
873   int fd;
874   t_uchar * answer = 0;
875 
876   tmp_path = tmp_file_name ("/tmp", ",,pfs-sftp-file-contents");
877   fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0000);
878   safe_unlink (tmp_path);
879 
880   if (0 > sftp_get (pfs, fd, path, soft_errors))
881     {
882       safe_close(fd);
883       return 0;
884     }
885 
886   safe_lseek (fd, (off_t)0, SEEK_SET);
887   answer = fd_contents (fd);
888   safe_close (fd);
889 
890   return answer;
891 }
892 
893 
894 static rel_table
pfs_directory_files(struct arch_pfs_session * p,const t_uchar * path,int soft_errors)895 pfs_directory_files (struct arch_pfs_session * p,
896                      const t_uchar * path,
897                      int soft_errors)
898 {
899   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
900   struct sftp_attrs attrs;
901   rel_table answer = rel_table_nil;
902   unsigned int pkt_len, j, count;
903   char *pkt, *dir, *file;
904   int len, handle_len, dots, i;
905   char *handle;
906 
907   dir = sftp_abs_path (pfs->cwd, path);
908 
909  chop_slashes:
910   i = str_length (dir);
911   if (i > 1 && dir[i - 1] == '/')
912     {
913       dir[i - 1] = 0;
914       goto chop_slashes;
915     }
916 
917   if (0 > sftp_write_pkt (pfs, soft_errors,
918                           "14s", SSH_FXP_OPENDIR, ++pfs->request_id,
919                           dir, str_length (dir)))
920     return rel_table_nil;
921   lim_free (0, dir);
922 
923   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_HANDLE,
924                                      soft_errors)))
925     return rel_table_nil;
926 
927   handle_len = READ_UINT4 (pkt + i);
928   i += 4;
929   if (handle_len > pkt_len - i)
930     {
931       if (! soft_errors) {
932         safe_printfmt(2, "sftp_client_list: Packet too short(4): did we get garbage from server?\n");
933         exit (2);
934       }
935       lim_free (0, pkt);
936       return rel_table_nil;
937     }
938   handle = lim_malloc (0, handle_len);
939   mem_move (handle, pkt + i, handle_len);
940 
941   lim_free (0, pkt);
942 
943   while (1)
944     {
945       if (0 >sftp_write_pkt (pfs, soft_errors,
946                              "14s", SSH_FXP_READDIR, ++pfs->request_id,
947                              handle, (size_t)handle_len))
948         return rel_table_nil;
949 
950       if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_NAME,
951                                          soft_errors)))
952         return rel_table_nil;
953       if (i == 0)
954         {
955           lim_free (0, pkt);
956           break;
957         }
958 
959       count = READ_UINT4 (pkt + i);
960       i += 4;
961 
962       for (j = 0; j < count; j++)
963         {
964           len = READ_UINT4 (pkt + i);
965           i += 4;
966           if (len > pkt_len - i)
967             {
968               if (!soft_errors)
969                 {
970                   safe_printfmt(2, "sftp_client_list: Packet too short(5): did we get garbage from server? (%d > %d - %d)\n", len, pkt_len, i);
971                   lim_free (0, handle);
972                   lim_free (0, pkt);
973                   exit (2);
974                 }
975               return rel_table_nil;
976             }
977 
978           dots = 0;
979           if ((len == 1 && pkt[i] == '.') ||
980               (len == 2 && pkt[i] == '.' && pkt[i+1] == '.'))
981             dots = 1;
982 
983           if (!dots)
984             {
985               file = str_save_n (0, pkt+i, len);
986               rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str (file)), rel_record_null);
987             }
988 
989           i += len;
990 
991           len = READ_UINT4 (pkt + i);
992           i += 4;
993           if (len > pkt_len - i)
994             {
995               if (! soft_errors)
996                 {
997                   safe_printfmt(2, "sftp_client_list: Packet too short(6): did we get garbage from server?\n");
998                   lim_free (0, handle);
999                   lim_free (0, pkt);
1000                   exit (2);
1001                 }
1002               return rel_table_nil;
1003             }
1004           i += len;
1005 
1006           if (0 > (i += sftp_import_attrs (&attrs, pkt + i, pkt_len - i,
1007                                            soft_errors)))
1008             return rel_table_nil;
1009         }
1010 
1011       lim_free (0, pkt);
1012     }
1013 
1014   if (0 > sftp_write_pkt (pfs, soft_errors,
1015                           "14s", SSH_FXP_CLOSE, ++pfs->request_id,
1016                           handle, (size_t)handle_len))
1017     return rel_table_nil;
1018   lim_free (0, handle);
1019 
1020   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_HANDLE,
1021                                      soft_errors)))
1022     return rel_table_nil;
1023 
1024   if (i >= 0)
1025       lim_free (0, pkt);
1026 
1027   return answer;
1028 }
1029 
1030 
1031 
1032 /* take an sftp attr string and initialize a structure from it */
1033 static int
sftp_import_attrs(struct sftp_attrs * a,char * pkt,unsigned int pkt_len,int soft_errors)1034 sftp_import_attrs (struct sftp_attrs *a,
1035                    char * pkt,
1036                    unsigned int pkt_len,
1037                    int soft_errors)
1038 {
1039   int count, i, j;
1040   unsigned int len;
1041 
1042   i = 0;
1043 
1044   a->flags = READ_UINT4 (pkt);
1045   i += 4;
1046 
1047   len = (a->flags & SSH_FILEXFER_ATTR_SIZE ? sizeof (a->size) : 0)
1048       + (a->flags & SSH_FILEXFER_ATTR_UIDGID ? sizeof (a->uid)*2 : 0)
1049       + (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS ? sizeof (a->perms) : 0)
1050       + (a->flags & SSH_FILEXFER_ATTR_ACMODTIME ? sizeof (a->atime)*2 : 0);
1051 
1052   if (len > pkt_len - i)
1053     {
1054       if (!soft_errors)
1055         {
1056           safe_printfmt (2, "sftp_import_attrs: Packet too short(7): did we get garbage from server?\n");
1057           exit (2);
1058         }
1059       return -1;
1060     }
1061 
1062   if (a->flags & SSH_FILEXFER_ATTR_SIZE)
1063     {
1064       a->size = READ_UINT8 (pkt + i);
1065       i += 8;
1066     }
1067 
1068   if (a->flags & SSH_FILEXFER_ATTR_UIDGID)
1069     {
1070       a->uid = READ_UINT4 (pkt + i);
1071       i += 4;
1072       a->gid = READ_UINT4 (pkt + i);
1073       i += 4;
1074     }
1075 
1076   if (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS)
1077     {
1078       a->perms = READ_UINT4 (pkt + i);
1079       i += 4;
1080     }
1081 
1082   if (a->flags & SSH_FILEXFER_ATTR_ACMODTIME)
1083     {
1084       a->atime = READ_UINT4 (pkt + i);
1085       i += 4;
1086       a->mtime = READ_UINT4 (pkt + i);
1087       i += 4;
1088     }
1089 
1090   if (a->flags & SSH_FILEXFER_ATTR_EXTENDED)
1091     {
1092       count = READ_UINT4 (pkt + i);
1093       for (j = 0; j < count; j++)
1094         {
1095           i += READ_UINT4 (pkt + i);
1096           i += READ_UINT4 (pkt + i);
1097         }
1098     }
1099 
1100   return i;
1101 }
1102 
1103 static int
pfs_put_file(struct arch_pfs_session * p,const t_uchar * path,mode_t perms,int data_fd,int soft_errors)1104 pfs_put_file (struct arch_pfs_session *p,
1105               const t_uchar * path,
1106               mode_t perms,
1107               int data_fd,
1108               int soft_errors)
1109 {
1110   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
1111   struct sftp_attrs attrs;
1112   unsigned int pkt_len;
1113   char *pkt, *file;
1114   int ret, handle_len, i, errn;
1115   unsigned long long total_written;
1116   char *handle;
1117   char buf[SFTP_RWSIZE];
1118   int len, eof;
1119 
1120   file = sftp_abs_path (pfs->cwd, path);
1121 
1122   attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
1123   attrs.perms = perms;
1124 
1125   ret = sftp_write_pkt (pfs, soft_errors,
1126                         "14s4a", SSH_FXP_OPEN, ++pfs->request_id, file,
1127                         str_length (file),
1128                         (t_uint32)(SSH_FXF_WRITE | SSH_FXF_CREAT |
1129 				   SSH_FXF_TRUNC),
1130 			&attrs);
1131   lim_free (0, file);
1132 
1133   if (0 > ret)
1134       return -1;
1135 
1136   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_HANDLE,
1137                                      soft_errors)))
1138       return i;
1139 
1140   handle_len = READ_UINT4 (pkt + i);
1141   i += 4;
1142   if (handle_len > pkt_len - i)
1143     {
1144       if (!soft_errors)
1145         {
1146           safe_printfmt (2, "sftp_client_put: Packet to short(8): did we get garbage from server?\n");
1147           exit (2);
1148         }
1149       lim_free (0, pkt);
1150       return -1;
1151     }
1152   handle = lim_malloc (0, handle_len);
1153   mem_move (handle, pkt + i, handle_len);
1154 
1155   lim_free (0, pkt);
1156 
1157   total_written = 0;
1158   eof = 0;
1159   while (1)
1160     {
1161       for (; ! eof && pfs->plreqs <= MAX_PL_REQS; pfs->plreqs++)
1162 	{
1163 	  if (0 > (len = vu_read_retry (&errn, data_fd, buf, sizeof (buf))))
1164 	    {
1165 	      if (!soft_errors)
1166 		{
1167 		  safe_printfmt (2, "Failed to read file to put()!\n");
1168 		  exit (2);
1169 		}
1170 	      lim_free (0, handle);
1171 	      return -1;
1172 	    }
1173 
1174 	  ret = sftp_write_pkt (pfs, soft_errors, "14s8s", SSH_FXP_WRITE,
1175 				++pfs->request_id, handle, (size_t)handle_len,
1176 				total_written + pfs->plreqs * sizeof (buf),
1177 				buf, (size_t)len);
1178 	  if (0 > ret)
1179 	    {
1180 	      lim_free (0, handle);
1181 	      return -1;
1182 	    }
1183 
1184 	  if (len < sizeof (buf))
1185 	    eof = 1;
1186 	}
1187 
1188       if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_STATUS,
1189                                          soft_errors)))
1190         {
1191           lim_free (0, handle);
1192           return i;
1193         }
1194 
1195       i = sftp_decode_status (pkt, pkt_len, i, soft_errors);
1196       lim_free (0, pkt);
1197       if (i == -1)
1198         {
1199           lim_free (0, handle);
1200           return -1;
1201         }
1202 
1203       total_written += len;
1204 
1205       if (! --pfs->plreqs)
1206 	break;
1207     }
1208 
1209   if (pfs->plreqs)
1210     {
1211       safe_printfmt (2, "pfs_put_file: Requests still in pipe-line!\n");
1212       exit (2);
1213     }
1214 
1215   ret = sftp_write_pkt (pfs, soft_errors,
1216                         "14s", SSH_FXP_CLOSE, ++pfs->request_id, handle,
1217                         (size_t)handle_len);
1218   lim_free (0, handle);
1219 
1220   if (0 > ret)
1221     return -1;
1222 
1223   i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_STATUS, soft_errors);
1224 
1225   if (i >= 0)
1226     {
1227       lim_free (0, pkt);
1228       i = 0;
1229     }
1230   else
1231     i = -1;
1232 
1233   return i;
1234 }
1235 
1236 static int
pfs_mkdir(struct arch_pfs_session * p,const t_uchar * path,mode_t mode,int soft_errors)1237 pfs_mkdir (struct arch_pfs_session * p,
1238            const t_uchar * path,
1239            mode_t mode,
1240            int soft_errors)
1241 {
1242   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
1243   struct sftp_attrs attrs;
1244   t_uchar * dir = 0;
1245   int answer = 0;
1246   int ret, i;
1247   unsigned int pkt_len;
1248   char *pkt;
1249 
1250   attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
1251   attrs.perms = mode;
1252 
1253   dir = sftp_abs_path (pfs->cwd, path);
1254 
1255   if (pfs_file_exists (p, dir))
1256     {
1257       lim_free (0, dir);
1258 
1259       if (soft_errors)
1260         {
1261           return -1;
1262         }
1263       else
1264         {
1265           safe_printfmt(2, "Error creating directory %s in path %s: File exists\n", path, pfs->cwd);
1266           exit(2);
1267         }
1268     }
1269 
1270   ret = sftp_write_pkt (pfs, soft_errors,
1271                         "14sa", SSH_FXP_MKDIR, ++pfs->request_id,
1272                         dir, str_length (dir), &attrs);
1273   if (0 > ret)
1274     {
1275       lim_free (0, dir);
1276       return -1;
1277     }
1278 
1279   lim_free (0, dir);
1280 
1281   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_STATUS,
1282                                      soft_errors)))
1283     return -1;
1284 
1285   if (0 > sftp_decode_status (pkt, pkt_len, i, 1))
1286     {
1287       if (soft_errors)
1288         {
1289           return -1;
1290         }
1291       else
1292         {
1293           safe_printfmt(2, "Error creating directory %s in path %s\n", path, pfs->cwd);
1294           safe_printfmt(2, "Server: ");
1295           sftp_decode_status(pkt, pkt_len, i, 0);
1296         }
1297     }
1298 
1299   lim_free (0, pkt);
1300 
1301   return answer;
1302 }
1303 
1304 
1305 static int
pfs_file_exists(struct arch_pfs_session * p,const t_uchar * path)1306 pfs_file_exists (struct arch_pfs_session * p,
1307                  const t_uchar * path)
1308 {
1309   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
1310   struct sftp_attrs attrs;
1311   t_uchar * path_dir = 0;
1312   char *pkt;
1313   unsigned int pkt_len;
1314   int i;
1315 
1316   path_dir = sftp_abs_path(pfs->cwd, path);
1317 
1318   if (0 > sftp_write_pkt(pfs, 1, "14s",
1319                          SSH_FXP_STAT, ++pfs->request_id, path_dir,
1320                          str_length (path_dir)))
1321     return 0;
1322 
1323   lim_free (0, path_dir);
1324 
1325   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_ATTRS,
1326                                      1)))
1327     return 0;
1328 
1329   if (0 > (i = sftp_import_attrs (&attrs, pkt + i, pkt_len - i, 1)))
1330     return 0;
1331 
1332   lim_free (0, pkt);
1333 
1334   return 1;
1335 }
1336 
1337 
1338 static int
pfs_is_dir(struct arch_pfs_session * p,const t_uchar * path)1339 pfs_is_dir (struct arch_pfs_session * p,
1340             const t_uchar * path)
1341 {
1342   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
1343   int ret, i;
1344   unsigned int pkt_len;
1345   char *pkt, *dir;
1346   struct sftp_attrs attrs;
1347 
1348   dir = sftp_abs_path (pfs->cwd, path);
1349 
1350   if (0 > sftp_write_pkt (pfs, 1,
1351                           "14s", SSH_FXP_STAT, ++pfs->request_id, dir,
1352                           str_length (dir)))
1353     {
1354       lim_free (0, dir);
1355       return -1;
1356     }
1357 
1358   lim_free (0, dir);
1359 
1360   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_ATTRS, 1)))
1361     return -1;
1362 
1363   if (0 > (i = sftp_import_attrs (&attrs, pkt + i, pkt_len - i, 1)))
1364     {
1365       lim_free (0, pkt);
1366       return -1;
1367     }
1368 
1369   ret = 0;
1370   if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS && attrs.perms & S_IFDIR)
1371      ret = 1;
1372 
1373   lim_free (0, pkt);
1374 
1375   return ret;
1376 }
1377 
1378 
1379 static int
pfs_rename(struct arch_pfs_session * p,t_uchar ** errstr,const t_uchar * from,const t_uchar * to,int soft_errors)1380 pfs_rename (struct arch_pfs_session * p,
1381             t_uchar ** errstr,
1382             const t_uchar * from,
1383             const t_uchar * to,
1384             int soft_errors)
1385 {
1386   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
1387   int ret, i;
1388   unsigned int pkt_len;
1389   char *pkt, *from2, *to2, *file, *b;
1390 
1391   from2 = sftp_abs_path (pfs->cwd, from);
1392   to2 = sftp_abs_path (pfs->cwd, to);
1393 
1394   if (0 < pfs_is_dir (p, to2))
1395     {
1396       b = file = str_save (0, from);
1397 
1398     filename:
1399       file = str_chr_rindex (file, '/');
1400       if (!file)
1401           file = b;
1402       else if (!file[1])
1403         {
1404           file[0] = 0;
1405           goto filename;
1406                }
1407       else
1408           file++;
1409 
1410       to2 = str_realloc_cat_many (0, to2, "/", file, str_end);
1411       lim_free (0, b);
1412     }
1413 
1414   ret = sftp_write_pkt (pfs, soft_errors,
1415                         "14ss", SSH_FXP_RENAME, ++pfs->request_id, from2,
1416                         str_length (from2), to2, str_length (to2));
1417 
1418   lim_free (0, from2);
1419   lim_free (0, to2);
1420 
1421   if (0 > ret)
1422     return -1;
1423 
1424   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_STATUS,
1425                                      soft_errors)))
1426       return -1;
1427 
1428   if (0 > (i = sftp_decode_status (pkt, pkt_len, i, soft_errors)))
1429     {
1430       lim_free (0, pkt);
1431       return -1;
1432     }
1433 
1434   lim_free (0, pkt);
1435 
1436   return i;
1437 }
1438 
1439 
1440 static int
pfs_rmdir(struct arch_pfs_session * p,const t_uchar * path,int soft_errors)1441 pfs_rmdir (struct arch_pfs_session * p,
1442            const t_uchar * path,
1443            int soft_errors)
1444 {
1445   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
1446 
1447   int ret, i;
1448   unsigned int pkt_len;
1449   char *pkt, *dir;
1450 
1451   dir = sftp_abs_path (pfs->cwd, path);
1452 
1453   ret = sftp_write_pkt (pfs, soft_errors,
1454                         "14s", SSH_FXP_RMDIR, ++pfs->request_id, dir,
1455                         str_length (dir));
1456 
1457   lim_free (0, dir);
1458 
1459   if (0 > ret)
1460       return -1;
1461 
1462   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_STATUS,
1463                                      soft_errors)))
1464     return -1;
1465 
1466   if (0 > sftp_decode_status (pkt, pkt_len, i, 1))
1467     {
1468       if (soft_errors)
1469         {
1470           return -1;
1471         }
1472       else
1473         {
1474           safe_printfmt(2, "Error creating directory %s in path %s\n", path, pfs->cwd);
1475           safe_printfmt(2, "Server: ");
1476           sftp_decode_status(pkt, pkt_len, i, 0);
1477         }
1478     }
1479 
1480   lim_free (0, pkt);
1481 
1482   return i;
1483 }
1484 
1485 static int
pfs_rm(struct arch_pfs_session * p,const t_uchar * path,int soft_errors)1486 pfs_rm (struct arch_pfs_session * p,
1487         const t_uchar * path,
1488         int soft_errors)
1489 {
1490   struct arch_pfs_sftp_session * pfs = (struct arch_pfs_sftp_session *)p;
1491   int ret, i;
1492   unsigned int pkt_len;
1493   char *pkt, *file;
1494 
1495   file = sftp_abs_path (pfs->cwd, path);
1496 
1497   ret = sftp_write_pkt (pfs, soft_errors,
1498                         "14s", SSH_FXP_REMOVE, ++pfs->request_id, file,
1499                         str_length (file));
1500 
1501   lim_free (0, file);
1502 
1503   if (0 > ret)
1504     return -1;
1505 
1506   if (0 > (i = sftp_server_response (pfs, &pkt, &pkt_len, SSH_FXP_STATUS,
1507                                      soft_errors)))
1508     return -1;
1509 
1510   i = sftp_decode_status (pkt, pkt_len, i, soft_errors);
1511 
1512   lim_free (0, pkt);
1513 
1514   return i;
1515 }
1516 
1517 
1518 static t_uchar *
dirfold(t_uchar * dir)1519 dirfold (t_uchar *dir)
1520 {
1521   t_uchar * buf;
1522   t_uchar * this;
1523   t_uchar * next;
1524   int dir_i = 0;
1525 
1526   this = next = buf = str_save (0, dir);
1527   while ((this = str_separate (&next, "/")) != NULL)
1528     {
1529       if (str_length (this) == 0 || (str_length (this) == 1 && this[0] == '.'))
1530         continue;
1531       else if (str_length (this) == 2 && *this == '.' && this[1] == '.')
1532         {
1533           if (dir_i > 0)
1534             dir_i = (int)((char *)strrchr (dir, '/') - (char *)dir);
1535           dir[dir_i] = 0;
1536         }
1537       else
1538         {
1539           dir[dir_i++] = '/';
1540           strcpy (dir + dir_i, this);
1541           dir_i += str_length (this);
1542         }
1543     }
1544   lim_free (0, buf);
1545 
1546   if (!str_length (dir))
1547       str_cpy (dir, "/");
1548 
1549   return dir;
1550 }
1551 
1552 
1553 int
arch_pfs_sftp_parse_uri(t_uchar ** user,t_uchar ** hostname,t_uchar ** port,char ** path,const t_uchar * uri)1554 arch_pfs_sftp_parse_uri (t_uchar ** user,
1555                          t_uchar ** hostname,
1556                          t_uchar ** port,
1557                          char ** path,
1558                          const t_uchar * uri)
1559 {
1560   const t_uchar * start;
1561   const t_uchar * stop;
1562   t_uchar * host_part = 0;
1563   const t_uchar * at;
1564   const t_uchar * h0;
1565   const t_uchar * h1;
1566   const t_uchar * colon;
1567 
1568   /* [user@]hostname[:port]/path
1569    * ^    ^ ^       ^      ^
1570    * |    | |       |      |
1571    * |   at?|       |      stop[*]
1572    * |      |       |	     0-byte of host_part
1573    * |     h0       |
1574    * |              |
1575    * |              |
1576    * |              |
1577    * start[*]     colon?
1578    * host_part    h1
1579    *
1580    *
1581    * start and stop point to one copy of the string
1582    * (the original uri which includes the /path).
1583    *
1584    * host_part points to another copy of the string
1585    * (local to this block) which all the other pointers
1586    * point into.
1587    *
1588    * at? and colon? are 0 if @ and : are not present.
1589    *
1590    * The reason to make host_part is so that str_chr_index
1591    * searches are confined to just the host_part.
1592    */
1593 
1594 
1595   start = uri + sizeof ("sftp://") - 1;
1596   stop = str_chr_index (start, '/');
1597   if (!stop)
1598     return -1;
1599   *path = str_save (0, stop);
1600 
1601   host_part = str_save_n (0, start, stop - start);
1602 
1603   at = str_chr_index (host_part, '@');
1604   if (at)
1605     h0 = at + 1;
1606   else
1607     h0 = host_part;
1608 
1609   colon = str_chr_index (h0, ':');
1610   if (colon)
1611     h1 = colon;
1612   else
1613     h1 = h0 + str_length (h0);
1614 
1615   if (at)
1616     *user = str_save_n (0, host_part, at - host_part);
1617 
1618   if (colon)
1619     *port = str_save (0, colon + 1);
1620 
1621   *hostname = str_save_n (0, h0, h1 - h0);
1622 
1623   lim_free (0, host_part);
1624   return 0;
1625 }
1626 
1627 
1628 
1629 
1630 /* tag: Tom Lord Thu Jun  5 15:23:06 2003 (pfs-sftp.c)
1631  */
1632