Skip to content

Commit

Permalink
QFileSystemEngine::canonicalName: don't use malloc()'ing realpath()
Browse files Browse the repository at this point in the history
It is available in POSIX.1-2008 but is not worth it. The Linux man page
says it only works up to PATH_MAX anyway and the POSIX documentation
says it's UB if PATH_MAX isn't defined.

This cleans up the source code out of these messy #if and should be
faster, because we avoid a heap allocation (stack is always faster).

Instead, we only relegate the heap version to the case where PATH_MAX
isn't defined (i.e., GNU HURD), because in that case the realpath()
function can't be used with a stack allocation.

Pick-to: 6.7
Change-Id: Ie30a3caf09ef4176bb36fffd17cde1ed5c5dad59
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
  • Loading branch information
thiagomacieira authored and ahmadsamir committed May 24, 2024
1 parent 48aad48 commit e32009f
Showing 1 changed file with 10 additions and 24 deletions.
34 changes: 10 additions & 24 deletions src/corelib/io/qfilesystemengine_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,30 +641,17 @@ QFileSystemEntry QFileSystemEngine::getRawLinkPath(const QFileSystemEntry &link,
QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry, QFileSystemMetaData &data)
{
Q_CHECK_FILE_NAME(entry, entry);
char *resolved_name = nullptr;

#if !defined(Q_OS_DARWIN) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L && !defined(Q_OS_VXWORKS)
// realpath(X,0) is not supported
Q_UNUSED(data);
return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath()));
#else
# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || _POSIX_VERSION < 200801L
// used to store the result of realpath in case where realpath cannot allocate itself
#ifdef PATH_MAX
// use the stack to avoid the overhead of memory allocation
char stack_result[PATH_MAX + 1];
#else
// enables unconditionally passing stack_result below
// system with unlimited file paths -> must use heap
std::nullptr_t stack_result = nullptr;
# endif
auto resolved_path_deleter = [&](char *ptr) {
// frees resolved_name if it was allocated by realpath
# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID) || _POSIX_VERSION < 200801L
// ptr is either null, or points to stack_result
Q_ASSERT(!ptr || ptr == stack_result);
return;
#else
free(ptr);
# endif
};
std::unique_ptr<char, decltype (resolved_path_deleter)> resolved_name {nullptr, resolved_path_deleter};
auto freer = qScopeGuard([&] { free(resolved_name); });
#endif

# if defined(Q_OS_DARWIN) || defined(Q_OS_ANDROID)
// On some Android and macOS versions, realpath() will return a path even if
// it does not exist. To work around this, we check existence in advance.
Expand All @@ -674,22 +661,21 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
if (!data.exists())
errno = ENOENT;
else
resolved_name.reset(realpath(entry.nativeFilePath().constData(), stack_result));
resolved_name = realpath(entry.nativeFilePath().constData(), stack_result);
# else
resolved_name.reset(realpath(entry.nativeFilePath().constData(), stack_result));
resolved_name = realpath(entry.nativeFilePath().constData(), stack_result);
# endif
if (resolved_name) {
data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
data.entryFlags |= QFileSystemMetaData::ExistsAttribute;
QString canonicalPath = QDir::cleanPath(QFile::decodeName(resolved_name.get()));
QString canonicalPath = QDir::cleanPath(QFile::decodeName(resolved_name));
return QFileSystemEntry(canonicalPath);
} else if (errno == ENOENT || errno == ENOTDIR) { // file doesn't exist
data.knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
data.entryFlags &= ~(QFileSystemMetaData::ExistsAttribute);
return QFileSystemEntry();
}
return entry;
#endif
}

//static
Expand Down

0 comments on commit e32009f

Please sign in to comment.