File indexing completed on 2025-02-09 04:24:08
0001 /* 0002 SPDX-FileCopyrightText: 2000-2002 Stephan Kulow <coolo@kde.org> 0003 SPDX-FileCopyrightText: 2000-2002 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2000-2002 Waldo Bastian <bastian@kde.org> 0005 SPDX-FileCopyrightText: 2006 Allan Sandfeld Jensen <sandfeld@kde.org> 0006 SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org> 0007 SPDX-FileCopyrightText: 2007 Christian Ehrlicher <ch.ehrlicher@gmx.de> 0008 0009 SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 0012 #include "file.h" 0013 0014 #include <qt_windows.h> 0015 0016 #include <QDir> 0017 #include <QDirIterator> 0018 #include <QFileInfo> 0019 #include <QUrl> 0020 0021 #include <KConfigGroup> 0022 #include <QDebug> 0023 0024 #include "kioglobal_p.h" 0025 0026 using namespace KIO; 0027 0028 static DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, 0029 LARGE_INTEGER TotalBytesTransferred, 0030 LARGE_INTEGER StreamSize, 0031 LARGE_INTEGER StreamBytesTransferred, 0032 DWORD dwStreamNumber, 0033 DWORD dwCallbackReason, 0034 HANDLE hSourceFile, 0035 HANDLE hDestinationFile, 0036 LPVOID lpData) 0037 { 0038 FileProtocol *f = reinterpret_cast<FileProtocol *>(lpData); 0039 f->processedSize(TotalBytesTransferred.QuadPart); 0040 return PROGRESS_CONTINUE; 0041 } 0042 0043 static UDSEntry createUDSEntryWin(const QFileInfo &fileInfo) 0044 { 0045 UDSEntry entry; 0046 0047 entry.fastInsert(KIO::UDSEntry::UDS_NAME, fileInfo.fileName()); 0048 if (fileInfo.isSymLink()) { 0049 entry.fastInsert(KIO::UDSEntry::UDS_TARGET_URL, fileInfo.symLinkTarget()); 0050 /* TODO - or not useful on windows? 0051 if ( details > 1 ) { 0052 // It is a link pointing to nowhere 0053 type = S_IFMT - 1; 0054 access = S_IRWXU | S_IRWXG | S_IRWXO; 0055 0056 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, type ); 0057 entry.insert( KIO::UDSEntry::UDS_ACCESS, access ); 0058 entry.insert( KIO::UDSEntry::UDS_SIZE, 0LL ); 0059 goto notype; 0060 0061 } 0062 */ 0063 } 0064 int type = S_IFREG; 0065 int access = 0; 0066 if (fileInfo.isDir()) { 0067 type = S_IFDIR; 0068 } else if (fileInfo.isSymLink()) { 0069 type = QT_STAT_LNK; 0070 } 0071 if (fileInfo.isReadable()) { 0072 access |= S_IRUSR; 0073 } 0074 if (fileInfo.isWritable()) { 0075 access |= S_IWUSR; 0076 } 0077 if (fileInfo.isExecutable()) { 0078 access |= S_IXUSR; 0079 } 0080 0081 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, type); 0082 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, access); 0083 entry.fastInsert(KIO::UDSEntry::UDS_SIZE, fileInfo.size()); 0084 if (fileInfo.isHidden()) { 0085 entry.fastInsert(KIO::UDSEntry::UDS_HIDDEN, true); 0086 } 0087 0088 entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, fileInfo.lastModified().toSecsSinceEpoch()); 0089 entry.fastInsert(KIO::UDSEntry::UDS_USER, fileInfo.owner()); 0090 entry.fastInsert(KIO::UDSEntry::UDS_GROUP, fileInfo.group()); 0091 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, fileInfo.lastRead().toSecsSinceEpoch()); 0092 entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, fileInfo.birthTime().toSecsSinceEpoch()); 0093 0094 QT_STATBUF buf; 0095 QUrl url(fileInfo.absoluteFilePath()); 0096 const QString path = url.toString(QUrl::StripTrailingSlash | QUrl::PreferLocalFile); 0097 const QByteArray pathBA = QFile::encodeName(path); 0098 0099 if (QT_LSTAT(pathBA.constData(), &buf) == 0) { 0100 entry.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, buf.st_dev); 0101 entry.fastInsert(KIO::UDSEntry::UDS_INODE, buf.st_ino); 0102 } 0103 0104 return entry; 0105 } 0106 0107 WorkerResult FileProtocol::copy(const QUrl &src, const QUrl &dest, int _mode, JobFlags _flags) 0108 { 0109 // qDebug() << "copy(): " << src << " -> " << dest << ", mode=" << _mode; 0110 0111 QFileInfo _src(src.toLocalFile()); 0112 QFileInfo _dest(dest.toLocalFile()); 0113 DWORD dwFlags = COPY_FILE_FAIL_IF_EXISTS; 0114 0115 if (_src == _dest) { 0116 return WorkerResult::fail(KIO::ERR_IDENTICAL_FILES, _dest.filePath()); 0117 } 0118 0119 if (!_src.exists()) { 0120 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath()); 0121 } 0122 0123 if (_src.isDir()) { 0124 return WorkerResult::fail(KIO::ERR_IS_DIRECTORY, _src.filePath()); 0125 } 0126 0127 if (_dest.exists()) { 0128 if (_dest.isDir()) { 0129 return WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, _dest.filePath()); 0130 } 0131 0132 if (!(_flags & KIO::Overwrite)) { 0133 return WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, _dest.filePath()); 0134 } 0135 0136 dwFlags = 0; 0137 } 0138 0139 if (!QFileInfo(_dest.dir().absolutePath()).exists()) { 0140 _dest.dir().mkdir(_dest.dir().absolutePath()); 0141 } 0142 0143 if (CopyFileExW((LPCWSTR)_src.filePath().utf16(), (LPCWSTR)_dest.filePath().utf16(), CopyProgressRoutine, (LPVOID)this, FALSE, dwFlags) == 0) { 0144 DWORD dwLastErr = GetLastError(); 0145 if (dwLastErr == ERROR_FILE_NOT_FOUND) { 0146 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath()); 0147 } else if (dwLastErr == ERROR_ACCESS_DENIED) { 0148 return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _dest.filePath()); 0149 } else { 0150 // qDebug() << "Copying file " << _src.filePath() << " failed (" << dwLastErr << ")"; 0151 return WorkerResult::fail(KIO::ERR_CANNOT_RENAME, _src.filePath()); 0152 } 0153 } 0154 0155 return WorkerResult::pass(); 0156 } 0157 0158 WorkerResult FileProtocol::listDir(const QUrl &url) 0159 { 0160 // qDebug() << "========= LIST " << url << " ========="; 0161 0162 if (!url.isLocalFile()) { 0163 QUrl redir(url); 0164 redir.setScheme(configValue(QStringLiteral("DefaultRemoteProtocol"), QStringLiteral("smb"))); 0165 redirection(redir); 0166 // qDebug() << "redirecting to " << redir; 0167 return WorkerResult::pass(); 0168 } 0169 0170 QString path = url.toLocalFile(); 0171 // C: means current directory, a concept which makes no sense in a GUI 0172 // KCoreDireLister strips trailing slashes, let's put it back again here for C:/ 0173 if (path.length() == 2 && path.at(1) == QLatin1Char(':')) 0174 path += QLatin1Char('/'); 0175 const QFileInfo info(path); 0176 if (info.isFile()) { 0177 return WorkerResult::fail(KIO::ERR_IS_FILE, path); 0178 } 0179 0180 QDir dir(path); 0181 dir.setFilter(QDir::AllEntries | QDir::Hidden); 0182 0183 if (!dir.exists()) { 0184 // qDebug() << "========= ERR_DOES_NOT_EXIST ========="; 0185 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, path); 0186 } 0187 0188 if (!dir.isReadable()) { 0189 // qDebug() << "========= ERR_CANNOT_ENTER_DIRECTORY ========="; 0190 return WorkerResult::fail(KIO::ERR_CANNOT_ENTER_DIRECTORY, path); 0191 } 0192 QDirIterator it(dir); 0193 UDSEntry entry; 0194 while (it.hasNext()) { 0195 it.next(); 0196 UDSEntry entry = createUDSEntryWin(it.fileInfo()); 0197 0198 listEntry(entry); 0199 entry.clear(); 0200 } 0201 0202 // qDebug() << "============= COMPLETED LIST ============"; 0203 0204 return WorkerResult::pass(); 0205 } 0206 0207 WorkerResult FileProtocol::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags _flags) 0208 { 0209 // qDebug() << "rename(): " << src << " -> " << dest; 0210 0211 QFileInfo _src(src.toLocalFile()); 0212 QFileInfo _dest(dest.toLocalFile()); 0213 DWORD dwFlags = 0; 0214 0215 if (_src == _dest) { 0216 return WorkerResult::fail(KIO::ERR_IDENTICAL_FILES, _dest.filePath()); 0217 } 0218 0219 if (!_src.exists()) { 0220 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath()); 0221 } 0222 0223 if (_dest.exists()) { 0224 if (_dest.isDir()) { 0225 return WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, _dest.filePath()); 0226 } 0227 0228 if (!(_flags & KIO::Overwrite)) { 0229 return WorkerResult::fail(KIO::ERR_FILE_ALREADY_EXIST, _dest.filePath()); 0230 } 0231 0232 dwFlags = MOVEFILE_REPLACE_EXISTING; 0233 } 0234 // To avoid error 17 - The system cannot move the file to a different disk drive. 0235 dwFlags |= MOVEFILE_COPY_ALLOWED; 0236 0237 if (MoveFileExW((LPCWSTR)_src.filePath().utf16(), (LPCWSTR)_dest.filePath().utf16(), dwFlags) == 0) { 0238 DWORD dwLastErr = GetLastError(); 0239 if (dwLastErr == ERROR_FILE_NOT_FOUND) { 0240 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _src.filePath()); 0241 } else if (dwLastErr == ERROR_ACCESS_DENIED) { 0242 return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _dest.filePath()); 0243 } else { 0244 qCDebug(KIO_FILE) << "Renaming file " << _src.filePath() << " failed (" << dwLastErr << ")"; 0245 return WorkerResult::fail(KIO::ERR_CANNOT_RENAME, _src.filePath()); 0246 } 0247 } 0248 0249 return WorkerResult::pass(); 0250 } 0251 0252 WorkerResult FileProtocol::symlink(const QString &target, const QUrl &dest, KIO::JobFlags flags) 0253 { 0254 QString localDest = dest.toLocalFile(); 0255 // TODO handle overwrite, etc 0256 if (!KIOPrivate::createSymlink(target, localDest)) { 0257 return WorkerResult::fail(KIO::ERR_UNKNOWN, localDest); 0258 } 0259 0260 return WorkerResult::pass(); 0261 } 0262 0263 WorkerResult FileProtocol::del(const QUrl &url, bool isfile) 0264 { 0265 QString _path(url.toLocalFile()); 0266 /***** 0267 * Delete files 0268 *****/ 0269 0270 if (isfile) { 0271 // qDebug() << "Deleting file " << _path; 0272 0273 if (DeleteFileW((LPCWSTR)_path.utf16()) == 0) { 0274 DWORD dwLastErr = GetLastError(); 0275 if (dwLastErr == ERROR_PATH_NOT_FOUND) { 0276 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _path); 0277 } else if (dwLastErr == ERROR_ACCESS_DENIED) { 0278 return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _path); 0279 } else { 0280 return WorkerResult::fail(KIO::ERR_CANNOT_DELETE, _path); 0281 // qDebug() << "Deleting file " << _path << " failed (" << dwLastErr << ")"; 0282 } 0283 } 0284 } else { 0285 // qDebug() << "Deleting directory " << _path; 0286 auto deleteResult = deleteRecursive(_path); 0287 if (!deleteResult.success()) { 0288 return deleteResult; 0289 } 0290 if (RemoveDirectoryW((LPCWSTR)_path.utf16()) == 0) { 0291 DWORD dwLastErr = GetLastError(); 0292 if (dwLastErr == ERROR_FILE_NOT_FOUND) { 0293 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, _path); 0294 } else if (dwLastErr == ERROR_ACCESS_DENIED) { 0295 return WorkerResult::fail(KIO::ERR_ACCESS_DENIED, _path); 0296 } else { 0297 return WorkerResult::fail(KIO::ERR_CANNOT_DELETE, _path); 0298 // qDebug() << "Deleting directory " << _path << " failed (" << dwLastErr << ")"; 0299 } 0300 } 0301 } 0302 return WorkerResult::pass(); 0303 } 0304 0305 WorkerResult FileProtocol::chown(const QUrl &url, const QString &, const QString &) 0306 { 0307 return WorkerResult::fail(KIO::ERR_CANNOT_CHOWN, url.toLocalFile()); 0308 } 0309 0310 WorkerResult FileProtocol::stat(const QUrl &url) 0311 { 0312 if (!url.isLocalFile()) { 0313 return redirect(url); 0314 } 0315 0316 const QString sDetails = metaData(QLatin1String("details")); 0317 int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); 0318 // qDebug() << "FileProtocol::stat details=" << details; 0319 0320 const QString localFile = url.toLocalFile(); 0321 QFileInfo fileInfo(localFile); 0322 if (!fileInfo.exists()) { 0323 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, localFile); 0324 } 0325 0326 UDSEntry entry = createUDSEntryWin(fileInfo); 0327 0328 statEntry(entry); 0329 0330 return WorkerResult::pass(); 0331 } 0332 0333 bool FileProtocol::privilegeOperationUnitTestMode() 0334 { 0335 return false; 0336 } 0337 0338 WorkerResult FileProtocol::execWithElevatedPrivilege(ActionType, const QVariantList &, int err) 0339 { 0340 return WorkerResult::fail(err); 0341 } 0342 WorkerResult FileProtocol::tryOpen(QFile &f, const QByteArray &, int, int, int err) 0343 { 0344 return WorkerResult::fail(err); 0345 } 0346 0347 WorkerResult FileProtocol::tryChangeFileAttr(ActionType, const QVariantList &, int err) 0348 { 0349 return WorkerResult::fail(err); 0350 } 0351 0352 int FileProtocol::setACL(const char *path, mode_t perm, bool directoryDefault) 0353 { 0354 Q_UNUSED(path); 0355 Q_UNUSED(perm); 0356 Q_UNUSED(directoryDefault); 0357 return 0; 0358 }