From c934ea341e0749aef9c927ca3def68976bdc976f Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Tue, 5 Nov 2013 13:16:59 +0100 Subject: [PATCH] Cocoa File Dialog: Remove sandbox-ufriendly file system watcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QFileSystemWatcher causes sandboxing errors since its backend uses POSIX API in a relatively liberal way. Also, Cocoa already acts as a file system watcher, and calls -[QNSOpenSavePanelDelegate panel:shouldShowFilename:] on each file. From a logical point of view, caching the directory content can be replaced by testing the current file against the filter setting. We expect Cocoa to cache results, and by using NSFileManager things should remain relatively fast. Task-number: QTBUG-34107 Change-Id: Ia872b9b1244f7b390d173a498011379b9309b3c6 Reviewed-by: Cyril Oblikov Reviewed-by: Morten Johan Sørvig --- .../platforms/cocoa/qcocoafiledialoghelper.mm | 93 ++++++++++--------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index bbf5227a6c..1ad833ee44 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -61,12 +61,12 @@ #include #include #include "qcocoaautoreleasepool.h" -#include #include #include #import +#import QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QStringList) @@ -74,30 +74,6 @@ QT_FORWARD_DECLARE_CLASS(QFileInfo) QT_FORWARD_DECLARE_CLASS(QWindow) QT_USE_NAMESPACE -class CachedEntries: public QObject { -public: - CachedEntries(QDir::Filters filters) : mFilters(filters) { - QObject::connect(&mFSWatcher, &QFileSystemWatcher::directoryChanged, this, &CachedEntries::updateDirCache); - } - QString directory() const { - const QStringList &dirs = mFSWatcher.directories(); - return (dirs.count() ? dirs[0] : QString()); - } - QStringList entries() const { - return mQDirFilterEntryList; - } - void updateDirCache(const QString &path) { - mFSWatcher.removePaths(mFSWatcher.directories()); - mFSWatcher.addPath(path); - mQDirFilterEntryList = QDir(path).entryList(mFilters); - } - -private: - QFileSystemWatcher mFSWatcher; - QStringList mQDirFilterEntryList; - QDir::Filters mFilters; -}; - typedef QSharedPointer SharedPointerFileDialogOptions; @class QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate); @@ -117,7 +93,6 @@ typedef QSharedPointer SharedPointerFileDialogOptions; int mReturnCode; SharedPointerFileDialogOptions mOptions; - CachedEntries *mCachedEntries; QString *mCurrentSelection; QStringList *mNameFilterDropDownList; QStringList *mSelectedNameFilter; @@ -164,7 +139,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate); [mSavePanel setDelegate:self]; mReturnCode = -1; mHelper = helper; - mCachedEntries = new CachedEntries(mOptions->filter()); mNameFilterDropDownList = new QStringList(mOptions->nameFilters()); QString selectedVisualNameFilter = mOptions->initiallySelectedNameFilter(); mSelectedNameFilter = new QStringList([self findStrippedFilterWithVisualFilterName:selectedVisualNameFilter]); @@ -197,7 +171,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate); - (void)dealloc { - delete mCachedEntries; delete mNameFilterDropDownList; delete mSelectedNameFilter; delete mCurrentSelection; @@ -308,6 +281,22 @@ static QString strippedText(QString s) }]; } +- (BOOL)isHiddenFile:(NSString *)filename isDir:(BOOL)isDir +{ + CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filename, kCFURLPOSIXPathStyle, isDir); + CFBooleanRef isHidden; + Boolean errorOrHidden = false; + if (!CFURLCopyResourcePropertyForKey(url, kCFURLIsHiddenKey, &isHidden, NULL)) { + errorOrHidden = true; + } else { + if (CFBooleanGetValue(isHidden)) + errorOrHidden = true; + CFRelease(isHidden); + } + CFRelease(url); + return errorOrHidden; +} + - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename { Q_UNUSED(sender); @@ -316,8 +305,13 @@ static QString strippedText(QString s) return NO; // Always accept directories regardless of their names (unless it is a bundle): - BOOL isDir; - if ([[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir] && isDir) { + NSFileManager *fm = [NSFileManager defaultManager]; + NSDictionary *fileAttrs = [fm attributesOfItemAtPath:filename error:nil]; + if (!fileAttrs) + return NO; // Error accessing the file means 'no'. + NSString *fileType = [fileAttrs fileType]; + bool isDir = [fileType isEqualToString:NSFileTypeDirectory]; + if (isDir) { if ([mSavePanel treatsFilePackagesAsDirectories] == NO) { if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO) return YES; @@ -325,24 +319,35 @@ static QString strippedText(QString s) } QString qtFileName = QCFString::toQString(filename); - QFileInfo info(qtFileName.normalized(QString::NormalizationForm_C)); - QString path = info.absolutePath(); - if (mCachedEntries->directory() != path) { - mCachedEntries->updateDirCache(path); + // No filter means accept everything + bool nameMatches = mSelectedNameFilter->isEmpty(); + // Check if the current file name filter accepts the file: + for (int i = 0; !nameMatches && i < mSelectedNameFilter->size(); ++i) { + if (QDir::match(mSelectedNameFilter->at(i), qtFileName)) + nameMatches = true; } - // Check if the QDir filter accepts the file: - if (!mCachedEntries->entries().contains(info.fileName())) + if (!nameMatches) return NO; - // No filter means accept everything - if (mSelectedNameFilter->isEmpty()) - return YES; - // Check if the current file name filter accepts the file: - for (int i=0; isize(); ++i) { - if (QDir::match(mSelectedNameFilter->at(i), qtFileName)) - return YES; + QDir::Filters filter = mOptions->filter(); + if ((!(filter & (QDir::Dirs | QDir::AllDirs)) && isDir) + || (!(filter & QDir::Files) && [fileType isEqualToString:NSFileTypeRegular]) + || ((filter & QDir::NoSymLinks) && [fileType isEqualToString:NSFileTypeSymbolicLink])) + return NO; + + bool filterPermissions = ((filter & QDir::PermissionMask) + && (filter & QDir::PermissionMask) != QDir::PermissionMask); + if (filterPermissions) { + if ((!(filter & QDir::Readable) && [fm isReadableFileAtPath:filename]) + || (!(filter & QDir::Writable) && [fm isWritableFileAtPath:filename]) + || (!(filter & QDir::Executable) && [fm isExecutableFileAtPath:filename])) + return NO; } - return NO; + if (!(filter & QDir::Hidden) + && (qtFileName.startsWith(QLatin1Char('.')) || [self isHiddenFile:filename isDir:isDir])) + return NO; + + return YES; } - (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag