Changeset View
Changeset View
Standalone View
Standalone View
krusader/FileSystem/filesystem.cpp
Show All 25 Lines | |||||
26 | * * | 26 | * * | ||
27 | * This program is free software; you can redistribute it and/or modify * | 27 | * This program is free software; you can redistribute it and/or modify * | ||
28 | * it under the terms of the GNU General Public License as published by * | 28 | * it under the terms of the GNU General Public License as published by * | ||
29 | * the Free Software Foundation; either version 2 of the License, or * | 29 | * the Free Software Foundation; either version 2 of the License, or * | ||
30 | * (at your option) any later version. * | 30 | * (at your option) any later version. * | ||
31 | * * | 31 | * * | ||
32 | ***************************************************************************/ | 32 | ***************************************************************************/ | ||
33 | 33 | | |||
34 | #include <memory> | ||||
35 | | ||||
34 | #include "filesystem.h" | 36 | #include "filesystem.h" | ||
35 | 37 | | |||
36 | // QtCore | 38 | // QtCore | ||
37 | #include <QDebug> | 39 | #include <QDebug> | ||
38 | #include <QDir> | 40 | #include <QDir> | ||
39 | #include <QList> | 41 | #include <QList> | ||
40 | // QtWidgets | 42 | // QtWidgets | ||
41 | #include <qplatformdefs.h> | 43 | #include <qplatformdefs.h> | ||
▲ Show 20 Lines • Show All 169 Lines • ▼ Show 20 Line(s) | |||||
211 | { | 213 | { | ||
212 | _fileItems.insert(item->getName(), item); | 214 | _fileItems.insert(item->getName(), item); | ||
213 | } | 215 | } | ||
214 | 216 | | |||
215 | FileItem *FileSystem::createLocalFileItem(const QString &name, const QString &directory, bool virt) | 217 | FileItem *FileSystem::createLocalFileItem(const QString &name, const QString &directory, bool virt) | ||
216 | { | 218 | { | ||
217 | const QDir dir = QDir(directory); | 219 | const QDir dir = QDir(directory); | ||
218 | const QString path = dir.filePath(name); | 220 | const QString path = dir.filePath(name); | ||
219 | const QByteArray filePath = path.toLocal8Bit(); | 221 | const QByteArray pathByteArray = path.toLocal8Bit(); | ||
222 | const QString fileItemName = virt ? path : name; | ||||
223 | const QUrl fileItemUrl = QUrl::fromLocalFile(path); | ||||
220 | 224 | | |||
225 | // read file status; in case of error create a "broken" file item | ||||
221 | QT_STATBUF stat_p; | 226 | QT_STATBUF stat_p; | ||
222 | stat_p.st_size = 0; | 227 | memset(&stat_p, 0, sizeof(stat_p)); | ||
223 | stat_p.st_mode = 0; | 228 | if (QT_LSTAT(pathByteArray.data(), &stat_p) < 0) | ||
224 | stat_p.st_mtime = 0; | 229 | return FileItem::createBroken(fileItemName, fileItemUrl); | ||
225 | stat_p.st_uid = 0; | | |||
226 | stat_p.st_gid = 0; | | |||
227 | QT_LSTAT(filePath.data(), &stat_p); | | |||
228 | const KIO::filesize_t size = stat_p.st_size; | | |||
229 | 230 | | |||
231 | const KIO::filesize_t size = stat_p.st_size; | ||||
230 | bool isDir = S_ISDIR(stat_p.st_mode); | 232 | bool isDir = S_ISDIR(stat_p.st_mode); | ||
231 | const bool isLink = S_ISLNK(stat_p.st_mode); | 233 | const bool isLink = S_ISLNK(stat_p.st_mode); | ||
232 | 234 | | |||
235 | // for links, read link destination and determine whether it's broken or not | ||||
233 | QString linkDestination; | 236 | QString linkDestination; | ||
234 | bool brokenLink = false; | 237 | bool brokenLink = false; | ||
235 | if (isLink) { | 238 | if (isLink) { | ||
236 | // find where the link is pointing to | 239 | linkDestination = readLinkSafely(pathByteArray.data()); | ||
237 | qDebug() << "link name=" << path; | 240 | | ||
238 | // the path of the symlink target cannot be longer than the file size of the symlink | 241 | if (linkDestination.isNull()) | ||
239 | char buffer[stat_p.st_size]; | 242 | brokenLink = true; | ||
240 | memset(buffer, 0, sizeof(buffer)); | 243 | else { | ||
241 | int bytesRead = readlink(filePath.data(), buffer, sizeof(buffer)); | | |||
242 | if (bytesRead != -1) { | | |||
243 | linkDestination = QString::fromLocal8Bit(buffer, bytesRead); // absolute or relative | | |||
244 | const QFileInfo linkFile(dir, linkDestination); | 244 | const QFileInfo linkFile(dir, linkDestination); | ||
245 | if (!linkFile.exists()) | 245 | if (!linkFile.exists()) | ||
246 | brokenLink = true; | 246 | brokenLink = true; | ||
247 | else if (linkFile.isDir()) | 247 | else if (linkFile.isDir()) | ||
248 | isDir = true; | 248 | isDir = true; | ||
249 | } else { | | |||
250 | qWarning() << "failed to read link, path=" << path; | | |||
251 | } | 249 | } | ||
252 | } | 250 | } | ||
253 | 251 | | |||
254 | return new FileItem(virt ? path : name, QUrl::fromLocalFile(path), isDir, | 252 | // create normal file item | ||
253 | return new FileItem(fileItemName, fileItemUrl, isDir, | ||||
255 | size, stat_p.st_mode, | 254 | size, stat_p.st_mode, | ||
256 | stat_p.st_mtime, stat_p.st_ctime, stat_p.st_atime, | 255 | stat_p.st_mtime, stat_p.st_ctime, stat_p.st_atime, | ||
257 | stat_p.st_uid, stat_p.st_gid, QString(), QString(), | 256 | stat_p.st_uid, stat_p.st_gid, QString(), QString(), | ||
258 | isLink, linkDestination, brokenLink); | 257 | isLink, linkDestination, brokenLink); | ||
259 | } | 258 | } | ||
260 | 259 | | |||
260 | QString FileSystem::readLinkSafely(const char *path) | ||||
261 | { | ||||
262 | // inspired by the areadlink_with_size function from gnulib, which is used for coreutils | ||||
263 | // idea: start with a small buffer and gradually increase it as we discover it wasn't enough | ||||
264 | | ||||
265 | QT_OFF_T bufferSize = 1024; // start with 1 KiB | ||||
266 | QT_OFF_T maxBufferSize = std::numeric_limits<QT_OFF_T>::max(); | ||||
267 | | ||||
268 | while (true) { | ||||
269 | // try to read the link | ||||
270 | std::unique_ptr<char[]> buffer(new char[bufferSize]); | ||||
271 | auto nBytesRead = readlink(path, buffer.get(), bufferSize); | ||||
272 | | ||||
273 | // should never happen, asserted by the readlink | ||||
274 | if (nBytesRead > bufferSize) | ||||
275 | return QString(); | ||||
276 | | ||||
277 | // read failure | ||||
278 | if (nBytesRead < 0) { | ||||
279 | qDebug() << "Failed to read the link " << path; | ||||
280 | return QString(); | ||||
281 | } | ||||
282 | | ||||
283 | // read success | ||||
284 | if (nBytesRead < bufferSize || nBytesRead == maxBufferSize) | ||||
285 | return QString::fromLocal8Bit(buffer.get(), nBytesRead); | ||||
286 | | ||||
287 | // increase the buffer and retry again | ||||
288 | // bufferSize < maxBufferSize is implied from previous checks | ||||
289 | if (bufferSize <= maxBufferSize / 2) | ||||
290 | bufferSize *= 2; | ||||
291 | else | ||||
292 | bufferSize = maxBufferSize; | ||||
293 | } | ||||
294 | } | ||||
295 | | ||||
261 | FileItem *FileSystem::createFileItemFromKIO(const KIO::UDSEntry &entry, const QUrl &directory, bool virt) | 296 | FileItem *FileSystem::createFileItemFromKIO(const KIO::UDSEntry &entry, const QUrl &directory, bool virt) | ||
262 | { | 297 | { | ||
263 | const KFileItem kfi(entry, directory, true, true); | 298 | const KFileItem kfi(entry, directory, true, true); | ||
264 | 299 | | |||
265 | const QString name = kfi.text(); | 300 | const QString name = kfi.text(); | ||
266 | // ignore un-needed entries | 301 | // ignore un-needed entries | ||
267 | if (name.isEmpty() || name == "." || name == "..") { | 302 | if (name.isEmpty() || name == "." || name == "..") { | ||
268 | return 0; | 303 | return 0; | ||
▲ Show 20 Lines • Show All 44 Lines • Show Last 20 Lines |