File indexing completed on 2024-11-10 04:30:38
0001 /* 0002 SPDX-License-Identifier: LGPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2000 Alexander Neundorf <neundorf@kde.org> 0004 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0005 */ 0006 0007 #include "kio_smb.h" 0008 0009 #include <KLocalizedString> 0010 #include <KProcess> 0011 #include <KShell> 0012 0013 #include <QByteArray> 0014 #include <QDataStream> 0015 #include <QDir> 0016 #include <unistd.h> 0017 0018 WorkerResult SMBWorker::getACE(QDataStream &stream) 0019 { 0020 QUrl qurl; 0021 stream >> qurl; 0022 const SMBUrl url(qurl); 0023 0024 // Use the same buffer for all properties to reduce the likelihood of having needless allocations. ACL will usually 0025 // be the largest of the lot and if we try to get that first we'll probably fit all the other properties into the 0026 // same buffer easy peasy. 0027 constexpr auto defaultArraySize = 4096; // arbitrary, the ACL is of unknown size 0028 const int pageSize = static_cast<int>(sysconf(_SC_PAGESIZE)); 0029 Q_ASSERT(pageSize == sysconf(_SC_PAGESIZE)); // ensure conversion accuracy 0030 QVarLengthArray<char, defaultArraySize> value(pageSize); 0031 0032 for (auto xattr : { 0033 "system.nt_sec_desc.acl.*+", 0034 "system.nt_sec_desc.owner+", 0035 "system.nt_sec_desc.group+", 0036 }) { 0037 while (true) { 0038 const auto result = smbc_getxattr(url.toSmbcUrl(), xattr, value.data(), value.size()); 0039 if (const auto error = errno; result < 0) { 0040 // https://bugzilla.samba.org/show_bug.cgi?id=15088 0041 if (error == ERANGE) { 0042 value.resize(value.size() + pageSize); 0043 continue; 0044 } 0045 qWarning() << xattr << strerror(error); 0046 return WorkerResult::fail(ERR_INTERNAL, strerror(error)); 0047 } 0048 qCDebug(KIO_SMB_LOG) << "XATTR" << xattr << value.data(); 0049 if (QLatin1String("system.nt_sec_desc.acl.*+") == QLatin1String(xattr)) { 0050 setMetaData("ACL", QString::fromUtf8(value.constData())); 0051 } 0052 if (QLatin1String("system.nt_sec_desc.owner+") == QLatin1String(xattr)) { 0053 setMetaData("OWNER", QString::fromUtf8(value.constData())); 0054 } 0055 if (QLatin1String("system.nt_sec_desc.group+") == QLatin1String(xattr)) { 0056 setMetaData("GROUP", QString::fromUtf8(value.constData())); 0057 } 0058 break; 0059 } 0060 } 0061 return WorkerResult::pass(); 0062 } 0063 0064 KIO::WorkerResult SMBWorker::setACE(QDataStream &stream) 0065 { 0066 QUrl qurl; 0067 stream >> qurl; 0068 const SMBUrl url(qurl); 0069 0070 QString sid; 0071 QString aceString; 0072 stream >> sid >> aceString; 0073 0074 const QString attr = QLatin1String("system.nt_sec_desc.acl+:") + sid; 0075 qCDebug(KIO_SMB_LOG) << attr << aceString; 0076 0077 // https://bugzilla.samba.org/show_bug.cgi?id=15089 0078 auto flags = SMBC_XATTR_FLAG_REPLACE | SMBC_XATTR_FLAG_CREATE; 0079 const QByteArray ace = aceString.toUtf8(); 0080 int result = smbc_setxattr(url.toSmbcUrl(), qUtf8Printable(attr), ace.constData(), ace.size(), flags); 0081 if (const auto error = errno; result < 0) { 0082 qCDebug(KIO_SMB_LOG) << "smbc_setxattr" << result << strerror(error); 0083 return WorkerResult::fail(ERR_INTERNAL, strerror(error)); 0084 } 0085 return WorkerResult::pass(); 0086 } 0087 0088 // TODO: rename this file _special instead of _mount. Or better yet: stop having multiple cpp files for the same class. 0089 KIO::WorkerResult SMBWorker::special(const QByteArray &data) 0090 { 0091 qCDebug(KIO_SMB_LOG) << "Smb::special()"; 0092 int tmp; 0093 QDataStream stream(data); 0094 stream >> tmp; 0095 // mounting and umounting are both blocking, "guarded" by a SIGALARM in the future 0096 switch (tmp) { 0097 case 1: 0098 case 3: { 0099 QString remotePath; 0100 QString mountPoint; 0101 QString user; 0102 stream >> remotePath >> mountPoint; 0103 0104 QStringList sl = remotePath.split('/'); 0105 QString share; 0106 QString host; 0107 if (sl.count() >= 2) { 0108 host = sl.at(0).mid(2); 0109 share = sl.at(1); 0110 qCDebug(KIO_SMB_LOG) << "special() host -" << host << "- share -" << share << "-"; 0111 } 0112 0113 remotePath.replace('\\', '/'); // smbmounterplugin sends \\host/share 0114 0115 qCDebug(KIO_SMB_LOG) << "mounting: " << remotePath.toLocal8Bit() << " to " << mountPoint.toLocal8Bit(); 0116 0117 if (tmp == 3) { 0118 if (!QDir().mkpath(mountPoint)) { 0119 return WorkerResult::fail(KIO::ERR_CANNOT_MKDIR, mountPoint); 0120 } 0121 } 0122 0123 SMBUrl smburl(QUrl("smb:///")); 0124 smburl.setHost(host); 0125 smburl.setPath('/' + share); 0126 0127 const int passwordError = checkPassword(smburl); 0128 if (passwordError != KJob::NoError && passwordError != KIO::ERR_USER_CANCELED) { 0129 return WorkerResult::fail(passwordError, smburl.toString()); 0130 } 0131 0132 // using smbmount instead of "mount -t smbfs", because mount does not allow a non-root 0133 // user to do a mount, but a suid smbmnt does allow this 0134 0135 KProcess proc; 0136 proc.setOutputChannelMode(KProcess::SeparateChannels); 0137 proc << "smbmount"; 0138 0139 QString options; 0140 0141 if (smburl.userName().isEmpty()) { 0142 user = "guest"; 0143 options = "guest"; 0144 } else { 0145 options = "username=" + smburl.userName(); 0146 user = smburl.userName(); 0147 0148 if (!smburl.password().isEmpty()) 0149 options += ",password=" + smburl.password(); 0150 } 0151 0152 // TODO: check why the control center uses encodings with a blank char, e.g. "cp 1250" 0153 // if ( ! m_default_encoding.isEmpty() ) 0154 // options += ",codepage=" + KShell::quoteArg(m_default_encoding); 0155 0156 proc << remotePath; 0157 proc << mountPoint; 0158 proc << "-o" << options; 0159 0160 proc.start(); 0161 if (!proc.waitForFinished()) { 0162 return WorkerResult::fail(KIO::ERR_CANNOT_LAUNCH_PROCESS, 0163 "smbmount" + i18n("\nMake sure that the samba package is installed properly on your system.")); 0164 } 0165 0166 QString mybuf = QString::fromLocal8Bit(proc.readAllStandardOutput()); 0167 QString mystderr = QString::fromLocal8Bit(proc.readAllStandardError()); 0168 0169 qCDebug(KIO_SMB_LOG) << "mount exit " << proc.exitCode() << "stdout:" << mybuf << "\nstderr:" << mystderr; 0170 0171 if (proc.exitCode() != 0) { 0172 return WorkerResult::fail(KIO::ERR_CANNOT_MOUNT, 0173 i18n("Mounting of share \"%1\" from host \"%2\" by user \"%3\" failed.\n%4", share, host, user, mybuf + '\n' + mystderr)); 0174 } 0175 } break; 0176 case 2: 0177 case 4: { 0178 QString mountPoint; 0179 stream >> mountPoint; 0180 0181 KProcess proc; 0182 proc.setOutputChannelMode(KProcess::SeparateChannels); 0183 proc << "smbumount"; 0184 proc << mountPoint; 0185 0186 proc.start(); 0187 if (!proc.waitForFinished()) { 0188 return WorkerResult::fail(KIO::ERR_CANNOT_LAUNCH_PROCESS, 0189 "smbumount" + i18n("\nMake sure that the samba package is installed properly on your system.")); 0190 } 0191 0192 QString mybuf = QString::fromLocal8Bit(proc.readAllStandardOutput()); 0193 QString mystderr = QString::fromLocal8Bit(proc.readAllStandardError()); 0194 0195 qCDebug(KIO_SMB_LOG) << "smbumount exit " << proc.exitCode() << "stdout:" << mybuf << "\nstderr:" << mystderr; 0196 0197 if (proc.exitCode() != 0) { 0198 return WorkerResult::fail(KIO::ERR_CANNOT_UNMOUNT, i18n("Unmounting of mountpoint \"%1\" failed.\n%2", mountPoint, mybuf + '\n' + mystderr)); 0199 } 0200 0201 if (tmp == 4) { 0202 bool ok; 0203 0204 QDir dir(mountPoint); 0205 dir.cdUp(); 0206 ok = dir.rmdir(mountPoint); 0207 if (ok) { 0208 QString p = dir.path(); 0209 dir.cdUp(); 0210 ok = dir.rmdir(p); 0211 } 0212 0213 if (!ok) { 0214 return WorkerResult::fail(KIO::ERR_CANNOT_RMDIR, mountPoint); 0215 } 0216 } 0217 } break; 0218 case 0xAC: { // ACL 0219 return getACE(stream); 0220 } 0221 case 0xACD: { // setACE 0222 return setACE(stream); 0223 } 0224 default: 0225 break; 0226 } 0227 return WorkerResult::pass(); 0228 }