Permission Denied

Test setup in docker: SGW 2.7 Server 6.5
Client: WPF app - Lite 2.7

Im loading an image from a file to MemoryStream , then im converting to a dumb array of bytes for my view to consume.

                    var blob = new Blob("image/jpeg", Avatar);// Avatar is a byte[] and was set during SelectImageFile()
                    NewDoc.SetBlob("Avatar", blob);

The exception occurs when I create 2 users who use the same image as the avatar. As these are simple byte arrays what could be going wrong?

CouchbaseLiteException (POSIXDomain / 13): Permission denied.

at LiteCore.Interop.NativeHandler.ThrowOrHandle()
at LiteCore.Interop.NativeHandler.Execute(C4TryLogicDelegate1 block)
at LiteCore.LiteCoreBridge.Check(C4TryLogicDelegate1 block)
at Couchbase.Lite.Blob.Install(Database db)
at Couchbase.Lite.Blob.FLEncode(FLEncoder* enc)
at LiteCore.Interop.FLSliceExtensions.FLEncode(Object obj, FLEncoder* enc)
at Couchbase.Lite.Internal.Serialization.MDict.FLEncode(FLEncoder* enc)
at LiteCore.Interop.FLSliceExtensions.FLEncode(Object obj, FLEncoder* enc)
at Couchbase.Lite.MutableDocument.<>c__DisplayClass20_0.b__0()
at Couchbase.Lite.Support.ThreadSafety.DoLocked(Action a)
at Couchbase.Lite.MutableDocument.Encode()
at Couchbase.Lite.Database.Save(Document doc, C4Document** outDoc, C4Document* baseDoc, Boolean deletion)
at Couchbase.Lite.Database.<>c__DisplayClass94_0.b__0()
at Couchbase.Lite.Support.ThreadSafety.DoLocked(Action a)
at Couchbase.Lite.Database.Save(Document document, Document baseDocument, ConcurrencyControl concurrencyControl, Boolean deletion)
at Couchbase.Lite.Database.Save(MutableDocument document, ConcurrencyControl concurrencyControl)
at Couchbase.Lite.Database.Save(MutableDocument document)
at Agy.Wpf.Views.AeTalentViewModel.Save() in C:\Users\mmcca\source\repos\agy\Agy.Wpf\Views\Talent\AeTalentViewModel.cs:line 671

I have a suspicion that because the blob has the same size object then the sha-1ā€™s may be the same? and therefore the blob is being rejected? As the avatar blobs are nested properties of each user object this seems like strange behaviour or maybe im barking up the wrong tree.

It works fine if I slightly resize each image using an approximate random width & height (different sha-1ā€™s?)

Itā€™s perfectly ok for two blobs to have the same data. The storage layer is de-duplicated, so the data is only stored once.

I donā€™t develop on Windows, but I know it has slightly different file semantics than Unix, so this might be a compatibility issue in the code that writes the blob to disk. But we have unit tests for this, (unless Iā€™m losing my mind.)

@borrrden, any idea?

@TinoMclaren
Can you provide us the issue repro code snipped? Thanks!

Weā€™re guessing the issue is that the existing blob file is open, when CBL tries to replace it with the new blob file. The overwrite happens because the files, which are named after the SHA-1 digest, have the same name. Itā€™s a harmless no-op because they also have the same data.

(Windows is kind of unique among current OSā€™s in not allowing an open file to be deleted.)

If this is the issue, you should be able to work around the problem by making sure there are no open streams on the first blob, when you save the second one.

Thanks @jens. I have the stream wrapped in a c# ā€˜usingā€™ but to make sure I disposed of it manually and got the same exception.

Also to be triple sure, if I save the blob, close & reopen the app and add another users avatar with same image we get the exception (so the stream is certainly closed).

If I understand you, there really isnā€™t anything we can do on the windows side, except if we get this exception then we assume the user is uploading the same image & notify them not to, although a more specific exception would have been welcomed as ā€˜permission deniedā€™ is quite far ranging.

Is there anywhere that you access the ContentStream property of a Blob? That also needs to be disposed or it could cause this kind of behavior. Although for the next release (maybe 2.7.1, definitely 2.8.0) I will make sure LiteCore doesnā€™t try to copy a blob being created to the database directory if it already exists. The content is guaranteed to be identical so it will be safe to ignore on the LiteCore side.

Hello @borrrden

No, im using the byte array overload of the blob, the Stream of the image is converted to a byte array and is disposed of before the blob even gets the array.

Its not a huge deal, as long as you chaps are aware it. I can work around it.

I also had the same issue. And I fixed by releasing the Blob.ContentStream properly after usage. So @TinoMclaren Please make sure you release the Blobā€™s ContentStream in your WPF app.

Thanks @Sandy_Chuang
I believe im releasing the stream by wrapping it in a ā€˜usingā€™ statement but as you and @borrrden have both mentioned the same resolution im not sure if a ā€˜usingā€™ is the correct way? Is there a release method on the documents blob object or some other way other than a ā€˜usingā€™ block?

Use Using can release the Stream after usage. The thing you need to watch out for is when you create new Blob or load Blob from existing document and if you use itā€™s ContentStream, you need to release the ContentStream after usage.

I reproduced my issue with an unit test: I got same exception when I did not add line 180 in link below. After I dispose the StreamContent after usage, test pass.

1 Like

Thank you so much sandy, that was the issue. I was disposing of my own internal streams but not this one of the blob!

Having fooled around with this a fair bit, I now also see that I can use

Avatar = data.GetBlob(ā€œAvatarā€).Content;

instead of

Avatar = Utils.StreamToBytes(data.GetBlob(ā€œAvatarā€).ContentStream);

employing the ā€˜.Contentā€™ also avoids the need to dispose.
Thanks for all the help