http://prdownloads.sourceforge.net/lufs/lufs-0.9.6.tar.gz?download
[lufs.git] / filesystems / sshfs / sftplib.cpp
1 /*
2  * sftplib.cpp
3  * Copyright (C) 2002 Florin Malita <mali@go.ro>
4  *
5  * This file is part of LUFS, a free userspace filesystem implementation.
6  * See http://lufs.sourceforge.net/ for updates.
7  *
8  * LUFS is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * LUFS is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <unistd.h>
24 #include <signal.h>
25 #include <stdarg.h>
26 #include <fcntl.h>
27 #include <pthread.h>
28 #include <stdio.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 #include <netinet/in.h>
34
35 #include <string>
36
37 #include <lufs/proto.h>
38 #include <lufs/fs.h>
39
40 #include "sftplib.h"
41
42 #ifndef SSHPROG
43 #error ssh not found!!!
44 #endif
45
46 char *args[] = {
47     SSHPROG,
48     "-oFallBackToRsh no",
49     "-oForwardX11 no",
50     "-oForwardAgent no",
51     "-oClearAllForwardings yes",
52     "-oProtocol 2",
53     NULL,
54     NULL,
55     "-s",
56     NULL,
57     "sftp",
58     NULL
59 };
60
61 void
62 hton(void *buf, ...){
63     va_list     ap;
64     char *p;
65     int i;
66     long l;
67
68     va_start(ap, buf);
69     
70     for(p = (char*)buf; (i = va_arg(ap, int)); p += i){
71         switch(i){
72         case 1:
73             break;
74
75         case 2:
76             *((short*)p) = htons(*((short*)p));
77             TRACE("fixing short");
78             break;
79
80         case 4:
81             *((long*)p) = htonl(*((long*)p));
82             TRACE("fixing long");
83             break;
84
85         case 8:
86             l = *((long*)p);
87             *((long*)p) = htonl(*(((long*)p) + 1));
88             *(((long*)p) + 1) = htonl(l);
89             TRACE("fixing quad");
90             break;
91
92         default:
93             WARN("invalid size " << i);
94             break;
95         }
96     }
97
98     va_end(ap);
99 }
100
101 void
102 ntoh(void *buf, ...){
103     va_list     ap;
104     char *p;
105     int i;
106     long l;
107
108     va_start(ap, buf);
109     
110     for(p = (char*)buf; (i = va_arg(ap, int)); p += i){
111         switch(i){
112
113         case 1:
114             break;
115
116         case 2:
117             *((short*)p) = ntohs(*((short*)p));
118             TRACE("fixing short");
119             break;
120
121         case 4:
122             *((long*)p) = ntohl(*((long*)p));
123             TRACE("fixing long");
124             break;
125
126         case 8:
127             l = *((long*)p);
128             *((long*)p) = ntohl(*(((long*)p) + 1));
129             *(((long*)p) + 1) = ntohl(l);
130             TRACE("fixing quad");
131             break;
132
133         default:
134             WARN("invalid size " << i);
135             break;
136         }
137     }
138
139     va_end(ap);
140 }
141
142
143 SConnection::SConnection(){
144     TRACE("in constructor");
145
146     connected = 0;
147     seq = 0;
148 }
149
150 SConnection::~SConnection(){
151     TRACE("in destructor");
152     if(connected)
153         disconnect();
154 }
155
156 int
157 SConnection::connect(char *host, char *user, int port){
158     char portstr[32];
159     int pin[2], pout[2];
160     int c_in, c_out;
161     int err_fd;
162     struct s_hdr hdr;
163
164     TRACE("attempting to connect to " << host << ":" << port << " as " << user);
165     string usr = string("-l") + user;
166     sprintf(portstr, "-p%d", port);
167
168     args[6] = portstr;
169     args[7] = (char*)usr.c_str();
170     args[9] = host;
171
172     for(int i = 0; args[i]; i++)
173         TRACE("args[" << i << "]=" << args[i]);
174
175     if((pipe(pin) == -1) || (pipe(pout) == -1)){
176         WARN("pipe failed");
177         return -1;
178     }
179     
180     f_in = pin[0];
181     f_out = pout[1];
182     c_in = pout[0];
183     c_out = pin[1];
184
185     if((sshpid = fork()) == -1){
186         WARN("fork failed!");
187         return -1;
188     }else if(sshpid == 0){
189
190         if((err_fd = ::open("/dev/null", O_WRONLY)) < 0){
191             WARN("could not open /dev/null!");
192             exit(1);
193         }
194         
195         TRACE("child launching ssh...");
196         if((dup2(c_in, 0) == -1) || (dup2(c_out, 1) == -1) || (dup2(err_fd, 2) == -1)){
197             WARN("dup2 failed!");
198             exit(1);
199         }
200
201         ::close(f_in);
202         ::close(f_out);
203         ::close(c_in);
204         ::close(c_out);
205         ::close(err_fd);
206                 
207         execv(SSHPROG, args);
208         WARN("execv failed!");
209         exit(1);
210     }
211
212     ::close(c_in);
213     ::close(c_out);
214
215     int version = htonl(SSH2_FILEXFER_VERSION);
216
217     if(send_packet(SSH2_FXP_INIT, &version, 4) < 0){
218         WARN("failed to init!");
219         disconnect();
220         return -1;
221     }
222
223     if(recv_packet(&hdr, NULL, 0) < 0){
224         WARN("failed to read version!");
225         disconnect();
226         return -1;
227     }
228     
229     if(hdr.type != SSH2_FXP_VERSION){
230         WARN("unknown response!");
231         disconnect();
232         return -1;
233     }
234
235     ntoh(this->buf, 4, 0);
236     TRACE("server protocol V" << *((long*)this->buf));
237
238     connected = 1;
239     username = string(user);
240     this->host = string(host);
241     this->port = port;
242     return 0;
243 }
244
245 void
246 SConnection::disconnect(){
247     ::close(f_in);
248     ::close(f_out);
249     kill(sshpid, SIGTERM);
250     connected = 0;
251 }
252
253 int 
254 SConnection::reconnect(){
255     disconnect();
256     return connect((char*)host.c_str(), (char*)username.c_str(), port);
257 }
258
259 void
260 SConnection::show_error(int xlate){
261     if(xlate)
262         ntoh(buf, 4, 4, 0);
263
264     string err = string(&buf[12], ntohl(*((uint32_t*)&buf[8])));
265     TRACE("SERVER FAILURE: "<<err);
266 }
267
268 int
269 SConnection::check_reply(int res, int expected){
270     if(res == expected)
271         if(ntohl(*(uint32_t*)buf) != seq - 1){
272             WARN("wrong sequence (" << *(uint32_t*)buf << ")!");
273             return -1;
274         }
275         else
276             return 0;
277     
278     if(res < 0){
279         WARN("execute failed!");
280         return res;
281     }
282     
283     if(res == SSH2_FXP_STATUS)
284         show_error(1);
285     else
286         WARN("unexpected reply (" << res << ")!");
287
288     return -1;
289 }
290
291 int
292 SConnection::check_status(int res, int status){
293     if(res < 0){
294         WARN("execute failed!");
295         return res;
296     }
297
298     if(res != SSH2_FXP_STATUS){
299         WARN("unexpected reply (" << res << ")!");
300         return -1;
301     }
302
303     if(ntohl(*(uint32_t*)&buf[4]) != (unsigned)status){
304         show_error(1);
305         return -1;
306     }
307
308     return 0;
309 }
310
311 int
312 SConnection::send_packet(unsigned type, void *buf, unsigned len){
313     int res;
314     struct s_hdr hdr;
315
316     TRACE("sending packet...");
317
318     hdr.type = type;
319     hdr.len = htonl(len + 1);
320
321     if((res = lu_atomic_write(f_out, (char*)&hdr, HDRSIZE, 0)) < 0)
322         return res;
323     
324     return lu_atomic_write(f_out, (char*)buf, len, 0);
325 }
326
327 int
328 SConnection::send_packet(unsigned type, struct iovec *iov, int count){
329     int res, len = 0;
330     struct s_hdr hdr;
331
332     for(int i = 0; i < count; i++)
333         len += iov[i].iov_len;
334
335     hdr.type = type;
336     hdr.len = htonl(len + 1);
337
338     TRACE("sending packet...");
339     if((res = lu_atomic_write(f_out, (char*)&hdr, HDRSIZE, 0)) < 0)
340         return res;
341
342     for(int i = 0; i < count; i++)
343         if((res = lu_atomic_write(f_out, (char*)iov[i].iov_base, iov[i].iov_len, 0)) < 0)
344             return res;
345
346     return 0;
347 }
348
349 int
350 SConnection::recv_packet(struct s_hdr *hdr, void *buf, unsigned max){
351     int res;
352     
353     TRACE("receiving packet...");
354     if((res = lu_atomic_read(f_in, (char*)hdr, HDRSIZE, 0)) < 0)
355         return res;
356
357     if(!buf){
358         buf = &this->buf;
359         max = MAXDATA;
360     }
361
362     ntoh(hdr, 4, 0);
363     hdr->len--;
364
365     if(hdr->len >= max){
366         WARN("packet too big!");
367         return -1;
368     } 
369
370     ((char*)buf)[hdr->len] = 0;
371
372     return lu_atomic_read(f_in, (char*)buf, hdr->len, 0);    
373 }
374
375 int
376 SConnection::execute(unsigned type, void *buf, unsigned len, struct s_hdr *hdr){
377
378     if((send_packet(type, buf, len) < 0) || (recv_packet(hdr, NULL, 0) < 0)){
379         TRACE("oops!");
380         disconnect();
381         pthread_exit(NULL);
382     }
383
384     last_cmd = type;
385
386     return hdr->type;
387 }
388
389 int
390 SConnection::execute(unsigned type, struct iovec *iov, int count, struct s_hdr *hdr){
391
392     if((send_packet(type, iov, count) < 0) || (recv_packet(hdr, NULL, 0) < 0)){
393         TRACE("oops!");
394         disconnect();
395         pthread_exit(NULL);
396     }
397     
398     last_cmd = type;
399
400     return hdr->type;
401 }
402
403 string
404 SConnection::opendir(char *dir){
405     struct iovec iov[3];
406     struct s_hdr hdr;
407     uint32_t id, slen;
408     int res;
409     string fail("");
410
411     TRACE("ssh_opendir: " << dir);
412
413     id = htonl(seq++);
414     slen = htonl(strlen(dir));
415
416     iov[0].iov_base = &id;
417     iov[0].iov_len = 4;
418     iov[1].iov_base = &slen;
419     iov[1].iov_len = 4;
420     iov[2].iov_base = dir;
421     iov[2].iov_len = ntohl(slen);;
422
423     if((res = execute(SSH2_FXP_OPENDIR, iov, 3, &hdr)) < 0){
424         WARN("execute failed!");
425         return fail;
426     }
427
428     if(res != SSH2_FXP_HANDLE){
429         WARN("unexpected response (" << res << ")!");
430         if(res == SSH2_FXP_STATUS)
431             show_error(1);
432         return fail;
433     }
434
435     ntoh(buf, 4, 4, 0);
436     id = *((uint32_t*)buf);
437     slen = *((uint32_t*)&buf[4]);
438
439     TRACE("id=" << id << ", slen=" << slen);
440
441     if((id != seq - 1) || (slen > MAXDATA - 9)){
442         WARN("wrong params!");
443         return fail;
444     }
445
446     return string(&buf[8], slen);
447 }
448
449 int
450 SConnection::close(string &handle){
451     struct iovec iov[3];
452     struct s_hdr hdr;
453     uint32_t id, slen;
454     int res;
455
456     id = htonl(seq++);
457     slen = htonl(handle.size());
458
459     iov[0].iov_base = &id;
460     iov[0].iov_len = 4;
461     iov[1].iov_base = &slen;
462     iov[1].iov_len = 4;
463     iov[2].iov_base = (void*)handle.data();
464     iov[2].iov_len = handle.size();
465
466     if((res = execute(SSH2_FXP_CLOSE, iov, 3, &hdr)) < 0){
467         WARN("execute failed!");
468         return res;
469     }
470
471     if(res != SSH2_FXP_STATUS){
472         WARN("unexpected response!");
473         return -1;
474     }
475
476     ntoh(buf, 4, 4, 0);
477     id = *((uint32_t*)buf);
478     slen = *((uint32_t*)&buf[4]);
479
480     if((id != seq -1) || (slen != SSH2_FX_OK)){
481         WARN("wrong params!");
482         return -1;
483     }
484
485     return 0;
486 }
487
488 int
489 SConnection::readdir(string &handle){
490     struct iovec iov[3];
491     struct s_hdr hdr;
492     uint32_t id, slen;
493     int res; 
494
495     id = htonl(seq++);
496     slen = htonl(handle.size());
497
498     iov[0].iov_base = &id;
499     iov[0].iov_len = 4;
500     iov[1].iov_base = &slen;
501     iov[1].iov_len = 4;
502     iov[2].iov_base = (void*)handle.data();
503     iov[2].iov_len = handle.size();
504
505     res = execute(SSH2_FXP_READDIR, iov, 3, &hdr);
506
507     if(ntohl(*((uint32_t*)buf)) != seq - 1){
508         WARN("out of sequence!");
509         return -1;
510     }
511
512     return res;
513 }
514
515 int
516 SConnection::readlink(char *link){
517     struct iovec iov[3];
518     struct s_hdr hdr;
519     uint32_t id, len;
520     int res;
521
522     id = htonl(seq++);
523     len = htonl(strlen(link));
524
525     iov[0].iov_base = &id;
526     iov[0].iov_len = 4;
527     iov[1].iov_base = &len;
528     iov[1].iov_len = 4;
529     iov[2].iov_base = link;
530     iov[2].iov_len = ntohl(len);
531
532     res = execute(SSH2_FXP_READLINK, iov, 3, &hdr);
533
534     if(ntohl(*((uint32_t*)buf)) != seq - 1){
535         WARN("out of sequence!");
536         return -1;
537     }
538
539     return res;    
540 }
541
542 char*
543 SConnection::attr2fattr(char *ptr, struct lufs_fattr *fattr){
544     uint32_t flags = ntohl(*(uint32_t*)ptr);
545
546     ptr += 4;
547
548     if(flags & SSH2_FILEXFER_ATTR_SIZE){
549         fattr->f_size = ntohl(*(uint32_t*)(ptr+4));
550         TRACE("size: " << fattr->f_size);
551         ptr += 8;
552     }
553
554     if(flags & SSH2_FILEXFER_ATTR_UIDGID){
555         ntoh(ptr, 4, 4, 0);
556         fattr->f_uid = *(uint32_t*)ptr;
557         fattr->f_gid = *(uint32_t*)(ptr+4);
558         TRACE("uid: " << fattr->f_uid << ", gid: " << fattr->f_gid);
559         ptr += 8;
560     }
561
562     if(flags & SSH2_FILEXFER_ATTR_PERMISSIONS){
563         fattr->f_mode = ntohl(*(uint32_t*)ptr);
564         TRACE("mode: " << std::oct<<fattr->f_mode<<std::hex);
565         ptr += 4;
566     }
567
568     if(flags & SSH2_FILEXFER_ATTR_ACMODTIME){
569         ntoh(ptr, 4, 4, 0);
570         fattr->f_atime = *(uint32_t*)ptr;
571         fattr->f_ctime = fattr->f_mtime = *(uint32_t*)(ptr+4);
572         TRACE("atime: " << fattr->f_atime << ", mtime: " << fattr->f_mtime);
573         ptr += 8;
574     }
575
576     if(flags & SSH2_FILEXFER_ATTR_EXTENDED){
577         TRACE("extended attributes!!!");
578
579         uint32_t count = *(uint32_t*)ptr;
580         ptr+=4;
581
582         for(; count > 0; count--){
583             string type = string(ptr + 4, ntohl(*(uint32_t*)ptr));
584             ptr += 4 + type.size();
585             string data = string(ptr + 4, ntohl(*(uint32_t*)ptr));
586             ptr += 4 + data.size();
587             
588             TRACE("type: " << type);
589             TRACE("count: " << data);
590         }
591     }
592
593     return ptr;
594 }
595
596 int
597 SConnection::lname2fattr(string &lname, struct lufs_fattr *fattr){
598     unsigned b, e;
599     
600     if((b = lname.find_first_not_of(" ")) == string::npos)
601         return -1;
602     if((b = lname.find(" ", b)) == string::npos)
603         return -1;
604     if((b = lname.find_first_not_of(" ", b)) == string::npos)
605         return -1;
606     if((e = lname.find(" ", b)) == string::npos)
607         return -1;
608
609     string nlink = lname.substr(b, e - b);
610     TRACE("nlink: " << nlink);
611     fattr->f_nlink = atoi(nlink.c_str());
612
613     return 0;
614 }
615
616 int
617 SConnection::create(char *file, unsigned mode){
618     struct iovec iov[6];
619     struct s_hdr hdr;
620     uint32_t id, slen, pflags, attr, perms;
621     int res;
622
623     TRACE("ssh_create: " << file << ", mode: " <<std::oct<< mode<<std::hex);
624
625     id = htonl(seq++);
626     slen = htonl(strlen(file));
627     pflags = htonl(SSH2_FXF_READ | SSH2_FXF_WRITE | SSH2_FXF_CREAT);
628     attr = htonl(SSH2_FILEXFER_ATTR_PERMISSIONS);
629     perms = htonl(mode);
630
631     iov[0].iov_base = &id;
632     iov[0].iov_len = 4;
633     iov[1].iov_base = &slen;
634     iov[1].iov_len = 4;
635     iov[2].iov_base = file;
636     iov[2].iov_len = ntohl(slen);
637     iov[3].iov_base = &pflags;
638     iov[3].iov_len = 4;
639     iov[4].iov_base = &attr;
640     iov[4].iov_len = 4;
641     iov[5].iov_base = &perms;
642     iov[5].iov_len = 4;
643
644     res = execute(SSH2_FXP_OPEN, iov, 6, &hdr);
645     if((res = check_reply(res, SSH2_FXP_HANDLE)) < 0)
646         return res;
647     string ss = string(&buf[8], ntohl(*(uint32_t*)&buf[4]));
648
649     return close(ss);
650 }
651
652 string
653 SConnection::open(char *file, unsigned mode){
654     string fail("");
655     struct iovec iov[5];
656     struct s_hdr hdr;
657     uint32_t id, len, pflags, attr;
658     int res;
659
660     TRACE("ssh_open: " << file << ", mode: " << mode);
661
662     id = htonl(seq++);
663     len = htonl(strlen(file));
664
665     switch(mode & O_ACCMODE){
666     case O_RDONLY:
667         TRACE("***READ");
668         pflags = SSH2_FXF_READ;
669         break;
670     case O_WRONLY:
671         TRACE("***WRITE");
672         pflags = SSH2_FXF_WRITE;
673         break;
674     case O_RDWR:
675         TRACE("***RDWR");
676         pflags = SSH2_FXF_READ | SSH2_FXF_WRITE;
677         break;
678     }
679
680     if(mode & O_CREAT){
681         TRACE("***creating");
682         pflags |= SSH2_FXF_CREAT;
683     }
684     if(mode & O_EXCL){
685         TRACE("***exclusive");
686         pflags |= SSH2_FXF_EXCL;
687     }
688     if(mode & O_APPEND){
689         TRACE("***appending");
690         pflags |= SSH2_FXF_APPEND;
691     }
692     if(mode & O_TRUNC){
693         TRACE("***truncating");
694         pflags |= SSH2_FXF_TRUNC;
695     }
696
697     pflags = htonl(pflags);
698     attr = 0;
699
700     iov[0].iov_base = &id;
701     iov[0].iov_len = 4;
702     iov[1].iov_base = &len;
703     iov[1].iov_len = 4;
704     iov[2].iov_base = file;
705     iov[2].iov_len = ntohl(len);
706     iov[3].iov_base = &pflags;
707     iov[3].iov_len = 4;
708     iov[4].iov_base = &attr;
709     iov[4].iov_len = 4;
710
711     res = execute(SSH2_FXP_OPEN, iov, 5, &hdr);
712
713     if(check_reply(res, SSH2_FXP_HANDLE) < 0)
714         return fail;
715
716     return string(&buf[8], ntohl(*(uint32_t*)&buf[4]));
717 }
718
719 int
720 SConnection::read(string &handle, long long offset, unsigned long count, char *b){
721     struct iovec iov[5];
722     struct s_hdr hdr;
723     int res;
724     uint32_t id, slen, len;
725     uint64_t off;
726
727     TRACE("ssh_read: " << offset << ", " << count);
728
729     if((last_cmd == SSH2_FXP_READ) && (readcache.handle == handle) && (offset > readcache.offset) && (offset + count <= readcache.offset + readcache.count)){
730         TRACE("data in cache");
731         memcpy(b, buf + 8 + (offset - readcache.offset), count);
732         return count;
733     }else{
734         TRACE("data not in cache, reading...");
735
736         id = htonl(seq++);
737         slen = htonl(handle.size());
738         len = htonl(MAXDATA - 20);
739         off = (uint64_t)offset;
740         hton(&off, 8, 0);
741
742         iov[0].iov_base = &id;
743         iov[0].iov_len = 4;
744         iov[1].iov_base = &slen;
745         iov[1].iov_len = 4;
746         iov[2].iov_base = (void*)handle.data();
747         iov[2].iov_len = ntohl(slen);
748         iov[3].iov_base = &off;
749         iov[3].iov_len = 8;
750         iov[4].iov_base = &len;
751         iov[4].iov_len = 4;
752
753         res = execute(SSH2_FXP_READ, iov, 5, &hdr);
754         
755         if(check_reply(res, SSH2_FXP_DATA) < 0)
756             return -1;
757
758         readcache.handle = handle;
759         readcache.offset = offset;
760         readcache.count = ntohl(*(uint32_t*)(buf + 4));
761
762         TRACE(readcache.count << " bytes read in cache");
763
764         res = (readcache.count > count) ? count : readcache.count;
765
766         memcpy(b, buf + 8, res);
767
768         return res;
769     }
770 }
771
772 int
773 SConnection::mkdir(char *dir, int mode){
774     struct iovec iov[5];
775     struct s_hdr hdr;
776     uint32_t id, slen, flags, perms;
777     int res;
778
779     TRACE("ssh_mkdir " << dir << ", " << mode);
780
781     id = htonl(seq++);
782     slen = htonl(strlen(dir));
783     flags = htonl(SSH2_FILEXFER_ATTR_PERMISSIONS);
784     perms = htonl(mode);
785
786     iov[0].iov_base = &id;
787     iov[0].iov_len = 4;
788     iov[1].iov_base = &slen;
789     iov[1].iov_len = 4;
790     iov[2].iov_base = dir;
791     iov[2].iov_len = ntohl(slen);
792     iov[3].iov_base = &flags;
793     iov[3].iov_len = 4;
794     iov[4].iov_base = &perms;
795     iov[4].iov_len = 4;
796
797     res = execute(SSH2_FXP_MKDIR, iov, 5, &hdr);
798     if((res = check_status(res, SSH2_FX_OK)) < 0)
799         return res;
800
801     return 0;
802 }
803
804 int
805 SConnection::rmdir(char *dir){
806     struct iovec iov[3];
807     struct s_hdr hdr;
808     uint32_t id, slen;
809     int res;
810
811     TRACE("ssh_rmdir: " << dir);
812
813     id = htonl(seq++);
814     slen = htonl(strlen(dir));
815
816     iov[0].iov_base = &id;
817     iov[0].iov_len = 4;
818     iov[1].iov_base = &slen;
819     iov[1].iov_len = 4;
820     iov[2].iov_base = dir;
821     iov[2].iov_len = ntohl(slen);
822
823     res = execute(SSH2_FXP_RMDIR, iov, 3, &hdr);
824     if((res = check_status(res, SSH2_FX_OK)) < 0)
825         return res;
826
827     return 0;
828 }
829
830 int
831 SConnection::remove(char *file){
832     struct iovec iov[3];
833     struct s_hdr hdr;
834     int res;
835     uint32_t id, slen;
836
837     TRACE("ssh_remove: " << file);
838
839     id = htonl(seq++);
840     slen = htonl(strlen(file));
841
842     iov[0].iov_base = &id;
843     iov[0].iov_len = 4;
844     iov[1].iov_base = &slen;
845     iov[1].iov_len = 4;
846     iov[2].iov_base = file;
847     iov[2].iov_len = ntohl(slen);
848
849     res = execute(SSH2_FXP_REMOVE, iov, 3, &hdr);
850     if((res = check_status(res, SSH2_FX_OK)) < 0)
851         return res;
852     
853     return 0;
854 }
855
856 int
857 SConnection::rename(char *old, char *nnew){
858     struct iovec iov[5];
859     struct s_hdr hdr;
860     int res;
861     uint32_t id, slen1, slen2;
862
863     TRACE("ssh_rename: " << old << " to " << nnew);
864
865     /* must delete existing entity first, otherwise will fail... */
866     remove(nnew);
867     rmdir(nnew);
868
869     id = htonl(seq++);
870     slen1 = htonl(strlen(old));
871     slen2 = htonl(strlen(nnew));
872     
873     iov[0].iov_base = &id;
874     iov[0].iov_len = 4;
875     iov[1].iov_base = &slen1;
876     iov[1].iov_len = 4;
877     iov[2].iov_base = old;
878     iov[2].iov_len = ntohl(slen1);
879     iov[3].iov_base = &slen2;
880     iov[3].iov_len = 4;
881     iov[4].iov_base = nnew;
882     iov[4].iov_len = ntohl(slen2);
883
884     if((res = check_status(execute(SSH2_FXP_RENAME, iov, 5, &hdr), SSH2_FX_OK)) < 0)
885         return res;
886
887     return 0;
888 }
889
890 int
891 SConnection::setattr(char *file, struct lufs_fattr *fattr){
892     struct iovec iov[4];
893     struct s_hdr hdr;
894     uint32_t id, slen;
895     uint32_t pack[4];
896     int res;
897
898     TRACE("ssh_setattr: " << file);
899     TRACE("mode: "<<std::oct<<fattr->f_mode<<std::hex);
900
901     slen = htonl(strlen(file));
902     
903     iov[0].iov_base = &id;
904     iov[0].iov_len = 4;
905     iov[1].iov_base = &slen;
906     iov[1].iov_len = 4;
907     iov[2].iov_base = file;
908     iov[2].iov_len = ntohl(slen);
909     iov[3].iov_base = pack;
910
911     if(S_ISLNK(fattr->f_mode)){
912         TRACE("it's a link, skip it...");
913         return 0;
914     }
915
916     if(!S_ISDIR(fattr->f_mode)){
917         id = htonl(seq++);
918         pack[0] = SSH2_FILEXFER_ATTR_SIZE;
919         *(uint64_t*)&pack[1] = fattr->f_size;
920         hton(pack, 4, 8, 0);
921         iov[3].iov_len = 12;
922
923         TRACE("setting size...");
924         if((res = check_status(execute(SSH2_FXP_SETSTAT, iov, 4, &hdr), SSH2_FX_OK)) < 0){
925             WARN("couldn't set size");
926             return res;
927         }
928     }
929     
930     id = htonl(seq++);
931     pack[0] = SSH2_FILEXFER_ATTR_ACMODTIME | SSH2_FILEXFER_ATTR_PERMISSIONS;
932     pack[1] = fattr->f_mode;
933     pack[2] = fattr->f_atime;
934     pack[3] = fattr->f_mtime;
935     hton(pack, 4, 4, 4, 4, 0);
936     iov[3].iov_len = 16;
937
938     TRACE("setting atime & mtime...");
939     if((res = check_status(execute(SSH2_FXP_SETSTAT, iov, 4, &hdr), SSH2_FX_OK)) < 0){
940         WARN("couldn't set times");
941         return res;
942     }
943
944     return 0;
945 }
946
947 int
948 SConnection::write(string &handle, long long offset, unsigned long count, char *b){
949     struct iovec iov[6];
950     struct s_hdr hdr;
951     int res;
952     uint32_t id, slen1, slen2;
953     uint64_t off;
954
955     TRACE("ssh_write: " << offset << ", " << count);
956
957     id = htonl(seq++);
958     slen1 = htonl(handle.size());
959     slen2 = htonl(count);
960     off = (uint64_t)offset;
961     hton(&off, 8, 0);
962
963     iov[0].iov_base = &id;
964     iov[0].iov_len = 4;
965     iov[1].iov_base = &slen1;
966     iov[1].iov_len = 4;
967     iov[2].iov_base = (void*)handle.data();
968     iov[2].iov_len = ntohl(slen1);
969     iov[3].iov_base = &off;
970     iov[3].iov_len = 8;
971     iov[4].iov_base = &slen2;
972     iov[4].iov_len = 4;
973     iov[5].iov_base = b;
974     iov[5].iov_len = ntohl(slen2);
975
976     if((res = check_status(execute(SSH2_FXP_WRITE, iov, 6, &hdr), SSH2_FX_OK)) < 0)
977         return res;
978
979     return 0;
980 }
981
982 int
983 SConnection::symlink(char *file, char *link){
984     struct iovec iov[5];
985     struct s_hdr hdr;
986     int res;
987     uint32_t id, slen1, slen2;
988
989     TRACE("ssh_symlink: " << file << " <=> " << link);
990     
991     id = htonl(seq++);
992     slen1 = htonl(strlen(file));
993     slen2 = htonl(strlen(link));
994     
995     iov[0].iov_base = &id;
996     iov[0].iov_len = 4;
997     iov[1].iov_base = &slen1;
998     iov[1].iov_len = 4;
999     iov[2].iov_base = file;
1000     iov[2].iov_len = ntohl(slen1);
1001     iov[3].iov_base = &slen2;
1002     iov[3].iov_len = 4;
1003     iov[4].iov_base = link;
1004     iov[4].iov_len = ntohl(slen2);
1005
1006     if((res = check_status(execute(SSH2_FXP_SYMLINK, iov, 5, &hdr), SSH2_FX_OK)) < 0)
1007         return res;
1008
1009     return 0;
1010 }
1011
1012 int
1013 SConnection::stat(char *file, struct lufs_fattr *fattr){
1014     struct iovec iov[3];
1015     struct s_hdr hdr;
1016     int res;
1017     uint32_t id, slen;
1018
1019     TRACE("ssh_stat: " << file);
1020
1021     id = htonl(seq++);
1022     slen = htonl(strlen(file));
1023
1024     iov[0].iov_base = &id;
1025     iov[0].iov_len = 4;
1026     iov[1].iov_base = &slen;
1027     iov[1].iov_len = 4;
1028     iov[2].iov_base = file;
1029     iov[2].iov_len = ntohl(slen);
1030
1031     if((res = check_reply(execute(SSH2_FXP_LSTAT, iov, 3, &hdr), SSH2_FXP_ATTRS)) < 0)
1032         return res;
1033
1034     attr2fattr(buf + 4, fattr);
1035     fattr->f_nlink = 1;
1036     
1037     return 0;
1038 }
1039