Libgit2sharp: Holding on to pack files after disposing repository

Created on 17 Aug 2016  路  15Comments  路  Source: libgit2/libgit2sharp

When I try to delete the checkout location after calling Dispose() on the repository I get an exception "Access to the path 'pack-c2fbbbf1184a91086eb349987ee6502504fd29b3.idx' is denied."

I would have assumed that calling Dispose() would actually release all file handles. I read somewhere that the library holds on to some memory mapped pack files. Are they not being released correctly?

Most helpful comment

we have seen the same but that was because objects are marked as readyonly.

Using the below code we can delete the git directory without any issues:

    private static void DeleteDirectory(string directory)
    {
        foreach (string subdirectory in Directory.EnumerateDirectories(directory))
        {
            DeleteDirectory(subdirectory);
        }

        foreach (string fileName in Directory.EnumerateFiles(directory))
        {
            var fileInfo = new FileInfo(fileName)
            {
                Attributes = FileAttributes.Normal
            };
            fileInfo.Delete();
        }

        Directory.Delete(directory);
    }

All 15 comments

Can you provide a short, self-contained example?

Not the exact same error, but this should work as well.

       [Test]
        public void Example()
        {
            var checkoutPath = Path.GetTempFileName();
            File.Delete(checkoutPath);
            Directory.CreateDirectory(checkoutPath);
            var repoPath = LibGit2Sharp.Repository.Init(checkoutPath);
            var repository = new LibGit2Sharp.Repository(repoPath);
            using (var w = File.CreateText(Path.Combine(checkoutPath, "Meh.txt")))
            {
                w.WriteLine("test");
            }
            repository.Index.Add("Meh.txt");
            repository.Commit("test");
            repository.Dispose();
            Directory.Delete(checkoutPath, true);
        }

Have you checked that the error isn't because those files are read-only but because we're holding on to them? I would expect the error message to say so, whereas an access-denied message sounds like the read-only behaviour for files on Windows.

If I look in the directory after the delete comman has thrown the exception I don't actually see any files in there (but retrying the delete doesn't work either).

And yes, I agree with you: it probably has nothing to do with permissions but with the library holding on to files.

I've also come across this same issue.

UnauthorizedAccessException路Access to the path 'pack-55a7957d1e63c13a19598d13804ead41e418246c.idx' is denied.

Took @roederja2 idea and created a test that verifies this issue.

I did some investigating, but didn't see anything _obvious_ that would be holding onto the file.

https://github.com/libgit2/libgit2sharp/compare/master...mglodack:fix_holding_resource_issue

we have seen the same but that was because objects are marked as readyonly.

Using the below code we can delete the git directory without any issues:

    private static void DeleteDirectory(string directory)
    {
        foreach (string subdirectory in Directory.EnumerateDirectories(directory))
        {
            DeleteDirectory(subdirectory);
        }

        foreach (string fileName in Directory.EnumerateFiles(directory))
        {
            var fileInfo = new FileInfo(fileName)
            {
                Attributes = FileAttributes.Normal
            };
            fileInfo.Delete();
        }

        Directory.Delete(directory);
    }

Yes, thank @gsaralms , this is exactly what I would expect to see. Git (and libgit2, and thus LibGit2Sharp) create object files _without_ write permission, and this is by-design. As a consequence, you must make them writable before removing them.

I am seeing this issue with PowerShell in CI when Pester tries to clean up the test folder with Remove-Item -Recurse -Force. What's interesting is that the failure is also on macOS and Linux, who don't have read only file attributes, only read/write permissions. Does LibGit2Sharp set specific permissions on macOS or Linux? Or is this caused by not calling Dispose()?
What's interesting is is that this only fails in CI.

Does LibGit2Sharp set specific permissions on macOS or Linux?

On all platforms, git (and libgit2) will remove the write bit from object files. You will need to rm -rf or the platform equivalent or set the files writable before the cleanup step.

Well it is using rm -rf

It sounds like it's doing a PowerShell Remove-Item -Recurse -Force, not a literal rm -rf.

So either that PowerShell command really will try to remove unwritable files (which presumably is what the -Force flag does) and there's likely a permissions problem: what does ls -FlasR say about the git repository? How does that correspond to id?

Or it _doesn't_. Can you try actually running rm -rf on macOS/Linux?

This issue, as opened, is reporting that the library had a file open, which prevents it from being removed on Windows, due to sharing semantics. There are no such issues on POSIX systems, you can remove a file that is open, which will remove the directory entry (existing readers/writers will still have a file descriptor to the file).

So if you're seeing this on a POSIX system, there's a permissions problem.

Hmm, that helps narrow it down.
As a test, I created a file on macOS and gave it only read perms, then tried to delete it:
``` > touch test > ls -la test -rw-r--r-- 1 felix staff 0 May 15 09:36 test

ls -FlasR test
0 -rw-r--r-- 1 felix staff 0 May 15 09:36 test
chmod -w test
ls -FlasR test
0 -r--r--r-- 1 felix staff 0 May 15 09:36 test
remove-item test
remove-item : You do not have sufficient access rights to perform this operation.
At line:1 char:1

  • remove-item test
  • ~~~~
    at , :1
    System.IO.IOException: You do not have sufficient access rights to perform this operation.
    remove-item -Force -Recurse test
`Remove-Item -Force` was able to remove it, and also the Error when not specifying `-Force` was different than what I was seeing in CI:

    UnauthorizedAccessException: Access to the path 'pack-029d08823bd8a8eab510ad6ac75c823cfd3ed31e.idx' is denied.

```

Maybe it's actually something else, e.g. a misleading error for accessing a file that is already deleted?

Maybe it's actually something else, e.g. a misleading error for accessing a file that is already deleted?

That would be surprising. What does the idx look like on disk? ie, what's the ls -FlasR of the running process?

Okay, so actually accidentally all the tests were running in Windows in CI. I fixed the CI configuration, and now the Linux and macOS CI jobs are passing while the Windows CI is failing. So I think it's caused by not calling Dispose() properly

Was this page helpful?
0 / 5 - 0 ratings