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 }