/* * sshfs.cpp * Copyright (C) 2002 Florin Malita * * This file is part of LUFS, a free userspace filesystem implementation. * See http://lufs.sourceforge.net/ for updates. * * LUFS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * LUFS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "sshfs.h" #include "sftplib.h" extern "C"{ void* sshfs_init(struct list_head *cfg, struct dir_cache *cache, struct credentials *cred, void **global_ctx){ if(!lu_opt_getchar(cfg, "MOUNT", "username") || !lu_opt_getchar(cfg, "MOUNT", "host")){ ERROR("you must specify at least a host and an username!"); return NULL; } return (void*)new SSHFS(cfg, cache, cred); } void sshfs_free(void *ctx){ SSHFS *p = (SSHFS*)ctx; delete p; } int sshfs_mount(void *ctx){ return ((SSHFS*)ctx)->do_mount(); } void sshfs_umount(void *ctx){ // return ((SSHFS*)ctx)->do_umount(); } int sshfs_readdir(void *ctx, char *dir_name, struct directory *dir){ return ((SSHFS*)ctx)->do_readdir(dir_name, dir); } int sshfs_stat(void *ctx, char *name, struct lufs_fattr *fattr){ return ((SSHFS*)ctx)->do_stat(name, fattr); } int sshfs_mkdir(void *ctx, char *dir, int mode){ return ((SSHFS*)ctx)->do_mkdir(dir, mode); } int sshfs_rmdir(void *ctx, char *dir){ return ((SSHFS*)ctx)->do_rmdir(dir); } int sshfs_create(void *ctx, char *file, int mode){ return ((SSHFS*)ctx)->do_create(file, mode); } int sshfs_unlink(void *ctx, char *file){ return ((SSHFS*)ctx)->do_unlink(file); } int sshfs_rename(void *ctx, char *old_name, char *new_name){ return ((SSHFS*)ctx)->do_rename(old_name, new_name); } int sshfs_open(void *ctx, char *file, unsigned mode){ return ((SSHFS*)ctx)->do_open(file, mode); } int sshfs_release(void *ctx, char *file){ return ((SSHFS*)ctx)->do_release(file); } int sshfs_read(void *ctx, char *file, long long offset, unsigned long count, char *buf){ return ((SSHFS*)ctx)->do_read(file, offset, count, buf); } int sshfs_write(void *ctx, char *file, long long offset, unsigned long count, char *buf){ return ((SSHFS*)ctx)->do_write(file, offset, count, buf); } int sshfs_readlink(void *ctx, char *link, char *buf, int buflen){ return ((SSHFS*)ctx)->do_readlink(link, buf, buflen); } int sshfs_link(void *ctx, char *target, char *link){ return -1; // return ((SSHFS*)ctx)->do_link(target, link); } int sshfs_symlink(void *ctx, char *target, char *link){ return ((SSHFS*)ctx)->do_symlink(target, link); } int sshfs_setattr(void *ctx, char *file, struct lufs_fattr *fattr){ return ((SSHFS*)ctx)->do_setattr(file, fattr); } } /* extern "C" */ SSHFS::SSHFS(struct list_head *c, struct dir_cache *cache, struct credentials *cred){ TRACE("in constructor"); cfg = c; this->cache = cache; this->cred = cred; conn = new SConnection(); seq = 0; } SSHFS::~SSHFS(){ TRACE("in destructor"); delete conn; } struct atbl* SSHFS::find_handle(char *name, unsigned mode, vector *vec){ TRACE("looking for " << name << ", size=" << vec->size()); for(vector::iterator i = vec->begin(); i != vec->end();){ TRACE("name: " << i->name); if(((time(NULL) - i->stamp) > HANDLES_TTL) || ((mode != 0xffff) && (i->name == name) && ((i->mode & O_ACCMODE) != O_RDWR) && ((i->mode & O_ACCMODE) != (mode & O_ACCMODE)))){ TRACE("invalid handle found..."); conn->close(i->handle); vec->erase(i); /* damn iterators, they break on erase */ i = vec->begin(); }else if(i->name == name){ TRACE("handle found"); i->stamp = time(NULL); return &*i; }else i++; } return NULL; } int SSHFS::do_mount(){ long int port; TRACE("do_mount"); if(lu_opt_getint(cfg, "MOUNT", "port", &port, 10) < 0) port = 22; if(conn->connect((char*)lu_opt_getchar(cfg, "MOUNT", "host"), (char*)lu_opt_getchar(cfg, "MOUNT", "username"), port) < 0) return 0; return 1; } void SSHFS::do_umount(){ TRACE("do_umount"); conn->disconnect(); } int SSHFS::do_readdir(char *dir, struct directory *d){ string handle, fname, lname; int res; char *buf; uint32_t count; struct lufs_fattr fattr; TRACE("do_readdir"); handle = conn->opendir(dir); if(!handle.size()){ ERROR("opendir failed!"); return -1; } do{ res = conn->readdir(handle); buf = conn->buf; if(res == SSH2_FXP_NAME){ ntoh(buf, 4, 4, 0); TRACE("got " << *((uint32_t*)&buf[4]) << " names"); for(count = *((uint32_t*)&buf[4]), buf = conn->buf + 8; count > 0; count--){ memset(&fattr, 0, sizeof(struct lufs_fattr)); fname = string(&buf[4], ntohl(*((uint32_t*)buf))); buf += 4 + fname.size(); lname = string(&buf[4], ntohl(*((uint32_t*)buf))); buf += 4 + lname.size(); buf = conn->attr2fattr(buf, &fattr); if(conn->lname2fattr(lname, &fattr) < 0) ERROR("couldn't parse long name:\n" << lname <<"\nerror:"); fattr.f_uid = ((int)fattr.f_uid == (int)cred->uid) ? 1 : 0; fattr.f_gid = ((int)fattr.f_gid == (int)cred->gid) ? 1 : 0; lu_cache_add2dir(d, (char*)fname.c_str(), NULL, &fattr); TRACE("fname: " << fname); TRACE("laname: " << lname); } } }while(res == SSH2_FXP_NAME); if(res != SSH2_FXP_STATUS){ ERROR("unexpected response (" << res << ")!"); res = -1; goto out; } ntoh(buf, 4, 4, 0); if(*((uint32_t*)&buf[4]) != SSH2_FX_EOF){ conn->show_error(0); res = -1; goto out; } TRACE("done reading dir"); res = 0; out: if(conn->close(handle) < 0) ERROR("close failed!"); return res; } int SSHFS::do_stat(char *file, struct lufs_fattr *fattr){ int res; TRACE("do_stat " << file); if((res = conn->stat(file, fattr)) < 0) return res; fattr->f_uid = ((int)fattr->f_uid == (int)cred->uid) ? 1 : 0; fattr->f_gid = ((int)fattr->f_gid == (int)cred->gid) ? 1 : 0; return res; } int SSHFS::do_readlink(char *link, char *buf, int buflen){ int res; char *b; TRACE("do_readlink:" << link); if((res = conn->readlink(link)) < 0){ ERROR("readlink failed!"); return -1; } if(res == SSH2_FXP_STATUS){ conn->show_error(1); return -1; } if(res != SSH2_FXP_NAME){ ERROR("unexpected response (" << res << ")!"); return -1; } b = conn->buf; ntoh(b, 4, 4, 4, 0); if(*(uint32_t*)&b[4] != 1){ ERROR("multiple names returned!"); return -1; } if(*(uint32_t*)&b[8] >= (unsigned)buflen){ ERROR("filename too long!"); return -1; } strncpy(buf, &b[12], *(uint32_t*)&b[8]); return *(uint32_t*)&b[8]; } int SSHFS::do_open(char *file, unsigned mode){ TRACE("do_open"); if(find_handle(file, mode, &handles)){ TRACE("file already opened."); return 0; } string handle = conn->open(file, mode); if(!handle.size()) return -1; handles.push_back((struct atbl){string(file), handle, time(NULL), mode}); TRACE(handles.size() << " files still opened."); return 0; } int SSHFS::do_release(char *file){ struct atbl *tbl; TRACE("do_release"); if(!(tbl = find_handle(file, 0xffff, &handles))){ ERROR("file not opened!"); return -1; } if(conn->close(tbl->handle) >= 0){ TRACE("file closed."); handles.erase((vector::iterator)tbl); }else return -1; TRACE(handles.size() << " files still opened."); return 0; } int SSHFS::do_read(char *file, long long offset, unsigned long count, char *b){ struct atbl *tbl; TRACE("do_read"); if(!(tbl = find_handle(file, O_RDONLY, &handles))){ TRACE("file not opened for reading..."); if(do_open(file, O_RDONLY) < 0){ ERROR("could not open file for reading!"); return -1; } if(!(tbl = find_handle(file, O_RDONLY, &handles))){ ERROR("file handle still not available?!"); return -1; } } return conn->read(tbl->handle, offset, count, b); } int SSHFS::do_mkdir(char *dir, int mode){ TRACE("do_mkdir"); return conn->mkdir(dir, mode); } int SSHFS::do_rmdir(char *dir){ TRACE("do_rmdir"); return conn->rmdir(dir); } int SSHFS::do_unlink(char *file){ TRACE("do_release"); return conn->remove(file); } int SSHFS::do_create(char *file, int mode){ TRACE("do_create"); return conn->create(file, mode); } int SSHFS::do_rename(char *old, char *nnew){ TRACE("do_rename"); return conn->rename(old, nnew); } int SSHFS::do_setattr(char *file, struct lufs_fattr *fattr){ TRACE("do_setattr"); return conn->setattr(file, fattr); } int SSHFS::do_write(char *file, long long offset, unsigned long count, char *b){ struct atbl *tbl; TRACE("do_write"); if(!(tbl = find_handle(file, O_WRONLY, &handles))){ TRACE("file not opened for writing..."); if(do_open(file, O_WRONLY) < 0){ ERROR("could not open file for writing!"); return -1; } if(!(tbl = find_handle(file, O_WRONLY, &handles))){ ERROR("file handle still not available?!"); return -1; } } return conn->write(tbl->handle, offset, count, b); } int SSHFS::do_symlink(char *file, char *link){ TRACE("do_symlink"); return conn->symlink(file, link); }