File indexing completed on 2024-04-14 04:52:32
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 "nfsv2.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 NFSv2 reference see http://tools.ietf.org/html/rfc1094 0046 0047 // This is for NFS version 2. 0048 #define NFSPROG 100003UL 0049 #define NFSVERS 2UL 0050 0051 NFSProtocolV2::NFSProtocolV2(NFSSlave *slave) 0052 : NFSProtocol(slave) 0053 , m_mountClient(nullptr) 0054 , m_mountSock(-1) 0055 , m_nfsClient(nullptr) 0056 , m_nfsSock(-1) 0057 { 0058 qCDebug(LOG_KIO_NFS); 0059 0060 clnt_timeout.tv_sec = 20; 0061 clnt_timeout.tv_usec = 0; 0062 } 0063 0064 NFSProtocolV2::~NFSProtocolV2() 0065 { 0066 closeConnection(); 0067 } 0068 0069 bool NFSProtocolV2::isCompatible(bool &connectionError) 0070 { 0071 int ret = -1; 0072 0073 CLIENT *client = nullptr; 0074 int sock = 0; 0075 if (NFSProtocol::openConnection(currentHost(), NFSPROG, NFSVERS, client, sock) == 0) { 0076 // Check if the NFS version is compatible 0077 ret = clnt_call(client, NFSPROC_NULL, (xdrproc_t)xdr_void, nullptr, (xdrproc_t)xdr_void, nullptr, clnt_timeout); 0078 0079 connectionError = false; 0080 } else { 0081 qCDebug(LOG_KIO_NFS) << "openConnection failed"; 0082 connectionError = true; 0083 } 0084 0085 if (sock != -1) { 0086 ::close(sock); 0087 } 0088 0089 if (client != nullptr) { 0090 CLNT_DESTROY(client); 0091 } 0092 0093 qCDebug(LOG_KIO_NFS) << "RPC status" << ret << "connectionError" << connectionError; 0094 return (ret == RPC_SUCCESS); 0095 } 0096 0097 bool NFSProtocolV2::isConnected() const 0098 { 0099 return (m_nfsClient != nullptr); 0100 } 0101 0102 void NFSProtocolV2::closeConnection() 0103 { 0104 qCDebug(LOG_KIO_NFS); 0105 0106 // Unmount all exported dirs(if any) 0107 if (m_mountClient != nullptr) { 0108 clnt_call(m_mountClient, MOUNTPROC_UMNTALL, (xdrproc_t)xdr_void, nullptr, (xdrproc_t)xdr_void, nullptr, clnt_timeout); 0109 } 0110 0111 if (m_mountSock >= 0) { 0112 ::close(m_mountSock); 0113 m_mountSock = -1; 0114 } 0115 if (m_nfsSock >= 0) { 0116 ::close(m_nfsSock); 0117 m_nfsSock = -1; 0118 } 0119 0120 if (m_mountClient != nullptr) { 0121 CLNT_DESTROY(m_mountClient); 0122 m_mountClient = nullptr; 0123 } 0124 if (m_nfsClient != nullptr) { 0125 CLNT_DESTROY(m_nfsClient); 0126 m_nfsClient = nullptr; 0127 } 0128 } 0129 0130 NFSFileHandle NFSProtocolV2::lookupFileHandle(const QString &path) 0131 { 0132 NFSFileHandle fh; 0133 int rpcStatus; 0134 diropres res; 0135 0136 if (lookupHandle(path, rpcStatus, res)) { 0137 fh = res.diropres_u.diropres.file; 0138 0139 // It it a link? Get the link target. 0140 if (res.diropres_u.diropres.attributes.type == NFLNK) { 0141 nfs_fh readLinkArgs; 0142 fh.toFH(readLinkArgs); 0143 0144 char dataBuffer[NFS_MAXPATHLEN]; 0145 0146 readlinkres readLinkRes; 0147 memset(&readLinkRes, 0, sizeof(readLinkRes)); 0148 readLinkRes.readlinkres_u.data = dataBuffer; 0149 0150 int rpcStatus = clnt_call(m_nfsClient, 0151 NFSPROC_READLINK, 0152 (xdrproc_t)xdr_nfs_fh, 0153 reinterpret_cast<caddr_t>(&readLinkArgs), 0154 (xdrproc_t)xdr_readlinkres, 0155 reinterpret_cast<caddr_t>(&readLinkRes), 0156 clnt_timeout); 0157 0158 if (rpcStatus == RPC_SUCCESS && readLinkRes.status == NFS_OK) { // get the absolute link target 0159 QString linkPath = QString::fromLocal8Bit(readLinkRes.readlinkres_u.data); 0160 linkPath = QFileInfo(QFileInfo(path).path(), linkPath).absoluteFilePath(); 0161 0162 // As with the tests done in NFSProtocol::isValidLink(), the link 0163 // target may not be valid on the NFS server (i.e. it may point 0164 // outside of the exported directories). Check for this before 0165 // calling lookupHandle() on the target of the link, as otherwise 0166 // an error will be set which is not relevant. 0167 if (isValidPath(linkPath)) { 0168 diropres linkRes; 0169 if (lookupHandle(linkPath, rpcStatus, linkRes)) { 0170 NFSFileHandle linkFh = linkRes.diropres_u.diropres.file; 0171 linkFh.setLinkSource(res.diropres_u.diropres.file); 0172 qCDebug(LOG_KIO_NFS) << "Found link target" << linkPath; 0173 return linkFh; 0174 } 0175 } 0176 } 0177 0178 // If we have reached this point the file is a link, 0179 // but we failed to read the target. 0180 fh.setBadLink(); 0181 qCDebug(LOG_KIO_NFS) << "Invalid link" << path; 0182 } 0183 } 0184 0185 return fh; 0186 } 0187 0188 /* Open connection connects to the mount daemon on the server side. 0189 In order to do this it needs authentication and calls auth_unix_create(). 0190 Then it asks the mount daemon for the exported shares. Then it tries 0191 to mount all these shares. If this succeeded for at least one of them, 0192 a client for the nfs daemon is created. 0193 */ 0194 void NFSProtocolV2::openConnection() 0195 { 0196 const QString host = currentHost(); 0197 qCDebug(LOG_KIO_NFS) << "to" << host; 0198 0199 KIO::Error connErr = NFSProtocol::openConnection(host, MOUNTPROG, MOUNTVERS, m_mountClient, m_mountSock); 0200 if (connErr != 0) { 0201 // Close the connection and send the error id to the slave 0202 closeConnection(); 0203 setError(connErr, host); 0204 return; 0205 } 0206 0207 exports exportlist; 0208 memset(&exportlist, 0, sizeof(exportlist)); 0209 0210 int clnt_stat = 0211 clnt_call(m_mountClient, MOUNTPROC_EXPORT, (xdrproc_t)xdr_void, nullptr, (xdrproc_t)xdr_exports, reinterpret_cast<caddr_t>(&exportlist), clnt_timeout); 0212 0213 if (!checkForError(clnt_stat, 0, host.toLatin1())) { 0214 return; 0215 } 0216 0217 int exportsCount = 0; 0218 bool mountHint = false; 0219 0220 fhstatus fhStatus; 0221 for (; exportlist != nullptr; exportlist = exportlist->ex_next, exportsCount++) { 0222 memset(&fhStatus, 0, sizeof(fhStatus)); 0223 0224 clnt_stat = clnt_call(m_mountClient, 0225 MOUNTPROC_MNT, 0226 (xdrproc_t)xdr_dirpath, 0227 reinterpret_cast<caddr_t>(&exportlist->ex_dir), 0228 (xdrproc_t)xdr_fhstatus, 0229 reinterpret_cast<caddr_t>(&fhStatus), 0230 clnt_timeout); 0231 0232 QString fname = QFileInfo(QDir::root(), exportlist->ex_dir).filePath(); 0233 if (fhStatus.fhs_status == 0) { 0234 // Check if the directory is already noted as exported, 0235 // if so there is no need to add it again. 0236 if (NFSProtocol::isExportedDir(fname)) { 0237 continue; 0238 } 0239 0240 // Save the exported directory and its NFS file handle. 0241 addFileHandle(fname, static_cast<NFSFileHandle>(fhStatus.fhstatus_u.fhs_fhandle)); 0242 addExportedDir(fname); 0243 } else { // mount failed with error 0244 qCDebug(LOG_KIO_NFS) << "Cannot mount" << fname << "- status" << fhStatus.fhs_status; 0245 0246 // Even if the mount failed, record the directory path as exported 0247 // so that it can be listed as a virtual directory. However, do 0248 // not record its (invalid) file handle in the cache. Trying to 0249 // access the directory in any way other than just listing it, or 0250 // accessing anything below it, will be detected in 0251 // NFSProtocol::getFileHandle() and fail with an appropriate 0252 // error. 0253 if (!isExportedDir(fname)) 0254 addExportedDir(fname); 0255 0256 // Many modern NFS servers by default reject any access attempted to 0257 // them from a non-reserved source port (i.e. above 1024). Since 0258 // this KIO slave runs as a normal user, it is not able to use the 0259 // reserved port numbers and hence the access will be rejected. Show 0260 // a hint if this could possibly be the problem - only once, as the 0261 // server may have many exported directories. 0262 if (fhStatus.fhs_status == NFSERR_ACCES) { 0263 if (!mountHint) { 0264 qCDebug(LOG_KIO_NFS) << "Check that the NFS server is exporting the filesystem"; 0265 qCDebug(LOG_KIO_NFS) << "with appropriate access permissions. Note that it must"; 0266 qCDebug(LOG_KIO_NFS) << "allow mount requests originating from an unprivileged"; 0267 qCDebug(LOG_KIO_NFS) << "source port (see exports(5), the 'insecure' option may"; 0268 qCDebug(LOG_KIO_NFS) << "be required)."; 0269 mountHint = true; 0270 } 0271 } 0272 } 0273 } 0274 0275 // If nothing can be mounted then there is no point trying to open the 0276 // NFS server connection here. However, call openConnection() anyway 0277 // and pretend that we are connected so that listing virtual directories 0278 // will work. 0279 if ((connErr = NFSProtocol::openConnection(host, NFSPROG, NFSVERS, m_nfsClient, m_nfsSock)) != 0) { 0280 closeConnection(); 0281 setError(connErr, host); 0282 } 0283 0284 slave()->connected(); 0285 0286 qCDebug(LOG_KIO_NFS) << "openConnection succeeded"; 0287 } 0288 0289 void NFSProtocolV2::listDir(const QUrl &url) 0290 { 0291 qCDebug(LOG_KIO_NFS) << url; 0292 0293 const QString path = listDirInternal(url); // check path, list virtual dir 0294 if (path.isEmpty()) 0295 return; // no more to do 0296 0297 const NFSFileHandle fh = getFileHandle(path); 0298 if (fh.isInvalid() || fh.isBadLink()) { 0299 setError(KIO::ERR_DOES_NOT_EXIST, path); 0300 return; 0301 } 0302 0303 readdirargs listargs; 0304 memset(&listargs, 0, sizeof(listargs)); 0305 listargs.count = 1024 * sizeof(entry); 0306 fh.toFH(listargs.dir); 0307 0308 readdirres listres; 0309 0310 QStringList filesToList; 0311 entry *lastEntry = nullptr; 0312 do { 0313 memset(&listres, 0, sizeof(listres)); 0314 // In case that we didn't get all entries we need to set the cookie to the last one we actually received. 0315 if (lastEntry != nullptr) { 0316 memcpy(listargs.cookie, lastEntry->cookie, NFS_COOKIESIZE); 0317 } 0318 0319 int clnt_stat = clnt_call(m_nfsClient, 0320 NFSPROC_READDIR, 0321 (xdrproc_t)xdr_readdirargs, 0322 reinterpret_cast<caddr_t>(&listargs), 0323 (xdrproc_t)xdr_readdirres, 0324 reinterpret_cast<caddr_t>(&listres), 0325 clnt_timeout); 0326 0327 if (!checkForError(clnt_stat, listres.status, path)) { 0328 return; 0329 } 0330 0331 for (entry *dirEntry = listres.readdirres_u.reply.entries; dirEntry != nullptr; dirEntry = dirEntry->nextentry) { 0332 if (dirEntry->name != QString("..")) { 0333 filesToList.append(QFile::decodeName(dirEntry->name)); 0334 } 0335 0336 lastEntry = dirEntry; 0337 } 0338 } while (!listres.readdirres_u.reply.eof); 0339 0340 for (QStringList::const_iterator it = filesToList.constBegin(); it != filesToList.constEnd(); ++it) { 0341 QString filePath = QFileInfo(QDir(path), (*it)).filePath(); 0342 0343 int rpcStatus; 0344 diropres dirres; 0345 if (!lookupHandle(filePath, rpcStatus, dirres)) { 0346 qCDebug(LOG_KIO_NFS) << "Failed to lookup" << filePath << ", rpc:" << rpcStatus << ", nfs:" << dirres.status; 0347 // Try the next file instead of failing 0348 continue; 0349 } 0350 0351 KIO::UDSEntry entry; 0352 entry.fastInsert(KIO::UDSEntry::UDS_NAME, (*it)); 0353 0354 // is it a symlink ? 0355 if (dirres.diropres_u.diropres.attributes.type == NFLNK) { 0356 int rpcStatus; 0357 readlinkres readLinkRes; 0358 char nameBuf[NFS_MAXPATHLEN]; 0359 if (symLinkTarget(filePath, rpcStatus, readLinkRes, nameBuf)) { 0360 const QString linkDest = QString::fromLocal8Bit(readLinkRes.readlinkres_u.data); 0361 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkDest); 0362 0363 bool badLink = true; 0364 NFSFileHandle linkFH; 0365 if (isValidLink(path, linkDest)) { 0366 QString linkPath = QFileInfo(path, linkDest).absoluteFilePath(); 0367 0368 int rpcStatus; 0369 diropres lookupRes; 0370 if (lookupHandle(linkPath, rpcStatus, lookupRes)) { 0371 attrstat attrAndStat; 0372 if (getAttr(linkPath, rpcStatus, attrAndStat)) { 0373 badLink = false; 0374 0375 linkFH = lookupRes.diropres_u.diropres.file; 0376 linkFH.setLinkSource(dirres.diropres_u.diropres.file); 0377 0378 completeUDSEntry(entry, attrAndStat.attrstat_u.attributes); 0379 } 0380 } 0381 } 0382 0383 if (badLink) { 0384 linkFH = dirres.diropres_u.diropres.file; 0385 linkFH.setBadLink(); 0386 0387 completeBadLinkUDSEntry(entry, dirres.diropres_u.diropres.attributes); 0388 } 0389 0390 addFileHandle(filePath, linkFH); 0391 } else { 0392 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, i18n("Unknown target")); 0393 completeBadLinkUDSEntry(entry, dirres.diropres_u.diropres.attributes); 0394 } 0395 } else { 0396 addFileHandle(filePath, dirres.diropres_u.diropres.file); 0397 completeUDSEntry(entry, dirres.diropres_u.diropres.attributes); 0398 } 0399 0400 slave()->listEntry(entry); 0401 } 0402 } 0403 0404 void NFSProtocolV2::stat(const QUrl &url) 0405 { 0406 qCDebug(LOG_KIO_NFS) << url; 0407 0408 const QString path = statInternal(url); // check path, process virtual dir 0409 if (path.isEmpty()) 0410 return; // no more to do 0411 0412 const NFSFileHandle fh = getFileHandle(path); 0413 if (fh.isInvalid()) { 0414 qCDebug(LOG_KIO_NFS) << "File handle is invalid"; 0415 setError(KIO::ERR_DOES_NOT_EXIST, path); 0416 return; 0417 } 0418 0419 int rpcStatus; 0420 attrstat attrAndStat; 0421 if (!getAttr(path, rpcStatus, attrAndStat)) { 0422 checkForError(rpcStatus, attrAndStat.status, path); 0423 return; 0424 } 0425 0426 const QFileInfo fileInfo(path); 0427 0428 KIO::UDSEntry entry; 0429 entry.fastInsert(KIO::UDSEntry::UDS_NAME, fileInfo.fileName()); 0430 0431 // Is it a symlink? 0432 if (attrAndStat.attrstat_u.attributes.type == NFLNK) { 0433 qCDebug(LOG_KIO_NFS) << "It's a symlink"; 0434 0435 QString linkDest; 0436 0437 int rpcStatus; 0438 readlinkres readLinkRes; 0439 char nameBuf[NFS_MAXPATHLEN]; 0440 if (symLinkTarget(path, rpcStatus, readLinkRes, nameBuf)) { 0441 linkDest = QString::fromLocal8Bit(readLinkRes.readlinkres_u.data); 0442 } else { 0443 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, i18n("Unknown target")); 0444 completeBadLinkUDSEntry(entry, attrAndStat.attrstat_u.attributes); 0445 0446 slave()->statEntry(entry); 0447 return; 0448 } 0449 0450 qCDebug(LOG_KIO_NFS) << "link dest is" << linkDest; 0451 0452 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, linkDest); 0453 if (!isValidLink(fileInfo.path(), linkDest)) { 0454 completeBadLinkUDSEntry(entry, attrAndStat.attrstat_u.attributes); 0455 } else { 0456 QString linkPath = QFileInfo(fileInfo.path(), linkDest).absoluteFilePath(); 0457 0458 int rpcStatus; 0459 attrstat attrAndStat; 0460 if (!getAttr(linkPath, rpcStatus, attrAndStat)) { 0461 checkForError(rpcStatus, attrAndStat.status, linkPath); 0462 return; 0463 } 0464 0465 completeUDSEntry(entry, attrAndStat.attrstat_u.attributes); 0466 } 0467 } else { 0468 completeUDSEntry(entry, attrAndStat.attrstat_u.attributes); 0469 } 0470 0471 slave()->statEntry(entry); 0472 } 0473 0474 void NFSProtocolV2::mkdir(const QUrl &url, int permissions) 0475 { 0476 qCDebug(LOG_KIO_NFS) << url; 0477 0478 const QString path(url.path()); 0479 const QFileInfo fileInfo(path); 0480 if (isExportedDir(fileInfo.path())) { 0481 setError(KIO::ERR_WRITE_ACCESS_DENIED, path); 0482 return; 0483 } 0484 0485 const NFSFileHandle fh = getFileHandle(fileInfo.path()); 0486 if (fh.isInvalid() || fh.isBadLink()) { 0487 setError(KIO::ERR_DOES_NOT_EXIST, path); 0488 return; 0489 } 0490 0491 createargs createArgs; 0492 fh.toFH(createArgs.where.dir); 0493 0494 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 0495 createArgs.where.name = tmpName.data(); 0496 0497 if (permissions == -1) { 0498 createArgs.attributes.mode = 0755; 0499 } else { 0500 createArgs.attributes.mode = permissions; 0501 } 0502 0503 diropres dirres; 0504 memset(&dirres, 0, sizeof(diropres)); 0505 0506 int clnt_stat = clnt_call(m_nfsClient, 0507 NFSPROC_MKDIR, 0508 (xdrproc_t)xdr_createargs, 0509 reinterpret_cast<caddr_t>(&createArgs), 0510 (xdrproc_t)xdr_diropres, 0511 reinterpret_cast<caddr_t>(&dirres), 0512 clnt_timeout); 0513 0514 checkForError(clnt_stat, dirres.status, path); 0515 } 0516 0517 void NFSProtocolV2::del(const QUrl &url, bool) 0518 { 0519 int rpcStatus; 0520 nfsstat nfsStatus; 0521 remove(url.path(), rpcStatus, nfsStatus); 0522 checkForError(rpcStatus, nfsStatus, url.path()); 0523 } 0524 0525 void NFSProtocolV2::chmod(const QUrl &url, int permissions) 0526 { 0527 qCDebug(LOG_KIO_NFS) << url; 0528 0529 const QString path(url.path()); 0530 if (isExportedDir(path)) { 0531 setError(KIO::ERR_ACCESS_DENIED, path); 0532 return; 0533 } 0534 0535 sattr attributes; 0536 memset(&attributes, 0xFF, sizeof(attributes)); 0537 attributes.mode = permissions; 0538 0539 int rpcStatus; 0540 nfsstat result; 0541 setAttr(path, attributes, rpcStatus, result); 0542 checkForError(rpcStatus, result, path); 0543 } 0544 0545 void NFSProtocolV2::get(const QUrl &url) 0546 { 0547 qCDebug(LOG_KIO_NFS) << url; 0548 0549 const QString path(url.path()); 0550 0551 const NFSFileHandle fh = getFileHandle(path); 0552 if (fh.isInvalid() || fh.isBadLink()) { 0553 setError(KIO::ERR_DOES_NOT_EXIST, path); 0554 return; 0555 } 0556 0557 readargs readArgs; 0558 fh.toFH(readArgs.file); 0559 readArgs.offset = 0; 0560 readArgs.count = NFS_MAXDATA; 0561 readArgs.totalcount = NFS_MAXDATA; 0562 0563 readres readRes; 0564 memset(&readRes, 0, sizeof(readres)); 0565 0566 char buf[NFS_MAXDATA]; 0567 readRes.readres_u.reply.data.data_val = buf; 0568 0569 bool validRead = false; 0570 int offset = 0; 0571 QByteArray readBuffer; 0572 do { 0573 int clnt_stat = clnt_call(m_nfsClient, 0574 NFSPROC_READ, 0575 (xdrproc_t)xdr_readargs, 0576 reinterpret_cast<caddr_t>(&readArgs), 0577 (xdrproc_t)xdr_readres, 0578 reinterpret_cast<caddr_t>(&readRes), 0579 clnt_timeout); 0580 0581 if (!checkForError(clnt_stat, readRes.status, path)) { 0582 return; 0583 } 0584 0585 if (readArgs.offset == 0) { 0586 slave()->totalSize(readRes.readres_u.reply.attributes.size); 0587 0588 const QMimeDatabase db; 0589 const QMimeType type = db.mimeTypeForFileNameAndData(url.fileName(), readBuffer); 0590 slave()->mimeType(type.name()); 0591 } 0592 0593 offset = readRes.readres_u.reply.data.data_len; 0594 readArgs.offset += offset; 0595 if (offset > 0) { 0596 validRead = true; 0597 0598 readBuffer = QByteArray::fromRawData(readRes.readres_u.reply.data.data_val, offset); 0599 slave()->data(readBuffer); 0600 readBuffer.clear(); 0601 0602 slave()->processedSize(readArgs.offset); 0603 } 0604 0605 } while (offset > 0); 0606 0607 if (validRead) { 0608 slave()->data(QByteArray()); 0609 slave()->processedSize(readArgs.offset); 0610 } 0611 } 0612 0613 void NFSProtocolV2::put(const QUrl &url, int _mode, KIO::JobFlags flags) 0614 { 0615 qCDebug(LOG_KIO_NFS) << url << _mode; 0616 0617 const QString destPath(url.path()); 0618 0619 const QFileInfo fileInfo(destPath); 0620 if (isExportedDir(fileInfo.path())) { 0621 setError(KIO::ERR_WRITE_ACCESS_DENIED, destPath); 0622 return; 0623 } 0624 0625 NFSFileHandle destFH = getFileHandle(destPath); 0626 if (destFH.isBadLink()) { 0627 setError(KIO::ERR_DOES_NOT_EXIST, destPath); 0628 return; 0629 } 0630 0631 // the file exists and we don't want to overwrite 0632 if (!destFH.isInvalid() && (!(flags & KIO::Overwrite))) { 0633 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0634 return; 0635 } 0636 0637 int rpcStatus; 0638 diropres dirOpRes; 0639 if (!create(destPath, _mode, rpcStatus, dirOpRes)) { 0640 checkForError(rpcStatus, dirOpRes.status, fileInfo.fileName()); 0641 return; 0642 } 0643 0644 destFH = dirOpRes.diropres_u.diropres.file.data; 0645 0646 writeargs writeArgs; 0647 memset(&writeArgs, 0, sizeof(writeargs)); 0648 destFH.toFH(writeArgs.file); 0649 writeArgs.beginoffset = 0; 0650 writeArgs.totalcount = 0; 0651 writeArgs.offset = 0; 0652 0653 attrstat attrStat; 0654 0655 int result = 0, bytesWritten = 0; 0656 do { 0657 // Request new data 0658 slave()->dataReq(); 0659 0660 QByteArray buffer; 0661 result = slave()->readData(buffer); 0662 0663 char *data = buffer.data(); 0664 int bytesToWrite = buffer.size(), writeNow = 0; 0665 if (result > 0) { 0666 do { 0667 if (bytesToWrite > NFS_MAXDATA) { 0668 writeNow = NFS_MAXDATA; 0669 } else { 0670 writeNow = bytesToWrite; 0671 } 0672 0673 writeArgs.data.data_val = data; 0674 writeArgs.data.data_len = writeNow; 0675 0676 int clnt_stat = clnt_call(m_nfsClient, 0677 NFSPROC_WRITE, 0678 (xdrproc_t)xdr_writeargs, 0679 reinterpret_cast<caddr_t>(&writeArgs), 0680 (xdrproc_t)xdr_attrstat, 0681 reinterpret_cast<caddr_t>(&attrStat), 0682 clnt_timeout); 0683 0684 if (!checkForError(clnt_stat, attrStat.status, fileInfo.fileName())) { 0685 return; 0686 } 0687 0688 bytesWritten += writeNow; 0689 writeArgs.offset = bytesWritten; 0690 0691 data = data + writeNow; 0692 bytesToWrite -= writeNow; 0693 } while (bytesToWrite > 0); 0694 } 0695 } while (result > 0); 0696 } 0697 0698 void NFSProtocolV2::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags _flags) 0699 { 0700 qCDebug(LOG_KIO_NFS) << src << "to" << dest; 0701 0702 const QString srcPath(src.path()); 0703 if (isExportedDir(srcPath)) { 0704 setError(KIO::ERR_CANNOT_RENAME, srcPath); 0705 return; 0706 } 0707 0708 const QString destPath(dest.path()); 0709 if (isExportedDir(destPath)) { 0710 setError(KIO::ERR_ACCESS_DENIED, destPath); 0711 return; 0712 } 0713 0714 if (!getFileHandle(destPath).isInvalid() && (_flags & KIO::Overwrite) == 0) { 0715 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0716 return; 0717 } 0718 0719 int rpcStatus; 0720 nfsstat nfsStatus; 0721 rename(src.path(), destPath, rpcStatus, nfsStatus); 0722 checkForError(rpcStatus, nfsStatus, destPath); 0723 } 0724 0725 void NFSProtocolV2::copySame(const QUrl &src, const QUrl &dest, int _mode, KIO::JobFlags _flags) 0726 { 0727 qCDebug(LOG_KIO_NFS) << src << "to" << dest; 0728 0729 const QString srcPath(src.path()); 0730 0731 const NFSFileHandle srcFH = getFileHandle(srcPath); 0732 if (srcFH.isInvalid()) { 0733 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 0734 return; 0735 } 0736 0737 const QString destPath = dest.path(); 0738 if (isExportedDir(QFileInfo(destPath).path())) { 0739 setError(KIO::ERR_ACCESS_DENIED, destPath); 0740 return; 0741 } 0742 0743 // The file exists and we don't want to overwrite 0744 if (!getFileHandle(destPath).isInvalid() && (_flags & KIO::Overwrite) == 0) { 0745 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0746 return; 0747 } 0748 0749 // Is it a link? No need to copy the data then, just copy the link destination. 0750 if (srcFH.isLink()) { 0751 // get the link dest 0752 int rpcStatus; 0753 readlinkres readLinkRes; 0754 char nameBuf[NFS_MAXPATHLEN]; 0755 if (!symLinkTarget(srcPath, rpcStatus, readLinkRes, nameBuf)) { 0756 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 0757 return; 0758 } 0759 0760 const QString linkPath = QString::fromLocal8Bit(readLinkRes.readlinkres_u.data); 0761 0762 nfsstat linkRes; 0763 symLink(linkPath, destPath, rpcStatus, linkRes); 0764 checkForError(rpcStatus, linkRes, linkPath); 0765 return; 0766 } 0767 0768 unsigned long resumeOffset = 0; 0769 bool bResume = false; 0770 const QString partFilePath = destPath + QLatin1String(".part"); 0771 const NFSFileHandle partFH = getFileHandle(partFilePath); 0772 const bool bPartExists = !partFH.isInvalid(); 0773 const bool bMarkPartial = slave()->configValue(QStringLiteral("MarkPartial"), true); 0774 0775 if (bPartExists) { 0776 int rpcStatus; 0777 diropres partRes; 0778 if (lookupHandle(partFilePath, rpcStatus, partRes)) { 0779 if (bMarkPartial && partRes.diropres_u.diropres.attributes.size > 0) { 0780 if (partRes.diropres_u.diropres.attributes.type == NFDIR) { 0781 setError(KIO::ERR_IS_DIRECTORY, partFilePath); 0782 return; 0783 } 0784 0785 bResume = slave()->canResume(partRes.diropres_u.diropres.attributes.size); 0786 if (bResume) { 0787 resumeOffset = partRes.diropres_u.diropres.attributes.size; 0788 } 0789 } 0790 } 0791 0792 // Remove the part file if we are not resuming 0793 if (!bResume) { 0794 if (!remove(partFilePath)) { 0795 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 0796 } 0797 } 0798 } 0799 0800 // Create the file if we are not resuming a parted transfer, 0801 // or if we are not using part files(bResume is false in that case) 0802 NFSFileHandle destFH; 0803 if (!bResume) { 0804 QString createPath; 0805 if (bMarkPartial) { 0806 createPath = partFilePath; 0807 } else { 0808 createPath = destPath; 0809 } 0810 0811 int rpcStatus; 0812 diropres dirOpRes; 0813 if (!create(createPath, _mode, rpcStatus, dirOpRes)) { 0814 checkForError(rpcStatus, dirOpRes.status, createPath); 0815 return; 0816 } 0817 0818 destFH = dirOpRes.diropres_u.diropres.file.data; 0819 } else { 0820 // Since we are resuming it's implied that we are using a part file, 0821 // which should exist at this point. 0822 destFH = getFileHandle(partFilePath); 0823 0824 qCDebug(LOG_KIO_NFS) << "Resuming old transfer"; 0825 } 0826 0827 char buf[NFS_MAXDATA]; 0828 0829 writeargs writeArgs; 0830 destFH.toFH(writeArgs.file); 0831 writeArgs.beginoffset = 0; 0832 writeArgs.totalcount = 0; 0833 writeArgs.offset = 0; 0834 writeArgs.data.data_val = buf; 0835 0836 readargs readArgs; 0837 srcFH.toFH(readArgs.file); 0838 readArgs.offset = 0; 0839 readArgs.count = NFS_MAXDATA; 0840 readArgs.totalcount = NFS_MAXDATA; 0841 0842 if (bResume) { 0843 writeArgs.offset = resumeOffset; 0844 readArgs.offset = resumeOffset; 0845 } 0846 0847 readres readRes; 0848 memset(&readRes, 0, sizeof(readres)); 0849 readRes.readres_u.reply.data.data_val = buf; 0850 0851 attrstat attrStat; 0852 memset(&attrStat, 0, sizeof(attrstat)); 0853 0854 bool error = false; 0855 int bytesRead = 0; 0856 do { 0857 int clnt_stat = clnt_call(m_nfsClient, 0858 NFSPROC_READ, 0859 (xdrproc_t)xdr_readargs, 0860 reinterpret_cast<caddr_t>(&readArgs), 0861 (xdrproc_t)xdr_readres, 0862 reinterpret_cast<caddr_t>(&readRes), 0863 clnt_timeout); 0864 0865 if (!checkForError(clnt_stat, readRes.status, destPath)) { 0866 error = true; 0867 break; 0868 } 0869 0870 bytesRead = readRes.readres_u.reply.data.data_len; 0871 0872 // We should only send out the total size and mimetype at the start of the transfer 0873 if (readArgs.offset == 0 || (bResume && writeArgs.offset == resumeOffset)) { 0874 slave()->totalSize(readRes.readres_u.reply.attributes.size); 0875 0876 QMimeDatabase db; 0877 QMimeType type = db.mimeTypeForFileNameAndData(src.fileName(), QByteArray::fromRawData(writeArgs.data.data_val, bytesRead)); 0878 slave()->mimeType(type.name()); 0879 } 0880 0881 if (bytesRead > 0) { 0882 readArgs.offset += bytesRead; 0883 0884 writeArgs.data.data_len = bytesRead; 0885 0886 clnt_stat = clnt_call(m_nfsClient, 0887 NFSPROC_WRITE, 0888 (xdrproc_t)xdr_writeargs, 0889 reinterpret_cast<caddr_t>(&writeArgs), 0890 (xdrproc_t)xdr_attrstat, 0891 reinterpret_cast<caddr_t>(&attrStat), 0892 clnt_timeout); 0893 0894 if (!checkForError(clnt_stat, attrStat.status, destPath)) { 0895 error = true; 0896 break; 0897 } 0898 0899 writeArgs.offset += bytesRead; 0900 0901 slave()->processedSize(readArgs.offset); 0902 } 0903 } while (bytesRead > 0); 0904 0905 if (error) { 0906 if (bMarkPartial) { 0907 // Remove the part file if it's smaller than the minimum keep size. 0908 const unsigned int size = slave()->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); 0909 if (writeArgs.offset < size) { 0910 if (!remove(partFilePath)) { 0911 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 0912 } 0913 } 0914 } 0915 } else { 0916 // Rename partial file to its original name. 0917 if (bMarkPartial) { 0918 // Remove the destination file(if it exists) 0919 if (!getFileHandle(destPath).isInvalid() && !remove(destPath)) { 0920 qCDebug(LOG_KIO_NFS) << "Could not remove destination file" << destPath << ", ignoring..."; 0921 } 0922 0923 if (!rename(partFilePath, destPath)) { 0924 qCDebug(LOG_KIO_NFS) << "Failed to rename" << partFilePath << "to" << destPath; 0925 setError(KIO::ERR_CANNOT_RENAME_PARTIAL, partFilePath); 0926 return; 0927 } 0928 } 0929 0930 // Restore modification time 0931 int rpcStatus; 0932 attrstat attrRes; 0933 if (getAttr(srcPath, rpcStatus, attrRes)) { 0934 sattr attributes; 0935 memset(&attributes, 0xFF, sizeof(attributes)); 0936 attributes.mtime.seconds = attrRes.attrstat_u.attributes.mtime.seconds; 0937 attributes.mtime.useconds = attrRes.attrstat_u.attributes.mtime.useconds; 0938 0939 nfsstat attrSetRes; 0940 if (!setAttr(destPath, attributes, rpcStatus, attrSetRes)) { 0941 qCDebug(LOG_KIO_NFS) << "Failed to restore mtime, ignoring..." << rpcStatus << attrSetRes; 0942 } 0943 } 0944 0945 qCDebug(LOG_KIO_NFS) << "Copied" << writeArgs.offset << "bytes of data"; 0946 0947 slave()->processedSize(readArgs.offset); 0948 } 0949 } 0950 0951 void NFSProtocolV2::copyFrom(const QUrl &src, const QUrl &dest, int _mode, KIO::JobFlags _flags) 0952 { 0953 qCDebug(LOG_KIO_NFS) << src << "to" << dest; 0954 0955 const QString srcPath(src.path()); 0956 0957 const NFSFileHandle srcFH = getFileHandle(srcPath); 0958 if (srcFH.isInvalid()) { 0959 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 0960 return; 0961 } 0962 0963 const QString destPath(dest.path()); 0964 0965 // The file exists and we don't want to overwrite 0966 if (QFile::exists(destPath) && (_flags & KIO::Overwrite) == 0) { 0967 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 0968 return; 0969 } 0970 0971 // Is it a link? No need to copy the data then, just copy the link destination. 0972 if (srcFH.isLink()) { 0973 qCDebug(LOG_KIO_NFS) << "Is a link"; 0974 0975 int rpcStatus; 0976 readlinkres readLinkRes; 0977 char nameBuf[NFS_MAXPATHLEN]; 0978 if (!symLinkTarget(srcPath, rpcStatus, readLinkRes, nameBuf)) { 0979 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 0980 return; 0981 } 0982 0983 QFile::link(QString::fromLocal8Bit(readLinkRes.readlinkres_u.data), destPath); 0984 return; 0985 } 0986 0987 bool bResume = false; 0988 const QFileInfo partInfo(destPath + QLatin1String(".part")); 0989 const bool bPartExists = partInfo.exists(); 0990 const bool bMarkPartial = slave()->configValue(QStringLiteral("MarkPartial"), true); 0991 0992 if (bMarkPartial && bPartExists && partInfo.size() > 0) { 0993 if (partInfo.isDir()) { 0994 setError(KIO::ERR_IS_DIRECTORY, partInfo.absoluteFilePath()); 0995 return; 0996 } 0997 0998 bResume = slave()->canResume(partInfo.size()); 0999 } 1000 1001 if (bPartExists && !bResume) { 1002 QFile::remove(partInfo.absoluteFilePath()); 1003 } 1004 1005 QFile::OpenMode openMode; 1006 QString outFileName; 1007 if (bResume) { 1008 outFileName = partInfo.absoluteFilePath(); 1009 openMode = QFile::WriteOnly | QFile::Append; 1010 } else { 1011 outFileName = (bMarkPartial ? partInfo.absoluteFilePath() : destPath); 1012 openMode = QFile::WriteOnly | QFile::Truncate; 1013 } 1014 1015 QFile destFile(outFileName); 1016 if (!bResume) { 1017 QFile::Permissions perms; 1018 if (_mode == -1) { 1019 perms = QFile::ReadOwner | QFile::WriteOwner; 1020 } else { 1021 perms = KIO::convertPermissions(_mode | QFile::WriteOwner); 1022 } 1023 destFile.setPermissions(perms); 1024 } 1025 1026 if (!destFile.open(openMode)) { 1027 switch (destFile.error()) { 1028 case QFile::OpenError: 1029 if (bResume) { 1030 setError(KIO::ERR_CANNOT_RESUME, destPath); 1031 } else { 1032 setError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, destPath); 1033 } 1034 break; 1035 case QFile::PermissionsError: 1036 setError(KIO::ERR_WRITE_ACCESS_DENIED, destPath); 1037 break; 1038 default: 1039 setError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, destPath); 1040 break; 1041 } 1042 return; 1043 } 1044 1045 char buf[NFS_MAXDATA]; 1046 1047 readargs readArgs; 1048 srcFH.toFH(readArgs.file); 1049 if (bResume) { 1050 readArgs.offset = partInfo.size(); 1051 } else { 1052 readArgs.offset = 0; 1053 } 1054 readArgs.count = NFS_MAXDATA; 1055 readArgs.totalcount = NFS_MAXDATA; 1056 1057 readres readRes; 1058 memset(&readRes, 0, sizeof(readres)); 1059 readRes.readres_u.reply.data.data_val = buf; 1060 1061 attrstat attrStat; 1062 memset(&attrStat, 0, sizeof(attrstat)); 1063 1064 bool error = false; 1065 int bytesRead = 0; 1066 do { 1067 int clnt_stat = clnt_call(m_nfsClient, 1068 NFSPROC_READ, 1069 (xdrproc_t)xdr_readargs, 1070 reinterpret_cast<caddr_t>(&readArgs), 1071 (xdrproc_t)xdr_readres, 1072 reinterpret_cast<caddr_t>(&readRes), 1073 clnt_timeout); 1074 1075 if (!checkForError(clnt_stat, readRes.status, destPath)) { 1076 error = true; 1077 break; 1078 } 1079 1080 bytesRead = readRes.readres_u.reply.data.data_len; 1081 1082 if (readArgs.offset == 0) { 1083 slave()->totalSize(readRes.readres_u.reply.attributes.size); 1084 1085 QMimeDatabase db; 1086 QMimeType type = db.mimeTypeForFileNameAndData(src.fileName(), QByteArray::fromRawData(readRes.readres_u.reply.data.data_val, bytesRead)); 1087 slave()->mimeType(type.name()); 1088 } 1089 1090 if (bytesRead > 0) { 1091 readArgs.offset += bytesRead; 1092 1093 if (destFile.write(readRes.readres_u.reply.data.data_val, bytesRead) != bytesRead) { 1094 setError(KIO::ERR_CANNOT_WRITE, destPath); 1095 1096 error = true; 1097 break; 1098 } 1099 1100 slave()->processedSize(readArgs.offset); 1101 } 1102 } while (bytesRead > 0); 1103 1104 // Close the file so we can modify the modification time later. 1105 destFile.close(); 1106 1107 if (error) { 1108 if (bMarkPartial) { 1109 // Remove the part file if it's smaller than the minimum keep 1110 const int size = slave()->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); 1111 if (partInfo.size() < size) { 1112 QFile::remove(partInfo.absoluteFilePath()); 1113 } 1114 } 1115 } else { 1116 // Rename partial file to its original name. 1117 if (bMarkPartial) { 1118 const QString sPart = partInfo.absoluteFilePath(); 1119 if (QFile::exists(destPath)) { 1120 QFile::remove(destPath); 1121 } 1122 if (!QFile::rename(sPart, destPath)) { 1123 qCDebug(LOG_KIO_NFS) << "Failed to rename" << sPart << "to" << destPath; 1124 setError(KIO::ERR_CANNOT_RENAME_PARTIAL, sPart); 1125 return; 1126 } 1127 } 1128 1129 // Restore the mtime on the file. 1130 const QString mtimeStr = slave()->metaData("modified"); 1131 if (!mtimeStr.isEmpty()) { 1132 QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); 1133 if (dt.isValid()) { 1134 qCDebug(LOG_KIO_NFS) << "Setting modification time to" << dt.toSecsSinceEpoch(); 1135 1136 struct utimbuf utbuf; 1137 utbuf.actime = QFileInfo(destPath).lastRead().toSecsSinceEpoch(); // access time, unchanged 1138 utbuf.modtime = dt.toSecsSinceEpoch(); // modification time 1139 utime(QFile::encodeName(destPath).constData(), &utbuf); 1140 } 1141 } 1142 1143 qCDebug(LOG_KIO_NFS) << "Copied" << readArgs.offset << "bytes of data"; 1144 1145 slave()->processedSize(readArgs.offset); 1146 } 1147 } 1148 1149 void NFSProtocolV2::copyTo(const QUrl &src, const QUrl &dest, int _mode, KIO::JobFlags _flags) 1150 { 1151 qCDebug(LOG_KIO_NFS) << src << "to" << dest; 1152 1153 // The source does not exist, how strange. 1154 const QString srcPath(src.path()); 1155 if (!QFile::exists(srcPath)) { 1156 setError(KIO::ERR_DOES_NOT_EXIST, srcPath); 1157 return; 1158 } 1159 1160 const QString destPath(dest.path()); 1161 if (isExportedDir(destPath)) { 1162 setError(KIO::ERR_ACCESS_DENIED, destPath); 1163 return; 1164 } 1165 1166 // The file exists and we don't want to overwrite. 1167 if (!getFileHandle(destPath).isInvalid() && (_flags & KIO::Overwrite) == 0) { 1168 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 1169 return; 1170 } 1171 1172 // Is it a link? No need to copy the data then, just copy the link destination. 1173 const QString symlinkTarget = QFile::symLinkTarget(srcPath); 1174 if (!symlinkTarget.isEmpty()) { 1175 int rpcStatus; 1176 nfsstat linkRes; 1177 symLink(symlinkTarget, destPath, rpcStatus, linkRes); 1178 checkForError(rpcStatus, linkRes, symlinkTarget); 1179 return; 1180 } 1181 1182 unsigned long resumeOffset = 0; 1183 bool bResume = false; 1184 const QString partFilePath = destPath + QLatin1String(".part"); 1185 const NFSFileHandle partFH = getFileHandle(partFilePath); 1186 const bool bPartExists = !partFH.isInvalid(); 1187 const bool bMarkPartial = slave()->configValue(QStringLiteral("MarkPartial"), true); 1188 1189 if (bPartExists) { 1190 int rpcStatus; 1191 diropres partRes; 1192 if (lookupHandle(partFilePath, rpcStatus, partRes)) { 1193 if (bMarkPartial && partRes.diropres_u.diropres.attributes.size > 0) { 1194 if (partRes.diropres_u.diropres.attributes.type == NFDIR) { 1195 setError(KIO::ERR_IS_DIRECTORY, partFilePath); 1196 return; 1197 } 1198 1199 bResume = slave()->canResume(partRes.diropres_u.diropres.attributes.size); 1200 if (bResume) { 1201 resumeOffset = partRes.diropres_u.diropres.attributes.size; 1202 } 1203 } 1204 } 1205 1206 // Remove the part file if we are not resuming 1207 if (!bResume) { 1208 if (!remove(partFilePath)) { 1209 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 1210 } 1211 } 1212 } 1213 1214 // Open the source file 1215 QFile srcFile(srcPath); 1216 if (!srcFile.open(QIODevice::ReadOnly)) { 1217 setError(KIO::ERR_CANNOT_OPEN_FOR_READING, srcPath); 1218 return; 1219 } 1220 1221 // Create the file if we are not resuming a parted transfer, 1222 // or if we are not using part files(bResume is false in that case) 1223 NFSFileHandle destFH; 1224 if (!bResume) { 1225 QString createPath; 1226 if (bMarkPartial) { 1227 createPath = partFilePath; 1228 } else { 1229 createPath = destPath; 1230 } 1231 1232 int rpcStatus; 1233 diropres dirOpRes; 1234 if (!create(createPath, _mode, rpcStatus, dirOpRes)) { 1235 checkForError(rpcStatus, dirOpRes.status, createPath); 1236 return; 1237 } 1238 1239 destFH = dirOpRes.diropres_u.diropres.file.data; 1240 } else { 1241 // Since we are resuming it's implied that we are using a part file, 1242 // which should exist at this point. 1243 destFH = getFileHandle(partFilePath); 1244 1245 qCDebug(LOG_KIO_NFS) << "Resuming old transfer"; 1246 } 1247 1248 // Send the total size to the slave. 1249 slave()->totalSize(srcFile.size()); 1250 1251 // Set up write arguments. 1252 char buf[NFS_MAXDATA]; 1253 1254 writeargs writeArgs; 1255 memset(&writeArgs, 0, sizeof(writeargs)); 1256 destFH.toFH(writeArgs.file); 1257 writeArgs.data.data_val = buf; 1258 writeArgs.beginoffset = 0; 1259 writeArgs.totalcount = 0; 1260 if (bResume) { 1261 writeArgs.offset = resumeOffset; 1262 } else { 1263 writeArgs.offset = 0; 1264 } 1265 1266 attrstat attrStat; 1267 memset(&attrStat, 0, sizeof(attrstat)); 1268 1269 bool error = false; 1270 int bytesRead = 0; 1271 do { 1272 bytesRead = srcFile.read(writeArgs.data.data_val, NFS_MAXDATA); 1273 if (bytesRead < 0) { 1274 setError(KIO::ERR_CANNOT_READ, srcPath); 1275 1276 error = true; 1277 break; 1278 } 1279 1280 if (bytesRead > 0) { 1281 writeArgs.data.data_len = bytesRead; 1282 1283 int clnt_stat = clnt_call(m_nfsClient, 1284 NFSPROC_WRITE, 1285 (xdrproc_t)xdr_writeargs, 1286 reinterpret_cast<caddr_t>(&writeArgs), 1287 (xdrproc_t)xdr_attrstat, 1288 reinterpret_cast<caddr_t>(&attrStat), 1289 clnt_timeout); 1290 1291 if (!checkForError(clnt_stat, attrStat.status, destPath)) { 1292 error = true; 1293 break; 1294 } 1295 1296 writeArgs.offset += bytesRead; 1297 1298 slave()->processedSize(writeArgs.offset); 1299 } 1300 } while (bytesRead > 0); 1301 1302 if (error) { 1303 if (bMarkPartial) { 1304 // Remove the part file if it's smaller than the minimum keep size. 1305 const unsigned int size = slave()->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); 1306 if (writeArgs.offset < size) { 1307 if (!remove(partFilePath)) { 1308 qCDebug(LOG_KIO_NFS) << "Could not remove part file, ignoring..."; 1309 } 1310 } 1311 } 1312 } else { 1313 // Rename partial file to its original name. 1314 if (bMarkPartial) { 1315 // Remove the destination file(if it exists) 1316 if (!getFileHandle(destPath).isInvalid() && !remove(destPath)) { 1317 qCDebug(LOG_KIO_NFS) << "Could not remove destination file" << destPath << ", ignoring..."; 1318 } 1319 1320 if (!rename(partFilePath, destPath)) { 1321 qCDebug(LOG_KIO_NFS) << "Failed to rename" << partFilePath << "to" << destPath; 1322 setError(KIO::ERR_CANNOT_RENAME_PARTIAL, partFilePath); 1323 return; 1324 } 1325 } 1326 1327 // Restore the mtime on the file. 1328 const QString mtimeStr = slave()->metaData("modified"); 1329 if (!mtimeStr.isEmpty()) { 1330 QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); 1331 if (dt.isValid()) { 1332 sattr attributes; 1333 memset(&attributes, 0xFF, sizeof(attributes)); 1334 attributes.mtime.seconds = dt.toSecsSinceEpoch(); 1335 attributes.mtime.useconds = attributes.mtime.seconds * 1000000ULL; 1336 1337 int rpcStatus; 1338 nfsstat attrSetRes; 1339 if (!setAttr(destPath, attributes, rpcStatus, attrSetRes)) { 1340 qCDebug(LOG_KIO_NFS) << "Failed to restore mtime, ignoring..." << rpcStatus << attrSetRes; 1341 } 1342 } 1343 } 1344 1345 qCDebug(LOG_KIO_NFS) << "Copied" << writeArgs.offset << "bytes of data"; 1346 1347 slave()->processedSize(writeArgs.offset); 1348 } 1349 } 1350 1351 void NFSProtocolV2::symlink(const QString &target, const QUrl &dest, KIO::JobFlags flags) 1352 { 1353 const QString destPath(dest.path()); 1354 if (isExportedDir(QFileInfo(destPath).path())) { 1355 setError(KIO::ERR_ACCESS_DENIED, destPath); 1356 return; 1357 } 1358 1359 if (!getFileHandle(destPath).isInvalid() && (flags & KIO::Overwrite) == 0) { 1360 setError(KIO::ERR_FILE_ALREADY_EXIST, destPath); 1361 return; 1362 } 1363 1364 int rpcStatus; 1365 nfsstat res; 1366 symLink(target, destPath, rpcStatus, res); 1367 checkForError(rpcStatus, res, destPath); 1368 } 1369 1370 bool NFSProtocolV2::create(const QString &path, int mode, int &rpcStatus, diropres &result) 1371 { 1372 memset(&rpcStatus, 0, sizeof(int)); 1373 memset(&result, 0, sizeof(result)); 1374 1375 if (!isConnected()) { 1376 result.status = NFSERR_ACCES; 1377 return false; 1378 } 1379 1380 const QFileInfo fileInfo(path); 1381 if (isExportedDir(fileInfo.path())) { 1382 result.status = NFSERR_ACCES; 1383 return false; 1384 } 1385 1386 const NFSFileHandle directoryFH = getFileHandle(fileInfo.path()); 1387 if (directoryFH.isInvalid()) { 1388 result.status = NFSERR_NOENT; 1389 return false; 1390 } 1391 1392 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 1393 1394 createargs args; 1395 directoryFH.toFH(args.where.dir); 1396 args.where.name = tmpName.data(); 1397 1398 memset(&args.attributes, 0xFF, sizeof(sattr)); 1399 if (mode == -1) { 1400 args.attributes.mode = 0644; 1401 } else { 1402 args.attributes.mode = mode; 1403 } 1404 args.attributes.uid = geteuid(); 1405 args.attributes.gid = getegid(); 1406 args.attributes.size = 0; 1407 1408 rpcStatus = clnt_call(m_nfsClient, 1409 NFSPROC_CREATE, 1410 (xdrproc_t)xdr_createargs, 1411 reinterpret_cast<caddr_t>(&args), 1412 (xdrproc_t)xdr_diropres, 1413 reinterpret_cast<caddr_t>(&result), 1414 clnt_timeout); 1415 1416 return (rpcStatus == RPC_SUCCESS && result.status == NFS_OK); 1417 } 1418 1419 bool NFSProtocolV2::getAttr(const QString &path, int &rpcStatus, attrstat &result) 1420 { 1421 memset(&rpcStatus, 0, sizeof(int)); 1422 memset(&result, 0, sizeof(result)); 1423 1424 if (!isConnected()) { 1425 result.status = NFSERR_ACCES; 1426 return false; 1427 } 1428 1429 const NFSFileHandle fileFH = getFileHandle(path); 1430 if (fileFH.isInvalid()) { 1431 result.status = NFSERR_NOENT; 1432 return false; 1433 } 1434 1435 nfs_fh fh; 1436 fileFH.toFH(fh); 1437 1438 rpcStatus = clnt_call(m_nfsClient, 1439 NFSPROC_GETATTR, 1440 (xdrproc_t)xdr_nfs_fh, 1441 reinterpret_cast<caddr_t>(&fh), 1442 (xdrproc_t)xdr_attrstat, 1443 reinterpret_cast<caddr_t>(&result), 1444 clnt_timeout); 1445 1446 return (rpcStatus == RPC_SUCCESS && result.status == NFS_OK); 1447 } 1448 1449 bool NFSProtocolV2::lookupHandle(const QString &path, int &rpcStatus, diropres &result) 1450 { 1451 memset(&rpcStatus, 0, sizeof(int)); 1452 memset(&result, 0, sizeof(result)); 1453 1454 if (!isConnected()) { 1455 result.status = NFSERR_ACCES; 1456 return false; 1457 } 1458 1459 const QFileInfo fileInfo(path); 1460 1461 const NFSFileHandle parentFH = getFileHandle(fileInfo.path()); 1462 if (parentFH.isInvalid()) { 1463 result.status = NFSERR_NOENT; 1464 return false; 1465 } 1466 1467 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 1468 1469 diropargs dirargs; 1470 memset(&dirargs, 0, sizeof(diropargs)); 1471 parentFH.toFH(dirargs.dir); 1472 dirargs.name = tmpName.data(); 1473 1474 memset(&result, 0, sizeof(diropres)); 1475 1476 rpcStatus = clnt_call(m_nfsClient, 1477 NFSPROC_LOOKUP, 1478 (xdrproc_t)xdr_diropargs, 1479 reinterpret_cast<caddr_t>(&dirargs), 1480 (xdrproc_t)xdr_diropres, 1481 reinterpret_cast<caddr_t>(&result), 1482 clnt_timeout); 1483 1484 return (rpcStatus == RPC_SUCCESS && result.status == NFS_OK); 1485 } 1486 1487 bool NFSProtocolV2::symLinkTarget(const QString &path, int &rpcStatus, readlinkres &result, char *dataBuffer) 1488 { 1489 const NFSFileHandle fh = getFileHandle(path); 1490 1491 nfs_fh nfsFH; 1492 if (fh.isLink() && !fh.isBadLink()) { 1493 fh.toFHLink(nfsFH); 1494 } else { 1495 fh.toFH(nfsFH); 1496 } 1497 1498 result.readlinkres_u.data = dataBuffer; 1499 1500 rpcStatus = clnt_call(m_nfsClient, 1501 NFSPROC_READLINK, 1502 (xdrproc_t)xdr_nfs_fh, 1503 reinterpret_cast<caddr_t>(&nfsFH), 1504 (xdrproc_t)xdr_readlinkres, 1505 reinterpret_cast<caddr_t>(&result), 1506 clnt_timeout); 1507 1508 return (rpcStatus == RPC_SUCCESS && result.status == NFS_OK); 1509 } 1510 1511 bool NFSProtocolV2::remove(const QString &path) 1512 { 1513 int rpcStatus; 1514 nfsstat nfsStatus; 1515 1516 return remove(path, rpcStatus, nfsStatus); 1517 } 1518 1519 bool NFSProtocolV2::remove(const QString &path, int &rpcStatus, nfsstat &result) 1520 { 1521 qCDebug(LOG_KIO_NFS) << path; 1522 1523 memset(&rpcStatus, 0, sizeof(int)); 1524 memset(&result, 0, sizeof(result)); 1525 1526 if (!isConnected()) { 1527 result = NFSERR_PERM; 1528 return false; 1529 } 1530 1531 const QFileInfo fileInfo(path); 1532 if (isExportedDir(fileInfo.path())) { 1533 result = NFSERR_ACCES; 1534 return false; 1535 } 1536 1537 const NFSFileHandle directoryFH = getFileHandle(fileInfo.path()); 1538 if (directoryFH.isInvalid()) { 1539 result = NFSERR_NOENT; 1540 return false; 1541 } 1542 1543 int rpcLookupStatus; 1544 diropres lookupRes; 1545 if (!lookupHandle(path, rpcLookupStatus, lookupRes)) { 1546 result = NFSERR_NOENT; 1547 return false; 1548 } 1549 1550 QByteArray tmpName = QFile::encodeName(fileInfo.fileName()); 1551 1552 diropargs dirargs; 1553 memset(&dirargs, 0, sizeof(diropargs)); 1554 directoryFH.toFH(dirargs.dir); 1555 dirargs.name = tmpName.data(); 1556 1557 if (lookupRes.diropres_u.diropres.attributes.type != NFDIR) { 1558 rpcStatus = clnt_call(m_nfsClient, 1559 NFSPROC_REMOVE, 1560 (xdrproc_t)xdr_diropargs, 1561 reinterpret_cast<caddr_t>(&dirargs), 1562 (xdrproc_t)xdr_nfsstat, 1563 reinterpret_cast<caddr_t>(&result), 1564 clnt_timeout); 1565 } else { 1566 rpcStatus = clnt_call(m_nfsClient, 1567 NFSPROC_RMDIR, 1568 (xdrproc_t)xdr_diropargs, 1569 reinterpret_cast<caddr_t>(&dirargs), 1570 (xdrproc_t)xdr_nfsstat, 1571 reinterpret_cast<caddr_t>(&result), 1572 clnt_timeout); 1573 } 1574 1575 bool ret = (rpcStatus == RPC_SUCCESS && result == NFS_OK); 1576 if (ret) { 1577 removeFileHandle(path); 1578 } 1579 1580 return ret; 1581 } 1582 1583 bool NFSProtocolV2::rename(const QString &src, const QString &dest) 1584 { 1585 int rpcStatus; 1586 nfsstat result; 1587 1588 return rename(src, dest, rpcStatus, result); 1589 } 1590 1591 bool NFSProtocolV2::rename(const QString &src, const QString &dest, int &rpcStatus, nfsstat &result) 1592 { 1593 qCDebug(LOG_KIO_NFS) << src << dest; 1594 1595 memset(&rpcStatus, 0, sizeof(int)); 1596 memset(&result, 0, sizeof(result)); 1597 1598 const QFileInfo srcFileInfo(src); 1599 if (isExportedDir(srcFileInfo.path())) { 1600 result = NFSERR_ACCES; 1601 return false; 1602 } 1603 1604 const NFSFileHandle srcDirectoryFH = getFileHandle(srcFileInfo.path()); 1605 if (srcDirectoryFH.isInvalid()) { 1606 result = NFSERR_NOENT; 1607 return false; 1608 } 1609 1610 const QFileInfo destFileInfo(dest); 1611 if (isExportedDir(destFileInfo.path())) { 1612 result = NFSERR_ACCES; 1613 return false; 1614 } 1615 1616 const NFSFileHandle destDirectoryFH = getFileHandle(destFileInfo.path()); 1617 if (destDirectoryFH.isInvalid()) { 1618 result = NFSERR_NOENT; 1619 return false; 1620 } 1621 1622 renameargs renameArgs; 1623 memset(&renameArgs, 0, sizeof(renameargs)); 1624 1625 QByteArray srcByteName = QFile::encodeName(srcFileInfo.fileName()); 1626 srcDirectoryFH.toFH(renameArgs.from.dir); 1627 renameArgs.from.name = srcByteName.data(); 1628 1629 QByteArray destByteName = QFile::encodeName(destFileInfo.fileName()); 1630 destDirectoryFH.toFH(renameArgs.to.dir); 1631 renameArgs.to.name = destByteName.data(); 1632 1633 rpcStatus = clnt_call(m_nfsClient, 1634 NFSPROC_RENAME, 1635 (xdrproc_t)xdr_renameargs, 1636 reinterpret_cast<caddr_t>(&renameArgs), 1637 (xdrproc_t)xdr_nfsstat, 1638 reinterpret_cast<caddr_t>(&result), 1639 clnt_timeout); 1640 1641 bool ret = (rpcStatus == RPC_SUCCESS && result == NFS_OK); 1642 if (ret) { 1643 // Can we actually find the new handle? 1644 int lookupStatus; 1645 diropres lookupRes; 1646 if (lookupHandle(dest, lookupStatus, lookupRes)) { 1647 // Remove the old file, and add the new one 1648 removeFileHandle(src); 1649 addFileHandle(dest, lookupRes.diropres_u.diropres.file); 1650 } 1651 } 1652 1653 return ret; 1654 } 1655 1656 bool NFSProtocolV2::setAttr(const QString &path, const sattr &attributes, int &rpcStatus, nfsstat &result) 1657 { 1658 qCDebug(LOG_KIO_NFS) << path; 1659 1660 memset(&rpcStatus, 0, sizeof(int)); 1661 memset(&result, 0, sizeof(result)); 1662 1663 const NFSFileHandle fh = getFileHandle(path); 1664 if (fh.isInvalid()) { 1665 result = NFSERR_NOENT; 1666 return false; 1667 } 1668 1669 sattrargs sAttrArgs; 1670 fh.toFH(sAttrArgs.file); 1671 memcpy(&sAttrArgs.attributes, &attributes, sizeof(attributes)); 1672 1673 rpcStatus = clnt_call(m_nfsClient, 1674 NFSPROC_SETATTR, 1675 (xdrproc_t)xdr_sattrargs, 1676 reinterpret_cast<caddr_t>(&sAttrArgs), 1677 (xdrproc_t)xdr_nfsstat, 1678 reinterpret_cast<caddr_t>(&result), 1679 clnt_timeout); 1680 1681 return (rpcStatus == RPC_SUCCESS && result == NFS_OK); 1682 } 1683 1684 bool NFSProtocolV2::symLink(const QString &target, const QString &dest, int &rpcStatus, nfsstat &result) 1685 { 1686 qCDebug(LOG_KIO_NFS) << target << dest; 1687 1688 memset(&rpcStatus, 0, sizeof(int)); 1689 memset(&result, 0, sizeof(result)); 1690 1691 // Remove dest first, we don't really care about the return value at this point, 1692 // the symlink call will fail if dest was not removed correctly. 1693 remove(dest); 1694 1695 const QFileInfo fileInfo(dest); 1696 if (isExportedDir(fileInfo.path())) { 1697 result = NFSERR_ACCES; 1698 return false; 1699 } 1700 1701 const NFSFileHandle fh = getFileHandle(fileInfo.path()); 1702 if (fh.isInvalid()) { 1703 result = NFSERR_NOENT; 1704 return false; 1705 } 1706 1707 QByteArray fromBytes = QFile::encodeName(fileInfo.fileName()); 1708 QByteArray toBytes = QFile::encodeName(target); 1709 1710 symlinkargs symLinkArgs; 1711 memset(&symLinkArgs, 0, sizeof(symLinkArgs)); 1712 1713 fh.toFH(symLinkArgs.from.dir); 1714 symLinkArgs.from.name = fromBytes.data(); 1715 symLinkArgs.to = toBytes.data(); 1716 1717 rpcStatus = clnt_call(m_nfsClient, 1718 NFSPROC_SYMLINK, 1719 (xdrproc_t)xdr_symlinkargs, 1720 reinterpret_cast<caddr_t>(&symLinkArgs), 1721 (xdrproc_t)xdr_nfsstat, 1722 reinterpret_cast<caddr_t>(&result), 1723 clnt_timeout); 1724 1725 // Add the new handle to the cache 1726 NFSFileHandle destFH = getFileHandle(dest); 1727 if (!destFH.isInvalid()) { 1728 addFileHandle(dest, destFH); 1729 } 1730 1731 return (rpcStatus == RPC_SUCCESS && result == NFS_OK); 1732 } 1733 1734 void NFSProtocolV2::completeUDSEntry(KIO::UDSEntry &entry, const fattr &attributes) 1735 { 1736 entry.replace(KIO::UDSEntry::UDS_SIZE, attributes.size); 1737 entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, attributes.mtime.seconds); 1738 entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, attributes.atime.seconds); 1739 entry.replace(KIO::UDSEntry::UDS_ACCESS, (attributes.mode & 07777)); 1740 entry.replace(KIO::UDSEntry::UDS_FILE_TYPE, attributes.mode & S_IFMT); // extract file type 1741 1742 NFSProtocol::completeUDSEntry(entry, attributes.uid, attributes.gid); 1743 } 1744 1745 void NFSProtocolV2::completeBadLinkUDSEntry(KIO::UDSEntry &entry, const fattr &attributes) 1746 { 1747 entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, attributes.mtime.seconds); 1748 entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, attributes.atime.seconds); 1749 1750 NFSProtocol::completeInvalidUDSEntry(entry); 1751 }