Catalina Beta 7: FileWrapper error “you don’t have permission to view it”.

Starting with Catalina Beta 6 and now with Beta 7 I’m seeing the following error in my Mac app when I try to write an NSFileWrapper to disk:


ObjC:


-[NSFileWrapper regularFileContents] tried to read the file wrapper's contents lazily but an error occurred: The file “problem.txt” couldn’t be opened because you don’t have permission to view it.


Swift:


Error Domain=NSCocoaErrorDomain Code=257 "The file “problem.txt” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/Users/test/Library/Caches/03B8158C-31C2-4CCA-8CCF-327E4EC7CCE3.bundle/problem.txt


I can reproduce this in a sample app. Here is what the sample app does:


(1) It creates the following sample folder:


~/Library/Caches/<UUID>/

test.txt

problem.txt


(2) It initializes a new NSFileWrapper with the folder.


(3) It removes the child file wrapper “problem.txt” and adds it again (to simulate an update)


(4) It writes the file wrapper to disk for the first time (this works)


(5) Right away it writes the file wrapper to disk a second time. This fails each time with the error above.



This is a serious issue, because it results in data-loss for all users of my app (sandboxed, Mac App Store) that are running the latest Catalina beta. I can also reproduce this in a sample app that is not sandboxed. Interestingly enough, the problem does not occur in the sample app when I enable sandboxing here.


Back in the day, the macOS High Sierra beta build 17C88 contained a similar issue which was fixed in the official release. This looks like it could be a regression.


References:


REFERENCES:

- FB5353407: macOS High Sierra beta bug report

- FB7101246: macOS Catalina beta 6 bug report

- FB7164053: macOS Catalina beta 7 bug report


To reproduce, run the code below in a new Cocoa app (not sandboxed):



import Foundation


class FileWrapperTestCaseSwift

{

func reproduce()

{

// Prepare sample data:

// ~/Library/Caches/<UUID>/

// test.txt

// problem.txt

let sampleDataFolderURL = temporaryDataFolderURL()

try! FileManager.default.createDirectory(at: sampleDataFolderURL, withIntermediateDirectories: true, attributes: nil)

try! "test" .write(to: sampleDataFolderURL.appendingPathComponent("test.txt" ), atomically: true, encoding: .utf8)

try! "problem".write(to: sampleDataFolderURL.appendingPathComponent("problem.txt"), atomically: true, encoding: .utf8)

// Read sample data:

let mainFileWrapper = try! FileWrapper(url: sampleDataFolderURL, options: [])

// Simulate a change:

updateFileWrapper(mainFileWrapper)


NSLog("About to write file wrapper for first time")

try! mainFileWrapper.write(to: sampleDataFolderURL, options: .atomic, originalContentsURL: sampleDataFolderURL)

updateFileWrapper(mainFileWrapper)

NSLog("About to write file wrapper a second time.")

do

{

// This fails on macOS Catalina Beta 6 & 7:

try mainFileWrapper.write(to: sampleDataFolderURL, options: .atomic, originalContentsURL: sampleDataFolderURL)

NSLog("Test PASSED")

}

catch

{

fatalError("TEST FAILED. Error: \(error)")

}

}

private func updateFileWrapper(_ mainFileWrapper:FileWrapper)

{

mainFileWrapper.fcRemoveFileWrapperWithName("test.txt")

mainFileWrapper.fcAddRegularFileNamed("test.txt", data: "test".data(using: .utf8)!)

#warning ("*** This file wrapper causes problems:")

if mainFileWrapper.fileWrappers?["problem.txt"] == nil

{

// For some reason, this file wrapper will cause problems the second time the parent

// file wrapper is written to disk. The file wrapper test.txt is removed and added each time

// to simulate an "update" in the app. Whereas problem.txt remains unchanged and is only added once.

// Error: Error Domain=NSCocoaErrorDomain Code=257 "The file “problem.txt” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/Users/test/Library/Caches/03B8158C-31C2-4CCA-8CCF-327E4EC7CCE3.bundle/problem.txt,

// Error in console: -[NSFileWrapper regularFileContents] tried to read the file wrapper's contents lazily but an error occurred: The file “problem.txt” couldn’t be opened because you don’t have permission to view it.

mainFileWrapper.fcAddRegularFileNamed("problem.txt", data: "problem".data(using: .utf8)!)

}

}

private func temporaryDataFolderURL() -> URL

{

let cachesPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first ?? NSTemporaryDirectory()

let tempDocumentName = UUID().uuidString + ".bundle";

return URL(fileURLWithPath: cachesPath, isDirectory: true).appendingPathComponent(tempDocumentName)

}

}


extension FileWrapper

{

func fcRemoveFileWrapperWithName(_ name:String)

{

if let fileWrapper = fileWrappers?[name]

{

removeFileWrapper(fileWrapper)

}

}

func fcAddRegularFileNamed(_ name:String, data:Data)

{

let child = FileWrapper(regularFileWithContents: data)

child.preferredFilename = name

addFileWrapper(child)

}

}

Replies

We saw this exact same problems with some machines on Catalina beta 7 (FB7237260).


The test code above ran fine on some of our beta 7 machines, but gave the exact same symptoms as _Rico reports on others. Turning Sandboxing off triggered the problem for those machines that were affected.


It appears that the problem may be resolved in beta 8

I have had the exact same problem on Catalina Beta 10.15.2 (19C32e). November 12, 2019


In my case, the file is INSIDE the app sandbox at:


/Users/test/Library/Containers/com.centcom.Fractal-Architect5/Data/Library/Caches/com.centcom.Fractal-Architect5/eJulias.mvrs/movie.flame


I tested a non-sandboxed app configuration, and received the exact same problem

/Users/test/Library/Caches/com.centcom.Fractal-Architect5-CUDA/Untitled.mvrs/movie.flame


Special considerations:


1) The file package "Untitled.mvrs" (that contains "movie.flame" file) was created by the main app process moments

before a separate XPC process tried to read the file. The XPC process executable is signed and included in the main app bundle.


2) One key finding: Both my app and _Rico's app were both trying to open a file in ~/Library/Caches directory.


3) The bug occurred in code written in June 2018 and was released and worked corrrectly on High Sierra and Mojave.

So this is a new Catalina bug.


4) The main app creates the file package, saves it, and inside the save operation's completion handler invokes the XPC method which then tries to open the file inside the file package.


So perhaps the Catalina bug occurs because the system calls the completion handler BEFORE the whole file package is wirtten to disk.


Nasty race conditions here if this is the case.

Further update:


My app gets a file access denied error only from inside a sandboxed XPC service.


From the host app, no file access denied error.