diff --git a/launcher/macsandbox/SecurityBookmarkFileAccess.h b/launcher/macsandbox/SecurityBookmarkFileAccess.h index 10d75cbd5..5bddf0e31 100644 --- a/launcher/macsandbox/SecurityBookmarkFileAccess.h +++ b/launcher/macsandbox/SecurityBookmarkFileAccess.h @@ -39,9 +39,12 @@ class SecurityBookmarkFileAccess { /// Contains URLs that are currently being accessed. NSMutableSet* m_activeURLs; + bool m_readOnly; + NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale); public: - SecurityBookmarkFileAccess(); + /// \param readOnly A boolean indicating whether the bookmark should be read-only. + SecurityBookmarkFileAccess(bool readOnly = false); ~SecurityBookmarkFileAccess(); /// Get a security scoped bookmark from a URL. @@ -78,6 +81,9 @@ public: /// \return A boolean indicating whether the bookmark was successfully accessed. bool startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale); void stopUsingSecurityScopedBookmark(QByteArray& bookmark); + + /// Returns true if access to the `path` is currently being maintained by this object. + bool isAccessingPath(const QString& path); }; #endif //FILEACCESS_H diff --git a/launcher/macsandbox/SecurityBookmarkFileAccess.mm b/launcher/macsandbox/SecurityBookmarkFileAccess.mm index 721c3fd47..bee854abe 100644 --- a/launcher/macsandbox/SecurityBookmarkFileAccess.mm +++ b/launcher/macsandbox/SecurityBookmarkFileAccess.mm @@ -29,25 +29,41 @@ QByteArray SecurityBookmarkFileAccess::urlToSecurityScopedBookmark(const QUrl& u NSError* error = nil; NSURL* nsurl = [url.toNSURL() absoluteURL]; + NSData* bookmark; if ([m_paths objectForKey:[nsurl path]]) { - return QByteArray::fromNSData(m_paths[[nsurl path]]); + bookmark = m_paths[[nsurl path]]; + } else { + bookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope | + (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) + includingResourceValuesForKeys:nil + relativeToURL:nil + error:&error]; } - [m_activeURLs addObject:nsurl]; - NSData* bookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope - includingResourceValuesForKeys:nil - relativeToURL:nil - error:&error]; if (error) { return {}; } + + // remove/reapply access to ensure that write access is immediately cut off for read-only bookmarks + // sometimes you need to call this twice to actually stop access (extra calls aren't harmful) + [nsurl stopAccessingSecurityScopedResource]; + [nsurl stopAccessingSecurityScopedResource]; + nsurl = [NSURL URLByResolvingBookmarkData:bookmark + options:NSURLBookmarkResolutionWithSecurityScope | + (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) + relativeToURL:nil + bookmarkDataIsStale:nil + error:&error]; m_paths[[nsurl path]] = bookmark; m_bookmarks[bookmark] = nsurl; - [m_activeURLs addObject:nsurl]; - return QByteArray::fromNSData(bookmark); + QByteArray qBookmark = QByteArray::fromNSData(bookmark); + bool isStale = false; + startUsingSecurityScopedBookmark(qBookmark, isStale); + + return qBookmark; } -SecurityBookmarkFileAccess::SecurityBookmarkFileAccess() +SecurityBookmarkFileAccess::SecurityBookmarkFileAccess(bool readOnly) : m_readOnly(readOnly) { m_bookmarks = [NSMutableDictionary new]; m_paths = [NSMutableDictionary new]; @@ -59,9 +75,6 @@ SecurityBookmarkFileAccess::~SecurityBookmarkFileAccess() for (NSURL* url : m_activeURLs) { [url stopAccessingSecurityScopedResource]; } - [m_bookmarks release]; - [m_paths release]; - [m_activeURLs release]; } QByteArray SecurityBookmarkFileAccess::pathToSecurityScopedBookmark(const QString& path) @@ -74,7 +87,8 @@ NSURL* SecurityBookmarkFileAccess::securityScopedBookmarkToNSURL(QByteArray& boo NSError* error = nil; BOOL localStale = NO; NSURL* nsurl = [NSURL URLByResolvingBookmarkData:bookmark.toNSData() - options:NSURLBookmarkResolutionWithSecurityScope + options:NSURLBookmarkResolutionWithSecurityScope | + (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) relativeToURL:nil bookmarkDataIsStale:&localStale error:&error]; @@ -83,18 +97,21 @@ NSURL* SecurityBookmarkFileAccess::securityScopedBookmarkToNSURL(QByteArray& boo } isStale = localStale; if (isStale) { - NSData* nsBookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope + NSData* nsBookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope | + (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; if (error) { return nil; } - m_paths[[nsurl path]] = nsBookmark; - m_bookmarks[nsBookmark] = nsurl; bookmark = QByteArray::fromNSData(nsBookmark); } + NSData* nsBookmark = bookmark.toNSData(); + m_paths[[nsurl path]] = nsBookmark; + m_bookmarks[nsBookmark] = nsurl; + return nsurl; } @@ -112,10 +129,12 @@ QUrl SecurityBookmarkFileAccess::securityScopedBookmarkToURL(QByteArray& bookmar bool SecurityBookmarkFileAccess::startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale) { - NSURL* url = [m_bookmarks objectForKey:bookmark.toNSData()] ? m_bookmarks[bookmark.toNSData()] : securityScopedBookmarkToNSURL(bookmark, isStale); + NSURL* url = [m_bookmarks objectForKey:bookmark.toNSData()] ? m_bookmarks[bookmark.toNSData()] + : securityScopedBookmarkToNSURL(bookmark, isStale); if ([m_activeURLs containsObject:url]) return false; + [url stopAccessingSecurityScopedResource]; if ([url startAccessingSecurityScopedResource]) { [m_activeURLs addObject:url]; return true; @@ -131,8 +150,23 @@ void SecurityBookmarkFileAccess::stopUsingSecurityScopedBookmark(QByteArray& boo if ([m_activeURLs containsObject:url]) { [url stopAccessingSecurityScopedResource]; + [url stopAccessingSecurityScopedResource]; + [m_activeURLs removeObject:url]; - [m_bookmarks removeObjectForKey:bookmark.toNSData()]; [m_paths removeObjectForKey:[url path]]; + [m_bookmarks removeObjectForKey:bookmark.toNSData()]; } } + +bool SecurityBookmarkFileAccess::isAccessingPath(const QString& path) +{ + NSData* bookmark = [m_paths objectForKey:path.toNSString()]; + if (!bookmark && path.endsWith('/')) { + bookmark = [m_paths objectForKey:path.left(path.length() - 1).toNSString()]; + } + if (!bookmark) { + return false; + } + NSURL* url = [m_bookmarks objectForKey:bookmark]; + return [m_activeURLs containsObject:url]; +}