/* This file is part of the KDE project Copyright (C) 2000 Alexander Neundorf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include // This is needed on Solaris so that rpc.h defines clnttcp_create etc. #ifndef PORTMAP #define PORTMAP #endif #include // for rpc calls #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nfs_prot.h" #define fhandle _fhandle #include "mount.h" #include "tdeio_nfs.h" #define MAXHOSTLEN 256 //#define MAXFHAGE 60*15 //15 minutes maximum age for file handles //this ioslave is for NFS version 2 #define NFSPROG ((u_long)100003) #define NFSVERS ((u_long)2) using namespace TDEIO; using namespace std; //this is taken from tdelibs/tdecore/fakes.cpp //#if !defined(HAVE_GETDOMAINNAME) int x_getdomainname(char *name, size_t len) { struct utsname uts; struct hostent *hent; int rv = -1; if (name == 0L) errno = EINVAL; else { name[0] = '\0'; if (uname(&uts) >= 0) { if ((hent = gethostbyname(uts.nodename)) != 0L) { char *p = strchr(hent->h_name, '.'); if (p != 0L) { ++p; if (strlen(p) > len-1) errno = EINVAL; else { strcpy(name, p); rv = 0; } } } } } return rv; } //#endif extern "C" { int KDE_EXPORT kdemain(int argc, char **argv); } int kdemain( int argc, char **argv ) { TDEInstance instance( "tdeio_nfs" ); if (argc != 4) { fprintf(stderr, "Usage: tdeio_nfs protocol domain-socket1 domain-socket2\n"); exit(-1); } kdDebug(7121) << "NFS: kdemain: starting" << endl; NFSProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } static bool isRoot(const TQString& path) { return (path.isEmpty() || (path=="/")); } static bool isAbsoluteLink(const TQString& path) { //hmm, don't know if (path.isEmpty()) return TRUE; if (path[0]=='/') return TRUE; return FALSE; } static void createVirtualDirEntry(UDSEntry & entry) { UDSAtom atom; atom.m_uds = TDEIO::UDS_FILE_TYPE; atom.m_long = S_IFDIR; entry.append( atom ); atom.m_uds = TDEIO::UDS_ACCESS; atom.m_long = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; entry.append( atom ); atom.m_uds = TDEIO::UDS_USER; atom.m_str = "root"; entry.append( atom ); atom.m_uds = TDEIO::UDS_GROUP; atom.m_str = "root"; entry.append( atom ); //a dummy size atom.m_uds = TDEIO::UDS_SIZE; atom.m_long = 1024; entry.append( atom ); } static void stripTrailingSlash(TQString& path) { //if (path=="/") return; if (path=="/") path=""; else if (path[path.length()-1]=='/') path.truncate(path.length()-1); } static void getLastPart(const TQString& path, TQString& lastPart, TQString& rest) { int slashPos=path.findRev("/"); lastPart=path.mid(slashPos+1); rest=path.left(slashPos+1); } static TQString removeFirstPart(const TQString& path) { TQString result(""); if (path.isEmpty()) return result; result=path.mid(1); int slashPos=result.find("/"); return result.mid(slashPos+1); } NFSFileHandle::NFSFileHandle() :m_isInvalid(FALSE) { memset(m_handle,'\0',NFS_FHSIZE+1); // m_detectTime=time(0); } NFSFileHandle::NFSFileHandle(const NFSFileHandle & handle) :m_isInvalid(FALSE) { m_handle[NFS_FHSIZE]='\0'; memcpy(m_handle,handle.m_handle,NFS_FHSIZE); m_isInvalid=handle.m_isInvalid; // m_detectTime=handle.m_detectTime; } NFSFileHandle::~NFSFileHandle() {} NFSFileHandle& NFSFileHandle::operator= (const NFSFileHandle& src) { memcpy(m_handle,src.m_handle,NFS_FHSIZE); m_isInvalid=src.m_isInvalid; // m_detectTime=src.m_detectTime; return *this; } NFSFileHandle& NFSFileHandle::operator= (const char* src) { if (src==0) { m_isInvalid=TRUE; return *this; }; memcpy(m_handle,src,NFS_FHSIZE); m_isInvalid=FALSE; // m_detectTime=time(0); return *this; } /*time_t NFSFileHandle::age() const { return (time(0)-m_detectTime); }*/ NFSProtocol::NFSProtocol (const TQCString &pool, const TQCString &app ) :SlaveBase( "nfs", pool, app ) ,m_client(0) ,m_sock(-1) ,m_lastCheck(time(0)) { kdDebug(7121)<<"NFS::NFS: -"<MAXFHAGE) { kdDebug(7121)<<"removing"<MAXFHAGE) checkForOldFHs(); stripTrailingSlash(path); kdDebug(7121)<<"getting FH for -"<= '0' && m_currentHost[0] <= '9') { server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(m_currentHost.latin1()); } else { struct hostent *hp=gethostbyname(m_currentHost.latin1()); if (hp==0) { error( ERR_UNKNOWN_HOST, m_currentHost.latin1() ); return; } server_addr.sin_family = AF_INET; memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length); } // create mount deamon client closeConnection(); server_addr.sin_port = 0; m_sock = RPC_ANYSOCK; m_client=clnttcp_create(&server_addr,MOUNTPROG, MOUNTVERS, &m_sock, 0, 0); if (m_client==0) { server_addr.sin_port = 0; m_sock = RPC_ANYSOCK; pertry_timeout.tv_sec = 3; pertry_timeout.tv_usec = 0; m_client = clntudp_create(&server_addr,MOUNTPROG, MOUNTVERS, pertry_timeout, &m_sock); if (m_client==0) { clnt_pcreateerror(const_cast("mount clntudp_create")); error(ERR_COULD_NOT_CONNECT, m_currentHost.latin1()); return; } } TQCString hostName("localhost"); char nameBuffer[1024]; nameBuffer[0] = '\0'; if (gethostname(nameBuffer, 1024)==0) { nameBuffer[sizeof(nameBuffer)-1] = '\0'; hostName=nameBuffer; // I have the same problem here as Stefan Westerfeld, that's why I use // the getdomainname() from fakes.cpp (renamed to x_getdomainname()), this one works // taken from tdelibs/arts/mcopy/mcoputils.cc nameBuffer[0] = '\0'; if (x_getdomainname(nameBuffer, 1024)==0) { nameBuffer[sizeof(nameBuffer)-1] = '\0'; /* * I don't know why, but on my linux machine, the domainname * always ends up being (none), which is certainly no valid * domainname */ if(strcmp(nameBuffer,"(none)") != 0) { hostName += "."; hostName += nameBuffer; } } } kdDebug(7121) << "hostname is -" << hostName << "-" << endl; m_client->cl_auth = authunix_create(hostName.data(), geteuid(), getegid(), 0, 0); total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; exports exportlist; //now do the stuff memset(&exportlist, '\0', sizeof(exportlist)); int clnt_stat = clnt_call(m_client, MOUNTPROC_EXPORT,(xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_exports, (char*)&exportlist,total_timeout); if (!checkForError(clnt_stat, 0, m_currentHost.latin1())) return; fhstatus fhStatus; bool atLeastOnceSucceeded(FALSE); for(; exportlist!=0;exportlist = exportlist->ex_next) { kdDebug(7121) << "found export: " << exportlist->ex_dir << endl; memset(&fhStatus, 0, sizeof(fhStatus)); clnt_stat = clnt_call(m_client, MOUNTPROC_MNT,(xdrproc_t) xdr_dirpath, (char*)(&(exportlist->ex_dir)), (xdrproc_t) xdr_fhstatus,(char*) &fhStatus,total_timeout); if (fhStatus.fhs_status==0) { atLeastOnceSucceeded=TRUE; NFSFileHandle fh; fh=fhStatus.fhstatus_u.fhs_fhandle; TQString fname; if ( exportlist->ex_dir[0] == '/' ) fname = TDEIO::encodeFileName(exportlist->ex_dir + 1); else fname = TDEIO::encodeFileName(exportlist->ex_dir); m_handleCache.insert(TQString("/")+fname,fh); m_exportedDirs.append(fname); // kdDebug() <<"appending file -"<("NFS clntudp_create")); error(ERR_COULD_NOT_CONNECT, m_currentHost.latin1()); return; } } m_client->cl_auth = authunix_create(hostName.data(),geteuid(),getegid(),0,0); connected(); kdDebug(7121)<<"openConnection succeeded"<nextentry) { if ((TQString(".")!=dirEntry->name) && (TQString("..")!=dirEntry->name)) filesToList.append(dirEntry->name); } } while (!listres.readdirres_u.reply.eof); totalSize( filesToList.count()); UDSEntry entry; //stat all files in filesToList for (TQStringList::Iterator it=filesToList.begin(); it!=filesToList.end(); it++) { UDSAtom atom; diropargs dirargs; diropres dirres; memcpy(dirargs.dir.data,fh,NFS_FHSIZE); TQCString tmpStr=TQFile::encodeName(*it); dirargs.name=tmpStr.data(); kdDebug(7121)<<"calling rpc: FH: -"<1) && (tmpPath[0]=='/')) tmpPath=tmpPath.mid(1); // We can't stat root, but we know it's a dir if (isRoot(path) || isExportedDir(path)) { UDSEntry entry; UDSAtom atom; atom.m_uds = TDEIO::UDS_NAME; atom.m_str = path; entry.append( atom ); createVirtualDirEntry(entry); // no size statEntry( entry ); finished(); kdDebug(7121)<<"succeeded"<pw_name)) ); atom.m_str = user->pw_name; } else atom.m_str = "???"; } else atom.m_str = *temp; entry.append( atom ); atom.m_uds = TDEIO::UDS_GROUP; gid_t gid = buff.st_gid; temp = m_groupcache.find( gid ); if ( !temp ) { struct group *grp = getgrgid( gid ); if ( grp ) { m_groupcache.insert( gid, new TQString(TQString::fromLatin1(grp->gr_name)) ); atom.m_str = grp->gr_name; } else atom.m_str = "???"; } else atom.m_str = *temp; entry.append( atom ); atom.m_uds = TDEIO::UDS_ACCESS_TIME; atom.m_long = buff.st_atime; entry.append( atom ); atom.m_uds = TDEIO::UDS_CREATION_TIME; atom.m_long = buff.st_ctime; entry.append( atom ); } void NFSProtocol::completeBadLinkUDSEntry(UDSEntry& entry, fattr& attributes) { // It is a link pointing to nowhere completeUDSEntry(entry,attributes); UDSAtom atom; atom.m_uds = TDEIO::UDS_FILE_TYPE; atom.m_long = S_IFMT - 1; entry.append( atom ); atom.m_uds = TDEIO::UDS_ACCESS; atom.m_long = S_IRWXU | S_IRWXG | S_IRWXO; entry.append( atom ); atom.m_uds = TDEIO::UDS_SIZE; atom.m_long = 0L; entry.append( atom ); } void NFSProtocol::completeUDSEntry(UDSEntry& entry, fattr& attributes) { UDSAtom atom; atom.m_uds = TDEIO::UDS_SIZE; atom.m_long = attributes.size; entry.append(atom); atom.m_uds = TDEIO::UDS_MODIFICATION_TIME; atom.m_long = attributes.mtime.seconds; entry.append( atom ); atom.m_uds = TDEIO::UDS_ACCESS_TIME; atom.m_long = attributes.atime.seconds; entry.append( atom ); atom.m_uds = TDEIO::UDS_CREATION_TIME; atom.m_long = attributes.ctime.seconds; entry.append( atom ); atom.m_uds = TDEIO::UDS_ACCESS; atom.m_long = (attributes.mode & 07777); entry.append( atom ); atom.m_uds = TDEIO::UDS_FILE_TYPE; atom.m_long =attributes.mode & S_IFMT; // extract file type entry.append( atom ); atom.m_uds = TDEIO::UDS_USER; uid_t uid = attributes.uid; TQString *temp = m_usercache.find( uid ); if ( !temp ) { struct passwd *user = getpwuid( uid ); if ( user ) { m_usercache.insert( uid, new TQString(user->pw_name) ); atom.m_str = user->pw_name; } else atom.m_str = "???"; } else atom.m_str = *temp; entry.append( atom ); atom.m_uds = TDEIO::UDS_GROUP; gid_t gid = attributes.gid; temp = m_groupcache.find( gid ); if ( !temp ) { struct group *grp = getgrgid( gid ); if ( grp ) { m_groupcache.insert( gid, new TQString(grp->gr_name) ); atom.m_str = grp->gr_name; } else atom.m_str = "???"; } else atom.m_str = *temp; entry.append( atom ); /* TDEIO::UDSEntry::ConstIterator it = entry.begin(); for( ; it != entry.end(); it++ ) { switch ((*it).m_uds) { case TDEIO::UDS_FILE_TYPE: kdDebug(7121) << "File Type : " << (mode_t)((*it).m_long) << endl; break; case TDEIO::UDS_ACCESS: kdDebug(7121) << "Access permissions : " << (mode_t)((*it).m_long) << endl; break; case TDEIO::UDS_USER: kdDebug(7121) << "User : " << ((*it).m_str.ascii() ) << endl; break; case TDEIO::UDS_GROUP: kdDebug(7121) << "Group : " << ((*it).m_str.ascii() ) << endl; break; case TDEIO::UDS_NAME: kdDebug(7121) << "Name : " << ((*it).m_str.ascii() ) << endl; //m_strText = decodeFileName( (*it).m_str ); break; case TDEIO::UDS_URL: kdDebug(7121) << "URL : " << ((*it).m_str.ascii() ) << endl; break; case TDEIO::UDS_MIME_TYPE: kdDebug(7121) << "MimeType : " << ((*it).m_str.ascii() ) << endl; break; case TDEIO::UDS_LINK_DEST: kdDebug(7121) << "LinkDest : " << ((*it).m_str.ascii() ) << endl; break; } }*/ } void NFSProtocol::setHost(const TQString& host, int /*port*/, const TQString& /*user*/, const TQString& /*pass*/) { kdDebug(7121)<<"setHost: -"<0) { array.setRawData(readRes.readres_u.reply.data.data_val, offset); data( array ); array.resetRawData(readRes.readres_u.reply.data.data_val, offset); processedSize(readArgs.offset); } } while (offset>0); data( TQByteArray() ); finished(); } //TODO the partial putting thing is not yet implemented void NFSProtocol::put( const KURL& url, int _mode, bool _overwrite, bool /*_resume*/ ) { TQString destPath( TQFile::encodeName(url.path())); kdDebug( 7121 ) << "Put -" << destPath <<"-"< 0) { do { if (bytesToWrite>NFS_MAXDATA) { writeNow=NFS_MAXDATA; } else { writeNow=bytesToWrite; }; writeArgs.data.data_val=data; writeArgs.data.data_len=writeNow; int clnt_stat = clnt_call(m_client, NFSPROC_WRITE, (xdrproc_t) xdr_writeargs, (char*)&writeArgs, (xdrproc_t) xdr_attrstat, (char*)&attrStat,total_timeout); //kdDebug(7121)<<"written"<0); } } while ( result > 0 ); finished(); } void NFSProtocol::rename( const KURL &src, const KURL &dest, bool _overwrite ) { TQString srcPath( TQFile::encodeName(src.path())); TQString destPath( TQFile::encodeName(dest.path())); stripTrailingSlash(srcPath); stripTrailingSlash(destPath); kdDebug(7121)<<"renaming -"<0) { readArgs.offset+=bytesRead; writeArgs.data.data_len=bytesRead; clnt_stat = clnt_call(m_client, NFSPROC_WRITE, (xdrproc_t) xdr_writeargs, (char*)&writeArgs, (xdrproc_t) xdr_attrstat, (char*)&attrStat,total_timeout); //kdDebug(7121)<<"written"<0); finished(); } //TODO why isn't this even called ? void NFSProtocol::symlink( const TQString &target, const KURL &dest, bool ) { kdDebug(7121)<<"symlinking "<