File indexing completed on 2024-11-10 04:30:38
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2000 Caldera Systems Inc. 0004 SPDX-FileCopyrightText: 2018-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 <utility> 0012 0013 #include <KDNSSD/RemoteService> 0014 #include <KDNSSD/ServiceBrowser> 0015 #include <KIO/Job> 0016 #include <KLocalizedString> 0017 0018 #include <QEventLoop> 0019 #include <QTimer> 0020 0021 #include <grp.h> 0022 #include <pwd.h> 0023 0024 #include "dnssddiscoverer.h" 0025 #include "smbcdiscoverer.h" 0026 #include "wsdiscoverer.h" 0027 #include <config-runtime.h> 0028 0029 using namespace KIO; 0030 0031 int SMBWorker::cache_stat(const SMBUrl &url, struct stat *st) 0032 { 0033 int cacheStatErr = 0; 0034 int result = smbc_stat(url.toSmbcUrl(), st); 0035 if (result == 0) { 0036 cacheStatErr = 0; 0037 } else { 0038 cacheStatErr = errno; 0039 } 0040 qCDebug(KIO_SMB_LOG) << "size " << static_cast<KIO::filesize_t>(st->st_size); 0041 return cacheStatErr; 0042 } 0043 0044 int SMBWorker::browse_stat_path(const SMBUrl &url, UDSEntry &udsentry) 0045 { 0046 int cacheStatErr = cache_stat(url, &st); 0047 if (cacheStatErr == 0) { 0048 return statToUDSEntry(url, st, udsentry); 0049 } 0050 0051 return cacheStatErr; 0052 } 0053 0054 int SMBWorker::statToUDSEntry(const QUrl &url, const struct stat &st, KIO::UDSEntry &udsentry) 0055 { 0056 if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) { 0057 qCDebug(KIO_SMB_LOG) << "mode: " << st.st_mode; 0058 warning( 0059 i18n("%1:\n" 0060 "Unknown file type, neither directory or file.", 0061 url.toDisplayString())); 0062 return EINVAL; 0063 } 0064 0065 if (!S_ISDIR(st.st_mode)) { 0066 // Awkwardly documented at 0067 // https://www.samba.org/samba/docs/using_samba/ch08.html 0068 // libsmb_stat.c assigns special meaning to +x permissions 0069 // (obviously only on files, all dirs are +x so this hacky representation 0070 // wouldn't work!): 0071 // - S_IXUSR = DOS archive: This file has been touched since the last DOS backup was performed on it. 0072 // - S_IXGRP = DOS system: This file has a specific purpose required by the operating system. 0073 // - S_IXOTH = DOS hidden: This file has been marked to be invisible to the user, unless the operating system is explicitly set to show it. 0074 // Only hiding has backing through KIO right now. 0075 if (st.st_mode & S_IXOTH) { // DOS hidden 0076 udsentry.fastInsert(KIO::UDSEntry::UDS_HIDDEN, true); 0077 } 0078 } 0079 0080 // UID and GID **must** not be mapped. The values returned by libsmbclient are 0081 // simply the getuid/getgid of the process. They mean absolutely nothing. 0082 // Also see libsmb_stat.c. 0083 // Related: https://bugs.kde.org/show_bug.cgi?id=212801 0084 0085 // POSIX Access mode must not be mapped either! 0086 // It's meaningless for smb shares and downright disadvantageous. 0087 // The mode attributes outside the ones used and document above are 0088 // useless. The only one actively set is readonlyness. 0089 // 0090 // BUT the READONLY attribute does nothing on NT systems: 0091 // https://support.microsoft.com/en-us/help/326549/you-cannot-view-or-change-the-read-only-or-the-system-attributes-of-fo 0092 // The Read-only and System attributes is only used by Windows Explorer to determine 0093 // whether the folder is a special folder, such as a system folder that has its view 0094 // customized by Windows (for example, My Documents, Favorites, Fonts, Downloaded Program Files), 0095 // or a folder that you customized by using the Customize tab of the folder's Properties dialog box. 0096 // 0097 // As such respecting it on a KIO level is actually wrong as it doesn't indicate actual 0098 // readonlyness since the 90s and causes us to show readonly UI states when in fact 0099 // the directory is perfectly writable. 0100 // https://bugs.kde.org/show_bug.cgi?id=414482 0101 // 0102 // Should we ever want to parse desktop.ini like we do .directory we'd only want to when a 0103 // dir is readonly as per the above microsoft support article. 0104 // Also see: 0105 // https://docs.microsoft.com/en-us/windows/win32/shell/how-to-customize-folders-with-desktop-ini 0106 0107 udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, st.st_mode & S_IFMT); 0108 udsentry.fastInsert(KIO::UDSEntry::UDS_SIZE, st.st_size); 0109 udsentry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, st.st_mtime); 0110 udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, st.st_atime); 0111 // No, st_ctime is not UDS_CREATION_TIME... 0112 0113 return 0; 0114 } 0115 0116 WorkerResult SMBWorker::stat(const QUrl &kurl) 0117 { 0118 qCDebug(KIO_SMB_LOG) << kurl; 0119 // make a valid URL 0120 QUrl url = checkURL(kurl); 0121 0122 // if URL is not valid we have to redirect to correct URL 0123 if (url != kurl) { 0124 qCDebug(KIO_SMB_LOG) << "redirection " << url; 0125 redirection(url); 0126 return WorkerResult::pass(); 0127 } 0128 0129 m_current_url = url; 0130 0131 UDSEntry udsentry; 0132 // Set name 0133 udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, kurl.fileName()); 0134 0135 switch (m_current_url.getType()) { 0136 case SMBURLTYPE_UNKNOWN: 0137 return WorkerResult::fail(ERR_MALFORMED_URL, url.toDisplayString()); 0138 case SMBURLTYPE_PRINTER: 0139 return WorkerResult::fail(ERR_UNSUPPORTED_ACTION, url.toDisplayString()); 0140 case SMBURLTYPE_ENTIRE_NETWORK: 0141 case SMBURLTYPE_WORKGROUP_OR_SERVER: 0142 udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); 0143 statEntry(udsentry); 0144 return WorkerResult::pass(); 0145 case SMBURLTYPE_SHARE_OR_PATH: { 0146 int ret = browse_stat_path(m_current_url, udsentry); 0147 0148 if (ret == EPERM || ret == EACCES || workaroundEEXIST(ret)) { 0149 SMBUrl smbUrl(url); 0150 0151 const int passwordError = checkPassword(smbUrl); 0152 if (passwordError == KJob::NoError) { 0153 redirection(smbUrl); 0154 return WorkerResult::pass(); 0155 } 0156 if (passwordError == KIO::ERR_USER_CANCELED) { 0157 return reportError(url, ret); 0158 } 0159 return WorkerResult::fail(passwordError, url.toString()); 0160 } 0161 if (ret != 0) { 0162 qCDebug(KIO_SMB_LOG) << "stat() error" << ret << url; 0163 return reportError(url, ret); 0164 } 0165 0166 statEntry(udsentry); 0167 return WorkerResult::pass(); 0168 } 0169 } 0170 0171 qCDebug(KIO_SMB_LOG) << "UNKNOWN " << url; 0172 return WorkerResult::pass(); 0173 } 0174 0175 // TODO: complete checking <-- what does that even mean? 0176 // TODO: why is this not part of SMBUrl or at the very least URL validation should 0177 // be 100% shared between this and SMBUrl. Notably SMBUrl has code that looks 0178 // to do a similar thing but is much less complete. 0179 QUrl SMBWorker::checkURL(const QUrl &kurl_) const 0180 { 0181 qCDebug(KIO_SMB_LOG) << "checkURL " << kurl_; 0182 0183 QUrl kurl(kurl_); 0184 // We treat cifs as an alias but need to translate it to smb. 0185 // https://bugs.kde.org/show_bug.cgi?id=327295 0186 // It's not IANA registered and also libsmbc internally expects 0187 // smb URIs so we do very broadly coerce cifs to smb. 0188 // Also see SMBUrl. 0189 if (kurl.scheme() == "cifs") { 0190 kurl.setScheme("smb"); 0191 } 0192 0193 // For WS-Discovered hosts we assume they'll respond to DNSSD names on .local but 0194 // they may only respond to llmnr/netbios names. Transparently fall back. 0195 // 0196 // Desktop linuxes tend to have llmnr disabled, by contrast win10 has dnssd enabled, 0197 // so chances are we'll be able to find a host.local more reliably. 0198 // Attempt to resolve foo.local natively, if that works use it, otherwise default to 0199 // the presumed LLMNR/netbios name found during discovery. 0200 // This should then yield reasonable results with any combination of WSD/DNSSD/LLMNR support. 0201 // - WSD+Avahi (on linux) 0202 // - WSD+Win10 (i.e. dnssd + llmnr) 0203 // - WSD+CrappyNAS (e.g. llmnr or netbios only) 0204 // 0205 // NB: smbc has no way to resolve a name without also triggering auth etc.: we must 0206 // rely on the system's ability to resolve DNSSD for this check. 0207 const QLatin1String wsdSuffix(".kio-discovery-wsd"); 0208 if (kurl.host().endsWith(wsdSuffix)) { 0209 QString host = kurl.host(); 0210 host.chop(wsdSuffix.size()); 0211 const QString dnssd(host + ".local"); 0212 auto dnssdHost = KDNSSD::ServiceBrowser::resolveHostName(dnssd); 0213 if (!dnssdHost.isNull()) { 0214 qCDebug(KIO_SMB_LOG) << "Resolved DNSSD name:" << dnssd; 0215 host = dnssd; 0216 } else { 0217 qCDebug(KIO_SMB_LOG) << "Failed to resolve DNSSD name:" << dnssd; 0218 qCDebug(KIO_SMB_LOG) << "Falling back to LLMNR name:" << host; 0219 } 0220 kurl.setHost(host); 0221 } 0222 0223 QString surl = kurl.url(); 0224 // transform any links in the form smb:/ into smb:// 0225 if (surl.startsWith(QLatin1String("smb:/"))) { 0226 if (surl.length() == 5) { 0227 return QUrl("smb://"); 0228 } 0229 if (surl.at(5) != '/') { 0230 surl = "smb://" + surl.mid(5); 0231 qCDebug(KIO_SMB_LOG) << "checkURL return1 " << surl << " " << QUrl(surl); 0232 return QUrl(surl); 0233 } 0234 } 0235 if (surl == QLatin1String("smb://")) { 0236 return kurl; // unchanged 0237 } 0238 0239 // smb:// normally have no userinfo 0240 // we must redirect ourself to remove the username and password 0241 if (surl.contains('@') && !surl.contains("smb://")) { 0242 QUrl url(kurl); 0243 url.setPath('/' + kurl.url().right(kurl.url().length() - kurl.url().indexOf('@') - 1)); 0244 QString userinfo = kurl.url().mid(5, kurl.url().indexOf('@') - 5); 0245 if (userinfo.contains(':')) { 0246 url.setUserName(userinfo.left(userinfo.indexOf(':'))); 0247 url.setPassword(userinfo.right(userinfo.length() - userinfo.indexOf(':') - 1)); 0248 } else { 0249 url.setUserName(userinfo); 0250 } 0251 qCDebug(KIO_SMB_LOG) << "checkURL return2 " << url; 0252 return url; 0253 } 0254 0255 // if there's a valid host, don't have an empty path 0256 QUrl url(kurl); 0257 0258 if (url.path().isEmpty()) { 0259 url.setPath("/"); 0260 } 0261 0262 qCDebug(KIO_SMB_LOG) << "checkURL return3 " << url; 0263 return url; 0264 } 0265 0266 SMBWorker::SMBError SMBWorker::errnumToKioError(const SMBUrl &url, const int errNum) 0267 { 0268 qCDebug(KIO_SMB_LOG) << "errNum" << errNum; 0269 0270 switch (errNum) { 0271 case ENOENT: 0272 if (url.getType() == SMBURLTYPE_ENTIRE_NETWORK) { 0273 return SMBError{ERR_WORKER_DEFINED, i18n("Unable to find any workgroups in your local network. This might be caused by an enabled firewall.")}; 0274 } 0275 return SMBError{ERR_DOES_NOT_EXIST, url.toDisplayString()}; 0276 #ifdef ENOMEDIUM 0277 case ENOMEDIUM: 0278 return SMBError{ERR_WORKER_DEFINED, i18n("No media in device for %1", url.toDisplayString())}; 0279 #endif 0280 #ifdef EHOSTDOWN 0281 case EHOSTDOWN: 0282 #endif 0283 case ECONNREFUSED: 0284 return SMBError{ERR_WORKER_DEFINED, i18n("Could not connect to host for %1", url.toDisplayString())}; 0285 case ENOTDIR: 0286 return SMBError{ERR_CANNOT_ENTER_DIRECTORY, url.toDisplayString()}; 0287 case EFAULT: 0288 case EINVAL: 0289 return SMBError{ERR_DOES_NOT_EXIST, url.toDisplayString()}; 0290 case EPERM: 0291 case EACCES: 0292 return SMBError{ERR_ACCESS_DENIED, url.toDisplayString()}; 0293 case EIO: 0294 case ENETUNREACH: 0295 if (url.getType() == SMBURLTYPE_ENTIRE_NETWORK || url.getType() == SMBURLTYPE_WORKGROUP_OR_SERVER) { 0296 return SMBError{ERR_WORKER_DEFINED, i18n("Error while connecting to server responsible for %1", url.toDisplayString())}; 0297 } 0298 return SMBError{ERR_CONNECTION_BROKEN, url.toDisplayString()}; 0299 case ENOMEM: 0300 return SMBError{ERR_OUT_OF_MEMORY, url.toDisplayString()}; 0301 case ENODEV: 0302 return SMBError{ERR_WORKER_DEFINED, i18n("Share could not be found on given server")}; 0303 case EBADF: 0304 return SMBError{ERR_INTERNAL, i18n("Bad file descriptor")}; 0305 case ETIMEDOUT: 0306 return SMBError{ERR_SERVER_TIMEOUT, url.host()}; 0307 case ENOTEMPTY: 0308 return SMBError{ERR_CANNOT_RMDIR, url.toDisplayString()}; 0309 #ifdef ENOTUNIQ 0310 case ENOTUNIQ: 0311 return SMBError{ERR_WORKER_DEFINED, 0312 i18n("The given name could not be resolved to a unique server. " 0313 "Make sure your network is setup without any name conflicts " 0314 "between names used by Windows and by UNIX name resolution.")}; 0315 #endif 0316 case ECONNABORTED: 0317 return SMBError{ERR_CONNECTION_BROKEN, url.host()}; 0318 case EHOSTUNREACH: 0319 return SMBError{ERR_CANNOT_CONNECT, 0320 i18nc("@info:status smb failed to reach the server (e.g. server offline or network failure). %1 is an ip address or hostname", 0321 "%1: Host unreachable", 0322 url.host())}; 0323 case 0: // success 0324 return SMBError{ERR_INTERNAL, 0325 i18n("libsmbclient reported an error, but did not specify " 0326 "what the problem is. This might indicate a severe problem " 0327 "with your network - but also might indicate a problem with " 0328 "libsmbclient.\n" 0329 "If you want to help us, please provide a tcpdump of the " 0330 "network interface while you try to browse (be aware that " 0331 "it might contain private data, so do not post it if you are " 0332 "unsure about that - you can send it privately to the developers " 0333 "if they ask for it)")}; 0334 default: 0335 return SMBError{ERR_INTERNAL, 0336 i18nc("%1 is an error number, %2 either a pretty string or the number", 0337 "Unknown error condition: [%1] %2", 0338 QString::number(errNum), 0339 QString::fromLocal8Bit(strerror(errNum)))}; 0340 } 0341 } 0342 0343 WorkerResult SMBWorker::reportError(const SMBUrl &url, const int errNum) 0344 { 0345 const SMBError smbErr = errnumToKioError(url, errNum); 0346 return WorkerResult::fail(smbErr.kioErrorId, smbErr.errorString); 0347 } 0348 0349 void SMBWorker::reportWarning(const SMBUrl &url, const int errNum) 0350 { 0351 const SMBError smbErr = errnumToKioError(url, errNum); 0352 const QString errorString = buildErrorString(smbErr.kioErrorId, smbErr.errorString); 0353 0354 warning(xi18n("Error occurred while trying to access %1<nl/>%2", url.url(), errorString)); 0355 } 0356 0357 WorkerResult SMBWorker::listDir(const QUrl &kurl) 0358 { 0359 qCDebug(KIO_SMB_LOG) << kurl; 0360 0361 // check (correct) URL 0362 QUrl url = checkURL(kurl); 0363 // if URL is not valid we have to redirect to correct URL 0364 if (url != kurl) { 0365 redirection(url); 0366 return WorkerResult::pass(); 0367 } 0368 0369 m_current_url = kurl; 0370 0371 QEventLoop e; 0372 0373 UDSEntryList list; 0374 QStringList discoveredNames; 0375 0376 const auto flushEntries = [this, &list]() { 0377 if (list.isEmpty()) { 0378 return; 0379 } 0380 listEntries(list); 0381 list.clear(); 0382 }; 0383 0384 // Since WorkerBase has no eventloop it wont publish results 0385 // on a timer, since we do not know how long our discovery 0386 // will take this is super meh because we may appear 0387 // stuck for a while. Implement our own listing system 0388 // based on QTimer to mitigate. 0389 QTimer sendTimer; 0390 sendTimer.setInterval(300); 0391 connect(&sendTimer, &QTimer::timeout, this, flushEntries); 0392 sendTimer.start(); 0393 0394 QSharedPointer<SMBCDiscoverer> smbc(new SMBCDiscoverer(m_current_url, &e, this)); 0395 0396 QVector<QSharedPointer<Discoverer>> discoverers; 0397 discoverers << smbc; 0398 0399 auto appendDiscovery = [&](const Discovery::Ptr &discovery) { 0400 if (discoveredNames.contains(discovery->udsName(), Qt::CaseInsensitive)) { 0401 return; 0402 } 0403 // Not tracking hosts. Tracking hosts means **guessing** if foo.local 0404 // and foo and foo.kio-discovery-wsd will actually resolve to the same 0405 // IP address, which is tricky to do at best. In the interest of efficiency 0406 // I'd rather have the de-duplication requirement be that the name of 0407 // two competing service discovery systems needs to be the same. 0408 discoveredNames << discovery->udsName(); 0409 list.append(discovery->toEntry()); 0410 }; 0411 0412 auto maybeFinished = [&] { // finishes if all discoveries finished 0413 bool allFinished = true; 0414 for (const auto &discoverer : std::as_const(discoverers)) { 0415 allFinished = allFinished && discoverer->isFinished(); 0416 } 0417 if (allFinished) { 0418 flushEntries(); 0419 e.quit(); 0420 } 0421 }; 0422 0423 connect(smbc.data(), &SMBCDiscoverer::newDiscovery, this, appendDiscovery); 0424 connect(smbc.data(), &SMBCDiscoverer::finished, this, maybeFinished); 0425 0426 // Run service discovery if the path is root. This augments 0427 // "native" results from libsmbclient. 0428 // Also, should native resolution have encountered an error it will not matter. 0429 if (m_current_url.getType() == SMBURLTYPE_ENTIRE_NETWORK) { 0430 QSharedPointer<DNSSDDiscoverer> dnssd(new DNSSDDiscoverer); 0431 QSharedPointer<WSDiscoverer> wsd(new WSDiscoverer); 0432 discoverers << dnssd << wsd; 0433 0434 qCDebug(KIO_SMB_LOG) << "Adding modern discovery (dnssd/wsdiscovery)"; 0435 0436 connect(dnssd.data(), &DNSSDDiscoverer::newDiscovery, this, appendDiscovery); 0437 connect(wsd.data(), &WSDiscoverer::newDiscovery, this, appendDiscovery); 0438 0439 connect(dnssd.data(), &DNSSDDiscoverer::finished, this, maybeFinished); 0440 connect(wsd.data(), &WSDiscoverer::finished, this, maybeFinished); 0441 0442 dnssd->start(); 0443 wsd->start(); 0444 0445 qCDebug(KIO_SMB_LOG) << "Modern discovery set up."; 0446 } 0447 0448 qCDebug(KIO_SMB_LOG) << "Starting discovery."; 0449 smbc->start(); 0450 e.exec(); 0451 0452 qCDebug(KIO_SMB_LOG) << "Discovery finished."; 0453 0454 if (m_current_url.getType() != SMBURLTYPE_ENTIRE_NETWORK && smbc->error() != 0) { 0455 // not smb:// and had an error -> handle it 0456 const int err = smbc->error(); 0457 if (err == EPERM || err == EACCES || err == EINVAL || workaroundEEXIST(err)) { 0458 qCDebug(KIO_SMB_LOG) << "trying checkPassword"; 0459 const int passwordError = checkPassword(m_current_url); 0460 if (passwordError == KJob::NoError) { 0461 redirection(m_current_url); 0462 return WorkerResult::pass(); 0463 } 0464 if (passwordError == KIO::ERR_USER_CANCELED) { 0465 qCDebug(KIO_SMB_LOG) << "user cancelled password request"; 0466 return reportError(m_current_url, err); 0467 } 0468 qCDebug(KIO_SMB_LOG) << "generic password error:" << passwordError; 0469 return WorkerResult::fail(passwordError, m_current_url.toString()); 0470 } 0471 0472 qCDebug(KIO_SMB_LOG) << "reporting generic error:" << err; 0473 return reportError(m_current_url, err); 0474 } 0475 0476 UDSEntry udsentry; 0477 if (smbc->dirWasRoot()) { 0478 udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); 0479 udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, "."); 0480 udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH)); 0481 } else { 0482 udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, "."); 0483 const int statErr = browse_stat_path(m_current_url, udsentry); 0484 if (statErr != 0) { 0485 if (statErr == ENOENT || statErr == ENOTDIR) { 0486 reportWarning(m_current_url, statErr); 0487 } 0488 // Create a default UDSEntry if we could not stat the actual directory 0489 udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); 0490 udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)); 0491 } 0492 } 0493 listEntry(udsentry); 0494 0495 return WorkerResult::pass(); 0496 } 0497 0498 WorkerResult SMBWorker::fileSystemFreeSpace(const QUrl &url) 0499 { 0500 if (url.host().endsWith("kio-discovery-wsd")) { 0501 return WorkerResult::fail(KIO::ERR_UNKNOWN_HOST, url.url()); 0502 } 0503 qCDebug(KIO_SMB_LOG) << url; 0504 0505 // Avoid crashing in smbc_fstatvfs below when 0506 // requesting free space for smb:// which doesn't 0507 // make sense to do to begin with 0508 if (url.host().isEmpty()) { 0509 return WorkerResult::fail(KIO::ERR_CANNOT_STAT, url.url()); 0510 } 0511 0512 SMBUrl smbUrl = url; 0513 0514 struct statvfs dirStat { 0515 }; 0516 memset(&dirStat, 0, sizeof(struct statvfs)); 0517 auto smbcUrl = smbUrl.toSmbcUrl(); // do not use temporary in function call, makes clazy happy 0518 const int err = smbc_statvfs(smbcUrl.data(), &dirStat); 0519 if (err < 0) { 0520 return WorkerResult::fail(KIO::ERR_CANNOT_STAT, url.url()); 0521 } 0522 0523 // libsmb_stat.c has very awkward conditional branching that results 0524 // in data meaning different things based on context: 0525 // A samba host with unix extensions has f_frsize==0 and the f_bsize is 0526 // the actual block size. Any other server (such as windows) has a non-zero 0527 // f_frsize denoting the amount of sectors in a block and the f_bsize is 0528 // the amount of bytes in a sector. As such frsize*bsize is the actual 0529 // block size. 0530 // This was also broken in different ways throughout history, so depending 0531 // on the specific libsmbc versions the milage will vary. 4.7 to 4.11 are 0532 // at least behaving as described though. 0533 // https://bugs.kde.org/show_bug.cgi?id=298801 0534 const auto frames = (dirStat.f_frsize == 0) ? 1 : dirStat.f_frsize; 0535 const auto blockSize = dirStat.f_bsize * frames; 0536 // Further more on older versions of samba f_bavail may not be set... 0537 const auto total = blockSize * dirStat.f_blocks; 0538 const auto available = blockSize * ((dirStat.f_bavail != 0) ? dirStat.f_bavail : dirStat.f_bfree); 0539 0540 // If total is 0 don't bother reporting it, it just makes dolphin misbehave. And is indicative of 0541 // us not having any viable data. 0542 // https://bugs.kde.org/show_bug.cgi?id=431050 0543 if (total <= 0) { 0544 return WorkerResult::fail(KIO::ERR_UNSUPPORTED_ACTION, url.url()); 0545 } 0546 0547 setMetaData("total", QString::number(total)); 0548 setMetaData("available", QString::number(available)); 0549 0550 return WorkerResult::pass(); 0551 } 0552 0553 bool SMBWorker::workaroundEEXIST(const int errNum) const 0554 { 0555 return (errNum == EEXIST) && m_enableEEXISTWorkaround; 0556 }