File indexing completed on 2024-04-28 04:58:01

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2000 Caldera Systems Inc.
0004     SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org>
0005     SPDX-FileContributor: Matthew Peterson <mpeterson@caldera.com>
0006 */
0007 
0008 #include "kio_smb.h"
0009 #include "smburl.h"
0010 
0011 #include <QFile>
0012 #include <QFileInfo>
0013 #include <QScopeGuard>
0014 
0015 #include <KConfigGroup>
0016 #include <kio/ioworker_defaults.h>
0017 
0018 #include <future>
0019 
0020 #include "transfer.h"
0021 #include "transfer_resume.h"
0022 
0023 WorkerResult SMBWorker::copy(const QUrl &src, const QUrl &dst, int permissions, KIO::JobFlags flags)
0024 {
0025     const bool isSourceLocal = src.isLocalFile();
0026     const bool isDestinationLocal = dst.isLocalFile();
0027 
0028     if (!isSourceLocal && isDestinationLocal) {
0029         return smbCopyGet(src, dst, permissions, flags);
0030     }
0031     if (isSourceLocal && !isDestinationLocal) {
0032         return smbCopyPut(src, dst, permissions, flags);
0033     }
0034     return smbCopy(src, dst, permissions, flags);
0035 }
0036 
0037 WorkerResult SMBWorker::smbCopy(const QUrl &ksrc, const QUrl &kdst, int permissions, KIO::JobFlags flags)
0038 {
0039     qCDebug(KIO_SMB_LOG) << "SMBWorker::copy with src = " << ksrc << "and dest = " << kdst << flags;
0040 
0041     // setup urls
0042     SMBUrl src = ksrc;
0043     SMBUrl dst = kdst;
0044 
0045     // Obtain information about source
0046     int errNum = cache_stat(src, &st);
0047     if (errNum != 0) {
0048         if (errNum == EACCES) {
0049             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, src.toDisplayString());
0050         }
0051         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString());
0052     }
0053     if (S_ISDIR(st.st_mode)) {
0054         return WorkerResult::fail(KIO::ERR_IS_DIRECTORY, src.toDisplayString());
0055     }
0056     const auto srcSize = st.st_size;
0057     totalSize(srcSize);
0058 
0059     // Check to se if the destination exists
0060     errNum = cache_stat(dst, &st);
0061     if (errNum == 0) {
0062         if (S_ISDIR(st.st_mode)) {
0063             return WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, dst.toDisplayString());
0064         }
0065         if (!(flags & KIO::Overwrite)) {
0066             return WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, dst.toDisplayString());
0067         }
0068     }
0069 
0070     // Open the source file
0071     const int srcfd = smbc_open(src.toSmbcUrl(), O_RDONLY, 0);
0072     auto closeSrcFd = qScopeGuard([srcfd] {
0073         smbc_close(srcfd);
0074     });
0075     if (srcfd < 0) {
0076         if (errno == EACCES) {
0077             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, src.toDisplayString());
0078         }
0079         return WorkerResult::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, src.toDisplayString());
0080     }
0081 
0082     mode_t initialmode = 0;
0083     // Determine initial creation mode
0084     if (permissions != -1) {
0085         initialmode = permissions | S_IWUSR;
0086     } else {
0087         initialmode = 0 | S_IWUSR; // 0666;
0088     }
0089 
0090     // Open the destination file
0091     int dstflags = O_CREAT | O_TRUNC | O_WRONLY;
0092     if (!(flags & KIO::Overwrite)) {
0093         dstflags |= O_EXCL;
0094     }
0095     const int dstfd = smbc_open(dst.toSmbcUrl(), dstflags, initialmode);
0096     auto closeDstFd = qScopeGuard([dstfd] {
0097         smbc_close(dstfd);
0098     });
0099     if (dstfd < 0) {
0100         if (errno == EACCES) {
0101             return WorkerResult::fail(KIO::ERR_WRITE_ACCESS_DENIED, dst.toDisplayString());
0102         }
0103         return WorkerResult::fail(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dst.toDisplayString());
0104     }
0105 
0106     // Perform copy
0107     // TODO: if and when smb_context becomes thread-safe, use two contexts connected with
0108     //   a ring buffer to optimize transfer speed (also see smbCopyGet)
0109     //   https://bugzilla.samba.org/show_bug.cgi?id=11413
0110     KIO::filesize_t processed_size = 0;
0111     TransferSegment segment(srcSize);
0112     while (true) {
0113         ssize_t n = smbc_read(srcfd, segment.buf.data(), segment.buf.size());
0114         if (n > 0) {
0115             n = smbc_write(dstfd, segment.buf.data(), n);
0116             if (n == -1) {
0117                 qCDebug(KIO_SMB_LOG) << "SMBWorker::copy copy now KIO::ERR_CANNOT_WRITE";
0118                 return WorkerResult::fail(KIO::ERR_CANNOT_WRITE, dst.toDisplayString());
0119             }
0120 
0121             processed_size += n;
0122             processedSize(processed_size);
0123         } else if (n == 0) {
0124             break; // finished
0125         } else {
0126             return WorkerResult::fail(KIO::ERR_CANNOT_READ, src.toDisplayString());
0127         }
0128     }
0129 
0130     //    FINISHED:
0131 
0132     smbc_close(srcfd);
0133 
0134     if (smbc_close(dstfd) == 0) {
0135         // TODO: set final permissions
0136     } else {
0137         return WorkerResult::fail(KIO::ERR_CANNOT_WRITE, dst.toDisplayString());
0138     }
0139 
0140     applyMTimeSMBC(dst);
0141 
0142     return WorkerResult::pass();
0143 }
0144 
0145 WorkerResult SMBWorker::smbCopyGet(const QUrl &ksrc, const QUrl &kdst, int permissions, KIO::JobFlags flags)
0146 {
0147     qCDebug(KIO_SMB_LOG) << "src = " << ksrc << ", dest = " << kdst << flags;
0148 
0149     // check if destination is ok ...
0150     const QString dstFile = kdst.toLocalFile();
0151     const QFileInfo dstInfo(dstFile);
0152 
0153     if (dstInfo.exists()) {
0154         if (dstInfo.isDir()) {
0155             return WorkerResult::fail(ERR_IS_DIRECTORY, kdst.toDisplayString());
0156         }
0157 
0158         if (!(flags & KIO::Overwrite)) {
0159             return WorkerResult::fail(ERR_FILE_ALREADY_EXIST, kdst.toDisplayString());
0160         }
0161     }
0162 
0163     auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(kdst, flags, this);
0164     if (std::holds_alternative<WorkerResult>(resumeVariant)) {
0165         return std::get<WorkerResult>(resumeVariant);
0166     }
0167     const auto resume = std::get<TransferContext>(resumeVariant);
0168 
0169     // open the output file...
0170     const QFile::OpenMode mode = resume.resuming ? (QFile::WriteOnly | QFile::Append) : (QFile::WriteOnly | QFile::Truncate);
0171 
0172     QFile file(resume.destination.path());
0173     if (!resume.resuming) {
0174         QFile::Permissions perms;
0175         if (permissions == -1) {
0176             perms = QFile::ReadOwner | QFile::WriteOwner;
0177         } else {
0178             perms = KIO::convertPermissions(permissions | QFile::WriteOwner);
0179         }
0180         file.setPermissions(perms);
0181     }
0182 
0183     if (!file.open(mode)) {
0184         qCDebug(KIO_SMB_LOG) << "could not write to" << dstFile;
0185         switch (file.error()) {
0186         case QFile::OpenError:
0187             if (resume.resuming) {
0188                 return WorkerResult::fail(ERR_CANNOT_RESUME, kdst.toDisplayString());
0189             }
0190             return WorkerResult::fail(ERR_CANNOT_OPEN_FOR_WRITING, kdst.toDisplayString());
0191         case QFile::PermissionsError:
0192             return WorkerResult::fail(ERR_WRITE_ACCESS_DENIED, kdst.toDisplayString());
0193         default:
0194             break;
0195         }
0196         return WorkerResult::fail(ERR_CANNOT_OPEN_FOR_WRITING, kdst.toDisplayString());
0197     }
0198 
0199     // setup the source urls
0200     const SMBUrl src(ksrc);
0201 
0202     // Obtain information about source
0203     int errNum = cache_stat(src, &st);
0204     if (errNum != 0) {
0205         if (errNum == EACCES) {
0206             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, src.toDisplayString());
0207         }
0208         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString());
0209     }
0210 
0211     if (S_ISDIR(st.st_mode)) {
0212         return WorkerResult::fail(KIO::ERR_IS_DIRECTORY, src.toDisplayString());
0213     }
0214     totalSize(st.st_size);
0215 
0216     // Open the source file
0217     KIO::filesize_t processed_size = 0;
0218     const int srcfd = smbc_open(src.toSmbcUrl(), O_RDONLY, 0);
0219     auto closeSrcFd = qScopeGuard([srcfd] {
0220         smbc_close(srcfd);
0221     });
0222     if (srcfd < 0) {
0223         if (errno == EACCES) {
0224             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, src.toDisplayString());
0225         }
0226         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString());
0227     }
0228     errNum = 0;
0229     if (resume.resuming) {
0230         qCDebug(KIO_SMB_LOG) << "seeking to size" << resume.destinationOffset;
0231         off_t offset = smbc_lseek(srcfd, resume.destinationOffset, SEEK_SET);
0232         if (offset == -1) {
0233             return WorkerResult::fail(KIO::ERR_CANNOT_SEEK, src.toDisplayString());
0234         }
0235         processed_size += offset;
0236     }
0237 
0238     std::atomic<bool> isErr(false);
0239     TransferRingBuffer buffer(st.st_size);
0240     auto future = std::async(std::launch::async, [&buffer, &srcfd, &isErr]() -> int {
0241         while (!isErr) {
0242             TransferSegment *segment = buffer.nextFree();
0243             segment->size = smbc_read(srcfd, segment->buf.data(), segment->buf.capacity());
0244             if (segment->size <= 0) {
0245                 buffer.push();
0246                 buffer.done();
0247                 if (segment->size < 0) {
0248                     return KIO::ERR_CANNOT_READ;
0249                 }
0250                 break;
0251             }
0252             buffer.push();
0253         }
0254         return KJob::NoError;
0255     });
0256 
0257     WorkerResult result = WorkerResult::pass();
0258     while (true) {
0259         TransferSegment *segment = buffer.pop();
0260         if (!segment) { // done, no more segments pending
0261             break;
0262         }
0263 
0264         const qint64 bytesWritten = file.write(segment->buf.data(), segment->size);
0265         if (bytesWritten == -1) {
0266             qCDebug(KIO_SMB_LOG) << "copy now KIO::ERR_CANNOT_WRITE";
0267             result = WorkerResult::fail(KIO::ERR_CANNOT_WRITE, kdst.toDisplayString());
0268             isErr = true;
0269             buffer.unpop();
0270             break;
0271         }
0272 
0273         processed_size += bytesWritten;
0274         processedSize(processed_size);
0275         buffer.unpop();
0276     }
0277     if (!result.success()) { // writing failed
0278         future.wait();
0279     } else if (future.get() != KJob::NoError) { // check if read had an error
0280         result = WorkerResult::fail(future.get(), ksrc.toDisplayString());
0281     }
0282 
0283     // FINISHED
0284     smbc_close(srcfd);
0285 
0286     // Handle error condition.
0287 
0288     if (auto conclusionResult = Transfer::concludeResumeHasError<QFileResumeIO>(result, resume, this); !conclusionResult.success()) {
0289         return conclusionResult; // NB: error() called inside if applicable
0290     }
0291 
0292     // set modification time (if applicable)
0293     applyMTime([dstFile](struct utimbuf utbuf) {
0294         utbuf.actime = QFileInfo(dstFile).lastRead().toSecsSinceEpoch(); // access time, unchanged
0295         utime(QFile::encodeName(dstFile).constData(), &utbuf);
0296     });
0297 
0298     return WorkerResult::pass();
0299 }
0300 
0301 WorkerResult SMBWorker::smbCopyPut(const QUrl &ksrc, const QUrl &kdst, int permissions, KIO::JobFlags flags)
0302 {
0303     qCDebug(KIO_SMB_LOG) << "src = " << ksrc << ", dest = " << kdst << flags;
0304 
0305     QFile srcFile(ksrc.toLocalFile());
0306     const QFileInfo srcInfo(srcFile);
0307 
0308     if (!srcInfo.exists()) {
0309         return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, ksrc.toDisplayString());
0310     }
0311     if (srcInfo.isDir()) {
0312         return WorkerResult::fail(KIO::ERR_IS_DIRECTORY, ksrc.toDisplayString());
0313     }
0314 
0315     if (!srcFile.open(QFile::ReadOnly)) {
0316         qCDebug(KIO_SMB_LOG) << "could not read from" << ksrc;
0317         switch (srcFile.error()) {
0318         case QFile::PermissionsError:
0319             return WorkerResult::fail(KIO::ERR_WRITE_ACCESS_DENIED, ksrc.toDisplayString());
0320         default:
0321             break;
0322         }
0323         return WorkerResult::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, ksrc.toDisplayString());
0324     }
0325 
0326     totalSize(static_cast<filesize_t>(srcInfo.size()));
0327 
0328     const SMBUrl dstOrigUrl(kdst);
0329 
0330     auto resumeVariant = Transfer::shouldResume<SMBResumeIO>(dstOrigUrl, flags, this);
0331     if (std::holds_alternative<WorkerResult>(resumeVariant)) { // had an error
0332         return std::get<WorkerResult>(resumeVariant);
0333     }
0334     const TransferContext &resume = std::get<TransferContext>(resumeVariant);
0335 
0336     KIO::filesize_t processed_size = 0;
0337     const SMBUrl dstUrl(resume.destination);
0338 
0339     int dstfd = -1;
0340     int errNum = 0;
0341     if (resume.resuming) {
0342         // append if resuming
0343         qCDebug(KIO_SMB_LOG) << "resume" << dstUrl;
0344         dstfd = smbc_open(dstUrl.toSmbcUrl(), O_RDWR, 0);
0345         if (dstfd < 0) {
0346             errNum = errno;
0347         } else {
0348             const off_t offset = smbc_lseek(dstfd, 0, SEEK_END);
0349             if (offset == (off_t)-1) {
0350                 smbc_close(dstfd);
0351                 return WorkerResult::fail(KIO::ERR_CANNOT_SEEK, dstUrl.toDisplayString());
0352             }
0353             processed_size = offset;
0354         }
0355     } else {
0356         mode_t mode = 0;
0357         if (permissions == -1) {
0358             mode = S_IRUSR | S_IWUSR;
0359         } else {
0360             mode = permissions | S_IRUSR | S_IWUSR;
0361         }
0362 
0363         qCDebug(KIO_SMB_LOG) << "NO resume" << dstUrl;
0364         dstfd = smbc_open(dstUrl.toSmbcUrl(), O_CREAT | O_TRUNC | O_WRONLY, mode);
0365         if (dstfd < 0) {
0366             errNum = errno;
0367         }
0368     }
0369 
0370     if (dstfd < 0) {
0371         if (errNum == EACCES) {
0372             qCDebug(KIO_SMB_LOG) << "access denied";
0373             return WorkerResult::fail(KIO::ERR_WRITE_ACCESS_DENIED, dstUrl.toDisplayString());
0374         }
0375         qCDebug(KIO_SMB_LOG) << "can not open for writing";
0376         return WorkerResult::fail(KIO::ERR_CANNOT_OPEN_FOR_WRITING, dstUrl.toDisplayString());
0377     }
0378 
0379     WorkerResult result = WorkerResult::pass();
0380     if (processed_size == 0 || srcFile.seek(processed_size)) {
0381         // Perform the copy
0382         TransferSegment segment(srcInfo.size());
0383         while (true) {
0384             const ssize_t bytesRead = srcFile.read(segment.buf.data(), segment.buf.size());
0385             if (bytesRead <= 0) {
0386                 if (bytesRead < 0) {
0387                     result = WorkerResult::fail(KIO::ERR_CANNOT_READ, ksrc.toDisplayString());
0388                 }
0389                 break;
0390             }
0391 
0392             const qint64 bytesWritten = smbc_write(dstfd, segment.buf.data(), bytesRead);
0393             if (bytesWritten == -1) {
0394                 result = WorkerResult::fail(KIO::ERR_CANNOT_WRITE, kdst.toDisplayString());
0395                 break;
0396             }
0397 
0398             processed_size += bytesWritten;
0399             processedSize(processed_size);
0400         }
0401     } else {
0402         result = WorkerResult::fail(KIO::ERR_CANNOT_SEEK, ksrc.toDisplayString());
0403     }
0404 
0405     // FINISHED
0406     if (smbc_close(dstfd) < 0) {
0407         qCDebug(KIO_SMB_LOG) << dstUrl << "could not write";
0408         return WorkerResult::fail(KIO::ERR_CANNOT_WRITE, dstUrl.toDisplayString());
0409     }
0410 
0411     if (auto conclusionResult = Transfer::concludeResumeHasError<SMBResumeIO>(result, resume, this); !conclusionResult.success()) {
0412         return conclusionResult;
0413     }
0414 
0415     applyMTimeSMBC(dstOrigUrl);
0416 
0417     return WorkerResult::pass();
0418 }
0419 
0420 WorkerResult SMBWorker::del(const QUrl &kurl, bool isfile)
0421 {
0422     qCDebug(KIO_SMB_LOG) << kurl;
0423     m_current_url = kurl;
0424     int errNum = 0;
0425     int retVal = 0;
0426 
0427     if (isfile) {
0428         // Delete file
0429         qCDebug(KIO_SMB_LOG) << "Deleting file" << kurl;
0430         retVal = smbc_unlink(m_current_url.toSmbcUrl());
0431         if (retVal < 0) {
0432             errNum = errno;
0433         } else {
0434             errNum = 0;
0435         }
0436     } else {
0437         qCDebug(KIO_SMB_LOG) << "Deleting directory" << kurl;
0438         // Delete directory
0439         retVal = smbc_rmdir(m_current_url.toSmbcUrl());
0440         if (retVal < 0) {
0441             errNum = errno;
0442         } else {
0443             errNum = 0;
0444         }
0445     }
0446 
0447     if (errNum != 0) {
0448         return reportError(kurl, errNum);
0449     }
0450     return WorkerResult::pass();
0451 }
0452 
0453 WorkerResult SMBWorker::mkdir(const QUrl &kurl, int permissions)
0454 {
0455     qCDebug(KIO_SMB_LOG) << kurl;
0456     int errNum = 0;
0457     int retVal = 0;
0458     m_current_url = kurl;
0459 
0460     retVal = smbc_mkdir(m_current_url.toSmbcUrl(), S_IRWXU | S_IRWXG | S_IRWXO);
0461     if (retVal < 0) {
0462         errNum = errno;
0463     } else {
0464         errNum = 0;
0465     }
0466 
0467     if (retVal < 0) {
0468         if (errNum == EEXIST) {
0469             errNum = cache_stat(m_current_url, &st);
0470             if (errNum == 0 && S_ISDIR(st.st_mode)) {
0471                 return WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, m_current_url.toDisplayString());
0472             }
0473             return WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, m_current_url.toDisplayString());
0474         }
0475         qCDebug(KIO_SMB_LOG) << "exit with error " << kurl;
0476         return reportError(kurl, errNum);
0477     }
0478 
0479     if (permissions != -1) {
0480         // TODO enable the following when complete
0481         // smbc_chmod( url.toSmbcUrl(), permissions );
0482     }
0483     return WorkerResult::pass();
0484 }
0485 
0486 static bool sameInodeStat(bool hasSrcStat, const struct stat srcStat, const struct stat dstStat)
0487 {
0488     if (!hasSrcStat) {
0489         return false;
0490     }
0491     const auto badInode = static_cast<ino_t>(-1); // Fun fact: smb actually has code paths that can return -1 cast to ino_t (i.e. unsigned) ... ... ... :(
0492     if (srcStat.st_ino == badInode || dstStat.st_ino == badInode) {
0493         // If either returns a bad inode we don't know and assume they aren't the same entity.
0494         return false;
0495     }
0496     const bool equal = (srcStat.st_ino == dstStat.st_ino && srcStat.st_dev == dstStat.st_dev);
0497     qDebug(KIO_SMB_LOG) << "sameInodeStat"
0498                         << "equal" << equal << "hasSrcStat" << hasSrcStat << "srcStat.st_ino" << srcStat.st_ino << "dstStat.st_ino" << dstStat.st_ino
0499                         << "badInode" << badInode << "srcStat.st_dev" << srcStat.st_dev << "dstStat.st_dev" << dstStat.st_dev;
0500     return equal;
0501 }
0502 
0503 WorkerResult SMBWorker::rename(const QUrl &ksrc, const QUrl &kdest, KIO::JobFlags flags)
0504 {
0505     SMBUrl src;
0506     SMBUrl dst;
0507     int errNum = 0;
0508     int retVal = 0;
0509 
0510     qCDebug(KIO_SMB_LOG) << "old name = " << ksrc << ", new name = " << kdest << flags;
0511 
0512     src = ksrc;
0513     dst = kdest;
0514 
0515     // Check to se if the destination exists
0516     // Samba can be case sensitive or insensitive, depending on server capabilities and configuration. As such if the src and dst have the same case insensitive
0517     // name we'll stat the src to ascertain its inode and device. On recent samba and windows servers these seem to provide actually valid numbers we can rely
0518     // on. When not we can still pretend like they aren't the same file. Worst case the user gets an overwrite prompt.
0519     // https://bugs.kde.org/show_bug.cgi?id=430585
0520     bool hasSrcStat = false;
0521     struct stat srcStat {
0522     };
0523     if (src.path().compare(dst.path(), Qt::CaseInsensitive) == 0) {
0524         qCDebug(KIO_SMB_LOG) << "smbc_rename "
0525                              << "src and dst insensitive equal, performing inode comparision";
0526         errNum = cache_stat(dst, &st);
0527         if (errNum == 0) {
0528             hasSrcStat = true;
0529             srcStat = st;
0530         }
0531     }
0532     errNum = cache_stat(dst, &st);
0533     if (errNum == 0 && !sameInodeStat(hasSrcStat, srcStat, st)) {
0534         if (S_ISDIR(st.st_mode)) {
0535             qCDebug(KIO_SMB_LOG) << "KIO::ERR_DIR_ALREADY_EXIST";
0536             return WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, dst.toDisplayString());
0537         }
0538         if (!(flags & KIO::Overwrite)) {
0539             qCDebug(KIO_SMB_LOG) << "KIO::ERR_FILE_ALREADY_EXIST";
0540             return WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, dst.toDisplayString());
0541         }
0542     }
0543 
0544     qCDebug(KIO_SMB_LOG) << "smbc_rename " << src.toSmbcUrl() << " " << dst.toSmbcUrl();
0545     retVal = smbc_rename(src.toSmbcUrl(), dst.toSmbcUrl());
0546     if (retVal < 0) {
0547         errNum = errno;
0548     } else {
0549         errNum = 0;
0550     }
0551 
0552     if (retVal < 0) {
0553         qCDebug(KIO_SMB_LOG) << "failed ";
0554         switch (errNum) {
0555         case ENOENT:
0556             errNum = cache_stat(src, &st);
0557             if (errNum != 0) {
0558                 if (errNum == EACCES) {
0559                     qCDebug(KIO_SMB_LOG) << "KIO::ERR_ACCESS_DENIED";
0560                     return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, src.toDisplayString());
0561                 }
0562                 qCDebug(KIO_SMB_LOG) << "KIO::ERR_DOES_NOT_EXIST";
0563                 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString());
0564             }
0565             break;
0566         case EACCES:
0567         case EPERM:
0568             qCDebug(KIO_SMB_LOG) << "KIO::ERR_ACCESS_DENIED";
0569             return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, dst.toDisplayString());
0570         }
0571         qCDebug(KIO_SMB_LOG) << "exit with error";
0572         return WorkerResult::fail(KIO::ERR_CANNOT_RENAME, src.toDisplayString());
0573     }
0574 
0575     qCDebug(KIO_SMB_LOG) << "everything fine\n";
0576     return WorkerResult::pass();
0577 }