Commit 4e41727c authored by Harald Sitter's avatar Harald Sitter 🌈
Browse files

smb: reshuffle discovery systems

the modern discoveries were conditional on smbc_opendir succeeding but
that is rather silly since smbc can fail on ENOENT if (for example)
netbios is disabled. since top level server discovery may happen through
all or none of NB/DNSSD/WSD that made no sense as even when NB fails
we may be able to produce service listing through one of the other two
discovery services.

this piece of code would actually benefit from major refactoring because
all three discovery systems should actually run at the same time while
currently NB holds up the entire show. alas, too invasive for 20.04...
so, reshuffling it is:

smbc_opendir still runs initially. after that, if the url is browsing
smb:// we'll jump into modern discovery. any errors produced by opendir
will be ignored! authentication errors and the like cannot happen for
smb:// because we aren't yet talking to any one server, which leaves
actual browsing errors which are not fatal considering we have other
options.
for non-top-level urls we'll otherwise jump into error handling (e.g.
server requires auth or something)
at the end if we haven't returned early with an error we'll inject our
`.` entries as per usual

test plan:
- add `disable netbios = yes` to smb.conf
- browse network
- discovers dnssd/wsd servers
- also works with the option removed again

BUG: 421624

FIXED-IN: 20.04.2
parent 12622509
......@@ -549,100 +549,82 @@ void SMBSlave::listDir(const QUrl &kurl)
udsentry.clear();
} while (dirp); // checked already in the head
// Run service discovery if the path is root. This augments
// "native" results from libsmbclient.
auto normalizedUrl = url.adjusted(QUrl::NormalizePathSegments);
if (normalizedUrl.path().isEmpty()) {
qCDebug(KIO_SMB_LOG) << "Trying modern discovery (dnssd/wsdiscovery)";
// clean up
smbc_closedir(dirfd);
}
QEventLoop e;
// Run service discovery if the path is root. This augments
// "native" results from libsmbclient.
// Also, should native resolution have encountered an error it will not matter.
auto normalizedUrl = url.adjusted(QUrl::NormalizePathSegments);
if (normalizedUrl.path().isEmpty()) {
qCDebug(KIO_SMB_LOG) << "Trying modern discovery (dnssd/wsdiscovery)";
UDSEntryList list;
QStringList discoveredNames;
QEventLoop e;
const auto flushEntries = [this, &list]() {
if (list.isEmpty()) {
return;
}
listEntries(list);
list.clear();
};
const auto quitLoop = [&e, &flushEntries]() {
flushEntries();
e.quit();
};
// Since slavebase has no eventloop it wont publish results
// on a timer, since we do not know how long our discovery
// will take this is super meh because we may appear
// stuck for a while. Implement our own listing system
// based on QTimer to mitigate.
QTimer sendTimer;
sendTimer.setInterval(300);
connect(&sendTimer, &QTimer::timeout, this, flushEntries);
sendTimer.start();
DNSSDDiscoverer d;
WSDiscoverer w;
const QList<Discoverer *> discoverers {&d, &w};
auto appendDiscovery = [&](const Discovery::Ptr &discovery) {
if (discoveredNames.contains(discovery->udsName())) {
return;
}
discoveredNames << discovery->udsName();
list.append(discovery->toEntry());
};
auto maybeFinished = [&] { // finishes if all discoveries finished
bool allFinished = true;
for (auto discoverer : discoverers) {
allFinished = allFinished && discoverer->isFinished();
}
if (allFinished) {
quitLoop();
}
};
UDSEntryList list;
QStringList discoveredNames;
connect(&d, &DNSSDDiscoverer::newDiscovery, this, appendDiscovery);
connect(&w, &WSDiscoverer::newDiscovery, this, appendDiscovery);
const auto flushEntries = [this, &list]() {
if (list.isEmpty()) {
return;
}
listEntries(list);
list.clear();
};
connect(&d, &DNSSDDiscoverer::finished, this, maybeFinished);
connect(&w, &WSDiscoverer::finished, this, maybeFinished);
const auto quitLoop = [&e, &flushEntries]() {
flushEntries();
e.quit();
};
d.start();
w.start();
// Since slavebase has no eventloop it wont publish results
// on a timer, since we do not know how long our discovery
// will take this is super meh because we may appear
// stuck for a while. Implement our own listing system
// based on QTimer to mitigate.
QTimer sendTimer;
sendTimer.setInterval(300);
connect(&sendTimer, &QTimer::timeout, this, flushEntries);
sendTimer.start();
QTimer::singleShot(16000, &e, quitLoop); // max execution time!
e.exec();
DNSSDDiscoverer d;
WSDiscoverer w;
qCDebug(KIO_SMB_LOG) << "Modern discovery finished.";
}
const QList<Discoverer *> discoverers {&d, &w};
if (dir_is_root) {
udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, ".");
udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH));
} else {
udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, ".");
const int statErr = browse_stat_path(m_current_url, udsentry);
if (statErr) {
if (statErr == ENOENT || statErr == ENOTDIR) {
reportWarning(m_current_url, statErr);
}
// Create a default UDSEntry if we could not stat the actual directory
udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH));
auto appendDiscovery = [&](const Discovery::Ptr &discovery) {
if (discoveredNames.contains(discovery->udsName())) {
return;
}
}
listEntry(udsentry);
udsentry.clear();
discoveredNames << discovery->udsName();
list.append(discovery->toEntry());
};
// clean up
smbc_closedir(dirfd);
} else {
auto maybeFinished = [&] { // finishes if all discoveries finished
bool allFinished = true;
for (auto discoverer : discoverers) {
allFinished = allFinished && discoverer->isFinished();
}
if (allFinished) {
quitLoop();
}
};
connect(&d, &DNSSDDiscoverer::newDiscovery, this, appendDiscovery);
connect(&w, &WSDiscoverer::newDiscovery, this, appendDiscovery);
connect(&d, &DNSSDDiscoverer::finished, this, maybeFinished);
connect(&w, &WSDiscoverer::finished, this, maybeFinished);
d.start();
w.start();
QTimer::singleShot(16000, &e, quitLoop); // max execution time!
e.exec();
qCDebug(KIO_SMB_LOG) << "Modern discovery finished.";
} else if (dirfd < 0) { // not smb:// and had an error -> handle it
if (errNum == EPERM || errNum == EACCES || workaroundEEXIST(errNum)) {
qCDebug(KIO_SMB_LOG) << "trying checkPassword";
const int passwordError = checkPassword(m_current_url);
......@@ -665,6 +647,24 @@ void SMBSlave::listDir(const QUrl &kurl)
return;
}
if (dir_is_root) {
udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, ".");
udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH));
} else {
udsentry.fastInsert(KIO::UDSEntry::UDS_NAME, ".");
const int statErr = browse_stat_path(m_current_url, udsentry);
if (statErr) {
if (statErr == ENOENT || statErr == ENOTDIR) {
reportWarning(m_current_url, statErr);
}
// Create a default UDSEntry if we could not stat the actual directory
udsentry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
udsentry.fastInsert(KIO::UDSEntry::UDS_ACCESS, (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH));
}
}
listEntry(udsentry);
finished();
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment