File indexing completed on 2024-04-21 04:57:39
0001 /* This file is part of the KDE project 0002 0003 SPDX-FileCopyrightText: 2000 Alexander Neundorf <neundorf@kde.org> 0004 SPDX-FileCopyrightText: 2014 Mathias Tillman <master.homer@gmail.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "nfsv3.h" 0010 #include "kio_nfs_debug.h" 0011 0012 #include <config-runtime.h> 0013 0014 #include <arpa/inet.h> 0015 0016 // This is needed on Solaris so that rpc.h defines clnttcp_create etc. 0017 #ifndef PORTMAP 0018 #define PORTMAP 0019 #endif 0020 #include <rpc/rpc.h> // for rpc calls 0021 0022 #include <errno.h> 0023 #include <grp.h> 0024 #include <memory.h> 0025 #include <netdb.h> 0026 #include <pwd.h> 0027 #include <stdio.h> 0028 #include <stdlib.h> 0029 #include <strings.h> 0030 #include <time.h> 0031 #include <unistd.h> 0032 #include <utime.h> 0033 0034 #include <QDebug> 0035 #include <QDir> 0036 #include <QFile> 0037 #include <QMimeDatabase> 0038 #include <QMimeType> 0039 #include <QUrl> 0040 0041 #include <KIO/Global> 0042 #include <KLocalizedString> 0043 #include <kio/ioworker_defaults.h> 0044 0045 // For the complete NFSv3 reference see http://tools.ietf.org/html/rfc1813 0046 0047 // This ioslave is for NFS version 3. 0048 #define NFSPROG 100003UL 0049 #define NFSVERS 3UL 0050 0051 #define NFS3_MAXDATA 32768 0052 #define NFS3_MAXPATHLEN PATH_MAX 0053 0054 NFSProtocolV3::NFSProtocolV3(NFSSlave *slave) 0055 : NFSProtocol(slave) 0056 , m_mountClient(nullptr) 0057 , m_mountSock(-1) 0058 , m_nfsClient(nullptr) 0059 , m_nfsSock(-1) 0060 , m_readBufferSize(0) 0061 , m_writeBufferSize(0) 0062 , m_readDirSize(0) 0063 { 0064 qCDebug(LOG_KIO_NFS); 0065 0066 clnt_timeout.tv_sec = 20; 0067 clnt_timeout.tv_usec = 0; 0068 } 0069 0070 NFSProtocolV3::~NFSProtocolV3() 0071 { 0072 closeConnection(); 0073 } 0074 0075 bool NFSProtocolV3::isCompatible(bool &connectionError) 0076 { 0077 int ret = -1; 0078 0079 CLIENT *client = nullptr; 0080 int sock = 0; 0081 if (NFSProtocol::openConnection(currentHost(), NFSPROG, NFSVERS, client, sock) == 0) { 0082 timeval check_timeout; 0083 check_timeout.tv_sec = 20; 0084 check_timeout.tv_usec = 0; 0085 0086 // Check if the NFS version is compatible 0087 ret = clnt_call(client, NFSPROC3_NULL, (xdrproc_t)xdr_void, nullptr, (xdrproc_t)xdr_void, nullptr, check_timeout); 0088 0089 connectionError = false; 0090 } else { 0091 qCDebug(LOG_KIO_NFS) << "openConnection failed"; 0092 connectionError = true; 0093 } 0094 0095 if (sock != -1) { 0096 ::close(sock); 0097 } 0098 0099 if (client != nullptr) { 0100 CLNT_DESTROY(client); 0101 } 0102 0103 qCDebug(LOG_KIO_NFS) << "RPC status" << ret << "connectionError" << connectionError; 0104 return (ret == RPC_SUCCESS); 0105 } 0106 0107 bool NFSProtocolV3::isConnected() const 0108 { 0109 return (m_nfsClient != nullptr); 0110 } 0111 0112 void NFSProtocolV3::closeConnection() 0113 { 0114 qCDebug(LOG_KIO_NFS); 0115 0116 // Unmount all exported dirs(if any) 0117 if (m_mountClient != nullptr) { 0118 clnt_call(m_mountClient, MOUNTPROC3_UMNTALL, (xdrproc_t)xdr_void, nullptr, (xdrproc_t)xdr_void, nullptr, clnt_timeout); 0119 } 0120 0121 if (m_mountSock >= 0) { 0122 ::close(m_mountSock); 0123 m_mountSock = -1; 0124 } 0125 if (m_nfsSock >= 0) { 0126 ::close(m_nfsSock); 0127 m_nfsSock = -1; 0128 } 0129 0130 if (m_mountClient != nullptr) { 0131 CLNT_DESTROY(m_mountClient); 0132 m_mountClient = nullptr; 0133 } 0134 if (m_nfsClient != nullptr) { 0135 CLNT_DESTROY(m_nfsClient); 0136 m_nfsClient = nullptr; 0137 } 0138 } 0139 0140 NFSFileHandle NFSProtocolV3::lookupFileHandle(const QString &path) 0141 { 0142 NFSFileHandle fh; 0143 int rpcStatus; 0144 LOOKUP3res res; 0145 if (lookupHandle(path, rpcStatus, res)) { 0146 fh = res.LOOKUP3res_u.resok.object; 0147 0148 // Is it a link? Get the link target. 0149 if (res.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.type == NF3LNK) { 0150 READLINK3args readLinkArgs; 0151 memset(&readLinkArgs, 0, sizeof(readLinkArgs)); 0152 fh.toFH(readLinkArgs.symlink); 0153 0154 char dataBuffer[NFS3_MAXPATHLEN]; 0155 0156 READLINK3res readLinkRes; 0157 memset(&readLinkRes, 0, sizeof(readLinkRes)); 0158 readLinkRes.READLINK3res_u.resok.data = dataBuffer; 0159 0160 int rpcStatus = clnt_call(m_nfsClient, 0161 NFSPROC3_READLINK, 0162 (xdrproc_t)xdr_READLINK3args, 0163 reinterpret_cast<caddr_t>(&readLinkArgs), 0164 (xdrproc_t)xdr_READLINK3res, 0165 reinterpret_cast<caddr_t>(&readLinkRes), 0166 clnt_timeout); 0167 0168 if (rpcStatus == RPC_SUCCESS && readLinkRes.status == NFS3_OK) { // get the absolute link target 0169 QString linkPath = QString::fromLocal8Bit(readLinkRes.READLINK3res_u.resok.data); 0170 linkPath = QFileInfo(QFileInfo(path).path(), linkPath).absoluteFilePath(); 0171 0172 // As with the tests done in NFSProtocol::isValidLink(), the link 0173 // target may not be valid on the NFS server (i.e. it may point 0174 // outside of the exported directories). Check for this before 0175 // calling lookupHandle() on the target of the link, as otherwise 0176 // an error will be set which is not relevant. 0177 if (isValidPath(linkPath)) { 0178 LOOKUP3res linkRes; 0179 if (lookupHandle(linkPath, rpcStatus, linkRes)) { 0180 // It's a link, so return the target file handle 0181 // with the link source recorded in it. 0182 NFSFileHandle linkFh = linkRes.LOOKUP3res_u.resok.object; 0183 linkFh.setLinkSource(res.LOOKUP3res_u.resok.object); 0184 qCDebug(LOG_KIO_NFS) << "Found link target" << linkPath; 0185 return linkFh; 0186 } 0187 } 0188 } 0189 0190 // If we have reached this point the file is a link, 0191 // but we failed to read the target. 0192 fh.setBadLink(); 0193 qCDebug(LOG_KIO_NFS) << "Invalid link" << path; 0194 } 0195 } 0196 0197 return fh; 0198 } 0199 0200 /* Open connection connects to the mount daemon on the server side. 0201 In order to do this it needs authentication and calls auth_unix_create(). 0202 Then it asks the mount daemon for the exported shares. Then it tries 0203 to mount all these shares. If this succeeded for at least one of them, 0204 a client for the nfs daemon is created. 0205 */ 0206 void NFSProtocolV3::openConnection() 0207 { 0208 const QString host = currentHost(); 0209 qCDebug(LOG_KIO_NFS) << "to" << host; 0210 0211 // Destroy the old connection first 0212 closeConnection(); 0213 0214 KIO::Error connErr = NFSProtocol::openConnection(host, MOUNT_PROGRAM, MOUNT_V3, m_mountClient, m_mountSock); 0215 if (connErr != 0) { 0216 closeConnection(); 0217 setError(connErr, host); 0218 return; 0219 } 0220 0221 exports3 exportlist; 0222 memset(&exportlist, 0, sizeof(exportlist)); 0223 0224 int clnt_stat = clnt_call(m_mountClient, 0225 MOUNTPROC3_EXPORT, 0226 (xdrproc_t)xdr_void, 0227 nullptr, 0228 (xdrproc_t)xdr_exports3, 0229 reinterpret_cast<caddr_t>(&exportlist), 0230 clnt_timeout); 0231 0232 if (!checkForError(clnt_stat, 0, host.toLatin1())) { 0233 closeConnection(); 0234 return; 0235 } 0236 0237 int exportsCount = 0; 0238 bool mountHint = false; 0239 0240 mountres3 fhStatus; 0241 for (; exportlist != nullptr; exportlist = exportlist->ex_next, exportsCount++) { 0242 memset(&fhStatus, 0, sizeof(fhStatus)); 0243 clnt_stat = clnt_call(m_mountClient, 0244 MOUNTPROC3_MNT, 0245 (xdrproc_t)xdr_dirpath3, 0246 reinterpret_cast<caddr_t>(&exportlist->ex_dir), 0247 (xdrproc_t)xdr_mountres3, 0248 reinterpret_cast<caddr_t>(&fhStatus), 0249 clnt_timeout); 0250 0251 QString fname = QFileInfo(QDir::root(), exportlist->ex_dir).filePath(); 0252 if (fhStatus.fhs_status == 0) { // mount succeeded 0253 0254 // Check if the directory is already noted as exported, 0255 // if so there is no need to add it again. 0256 if (isExportedDir(fname)) 0257 continue; 0258 0259 // Save the exported directory and its NFS file handle. 0260 addFileHandle(fname, static_cast<NFSFileHandle>(fhStatus.mountres3_u.mountinfo.fhandle)); 0261 addExportedDir(fname); 0262 } else { // mount failed with error 0263 qCDebug(LOG_KIO_NFS) << "Cannot mount" << fname << "- status" << fhStatus.fhs_status; 0264 0265 // Even if the mount failed, record the directory path as exported 0266 // so that it can be listed as a virtual directory. However, do 0267 // not record its (invalid) file handle in the cache. Trying to 0268 // access the directory in any way other than just listing it, or 0269 // accessing anything below it, will be detected in 0270 // NFSProtocol::getFileHandle() and fail with an appropriate 0271 // error. 0272 if (!isExportedDir(fname)) 0273 addExportedDir(fname); 0274 0275 // Many modern NFS servers by default reject any access attempted to 0276 // them from a non-reserved source port (i.e. above 1024). Since 0277 // this KIO slave runs as a normal user, it is not able to use the 0278 // reserved port numbers and hence the access will be rejected. Show 0279 // a hint if this could possibly be the problem - only once, as the 0280 // server may have many exported directories. 0281 if (fhStatus.fhs_status == MNT3ERR_ACCES) { 0282 if (!mountHint) { 0283 qCDebug(LOG_KIO_NFS) << "Check that the NFS server is exporting the filesystem"; 0284 qCDebug(LOG_KIO_NFS) << "with appropriate access permissions. Note that it must"; 0285 qCDebug(LOG_KIO_NFS) << "allow mount requests originating from an unprivileged"; 0286 qCDebug(LOG_KIO_NFS) << "source port (see exports(5), the 'insecure' option may"; 0287 qCDebug(LOG_KIO_NFS) << "be required)."; 0288 mountHint = true; 0289 } 0290 } 0291 } 0292 } 0293 0294 // If nothing can be mounted then there is no point trying to open the 0295 // NFS server connection here. However, call openConnection() anyway 0296 // and pretend that we are connected so that listing virtual directories 0297 // will work. 0298 if ((connErr = NFSProtocol::openConnection(host, NFSPROG, NFSVERS, m_nfsClient, m_nfsSock)) != 0) { 0299 closeConnection(); 0300 setError(connErr, host); 0301 } 0302 0303 slave()->connected(); 0304 0305 qCDebug(LOG_KIO_NFS) << "openConnection succeeded"; 0306 } 0307 0308 void NFSProtocolV3::listDir(const QUrl &url) 0309 { 0310 qCDebug(LOG_KIO_NFS) << url; 0311 0312 const QString path = listDirInternal(url); // check path, list virtual dir 0313 if (path.isEmpty()) 0314 return; // no more to do 0315 0316 const NFSFileHandle fh = getFileHandle(path); 0317 // There doesn't seem to be an invalid link error code in KIO, 0318 // so this will have to do. 0319 if (fh.isInvalid() || fh.isBadLink()) { 0320 setError(KIO::ERR_DOES_NOT_EXIST, path); 0321 return; 0322 } 0323 0324 // Get the preferred read dir size from the server 0325 if (m_readDirSize == 0) { 0326 initPreferredSizes(fh); 0327 } 0328 0329 if (!slave()->usedirplus3()) // want compatibility mode listing 0330 { 0331 listDirCompat(url); 0332 return; 0333 } 0334 0335 READDIRPLUS3args listargs; 0336 memset(&listargs, 0, sizeof(listargs)); 0337 listargs.dircount = m_readDirSize; 0338 listargs.maxcount = sizeof(entryplus3) * m_readDirSize; // Not really sure what this should be set to. 0339 0340 fh.toFH(listargs.dir); 0341 0342 READDIRPLUS3res listres; 0343 memset(&listres, 0, sizeof(listres)); 0344 0345 entryplus3 *lastEntry = nullptr; 0346 do { 0347 memset(&listres, 0, sizeof(listres)); 0348 0349 // In case that we didn't get all entries we need to set the cookie to the last one we actually received. 0350 if (lastEntry != nullptr) { 0351 listargs.cookie = lastEntry->cookie; 0352 } 0353 0354 int clnt_stat = clnt_call(m_nfsClient, 0355 NFSPROC3_READDIRPLUS, 0356 (xdrproc_t)xdr_READDIRPLUS3args, 0357 reinterpret_cast<caddr_t>(&listargs), 0358 (xdrproc_t)xdr_READDIRPLUS3res, 0359 reinterpret_cast<caddr_t>(&listres), 0360 clnt_timeout); 0361 0362 // Not a supported call? Try the old READDIR method. 0363 if (listres.status == NFS3ERR_NOTSUPP) { 0364 qCDebug(LOG_KIO_NFS) << "NFS server does not support READDIRPLUS3, listing in compatibility mode"; 0365 listDirCompat(url); 0366 return; 0367 } 0368 0369 // Do we have an error? There's not much more we can do but to abort at this point. 0370 if (!checkForError(clnt_stat, listres.status, path)) { 0371 return; 0372 } 0373 0374 for (entryplus3 *dirEntry = listres.READDIRPLUS3res_u.resok.reply.entries; dirEntry != nullptr; dirEntry = dirEntry->nextentry) { 0375 if (dirEntry->name == QString("..")) { 0376 continue; 0377 } 0378 0379 KIO::UDSEntry entry; 0380 entry.fastInsert(KIO::UDSEntry::UDS_NAME, dirEntry->name); 0381 0382 if (dirEntry->name == QString(".")) { 0383 createVirtualDirEntry(entry); 0384 completeUDSEntry(entry, dirEntry->name_attributes.post_op_attr_u.attributes); 0385 slave()->listEntry(entry); 0386 continue; 0387 } 0388 0389 const QString &filePath = QFileInfo(QDir(path), dirEntry->name).filePath(); 0390 0391 // Is it a symlink ? 0392 if (dirEntry->name_attributes.post_op_attr_u.attributes.type == NF3LNK) { 0393 int rpcStatus; 0394 READLINK3res readLinkRes; 0395 char nameBuf[NFS3_MAXPATHLEN]; 0396 0397 if (symLinkTarget(filePath, rpcStatus, readLinkRes, nameBuf)) { 0398 QString linkDest = QString::fromLocal8Bit(readLinkRes.READLINK3res_u.resok.data); 0399 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkDest); 0400 0401 bool badLink = true; 0402 NFSFileHandle linkFH; 0403 if (isValidLink(path, linkDest)) { 0404 const QString linkPath = QFileInfo(path, linkDest).absoluteFilePath(); 0405 // get the absolute link target 0406 int rpcStatus; 0407 LOOKUP3res lookupRes; 0408 if (lookupHandle(linkPath, rpcStatus, lookupRes)) { 0409 GETATTR3res attrAndStat; 0410 if (getAttr(linkPath, rpcStatus, attrAndStat)) { 0411 badLink = false; 0412 0413 linkFH = lookupRes.LOOKUP3res_u.resok.object; 0414 linkFH.setLinkSource(dirEntry->name_handle.post_op_fh3_u.handle); 0415 0416 completeUDSEntry(entry, attrAndStat.GETATTR3res_u.resok.obj_attributes); 0417 } 0418 } 0419 } 0420 0421 if (badLink) { 0422 linkFH = dirEntry->name_handle.post_op_fh3_u.handle; 0423 linkFH.setBadLink(); 0424 0425 completeBadLinkUDSEntry(entry, dirEntry->name_attributes.post_op_attr_u.attributes); 0426 } 0427 0428 addFileHandle(filePath, linkFH); 0429 } else { 0430 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, i18n("Unknown target")); 0431 completeBadLinkUDSEntry(entry, dirEntry->name_attributes.post_op_attr_u.attributes); 0432 } 0433 } else { 0434 NFSFileHandle entryFH = dirEntry->name_handle.post_op_fh3_u.handle; 0435 0436 // Some NFS servers seem to return names from the READDIRPLUS3 0437 // call without a valid file handle or attributes. This has 0438 // been observed with a WD MyCloud NAS running Debian wheezy. 0439 // The READDIR3 call does not have this problem, but by the time 0440 // it can be detected it is too late to repeat the listing in 0441 // compatibility mode since some listEntry()'s may already have 0442 // been done. 0443 // 0444 // Accept the name, and assume null values for the attributes. 0445 // Do not save the invalid file handle in the cache; if that 0446 // subdirectory is accessed later on it will be listed again 0447 // which seems to work properly. 0448 if (entryFH.isInvalid()) { 0449 qCDebug(LOG_KIO_NFS) << "NFS server returned invalid handle for" << (path + "/" + dirEntry->name); 0450 completeInvalidUDSEntry(entry); 0451 } else { 0452 addFileHandle(filePath, entryFH); 0453 completeUDSEntry(entry, dirEntry->name_attributes.post_op_attr_u.attributes); 0454 } 0455 } 0456 0457 slave()->listEntry(entry); 0458 0459 lastEntry = dirEntry; 0460 } 0461 } while (listres.READDIRPLUS3res_u.resok.reply.entries != nullptr && !listres.READDIRPLUS3res_u.resok.reply.eof); 0462 } 0463 0464 void NFSProtocolV3::listDirCompat(const QUrl &url) 0465 { 0466 const QString path(url.path()); 0467 0468 if (isExportedDir(path)) { 0469 // We should never get here, if the path is an exported dir 0470 // it will have been checked in listDir() and there will have been 0471 // no attempt to access the NFS server. 0472 qCWarning(LOG_KIO_NFS) << "Called for an exported dir"; 0473 setError(KIO::ERR_INTERNAL, path); 0474 return; 0475 } 0476 0477 const NFSFileHandle fh = getFileHandle(path); 0478 if (fh.isInvalid() || fh.isBadLink()) { 0479 setError(KIO::ERR_DOES_NOT_EXIST, path); 0480 return; 0481 } 0482 0483 QStringList filesToList; 0484 0485 READDIR3args listargs; 0486 memset(&listargs, 0, sizeof(listargs)); 0487 listargs.count = m_readDirSize; 0488 fh.toFH(listargs.dir); 0489 0490 READDIR3res listres; 0491 entry3 *lastEntry = nullptr; 0492 do { 0493 memset(&listres, 0, sizeof(listres)); 0494 0495 // In case that we didn't get all entries we need to set the cookie to the last one we actually received 0496 if (lastEntry != nullptr) { 0497 listargs.cookie = lastEntry->cookie; 0498 } 0499 0500 int clnt_stat = clnt_call(m_nfsClient, 0501 NFSPROC3_READDIR, 0502 (xdrproc_t)xdr_READDIR3args, 0503 reinterpret_cast<caddr_t>(&listargs), 0504 (xdrproc_t)xdr_READDIR3res, 0505 reinterpret_cast<caddr_t>(&listres), 0506 clnt_timeout); 0507 0508 if (!checkForError(clnt_stat, listres.status, path)) { 0509 return; 0510 } 0511 0512 for (entry3 *dirEntry = listres.READDIR3res_u.resok.reply.entries; dirEntry != nullptr; dirEntry = dirEntry->nextentry) { 0513 if (dirEntry->name != QString("..")) { 0514 filesToList.append(QFile::decodeName(dirEntry->name)); 0515 } 0516 0517 lastEntry = dirEntry; 0518 } 0519 } while (!listres.READDIR3res_u.resok.reply.eof); 0520 0521 // Loop through all files, getting attributes and link path. 0522 for (QStringList::const_iterator it = filesToList.constBegin(); it != filesToList.constEnd(); ++it) { 0523 QString filePath = QFileInfo(QDir(path), (*it)).filePath(); 0524 0525 int rpcStatus; 0526 LOOKUP3res dirres; 0527 if (!lookupHandle(filePath, rpcStatus, dirres)) { 0528 qCDebug(LOG_KIO_NFS) << "Failed to lookup" << filePath << ", rpc:" << rpcStatus << ", nfs:" << dirres.status; 0529 // Try the next file instead of aborting 0530 continue; 0531 } 0532 0533 KIO::UDSEntry entry; 0534 entry.fastInsert(KIO::UDSEntry::UDS_NAME, (*it)); 0535 0536 // Is it a symlink? 0537 if (dirres.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.type == NF3LNK) { 0538 int rpcStatus; 0539 READLINK3res readLinkRes; 0540 char nameBuf[NFS3_MAXPATHLEN]; 0541 if (symLinkTarget(filePath, rpcStatus, readLinkRes, nameBuf)) { 0542 const QString linkDest = QString::fromLocal8Bit(readLinkRes.READLINK3res_u.resok.data); 0543 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkDest); 0544 0545 bool badLink = true; 0546 NFSFileHandle linkFH; 0547 if (isValidLink(path, linkDest)) { 0548 const QString linkPath = QFileInfo(path, linkDest).absoluteFilePath(); 0549 // get the absolute link target 0550 int rpcStatus; 0551 LOOKUP3res lookupRes; 0552 if (lookupHandle(linkPath, rpcStatus, lookupRes)) { 0553 GETATTR3res attrAndStat; 0554 if (getAttr(linkPath, rpcStatus, attrAndStat)) { 0555 badLink = false; 0556 0557 linkFH = lookupRes.LOOKUP3res_u.resok.object; 0558 linkFH.setLinkSource(dirres.LOOKUP3res_u.resok.object); 0559 0560 completeUDSEntry(entry, attrAndStat.GETATTR3res_u.resok.obj_attributes); 0561 } 0562 } 0563 } 0564 0565 if (badLink) { 0566 linkFH = dirres.LOOKUP3res_u.resok.object; 0567 linkFH.setBadLink(); 0568 0569 completeBadLinkUDSEntry(entry, dirres.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes); 0570 } 0571 0572 addFileHandle(filePath, linkFH); 0573 } else { 0574 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, i18n("Unknown target")); 0575 completeBadLinkUDSEntry(entry, dirres.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes); 0576 } 0577 } else { 0578 addFileHandle(filePath, dirres.LOOKUP3res_u.resok.object); 0579 completeUDSEntry(entry, dirres.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes); 0580 } 0581 0582 slave()->listEntry(entry); 0583 } 0584 } 0585 0586 void NFSProtocolV3::stat(const QUrl &url) 0587 { 0588 qCDebug(LOG_KIO_NFS) << url; 0589 0590 const QString path = statInternal(url); // check path, process virtual dir 0591 if (path.isEmpty()) 0592 return; // no more to do 0593 0594 const NFSFileHandle fh = getFileHandle(path); 0595 if (fh.isInvalid()) { 0596 qCDebug(LOG_KIO_NFS) << "File handle is invalid"; 0597 setError(KIO::ERR_DOES_NOT_EXIST, path); 0598 return; 0599 } 0600 0601 int rpcStatus; 0602 GETATTR3res attrAndStat; 0603 if (!getAttr(path, rpcStatus, attrAndStat)) { 0604 checkForError(rpcStatus, attrAndStat.status, path); 0605 return; 0606 } 0607 0608 const QFileInfo fileInfo(path); 0609 0610 KIO::UDSEntry entry; 0611 entry.fastInsert(KIO::UDSEntry::UDS_NAME, fileInfo.fileName()); 0612 0613 // Is it a symlink? 0614 if (attrAndStat.GETATTR3res_u.resok.obj_attributes.type == NF3LNK) { 0615 qCDebug(LOG_KIO_NFS) << "It's a symlink"; 0616 0617 // get the link dest 0618 QString linkDest; 0619 0620 int rpcStatus; 0621 READLINK3res readLinkRes; 0622 char nameBuf[NFS3_MAXPATHLEN]; 0623 if (symLinkTarget(path, rpcStatus, readLinkRes, nameBuf)) { 0624 linkDest = QString::fromLocal8Bit(readLinkRes.READLINK3res_u.resok.data); 0625 } else { 0626 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkDest); 0627 completeBadLinkUDSEntry(entry, attrAndStat.GETATTR3res_u.resok.obj_attributes); 0628 0629 slave()->statEntry(entry); 0630 return; // have result, no more to do 0631 } 0632 0633 qCDebug(LOG_KIO_NFS) << "link dest is" << linkDest; 0634 0635 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkDest); 0636 0637 if (!isValidLink(fileInfo.path(), linkDest)) { 0638 completeBadLinkUDSEntry(entry, attrAndStat.GETATTR3res_u.resok.obj_attributes); 0639 } else { 0640 const QString linkPath = QFileInfo(fileInfo.path(), linkDest).absoluteFilePath(); 0641 // get the absolute link target 0642 int rpcStatus; 0643 GETATTR3res attrAndStat; 0644 if (!getAttr(linkPath, rpcStatus, attrAndStat)) { 0645 checkForError(rpcStatus, attrAndStat.status, linkPath); 0646 return; 0647 } 0648 0649 completeUDSEntry(entry, attrAndStat.GETATTR3res_u.resok.obj_attributes); 0650 } 0651 } else { 0652 completeUDSEntry(entry, attrAndStat.GETATTR3res_u.resok.obj_attributes); 0653 } 0654 0655 slave()->statEntry(entry); 0656 } 0657 0658 void NFSProtocolV3::mkdir(const QUrl &url, int permissions) 0659 { 0660 qCDebug(LOG_KIO_NFS) << url; 0661 0662 const QString path(url.path()); 0663 const QFileInfo fileInfo(path); 0664 if (isExportedDir(fileInfo.path())) { 0665 setError(KIO::ERR_ACCESS_DENIED, path); 0666 return; 0667 } 0668 0669 const NFSFileHandle fh = getFileHandle(fileInfo.path()); 0670 if (fh.isInvalid() || fh.isBadLink()) { 0671 setError(KIO::ERR_DOES_NOT_EXIST, path); 0672 return; 0673 } 0674 0675 MKDIR3args createArgs; 0676 memset(&createArgs, 0, sizeof(createArgs)); 0677 fh.toFH(createArgs.where.dir); 0678 0679 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 0680 createArgs.where.name = tmpName.data(); 0681 0682 createArgs.attributes.mode.set_it = true; 0683 if (permissions == -1) { 0684 createArgs.attributes.mode.set_mode3_u.mode = 0755; 0685 } else { 0686 createArgs.attributes.mode.set_mode3_u.mode = permissions; 0687 } 0688 0689 MKDIR3res dirres; 0690 memset(&dirres, 0, sizeof(dirres)); 0691 0692 int clnt_stat = clnt_call(m_nfsClient, 0693 NFSPROC3_MKDIR, 0694 (xdrproc_t)xdr_MKDIR3args, 0695 reinterpret_cast<caddr_t>(&createArgs), 0696 (xdrproc_t)xdr_MKDIR3res, 0697 reinterpret_cast<caddr_t>(&dirres), 0698 clnt_timeout); 0699 checkForError(clnt_stat, dirres.status, path); 0700 } 0701 0702 void NFSProtocolV3::del(const QUrl &url, bool /* isfile*/) 0703 { 0704 qCDebug(LOG_KIO_NFS) << url; 0705 0706 const QString path(url.path()); 0707 if (isExportedDir(QFileInfo(path).path())) { 0708 setError(KIO::ERR_ACCESS_DENIED, path); 0709 return; 0710 } 0711 0712 int rpcStatus; 0713 REMOVE3res res; 0714 remove(path, rpcStatus, res); 0715 checkForError(rpcStatus, res.status, path); 0716 } 0717 0718 void NFSProtocolV3::chmod(const QUrl &url, int permissions) 0719 { 0720 qCDebug(LOG_KIO_NFS) << url; 0721 0722 const QString path(url.path()); 0723 if (isExportedDir(path)) { 0724 setError(KIO::ERR_ACCESS_DENIED, path); 0725 return; 0726 } 0727 0728 sattr3 attributes; 0729 memset(&attributes, 0, sizeof(attributes)); 0730 attributes.mode.set_it = true; 0731 attributes.mode.set_mode3_u.mode = permissions; 0732 0733 int rpcStatus; 0734 SETATTR3res setAttrRes; 0735 setAttr(path, attributes, rpcStatus, setAttrRes); 0736 checkForError(rpcStatus, setAttrRes.status, path); 0737 } 0738 0739 void NFSProtocolV3::get(const QUrl &url) 0740 { 0741 qCDebug(LOG_KIO_NFS) << url; 0742 0743 const QString path(url.path()); 0744 const NFSFileHandle fh = getFileHandle(path); 0745 if (fh.isInvalid() || fh.isBadLink()) { 0746 setError(KIO::ERR_DOES_NOT_EXIST, path); 0747 return; 0748 } 0749 0750 // Get the optimal read buffer size. 0751 if (m_readBufferSize == 0) { 0752 initPreferredSizes(fh); 0753 } 0754 0755 READ3args readArgs; 0756 memset(&readArgs, 0, sizeof(readArgs)); 0757 fh.toFH(readArgs.file); 0758 readArgs.offset = 0; 0759 readArgs.count = m_readBufferSize; 0760 0761 READ3res readRes; 0762 memset(&readRes, 0, sizeof(readRes)); 0763 readRes.READ3res_u.resok.data.data_len = m_readBufferSize; 0764 readRes.READ3res_u.resok.data.data_val = new char[m_readBufferSize]; 0765 0766 // Most likely indicates out of memory 0767 if (!readRes.READ3res_u.resok.data.data_val) { 0768 setError(KIO::ERR_OUT_OF_MEMORY, path); 0769 return; 0770 } 0771 0772 bool validRead = false; 0773 int read = 0; 0774 QByteArray readBuffer; 0775 do { 0776 int clnt_stat = clnt_call(m_nfsClient, 0777 NFSPROC3_READ, 0778 (xdrproc_t)xdr_READ3args, 0779 reinterpret_cast<caddr_t>(&readArgs), 0780 (xdrproc_t)xdr_READ3res, 0781 reinterpret_cast<caddr_t>(&readRes), 0782 clnt_timeout); 0783 0784 // We are trying to read a directory, fail quietly 0785 if (readRes.status == NFS3ERR_ISDIR) { 0786 break; 0787 } 0788 0789 if (!checkForError(clnt_stat, readRes.status, path)) { 0790 break; 0791 } 0792 0793 read = readRes.READ3res_u.resok.count; 0794 readBuffer.setRawData(readRes.READ3res_u.resok.data.data_val, read); 0795 0796 if (readArgs.offset == 0) { 0797 const QMimeDatabase db; 0798 const QMimeType type = db.mimeTypeForFileNameAndData(url.fileName(), readBuffer); 0799 slave()->mimeType(type.name()); 0800 0801 slave()->totalSize(readRes.READ3res_u.resok.file_attributes.post_op_attr_u.attributes.size); 0802 } 0803 0804 readArgs.offset += read; 0805 if (read > 0) { 0806 validRead = true; 0807 0808 slave()->data(readBuffer); 0809 slave()->processedSize(readArgs.offset); 0810 } 0811 0812 } while (read > 0); 0813 0814 if (readRes.READ3res_u.resok.data.data_val != nullptr) { 0815 delete[] readRes.READ3res_u.resok.data.data_val; 0816 } 0817 0818 // Only send the read data to the slave if we have actually sent some. 0819 if (validRead) { 0820 slave()->data(QByteArray()); 0821 slave()->processedSize(readArgs.offset); 0822 } 0823 } 0824 0825 void NFSProtocolV3::put(const QUrl &url, int _mode, KIO::JobFlags flags) 0826 { 0827 qCDebug(LOG_KIO_NFS) << url; 0828 0829 const QString destPath(url.path()); 0830 if (isExportedDir(QFileInfo(destPath).path())) { 0831 setError(KIO::ERR_WRITE_ACCESS_DENIED, destPath); 0832 return; 0833 } 0834 0835 NFSFileHandle destFH = getFileHandle(destPath); 0836 if (destFH.isBadLink()) { 0837 setError(KIO::ERR_DOES_NOT_EXIST, destPath); 0838 return; 0839 } 0840 0841 // the file exists and we don't want to overwrite 0842 if (!destFH.isInvalid() && ((flags & KIO::Overwrite) == 0)) { 0843 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0844 return; 0845 } 0846 0847 // Get the optimal write buffer size 0848 if (m_writeBufferSize == 0) { 0849 initPreferredSizes(destFH); 0850 } 0851 0852 destFH = create(destPath, _mode); 0853 if (destFH.isInvalid()) { 0854 return; 0855 } 0856 // We created the file successfully. 0857 0858 int result; 0859 0860 WRITE3args writeArgs; 0861 memset(&writeArgs, 0, sizeof(writeArgs)); 0862 0863 destFH.toFH(writeArgs.file); 0864 writeArgs.offset = 0; 0865 writeArgs.stable = FILE_SYNC; 0866 0867 WRITE3res writeRes; 0868 memset(&writeRes, 0, sizeof(writeRes)); 0869 0870 // Loop until we get 0 (end of data). 0871 int bytesWritten = 0; 0872 bool error = false; 0873 do { 0874 QByteArray buffer; 0875 slave()->dataReq(); 0876 result = slave()->readData(buffer); 0877 0878 if (result > 0) { 0879 char *data = buffer.data(); 0880 uint32 bytesToWrite = buffer.size(); 0881 int writeNow(0); 0882 0883 do { 0884 if (bytesToWrite > m_writeBufferSize) { 0885 writeNow = m_writeBufferSize; 0886 } else { 0887 writeNow = bytesToWrite; 0888 } 0889 0890 writeArgs.data.data_val = data; 0891 writeArgs.data.data_len = writeNow; 0892 writeArgs.count = writeNow; 0893 0894 int clnt_stat = clnt_call(m_nfsClient, 0895 NFSPROC3_WRITE, 0896 (xdrproc_t)xdr_WRITE3args, 0897 reinterpret_cast<caddr_t>(&writeArgs), 0898 (xdrproc_t)xdr_WRITE3res, 0899 reinterpret_cast<caddr_t>(&writeRes), 0900 clnt_timeout); 0901 0902 if (!checkForError(clnt_stat, writeRes.status, destPath)) { 0903 error = true; 0904 break; 0905 } 0906 0907 writeNow = writeRes.WRITE3res_u.resok.count; 0908 0909 bytesWritten += writeNow; 0910 writeArgs.offset = bytesWritten; 0911 0912 data = data + writeNow; 0913 bytesToWrite -= writeNow; 0914 } while (bytesToWrite > 0); 0915 } 0916 0917 if (error) { 0918 break; 0919 } 0920 } while (result > 0); 0921 } 0922 0923 void NFSProtocolV3::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags _flags) 0924 { 0925 qCDebug(LOG_KIO_NFS) << src << dest; 0926 0927 const QString srcPath(src.path()); 0928 if (isExportedDir(srcPath)) { 0929 setError(KIO::ERR_CANNOT_RENAME, srcPath); 0930 return; 0931 } 0932 0933 const QString destPath(dest.path()); 0934 if (isExportedDir(destPath)) { 0935 setError(KIO::ERR_ACCESS_DENIED, destPath); 0936 return; 0937 } 0938 0939 if (!getFileHandle(destPath).isInvalid() && (_flags & KIO::Overwrite) == 0) { 0940 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0941 return; 0942 } 0943 0944 int rpcStatus; 0945 RENAME3res res; 0946 rename(srcPath, destPath, rpcStatus, res); 0947 checkForError(rpcStatus, res.status, destPath); 0948 } 0949 0950 void NFSProtocolV3::copySame(const QUrl &src, const QUrl &dest, int _mode, KIO::JobFlags _flags) 0951 { 0952 qCDebug(LOG_KIO_NFS) << src << "to" << dest; 0953 0954 const QString srcPath(src.path()); 0955 if (isExportedDir(QFileInfo(srcPath).path())) { 0956 setError(KIO::ERR_ACCESS_DENIED, srcPath); 0957 return; 0958 } 0959 0960 const NFSFileHandle srcFH = getFileHandle(srcPath); 0961 if (srcFH.isInvalid()) { 0962 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 0963 return; 0964 } 0965 0966 const QString destPath(dest.path()); 0967 if (isExportedDir(QFileInfo(destPath).path())) { 0968 setError(KIO::ERR_ACCESS_DENIED, destPath); 0969 return; 0970 } 0971 0972 // The file exists and we don't want to overwrite 0973 if (!getFileHandle(destPath).isInvalid() && (_flags & KIO::Overwrite) == 0) { 0974 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0975 return; 0976 } 0977 0978 // Is it a link? No need to copy the data then, just copy the link destination. 0979 if (srcFH.isLink()) { 0980 // get the link dest 0981 int rpcStatus; 0982 READLINK3res readLinkRes; 0983 char nameBuf[NFS3_MAXPATHLEN]; 0984 if (!symLinkTarget(srcPath, rpcStatus, readLinkRes, nameBuf)) { 0985 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 0986 return; 0987 } 0988 0989 const QString linkPath = QString::fromLocal8Bit(readLinkRes.READLINK3res_u.resok.data); 0990 0991 SYMLINK3res linkRes; 0992 symLink(linkPath, destPath, rpcStatus, linkRes); 0993 checkForError(rpcStatus, linkRes.status, linkPath); 0994 return; // done, no more to do 0995 } 0996 0997 unsigned long resumeOffset = 0; 0998 bool bResume = false; 0999 const QString partFilePath = destPath + QLatin1String(".part"); 1000 const NFSFileHandle partFH = getFileHandle(partFilePath); 1001 const bool bPartExists = !partFH.isInvalid(); 1002 const bool bMarkPartial = slave()->configValue(QStringLiteral("MarkPartial"), true); 1003 1004 if (bPartExists) { 1005 int rpcStatus; 1006 LOOKUP3res partRes; 1007 if (lookupHandle(partFilePath, rpcStatus, partRes)) { 1008 if (bMarkPartial && partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.size > 0) { 1009 if (partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.type == NF3DIR) { 1010 setError(KIO::ERR_IS_DIRECTORY, partFilePath); 1011 return; 1012 } 1013 1014 bResume = slave()->canResume(partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.size); 1015 if (bResume) { 1016 resumeOffset = partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.size; 1017 } 1018 } 1019 } 1020 1021 // Remove the part file if we are not resuming 1022 if (!bResume) { 1023 if (!remove(partFilePath)) { 1024 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 1025 } 1026 } 1027 } 1028 1029 // Create the file if we are not resuming a parted transfer, 1030 // or if we are not using part files(bResume is false in that case) 1031 NFSFileHandle destFH; 1032 if (!bResume) { 1033 QString createPath; 1034 if (bMarkPartial) { 1035 createPath = partFilePath; 1036 } else { 1037 createPath = destPath; 1038 } 1039 1040 destFH = create(createPath, _mode); 1041 if (destFH.isInvalid()) { 1042 return; 1043 } 1044 } else { 1045 // Since we are resuming it's implied that we are using a part file, 1046 // which should exist at this point. 1047 destFH = getFileHandle(partFilePath); 1048 1049 qCDebug(LOG_KIO_NFS) << "Resuming old transfer"; 1050 } 1051 1052 // Check what buffer size we should use, always use the smallest one. 1053 const int bufferSize = (m_readBufferSize < m_writeBufferSize) ? m_readBufferSize : m_writeBufferSize; 1054 1055 WRITE3args writeArgs; 1056 memset(&writeArgs, 0, sizeof(writeArgs)); 1057 1058 destFH.toFH(writeArgs.file); 1059 writeArgs.offset = 0; 1060 writeArgs.data.data_val = new char[bufferSize]; 1061 writeArgs.stable = FILE_SYNC; 1062 1063 READ3args readArgs; 1064 memset(&readArgs, 0, sizeof(readArgs)); 1065 1066 srcFH.toFH(readArgs.file); 1067 readArgs.offset = 0; 1068 readArgs.count = bufferSize; 1069 1070 if (bResume) { 1071 writeArgs.offset = resumeOffset; 1072 readArgs.offset = resumeOffset; 1073 } 1074 1075 READ3res readRes; 1076 readRes.READ3res_u.resok.data.data_val = writeArgs.data.data_val; 1077 1078 WRITE3res writeRes; 1079 memset(&writeRes, 0, sizeof(WRITE3res)); 1080 1081 bool error = false; 1082 int bytesRead = 0; 1083 do { 1084 int clnt_stat = clnt_call(m_nfsClient, 1085 NFSPROC3_READ, 1086 (xdrproc_t)xdr_READ3args, 1087 reinterpret_cast<caddr_t>(&readArgs), 1088 (xdrproc_t)xdr_READ3res, 1089 reinterpret_cast<caddr_t>(&readRes), 1090 clnt_timeout); 1091 1092 if (!checkForError(clnt_stat, readRes.status, srcPath)) { 1093 error = true; 1094 break; 1095 } 1096 1097 bytesRead = readRes.READ3res_u.resok.data.data_len; 1098 1099 // We should only send out the total size and mimetype at the start of the transfer 1100 if (readArgs.offset == 0 || (bResume && writeArgs.offset == resumeOffset)) { 1101 QMimeDatabase db; 1102 QMimeType type = db.mimeTypeForFileNameAndData(src.fileName(), QByteArray::fromRawData(writeArgs.data.data_val, bytesRead)); 1103 slave()->mimeType(type.name()); 1104 1105 slave()->totalSize(readRes.READ3res_u.resok.file_attributes.post_op_attr_u.attributes.size); 1106 } 1107 1108 if (bytesRead > 0) { 1109 readArgs.offset += bytesRead; 1110 1111 writeArgs.count = bytesRead; 1112 writeArgs.data.data_len = bytesRead; 1113 1114 clnt_stat = clnt_call(m_nfsClient, 1115 NFSPROC3_WRITE, 1116 (xdrproc_t)xdr_WRITE3args, 1117 reinterpret_cast<caddr_t>(&writeArgs), 1118 (xdrproc_t)xdr_WRITE3res, 1119 reinterpret_cast<caddr_t>(&writeRes), 1120 clnt_timeout); 1121 1122 if (!checkForError(clnt_stat, writeRes.status, destPath)) { 1123 error = true; 1124 break; 1125 } 1126 1127 writeArgs.offset += bytesRead; 1128 1129 slave()->processedSize(readArgs.offset); 1130 } 1131 } while (bytesRead > 0); 1132 1133 delete[] writeArgs.data.data_val; 1134 1135 if (error) { 1136 if (bMarkPartial) { 1137 // Remove the part file if it's smaller than the minimum keep size. 1138 const unsigned int size = slave()->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); 1139 if (writeArgs.offset < size) { 1140 if (!remove(partFilePath)) { 1141 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 1142 } 1143 } 1144 } 1145 } else { 1146 // Rename partial file to its original name. 1147 if (bMarkPartial) { 1148 // Remove the destination file(if it exists) 1149 if (!getFileHandle(destPath).isInvalid() && !remove(destPath)) { 1150 qCDebug(LOG_KIO_NFS) << "Could not remove destination file" << destPath << ", ignoring..."; 1151 } 1152 1153 if (!rename(partFilePath, destPath)) { 1154 qCDebug(LOG_KIO_NFS) << "failed to rename" << partFilePath << "to" << destPath; 1155 setError(KIO::ERR_CANNOT_RENAME_PARTIAL, partFilePath); 1156 return; 1157 } 1158 } 1159 1160 // Restore modification time 1161 int rpcStatus; 1162 GETATTR3res attrRes; 1163 if (getAttr(srcPath, rpcStatus, attrRes)) { 1164 sattr3 attributes; 1165 memset(&attributes, 0, sizeof(attributes)); 1166 attributes.mtime.set_it = SET_TO_CLIENT_TIME; 1167 attributes.mtime.set_mtime_u.mtime.seconds = attrRes.GETATTR3res_u.resok.obj_attributes.mtime.seconds; 1168 attributes.mtime.set_mtime_u.mtime.nseconds = attrRes.GETATTR3res_u.resok.obj_attributes.mtime.nseconds; 1169 1170 SETATTR3res attrSetRes; 1171 if (!setAttr(destPath, attributes, rpcStatus, attrSetRes)) { 1172 qCDebug(LOG_KIO_NFS) << "Failed to restore mtime, ignoring..." << rpcStatus << attrSetRes.status; 1173 } 1174 } 1175 1176 qCDebug(LOG_KIO_NFS) << "Copied" << writeArgs.offset << "bytes of data"; 1177 1178 slave()->processedSize(readArgs.offset); 1179 } 1180 } 1181 1182 void NFSProtocolV3::copyFrom(const QUrl &src, const QUrl &dest, int _mode, KIO::JobFlags _flags) 1183 { 1184 qCDebug(LOG_KIO_NFS) << src << "to" << dest; 1185 1186 const QString srcPath(src.path()); 1187 const NFSFileHandle srcFH = getFileHandle(srcPath); 1188 if (srcFH.isInvalid()) { 1189 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 1190 return; 1191 } 1192 1193 const QString destPath(dest.path()); 1194 // The file exists and we don't want to overwrite. 1195 if (QFile::exists(destPath) && (_flags & KIO::Overwrite) == 0) { 1196 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 1197 return; 1198 } 1199 1200 // Is it a link? No need to copy the data then, just copy the link destination. 1201 if (srcFH.isLink()) { 1202 qCDebug(LOG_KIO_NFS) << "Is a link"; 1203 1204 // get the link dest 1205 int rpcStatus; 1206 READLINK3res readLinkRes; 1207 char nameBuf[NFS3_MAXPATHLEN]; 1208 if (!symLinkTarget(srcPath, rpcStatus, readLinkRes, nameBuf)) { 1209 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 1210 return; 1211 } 1212 1213 QFile::link(QString::fromLocal8Bit(readLinkRes.READLINK3res_u.resok.data), destPath); 1214 return; // done, no more to do 1215 } 1216 1217 if (m_readBufferSize == 0) { 1218 initPreferredSizes(srcFH); 1219 } 1220 1221 unsigned int resumeOffset = 0; 1222 bool bResume = false; 1223 const QFileInfo partInfo(destPath + QLatin1String(".part")); 1224 const bool bPartExists = partInfo.exists(); 1225 const bool bMarkPartial = slave()->configValue(QStringLiteral("MarkPartial"), true); 1226 1227 if (bMarkPartial && bPartExists && partInfo.size() > 0) { 1228 if (partInfo.isDir()) { 1229 setError(KIO::ERR_IS_DIRECTORY, partInfo.absoluteFilePath()); 1230 return; 1231 } 1232 1233 bResume = slave()->canResume(partInfo.size()); 1234 resumeOffset = partInfo.size(); 1235 } 1236 1237 if (bPartExists && !bResume) { 1238 QFile::remove(partInfo.absoluteFilePath()); 1239 } 1240 1241 QFile::OpenMode openMode; 1242 QString outFileName; 1243 if (bResume) { 1244 outFileName = partInfo.absoluteFilePath(); 1245 openMode = QFile::WriteOnly | QFile::Append; 1246 } else { 1247 outFileName = (bMarkPartial ? partInfo.absoluteFilePath() : destPath); 1248 openMode = QFile::WriteOnly | QFile::Truncate; 1249 } 1250 1251 QFile destFile(outFileName); 1252 if (!bResume) { 1253 QFile::Permissions perms; 1254 if (_mode == -1) { 1255 perms = QFile::ReadOwner | QFile::WriteOwner; 1256 } else { 1257 perms = KIO::convertPermissions(_mode | QFile::WriteOwner); 1258 } 1259 1260 destFile.setPermissions(perms); 1261 } 1262 1263 if (!destFile.open(openMode)) { 1264 switch (destFile.error()) { 1265 case QFile::OpenError: 1266 if (bResume) { 1267 setError(KIO::ERR_CANNOT_RESUME, destPath); 1268 } else { 1269 setError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, destPath); 1270 } 1271 break; 1272 case QFile::PermissionsError: 1273 setError(KIO::ERR_WRITE_ACCESS_DENIED, destPath); 1274 break; 1275 default: 1276 setError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, destPath); 1277 break; 1278 } 1279 return; 1280 } 1281 1282 READ3args readArgs; 1283 srcFH.toFH(readArgs.file); 1284 if (bResume) { 1285 readArgs.offset = resumeOffset; 1286 } else { 1287 readArgs.offset = 0; 1288 } 1289 readArgs.count = m_readBufferSize; 1290 1291 READ3res readRes; 1292 memset(&readRes, 0, sizeof(readres)); 1293 readRes.READ3res_u.resok.data.data_val = new char[m_readBufferSize]; 1294 readRes.READ3res_u.resok.data.data_len = m_readBufferSize; 1295 1296 bool error = false; 1297 unsigned long bytesToRead = 0, bytesRead = 0; 1298 do { 1299 int clnt_stat = clnt_call(m_nfsClient, 1300 NFSPROC3_READ, 1301 (xdrproc_t)xdr_READ3args, 1302 reinterpret_cast<caddr_t>(&readArgs), 1303 (xdrproc_t)xdr_READ3res, 1304 reinterpret_cast<caddr_t>(&readRes), 1305 clnt_timeout); 1306 1307 if (!checkForError(clnt_stat, readRes.status, destPath)) { 1308 error = true; 1309 break; 1310 } 1311 1312 bytesRead = readRes.READ3res_u.resok.count; 1313 1314 if (readArgs.offset == 0 || (bResume && readArgs.offset == resumeOffset)) { 1315 bytesToRead = readRes.READ3res_u.resok.file_attributes.post_op_attr_u.attributes.size; 1316 1317 slave()->totalSize(bytesToRead); 1318 1319 QMimeDatabase db; 1320 QMimeType type = db.mimeTypeForFileNameAndData(src.fileName(), QByteArray::fromRawData(readRes.READ3res_u.resok.data.data_val, bytesRead)); 1321 slave()->mimeType(type.name()); 1322 } 1323 1324 if (bytesRead > 0) { 1325 readArgs.offset += bytesRead; 1326 1327 if (destFile.write(readRes.READ3res_u.resok.data.data_val, bytesRead) < 0) { 1328 setError(KIO::ERR_CANNOT_WRITE, destPath); 1329 1330 error = true; 1331 break; 1332 } 1333 1334 slave()->processedSize(readArgs.offset); 1335 } 1336 } while (readArgs.offset < bytesToRead); 1337 1338 delete[] readRes.READ3res_u.resok.data.data_val; 1339 1340 // Close the file so we can modify the modification time later. 1341 destFile.close(); 1342 1343 if (error) { 1344 if (bMarkPartial) { 1345 // Remove the part file if it's smaller than the minimum keep 1346 const int size = slave()->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); 1347 if (partInfo.size() < size) { 1348 QFile::remove(partInfo.absoluteFilePath()); 1349 } 1350 } 1351 } else { 1352 // Rename partial file to its original name. 1353 if (bMarkPartial) { 1354 const QString sPart = partInfo.absoluteFilePath(); 1355 if (QFile::exists(destPath)) { 1356 QFile::remove(destPath); 1357 } 1358 if (!QFile::rename(sPart, destPath)) { 1359 qCDebug(LOG_KIO_NFS) << "failed to rename" << sPart << "to" << destPath; 1360 setError(KIO::ERR_CANNOT_RENAME_PARTIAL, sPart); 1361 return; 1362 } 1363 } 1364 1365 // Restore the mtime on the file. 1366 const QString mtimeStr = slave()->metaData("modified"); 1367 if (!mtimeStr.isEmpty()) { 1368 QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); 1369 if (dt.isValid()) { 1370 struct utimbuf utbuf; 1371 utbuf.actime = QFileInfo(destPath).lastRead().toSecsSinceEpoch(); // access time, unchanged 1372 utbuf.modtime = dt.toSecsSinceEpoch(); // modification time 1373 utime(QFile::encodeName(destPath).constData(), &utbuf); 1374 } 1375 } 1376 1377 qCDebug(LOG_KIO_NFS) << "Copied" << readArgs.offset << "bytes of data"; 1378 1379 slave()->processedSize(readArgs.offset); 1380 } 1381 } 1382 1383 void NFSProtocolV3::copyTo(const QUrl &src, const QUrl &dest, int _mode, KIO::JobFlags _flags) 1384 { 1385 qCDebug(LOG_KIO_NFS) << src << "to" << dest; 1386 1387 // The source does not exist, how strange 1388 const QString srcPath(src.path()); 1389 if (!QFile::exists(srcPath)) { 1390 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 1391 return; 1392 } 1393 1394 const QString destPath(dest.path()); 1395 if (isExportedDir(QFileInfo(destPath).path())) { 1396 setError(KIO::ERR_ACCESS_DENIED, destPath); 1397 return; 1398 } 1399 1400 // The file exists and we don't want to overwrite. 1401 if (!getFileHandle(destPath).isInvalid() && (_flags & KIO::Overwrite) == 0) { 1402 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 1403 return; 1404 } 1405 1406 // Is it a link? No need to copy the data then, just copy the link destination. 1407 const QString symlinkTarget = QFile::symLinkTarget(srcPath); 1408 if (!symlinkTarget.isEmpty()) { 1409 int rpcStatus; 1410 SYMLINK3res linkRes; 1411 1412 symLink(symlinkTarget, destPath, rpcStatus, linkRes); 1413 checkForError(rpcStatus, linkRes.status, symlinkTarget); 1414 return; // done, no more to do 1415 } 1416 1417 unsigned long resumeOffset = 0; 1418 bool bResume = false; 1419 const QString partFilePath = destPath + QLatin1String(".part"); 1420 const NFSFileHandle partFH = getFileHandle(partFilePath); 1421 const bool bPartExists = !partFH.isInvalid(); 1422 const bool bMarkPartial = slave()->configValue(QStringLiteral("MarkPartial"), true); 1423 1424 if (bPartExists) { 1425 int rpcStatus; 1426 LOOKUP3res partRes; 1427 if (lookupHandle(partFilePath, rpcStatus, partRes)) { 1428 if (bMarkPartial && partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.size > 0) { 1429 if (partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.type == NF3DIR) { 1430 setError(KIO::ERR_IS_DIRECTORY, partFilePath); 1431 return; 1432 } 1433 1434 bResume = slave()->canResume(partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.size); 1435 if (bResume) { 1436 resumeOffset = partRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.size; 1437 } 1438 } 1439 } 1440 1441 // Remove the part file if we are not resuming 1442 if (!bResume) { 1443 if (!remove(partFilePath)) { 1444 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 1445 } 1446 } 1447 } 1448 1449 // Open the source file 1450 QFile srcFile(srcPath); 1451 if (!srcFile.open(QIODevice::ReadOnly)) { 1452 setError(KIO::ERR_CANNOT_OPEN_FOR_READING, srcPath); 1453 return; 1454 } 1455 1456 // Create the file if we are not resuming a parted transfer, 1457 // or if we are not using part files (bResume is false in that case) 1458 NFSFileHandle destFH; 1459 if (!bResume) { 1460 QString createPath; 1461 if (bMarkPartial) { 1462 createPath = partFilePath; 1463 } else { 1464 createPath = destPath; 1465 } 1466 1467 destFH = create(createPath, _mode); 1468 if (destFH.isInvalid()) { 1469 return; 1470 } 1471 } else { 1472 // Since we are resuming it's implied that we are using a part file, 1473 // which should exist at this point. 1474 destFH = getFileHandle(partFilePath); 1475 1476 qCDebug(LOG_KIO_NFS) << "Resuming old transfer"; 1477 } 1478 1479 // Send the total size to the slave. 1480 slave()->totalSize(srcFile.size()); 1481 1482 // Get the optimal write buffer size 1483 if (m_writeBufferSize == 0) { 1484 initPreferredSizes(destFH); 1485 } 1486 1487 // Set up write arguments. 1488 WRITE3args writeArgs; 1489 memset(&writeArgs, 0, sizeof(writeArgs)); 1490 destFH.toFH(writeArgs.file); 1491 writeArgs.data.data_val = new char[m_writeBufferSize]; 1492 writeArgs.stable = FILE_SYNC; 1493 if (bResume) { 1494 writeArgs.offset = resumeOffset; 1495 } else { 1496 writeArgs.offset = 0; 1497 } 1498 1499 WRITE3res writeRes; 1500 memset(&writeRes, 0, sizeof(writeRes)); 1501 1502 bool error = false; 1503 int bytesRead = 0; 1504 do { 1505 memset(writeArgs.data.data_val, 0, m_writeBufferSize); 1506 1507 bytesRead = srcFile.read(writeArgs.data.data_val, m_writeBufferSize); 1508 if (bytesRead < 0) { 1509 setError(KIO::ERR_CANNOT_READ, srcPath); 1510 1511 error = true; 1512 break; 1513 } 1514 1515 if (bytesRead > 0) { 1516 writeArgs.count = bytesRead; 1517 writeArgs.data.data_len = bytesRead; 1518 1519 int clnt_stat = clnt_call(m_nfsClient, 1520 NFSPROC3_WRITE, 1521 (xdrproc_t)xdr_WRITE3args, 1522 reinterpret_cast<caddr_t>(&writeArgs), 1523 (xdrproc_t)xdr_WRITE3res, 1524 reinterpret_cast<caddr_t>(&writeRes), 1525 clnt_timeout); 1526 1527 if (!checkForError(clnt_stat, writeRes.status, destPath)) { 1528 error = true; 1529 break; 1530 } 1531 1532 writeArgs.offset += bytesRead; 1533 1534 slave()->processedSize(writeArgs.offset); 1535 } 1536 } while (bytesRead > 0); 1537 1538 delete[] writeArgs.data.data_val; 1539 1540 if (error) { 1541 if (bMarkPartial) { 1542 // Remove the part file if it's smaller than the minimum keep size. 1543 const unsigned int size = slave()->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); 1544 if (writeArgs.offset < size) { 1545 if (!remove(partFilePath)) { 1546 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 1547 } 1548 } 1549 } 1550 } else { 1551 // Rename partial file to its original name. 1552 if (bMarkPartial) { 1553 // Remove the destination file(if it exists) 1554 if (!getFileHandle(destPath).isInvalid() && !remove(destPath)) { 1555 qCDebug(LOG_KIO_NFS) << "Could not remove destination file" << destPath << ", ignoring..."; 1556 } 1557 1558 if (!rename(partFilePath, destPath)) { 1559 qCDebug(LOG_KIO_NFS) << "failed to rename" << partFilePath << "to" << destPath; 1560 setError(KIO::ERR_CANNOT_RENAME_PARTIAL, partFilePath); 1561 return; 1562 } 1563 } 1564 1565 // Restore the mtime on the file. 1566 const QString mtimeStr = slave()->metaData("modified"); 1567 if (!mtimeStr.isEmpty()) { 1568 QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); 1569 if (dt.isValid()) { 1570 sattr3 attributes; 1571 memset(&attributes, 0, sizeof(attributes)); 1572 attributes.mtime.set_it = SET_TO_CLIENT_TIME; 1573 attributes.mtime.set_mtime_u.mtime.seconds = dt.toSecsSinceEpoch(); 1574 attributes.mtime.set_mtime_u.mtime.nseconds = attributes.mtime.set_mtime_u.mtime.seconds * 1000000000ULL; 1575 1576 int rpcStatus; 1577 SETATTR3res attrSetRes; 1578 if (!setAttr(destPath, attributes, rpcStatus, attrSetRes)) { 1579 qCDebug(LOG_KIO_NFS) << "Failed to restore mtime, ignoring..." << rpcStatus << attrSetRes.status; 1580 } 1581 } 1582 } 1583 1584 qCDebug(LOG_KIO_NFS) << "Copied" << writeArgs.offset << "bytes of data"; 1585 1586 slave()->processedSize(writeArgs.offset); 1587 } 1588 } 1589 1590 void NFSProtocolV3::symlink(const QString &target, const QUrl &dest, KIO::JobFlags flags) 1591 { 1592 const QString destPath(dest.path()); 1593 if (isExportedDir(QFileInfo(destPath).path())) { 1594 setError(KIO::ERR_ACCESS_DENIED, destPath); 1595 return; 1596 } 1597 1598 if (!getFileHandle(destPath).isInvalid() && (flags & KIO::Overwrite) == 0) { 1599 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 1600 return; 1601 } 1602 1603 int rpcStatus; 1604 SYMLINK3res res; 1605 symLink(target, destPath, rpcStatus, res); 1606 checkForError(rpcStatus, res.status, destPath); 1607 } 1608 1609 void NFSProtocolV3::initPreferredSizes(const NFSFileHandle &fh) 1610 { 1611 FSINFO3args fsArgs; 1612 memset(&fsArgs, 0, sizeof(fsArgs)); 1613 fh.toFH(fsArgs.fsroot); 1614 1615 FSINFO3res fsRes; 1616 memset(&fsRes, 0, sizeof(fsRes)); 1617 1618 int clnt_stat = clnt_call(m_nfsClient, 1619 NFSPROC3_FSINFO, 1620 (xdrproc_t)xdr_FSINFO3args, 1621 reinterpret_cast<caddr_t>(&fsArgs), 1622 (xdrproc_t)xdr_FSINFO3res, 1623 reinterpret_cast<caddr_t>(&fsRes), 1624 clnt_timeout); 1625 1626 if (clnt_stat == RPC_SUCCESS && fsRes.status == NFS3_OK) { 1627 m_writeBufferSize = fsRes.FSINFO3res_u.resok.wtpref; 1628 m_readBufferSize = fsRes.FSINFO3res_u.resok.rtpref; 1629 m_readDirSize = fsRes.FSINFO3res_u.resok.dtpref; 1630 } else { 1631 m_writeBufferSize = NFS3_MAXDATA; 1632 m_readBufferSize = NFS3_MAXDATA; 1633 m_readDirSize = NFS3_MAXDATA; 1634 } 1635 1636 qCDebug(LOG_KIO_NFS) << "Preferred sizes - write" << m_writeBufferSize << ", read" << m_readBufferSize << ", read dir" << m_readDirSize; 1637 } 1638 1639 NFSFileHandle NFSProtocolV3::create(const QString &path, int mode) 1640 { 1641 qCDebug(LOG_KIO_NFS) << path; 1642 1643 if (!isConnected()) { 1644 setError(KIO::ERR_CANNOT_CONNECT, path); 1645 return NFSFileHandle(); 1646 } 1647 1648 const QFileInfo fileInfo(path); 1649 1650 const NFSFileHandle directoryFH = getFileHandle(fileInfo.path()); 1651 if (directoryFH.isInvalid()) { 1652 setError(KIO::ERR_DOES_NOT_EXIST, fileInfo.path()); 1653 return NFSFileHandle(); 1654 } 1655 1656 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 1657 1658 int rpcStatus = 0; 1659 CREATE3res result; 1660 memset(&result, 0, sizeof(result)); 1661 1662 CREATE3args args; 1663 memset(&args, 0, sizeof(args)); 1664 1665 directoryFH.toFH(args.where.dir); 1666 args.where.name = tmpName.data(); 1667 1668 args.how.createhow3_u.obj_attributes.mode.set_it = true; 1669 args.how.createhow3_u.obj_attributes.uid.set_it = true; 1670 args.how.createhow3_u.obj_attributes.gid.set_it = true; 1671 args.how.createhow3_u.obj_attributes.size.set_it = true; 1672 1673 if (mode == -1) { 1674 args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = 0644; 1675 } else { 1676 args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = mode; 1677 } 1678 args.how.createhow3_u.obj_attributes.uid.set_uid3_u.uid = geteuid(); 1679 args.how.createhow3_u.obj_attributes.gid.set_gid3_u.gid = getegid(); 1680 args.how.createhow3_u.obj_attributes.size.set_size3_u.size = 0; 1681 1682 rpcStatus = clnt_call(m_nfsClient, 1683 NFSPROC3_CREATE, 1684 (xdrproc_t)xdr_CREATE3args, 1685 reinterpret_cast<caddr_t>(&args), 1686 (xdrproc_t)xdr_CREATE3res, 1687 reinterpret_cast<caddr_t>(&result), 1688 clnt_timeout); 1689 1690 if (rpcStatus != RPC_SUCCESS || result.status != NFS3_OK) { 1691 checkForError(rpcStatus, result.status, path); 1692 return NFSFileHandle(); 1693 } 1694 1695 return result.CREATE3res_u.resok.obj.post_op_fh3_u.handle; 1696 } 1697 1698 bool NFSProtocolV3::getAttr(const QString &path, int &rpcStatus, GETATTR3res &result) 1699 { 1700 qCDebug(LOG_KIO_NFS) << path; 1701 1702 memset(&rpcStatus, 0, sizeof(int)); 1703 memset(&result, 0, sizeof(result)); 1704 1705 if (!isConnected()) { 1706 result.status = NFS3ERR_ACCES; 1707 return false; 1708 } 1709 1710 const NFSFileHandle fileFH = getFileHandle(path); 1711 if (fileFH.isInvalid()) { 1712 result.status = NFS3ERR_NOENT; 1713 return false; 1714 } 1715 1716 GETATTR3args args; 1717 memset(&args, 0, sizeof(GETATTR3args)); 1718 fileFH.toFH(args.object); 1719 1720 rpcStatus = clnt_call(m_nfsClient, 1721 NFSPROC3_GETATTR, 1722 (xdrproc_t)xdr_GETATTR3args, 1723 reinterpret_cast<caddr_t>(&args), 1724 (xdrproc_t)xdr_GETATTR3res, 1725 reinterpret_cast<caddr_t>(&result), 1726 clnt_timeout); 1727 1728 return (rpcStatus == RPC_SUCCESS && result.status == NFS3_OK); 1729 } 1730 1731 bool NFSProtocolV3::lookupHandle(const QString &path, int &rpcStatus, LOOKUP3res &result) 1732 { 1733 memset(&rpcStatus, 0, sizeof(int)); 1734 memset(&result, 0, sizeof(result)); 1735 1736 if (!isConnected()) { 1737 result.status = NFS3ERR_ACCES; 1738 return false; 1739 } 1740 1741 const QFileInfo fileInfo(path); 1742 1743 const NFSFileHandle parentFH = getFileHandle(fileInfo.path()); 1744 if (parentFH.isInvalid()) { 1745 result.status = NFS3ERR_NOENT; 1746 return false; 1747 } 1748 1749 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 1750 1751 // do the rpc call 1752 LOOKUP3args args; 1753 memset(&args, 0, sizeof(args)); 1754 parentFH.toFH(args.what.dir); 1755 args.what.name = tmpName.data(); 1756 1757 rpcStatus = clnt_call(m_nfsClient, 1758 NFSPROC3_LOOKUP, 1759 (xdrproc_t)xdr_LOOKUP3args, 1760 reinterpret_cast<caddr_t>(&args), 1761 (xdrproc_t)xdr_LOOKUP3res, 1762 reinterpret_cast<caddr_t>(&result), 1763 clnt_timeout); 1764 1765 return (rpcStatus == RPC_SUCCESS && result.status == NFS3_OK); 1766 } 1767 1768 bool NFSProtocolV3::symLinkTarget(const QString &path, int &rpcStatus, READLINK3res &result, char *dataBuffer) 1769 { 1770 qCDebug(LOG_KIO_NFS) << path; 1771 1772 memset(&rpcStatus, 0, sizeof(int)); 1773 memset(&result, 0, sizeof(result)); 1774 1775 const NFSFileHandle fh = getFileHandle(path); 1776 if (fh.isInvalid()) { 1777 result.status = NFS3ERR_NOENT; 1778 return false; 1779 } 1780 1781 READLINK3args readLinkArgs; 1782 memset(&readLinkArgs, 0, sizeof(readLinkArgs)); 1783 if (fh.isLink() && !fh.isBadLink()) { 1784 fh.toFHLink(readLinkArgs.symlink); 1785 } else { 1786 fh.toFH(readLinkArgs.symlink); 1787 } 1788 1789 result.READLINK3res_u.resok.data = dataBuffer; 1790 1791 rpcStatus = clnt_call(m_nfsClient, 1792 NFSPROC3_READLINK, 1793 (xdrproc_t)xdr_READLINK3args, 1794 reinterpret_cast<caddr_t>(&readLinkArgs), 1795 (xdrproc_t)xdr_READLINK3res, 1796 reinterpret_cast<caddr_t>(&result), 1797 clnt_timeout); 1798 1799 return (rpcStatus == RPC_SUCCESS && result.status == NFS3_OK); 1800 } 1801 1802 bool NFSProtocolV3::remove(const QString &path) 1803 { 1804 int rpcStatus; 1805 REMOVE3res result; 1806 1807 return remove(path, rpcStatus, result); 1808 } 1809 1810 bool NFSProtocolV3::remove(const QString &path, int &rpcStatus, REMOVE3res &result) 1811 { 1812 qCDebug(LOG_KIO_NFS) << path; 1813 1814 memset(&rpcStatus, 0, sizeof(int)); 1815 memset(&result, 0, sizeof(result)); 1816 1817 if (!isConnected()) { 1818 result.status = NFS3ERR_PERM; 1819 return false; 1820 } 1821 1822 const QFileInfo fileInfo(path); 1823 if (isExportedDir(fileInfo.path())) { 1824 result.status = NFS3ERR_ACCES; 1825 return false; 1826 } 1827 1828 const NFSFileHandle directoryFH = getFileHandle(fileInfo.path()); 1829 if (directoryFH.isInvalid()) { 1830 result.status = NFS3ERR_NOENT; 1831 return false; 1832 } 1833 1834 int rpcLookupStatus; 1835 LOOKUP3res lookupRes; 1836 if (!lookupHandle(path, rpcLookupStatus, lookupRes)) { 1837 result.status = NFS3ERR_NOENT; 1838 return false; 1839 } 1840 1841 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 1842 1843 REMOVE3args args; 1844 memset(&args, 0, sizeof(args)); 1845 directoryFH.toFH(args.object.dir); 1846 args.object.name = tmpName.data(); 1847 1848 if (lookupRes.LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes.type != NF3DIR) { 1849 rpcStatus = clnt_call(m_nfsClient, 1850 NFSPROC3_REMOVE, 1851 (xdrproc_t)xdr_REMOVE3args, 1852 reinterpret_cast<caddr_t>(&args), 1853 (xdrproc_t)xdr_REMOVE3res, 1854 reinterpret_cast<caddr_t>(&result), 1855 clnt_timeout); 1856 } else { 1857 rpcStatus = clnt_call(m_nfsClient, 1858 NFSPROC3_RMDIR, 1859 (xdrproc_t)xdr_RMDIR3args, 1860 reinterpret_cast<caddr_t>(&args), 1861 (xdrproc_t)xdr_RMDIR3res, 1862 reinterpret_cast<caddr_t>(&result), 1863 clnt_timeout); 1864 } 1865 1866 bool ret = (rpcStatus == RPC_SUCCESS && result.status == NFS3_OK); 1867 if (ret) { 1868 // Remove it from the cache as well 1869 removeFileHandle(path); 1870 } 1871 1872 return ret; 1873 } 1874 1875 bool NFSProtocolV3::rename(const QString &src, const QString &dest) 1876 { 1877 int rpcStatus; 1878 RENAME3res result; 1879 1880 return rename(src, dest, rpcStatus, result); 1881 } 1882 1883 bool NFSProtocolV3::rename(const QString &src, const QString &dest, int &rpcStatus, RENAME3res &result) 1884 { 1885 qCDebug(LOG_KIO_NFS) << src << dest; 1886 1887 memset(&rpcStatus, 0, sizeof(int)); 1888 memset(&result, 0, sizeof(result)); 1889 1890 const QFileInfo srcFileInfo(src); 1891 if (isExportedDir(srcFileInfo.path())) { 1892 result.status = NFS3ERR_ACCES; 1893 return false; 1894 } 1895 1896 const NFSFileHandle srcDirectoryFH = getFileHandle(srcFileInfo.path()); 1897 if (srcDirectoryFH.isInvalid()) { 1898 result.status = NFS3ERR_NOENT; 1899 return false; 1900 } 1901 1902 const QFileInfo destFileInfo(dest); 1903 if (isExportedDir(destFileInfo.path())) { 1904 result.status = NFS3ERR_ACCES; 1905 return false; 1906 } 1907 1908 const NFSFileHandle destDirectoryFH = getFileHandle(destFileInfo.path()); 1909 if (destDirectoryFH.isInvalid()) { 1910 result.status = NFS3ERR_NOENT; 1911 return false; 1912 } 1913 1914 RENAME3args args; 1915 memset(&args, 0, sizeof(args)); 1916 1917 QByteArray srcByteName = QFile::encodeName(srcFileInfo.fileName()); 1918 srcDirectoryFH.toFH(args.from.dir); 1919 args.from.name = srcByteName.data(); 1920 1921 QByteArray destByteName = QFile::encodeName(destFileInfo.fileName()); 1922 destDirectoryFH.toFH(args.to.dir); 1923 args.to.name = destByteName.data(); 1924 1925 rpcStatus = clnt_call(m_nfsClient, 1926 NFSPROC3_RENAME, 1927 (xdrproc_t)xdr_RENAME3args, 1928 reinterpret_cast<caddr_t>(&args), 1929 (xdrproc_t)xdr_RENAME3res, 1930 reinterpret_cast<caddr_t>(&result), 1931 clnt_timeout); 1932 1933 bool ret = (rpcStatus == RPC_SUCCESS && result.status == NFS3_OK); 1934 if (ret) { 1935 // Can we actually find the new handle? 1936 int lookupStatus; 1937 LOOKUP3res lookupRes; 1938 if (lookupHandle(dest, lookupStatus, lookupRes)) { 1939 // Remove the old file, and add the new one 1940 removeFileHandle(src); 1941 addFileHandle(dest, lookupRes.LOOKUP3res_u.resok.object); 1942 } 1943 } 1944 1945 return ret; 1946 } 1947 1948 bool NFSProtocolV3::setAttr(const QString &path, const sattr3 &attributes, int &rpcStatus, SETATTR3res &result) 1949 { 1950 qCDebug(LOG_KIO_NFS) << path; 1951 1952 memset(&rpcStatus, 0, sizeof(int)); 1953 memset(&result, 0, sizeof(result)); 1954 1955 const NFSFileHandle fh = getFileHandle(path); 1956 if (fh.isInvalid()) { 1957 result.status = NFS3ERR_NOENT; 1958 return false; 1959 } 1960 1961 SETATTR3args setAttrArgs; 1962 memset(&setAttrArgs, 0, sizeof(setAttrArgs)); 1963 fh.toFH(setAttrArgs.object); 1964 memcpy(&setAttrArgs.new_attributes, &attributes, sizeof(attributes)); 1965 1966 rpcStatus = clnt_call(m_nfsClient, 1967 NFSPROC3_SETATTR, 1968 (xdrproc_t)xdr_SETATTR3args, 1969 reinterpret_cast<caddr_t>(&setAttrArgs), 1970 (xdrproc_t)xdr_SETATTR3res, 1971 reinterpret_cast<caddr_t>(&result), 1972 clnt_timeout); 1973 1974 return (rpcStatus == RPC_SUCCESS && result.status == NFS3_OK); 1975 } 1976 1977 bool NFSProtocolV3::symLink(const QString &target, const QString &dest, int &rpcStatus, SYMLINK3res &result) 1978 { 1979 qCDebug(LOG_KIO_NFS) << target << dest; 1980 1981 memset(&rpcStatus, 0, sizeof(int)); 1982 memset(&result, 0, sizeof(result)); 1983 1984 // Remove dest first, we don't really care about the return value at this point, 1985 // the symlink call will fail if dest was not removed correctly. 1986 remove(dest); 1987 1988 const QFileInfo fileInfo(dest); 1989 1990 const NFSFileHandle fh = getFileHandle(fileInfo.path()); 1991 if (fh.isInvalid()) { 1992 result.status = NFS3ERR_NOENT; 1993 return false; 1994 } 1995 1996 QByteArray tmpStr = QFile::encodeName(fileInfo.fileName()); 1997 QByteArray tmpStr2 = QFile::encodeName(target); 1998 1999 SYMLINK3args symLinkArgs; 2000 memset(&symLinkArgs, 0, sizeof(symLinkArgs)); 2001 2002 fh.toFH(symLinkArgs.where.dir); 2003 symLinkArgs.where.name = tmpStr.data(); 2004 symLinkArgs.symlink.symlink_data = tmpStr2.data(); 2005 2006 rpcStatus = clnt_call(m_nfsClient, 2007 NFSPROC3_SYMLINK, 2008 (xdrproc_t)xdr_SYMLINK3args, 2009 reinterpret_cast<caddr_t>(&symLinkArgs), 2010 (xdrproc_t)xdr_SYMLINK3res, 2011 reinterpret_cast<caddr_t>(&result), 2012 clnt_timeout); 2013 2014 // Add the new handle to the cache 2015 NFSFileHandle destFH = getFileHandle(dest); 2016 if (!destFH.isInvalid()) { 2017 addFileHandle(dest, destFH); 2018 } 2019 2020 return (rpcStatus == RPC_SUCCESS && result.status == NFS3_OK); 2021 } 2022 2023 // This function and completeBadLinkUDSEntry() must use KIO::UDSEntry::replace() 2024 // because they may be called with a UDSEntry that has already been partially 2025 // filled in by NFSProtocol::createVirtualDirEntry(). 2026 2027 void NFSProtocolV3::completeUDSEntry(KIO::UDSEntry &entry, const fattr3 &attributes) 2028 { 2029 entry.replace(KIO::UDSEntry::UDS_SIZE, attributes.size); 2030 entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, attributes.mtime.seconds); 2031 entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, attributes.atime.seconds); 2032 2033 // Some servers still send the file type information in the mode, even though 2034 // the reference specifies NFSv3 shouldn't, so we need to work around that here. 2035 // Not sure this is the best way to do it, but it works. 2036 if (attributes.mode > 0777) { 2037 entry.replace(KIO::UDSEntry::UDS_ACCESS, (attributes.mode & 07777)); 2038 } else { 2039 entry.replace(KIO::UDSEntry::UDS_ACCESS, attributes.mode); 2040 } 2041 2042 unsigned int type; 2043 switch (attributes.type) { 2044 case NF3DIR: 2045 type = S_IFDIR; 2046 break; 2047 case NF3BLK: 2048 type = S_IFBLK; 2049 break; 2050 case NF3CHR: 2051 type = S_IFCHR; 2052 break; 2053 case NF3LNK: 2054 type = S_IFLNK; 2055 break; 2056 case NF3SOCK: 2057 type = S_IFSOCK; 2058 break; 2059 case NF3FIFO: 2060 type = S_IFIFO; 2061 break; 2062 default: 2063 type = S_IFREG; 2064 break; 2065 } 2066 entry.replace(KIO::UDSEntry::UDS_FILE_TYPE, type); 2067 2068 NFSProtocol::completeUDSEntry(entry, attributes.uid, attributes.gid); 2069 } 2070 2071 void NFSProtocolV3::completeBadLinkUDSEntry(KIO::UDSEntry &entry, const fattr3 &attributes) 2072 { 2073 entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, attributes.mtime.seconds); 2074 entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, attributes.atime.seconds); 2075 2076 NFSProtocol::completeInvalidUDSEntry(entry); 2077 }