3 * Copyright (C) 2002 Florin Malita <mali@go.ro>
5 * This file is part of LUFS, a free userspace filesystem implementation.
6 * See http://lufs.sourceforge.net/ for updates.
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.
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.
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
30 #include <sys/types.h>
33 #include <netinet/in.h>
37 #include <lufs/proto.h>
43 #error ssh not found!!!
51 "-oClearAllForwardings yes",
70 for(p = (char*)buf; (i = va_arg(ap, int)); p += i){
76 *((short*)p) = htons(*((short*)p));
77 TRACE("fixing short");
81 *((long*)p) = htonl(*((long*)p));
87 *((long*)p) = htonl(*(((long*)p) + 1));
88 *(((long*)p) + 1) = htonl(l);
93 WARN("invalid size " << i);
102 ntoh(void *buf, ...){
110 for(p = (char*)buf; (i = va_arg(ap, int)); p += i){
117 *((short*)p) = ntohs(*((short*)p));
118 TRACE("fixing short");
122 *((long*)p) = ntohl(*((long*)p));
123 TRACE("fixing long");
128 *((long*)p) = ntohl(*(((long*)p) + 1));
129 *(((long*)p) + 1) = ntohl(l);
130 TRACE("fixing quad");
134 WARN("invalid size " << i);
143 SConnection::SConnection(){
144 TRACE("in constructor");
150 SConnection::~SConnection(){
151 TRACE("in destructor");
157 SConnection::connect(char *host, char *user, int port){
164 TRACE("attempting to connect to " << host << ":" << port << " as " << user);
165 string usr = string("-l") + user;
166 sprintf(portstr, "-p%d", port);
169 args[7] = (char*)usr.c_str();
172 for(int i = 0; args[i]; i++)
173 TRACE("args[" << i << "]=" << args[i]);
175 if((pipe(pin) == -1) || (pipe(pout) == -1)){
185 if((sshpid = fork()) == -1){
186 WARN("fork failed!");
188 }else if(sshpid == 0){
190 if((err_fd = ::open("/dev/null", O_WRONLY)) < 0){
191 WARN("could not open /dev/null!");
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!");
207 execv(SSHPROG, args);
208 WARN("execv failed!");
215 int version = htonl(SSH2_FILEXFER_VERSION);
217 if(send_packet(SSH2_FXP_INIT, &version, 4) < 0){
218 WARN("failed to init!");
223 if(recv_packet(&hdr, NULL, 0) < 0){
224 WARN("failed to read version!");
229 if(hdr.type != SSH2_FXP_VERSION){
230 WARN("unknown response!");
235 ntoh(this->buf, 4, 0);
236 TRACE("server protocol V" << *((long*)this->buf));
239 username = string(user);
240 this->host = string(host);
246 SConnection::disconnect(){
249 kill(sshpid, SIGTERM);
254 SConnection::reconnect(){
256 return connect((char*)host.c_str(), (char*)username.c_str(), port);
260 SConnection::show_error(int xlate){
264 string err = string(&buf[12], ntohl(*((uint32_t*)&buf[8])));
265 TRACE("SERVER FAILURE: "<<err);
269 SConnection::check_reply(int res, int expected){
271 if(ntohl(*(uint32_t*)buf) != seq - 1){
272 WARN("wrong sequence (" << *(uint32_t*)buf << ")!");
279 WARN("execute failed!");
283 if(res == SSH2_FXP_STATUS)
286 WARN("unexpected reply (" << res << ")!");
292 SConnection::check_status(int res, int status){
294 WARN("execute failed!");
298 if(res != SSH2_FXP_STATUS){
299 WARN("unexpected reply (" << res << ")!");
303 if(ntohl(*(uint32_t*)&buf[4]) != (unsigned)status){
312 SConnection::send_packet(unsigned type, void *buf, unsigned len){
316 TRACE("sending packet...");
319 hdr.len = htonl(len + 1);
321 if((res = lu_atomic_write(f_out, (char*)&hdr, HDRSIZE, 0)) < 0)
324 return lu_atomic_write(f_out, (char*)buf, len, 0);
328 SConnection::send_packet(unsigned type, struct iovec *iov, int count){
332 for(int i = 0; i < count; i++)
333 len += iov[i].iov_len;
336 hdr.len = htonl(len + 1);
338 TRACE("sending packet...");
339 if((res = lu_atomic_write(f_out, (char*)&hdr, HDRSIZE, 0)) < 0)
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)
350 SConnection::recv_packet(struct s_hdr *hdr, void *buf, unsigned max){
353 TRACE("receiving packet...");
354 if((res = lu_atomic_read(f_in, (char*)hdr, HDRSIZE, 0)) < 0)
366 WARN("packet too big!");
370 ((char*)buf)[hdr->len] = 0;
372 return lu_atomic_read(f_in, (char*)buf, hdr->len, 0);
376 SConnection::execute(unsigned type, void *buf, unsigned len, struct s_hdr *hdr){
378 if((send_packet(type, buf, len) < 0) || (recv_packet(hdr, NULL, 0) < 0)){
390 SConnection::execute(unsigned type, struct iovec *iov, int count, struct s_hdr *hdr){
392 if((send_packet(type, iov, count) < 0) || (recv_packet(hdr, NULL, 0) < 0)){
404 SConnection::opendir(char *dir){
411 TRACE("ssh_opendir: " << dir);
414 slen = htonl(strlen(dir));
416 iov[0].iov_base = &id;
418 iov[1].iov_base = &slen;
420 iov[2].iov_base = dir;
421 iov[2].iov_len = ntohl(slen);;
423 if((res = execute(SSH2_FXP_OPENDIR, iov, 3, &hdr)) < 0){
424 WARN("execute failed!");
428 if(res != SSH2_FXP_HANDLE){
429 WARN("unexpected response (" << res << ")!");
430 if(res == SSH2_FXP_STATUS)
436 id = *((uint32_t*)buf);
437 slen = *((uint32_t*)&buf[4]);
439 TRACE("id=" << id << ", slen=" << slen);
441 if((id != seq - 1) || (slen > MAXDATA - 9)){
442 WARN("wrong params!");
446 return string(&buf[8], slen);
450 SConnection::close(string &handle){
457 slen = htonl(handle.size());
459 iov[0].iov_base = &id;
461 iov[1].iov_base = &slen;
463 iov[2].iov_base = (void*)handle.data();
464 iov[2].iov_len = handle.size();
466 if((res = execute(SSH2_FXP_CLOSE, iov, 3, &hdr)) < 0){
467 WARN("execute failed!");
471 if(res != SSH2_FXP_STATUS){
472 WARN("unexpected response!");
477 id = *((uint32_t*)buf);
478 slen = *((uint32_t*)&buf[4]);
480 if((id != seq -1) || (slen != SSH2_FX_OK)){
481 WARN("wrong params!");
489 SConnection::readdir(string &handle){
496 slen = htonl(handle.size());
498 iov[0].iov_base = &id;
500 iov[1].iov_base = &slen;
502 iov[2].iov_base = (void*)handle.data();
503 iov[2].iov_len = handle.size();
505 res = execute(SSH2_FXP_READDIR, iov, 3, &hdr);
507 if(ntohl(*((uint32_t*)buf)) != seq - 1){
508 WARN("out of sequence!");
516 SConnection::readlink(char *link){
523 len = htonl(strlen(link));
525 iov[0].iov_base = &id;
527 iov[1].iov_base = &len;
529 iov[2].iov_base = link;
530 iov[2].iov_len = ntohl(len);
532 res = execute(SSH2_FXP_READLINK, iov, 3, &hdr);
534 if(ntohl(*((uint32_t*)buf)) != seq - 1){
535 WARN("out of sequence!");
543 SConnection::attr2fattr(char *ptr, struct lufs_fattr *fattr){
544 uint32_t flags = ntohl(*(uint32_t*)ptr);
548 if(flags & SSH2_FILEXFER_ATTR_SIZE){
549 fattr->f_size = ntohl(*(uint32_t*)(ptr+4));
550 TRACE("size: " << fattr->f_size);
554 if(flags & SSH2_FILEXFER_ATTR_UIDGID){
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);
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);
568 if(flags & SSH2_FILEXFER_ATTR_ACMODTIME){
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);
576 if(flags & SSH2_FILEXFER_ATTR_EXTENDED){
577 TRACE("extended attributes!!!");
579 uint32_t count = *(uint32_t*)ptr;
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();
588 TRACE("type: " << type);
589 TRACE("count: " << data);
597 SConnection::lname2fattr(string &lname, struct lufs_fattr *fattr){
600 if((b = lname.find_first_not_of(" ")) == string::npos)
602 if((b = lname.find(" ", b)) == string::npos)
604 if((b = lname.find_first_not_of(" ", b)) == string::npos)
606 if((e = lname.find(" ", b)) == string::npos)
609 string nlink = lname.substr(b, e - b);
610 TRACE("nlink: " << nlink);
611 fattr->f_nlink = atoi(nlink.c_str());
617 SConnection::create(char *file, unsigned mode){
620 uint32_t id, slen, pflags, attr, perms;
623 TRACE("ssh_create: " << file << ", mode: " <<std::oct<< mode<<std::hex);
626 slen = htonl(strlen(file));
627 pflags = htonl(SSH2_FXF_READ | SSH2_FXF_WRITE | SSH2_FXF_CREAT);
628 attr = htonl(SSH2_FILEXFER_ATTR_PERMISSIONS);
631 iov[0].iov_base = &id;
633 iov[1].iov_base = &slen;
635 iov[2].iov_base = file;
636 iov[2].iov_len = ntohl(slen);
637 iov[3].iov_base = &pflags;
639 iov[4].iov_base = &attr;
641 iov[5].iov_base = &perms;
644 res = execute(SSH2_FXP_OPEN, iov, 6, &hdr);
645 if((res = check_reply(res, SSH2_FXP_HANDLE)) < 0)
647 string ss = string(&buf[8], ntohl(*(uint32_t*)&buf[4]));
653 SConnection::open(char *file, unsigned mode){
657 uint32_t id, len, pflags, attr;
660 TRACE("ssh_open: " << file << ", mode: " << mode);
663 len = htonl(strlen(file));
665 switch(mode & O_ACCMODE){
668 pflags = SSH2_FXF_READ;
672 pflags = SSH2_FXF_WRITE;
676 pflags = SSH2_FXF_READ | SSH2_FXF_WRITE;
681 TRACE("***creating");
682 pflags |= SSH2_FXF_CREAT;
685 TRACE("***exclusive");
686 pflags |= SSH2_FXF_EXCL;
689 TRACE("***appending");
690 pflags |= SSH2_FXF_APPEND;
693 TRACE("***truncating");
694 pflags |= SSH2_FXF_TRUNC;
697 pflags = htonl(pflags);
700 iov[0].iov_base = &id;
702 iov[1].iov_base = &len;
704 iov[2].iov_base = file;
705 iov[2].iov_len = ntohl(len);
706 iov[3].iov_base = &pflags;
708 iov[4].iov_base = &attr;
711 res = execute(SSH2_FXP_OPEN, iov, 5, &hdr);
713 if(check_reply(res, SSH2_FXP_HANDLE) < 0)
716 return string(&buf[8], ntohl(*(uint32_t*)&buf[4]));
720 SConnection::read(string &handle, long long offset, unsigned long count, char *b){
724 uint32_t id, slen, len;
727 TRACE("ssh_read: " << offset << ", " << count);
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);
734 TRACE("data not in cache, reading...");
737 slen = htonl(handle.size());
738 len = htonl(MAXDATA - 20);
739 off = (uint64_t)offset;
742 iov[0].iov_base = &id;
744 iov[1].iov_base = &slen;
746 iov[2].iov_base = (void*)handle.data();
747 iov[2].iov_len = ntohl(slen);
748 iov[3].iov_base = &off;
750 iov[4].iov_base = &len;
753 res = execute(SSH2_FXP_READ, iov, 5, &hdr);
755 if(check_reply(res, SSH2_FXP_DATA) < 0)
758 readcache.handle = handle;
759 readcache.offset = offset;
760 readcache.count = ntohl(*(uint32_t*)(buf + 4));
762 TRACE(readcache.count << " bytes read in cache");
764 res = (readcache.count > count) ? count : readcache.count;
766 memcpy(b, buf + 8, res);
773 SConnection::mkdir(char *dir, int mode){
776 uint32_t id, slen, flags, perms;
779 TRACE("ssh_mkdir " << dir << ", " << mode);
782 slen = htonl(strlen(dir));
783 flags = htonl(SSH2_FILEXFER_ATTR_PERMISSIONS);
786 iov[0].iov_base = &id;
788 iov[1].iov_base = &slen;
790 iov[2].iov_base = dir;
791 iov[2].iov_len = ntohl(slen);
792 iov[3].iov_base = &flags;
794 iov[4].iov_base = &perms;
797 res = execute(SSH2_FXP_MKDIR, iov, 5, &hdr);
798 if((res = check_status(res, SSH2_FX_OK)) < 0)
805 SConnection::rmdir(char *dir){
811 TRACE("ssh_rmdir: " << dir);
814 slen = htonl(strlen(dir));
816 iov[0].iov_base = &id;
818 iov[1].iov_base = &slen;
820 iov[2].iov_base = dir;
821 iov[2].iov_len = ntohl(slen);
823 res = execute(SSH2_FXP_RMDIR, iov, 3, &hdr);
824 if((res = check_status(res, SSH2_FX_OK)) < 0)
831 SConnection::remove(char *file){
837 TRACE("ssh_remove: " << file);
840 slen = htonl(strlen(file));
842 iov[0].iov_base = &id;
844 iov[1].iov_base = &slen;
846 iov[2].iov_base = file;
847 iov[2].iov_len = ntohl(slen);
849 res = execute(SSH2_FXP_REMOVE, iov, 3, &hdr);
850 if((res = check_status(res, SSH2_FX_OK)) < 0)
857 SConnection::rename(char *old, char *nnew){
861 uint32_t id, slen1, slen2;
863 TRACE("ssh_rename: " << old << " to " << nnew);
865 /* must delete existing entity first, otherwise will fail... */
870 slen1 = htonl(strlen(old));
871 slen2 = htonl(strlen(nnew));
873 iov[0].iov_base = &id;
875 iov[1].iov_base = &slen1;
877 iov[2].iov_base = old;
878 iov[2].iov_len = ntohl(slen1);
879 iov[3].iov_base = &slen2;
881 iov[4].iov_base = nnew;
882 iov[4].iov_len = ntohl(slen2);
884 if((res = check_status(execute(SSH2_FXP_RENAME, iov, 5, &hdr), SSH2_FX_OK)) < 0)
891 SConnection::setattr(char *file, struct lufs_fattr *fattr){
898 TRACE("ssh_setattr: " << file);
899 TRACE("mode: "<<std::oct<<fattr->f_mode<<std::hex);
901 slen = htonl(strlen(file));
903 iov[0].iov_base = &id;
905 iov[1].iov_base = &slen;
907 iov[2].iov_base = file;
908 iov[2].iov_len = ntohl(slen);
909 iov[3].iov_base = pack;
911 if(S_ISLNK(fattr->f_mode)){
912 TRACE("it's a link, skip it...");
916 if(!S_ISDIR(fattr->f_mode)){
918 pack[0] = SSH2_FILEXFER_ATTR_SIZE;
919 *(uint64_t*)&pack[1] = fattr->f_size;
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");
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);
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");
948 SConnection::write(string &handle, long long offset, unsigned long count, char *b){
952 uint32_t id, slen1, slen2;
955 TRACE("ssh_write: " << offset << ", " << count);
958 slen1 = htonl(handle.size());
959 slen2 = htonl(count);
960 off = (uint64_t)offset;
963 iov[0].iov_base = &id;
965 iov[1].iov_base = &slen1;
967 iov[2].iov_base = (void*)handle.data();
968 iov[2].iov_len = ntohl(slen1);
969 iov[3].iov_base = &off;
971 iov[4].iov_base = &slen2;
974 iov[5].iov_len = ntohl(slen2);
976 if((res = check_status(execute(SSH2_FXP_WRITE, iov, 6, &hdr), SSH2_FX_OK)) < 0)
983 SConnection::symlink(char *file, char *link){
987 uint32_t id, slen1, slen2;
989 TRACE("ssh_symlink: " << file << " <=> " << link);
992 slen1 = htonl(strlen(file));
993 slen2 = htonl(strlen(link));
995 iov[0].iov_base = &id;
997 iov[1].iov_base = &slen1;
999 iov[2].iov_base = file;
1000 iov[2].iov_len = ntohl(slen1);
1001 iov[3].iov_base = &slen2;
1003 iov[4].iov_base = link;
1004 iov[4].iov_len = ntohl(slen2);
1006 if((res = check_status(execute(SSH2_FXP_SYMLINK, iov, 5, &hdr), SSH2_FX_OK)) < 0)
1013 SConnection::stat(char *file, struct lufs_fattr *fattr){
1014 struct iovec iov[3];
1019 TRACE("ssh_stat: " << file);
1022 slen = htonl(strlen(file));
1024 iov[0].iov_base = &id;
1026 iov[1].iov_base = &slen;
1028 iov[2].iov_base = file;
1029 iov[2].iov_len = ntohl(slen);
1031 if((res = check_reply(execute(SSH2_FXP_LSTAT, iov, 3, &hdr), SSH2_FXP_ATTRS)) < 0)
1034 attr2fattr(buf + 4, fattr);