Skip to content

Commit

Permalink
LocalFileSystem: Allow deleting read-only files and directories
Browse files Browse the repository at this point in the history
  • Loading branch information
Thealexbarney committed Dec 14, 2021
1 parent 6910049 commit 2540f07
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/LibHac/Common/HResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal static class HResult
public const int ERROR_ALREADY_EXISTS = unchecked((int)0x800700B7);
public const int ERROR_DIRECTORY = unchecked((int)0x8007010B);
public const int ERROR_SPACES_NOT_ENOUGH_DRIVES = unchecked((int)0x80E7000B);
public const int COR_E_IO = unchecked((int)0x80131620);

public static Result HResultToHorizonResult(int hResult) => hResult switch
{
Expand All @@ -35,6 +36,7 @@ internal static class HResult
ERROR_ALREADY_EXISTS => ResultFs.PathAlreadyExists.Value,
ERROR_DIRECTORY => ResultFs.PathNotFound.Value,
ERROR_SPACES_NOT_ENOUGH_DRIVES => ResultFs.UsableSpaceNotEnough.Value,
COR_E_IO => ResultFs.TargetLocked.Value,
_ => ResultFs.UnexpectedInLocalFileSystemE.Value
};
}
2 changes: 1 addition & 1 deletion src/LibHac/FsSystem/Impl/TargetLockedAvoidance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace LibHac.FsSystem.Impl;

internal static class TargetLockedAvoidance
{
private const int RetryCount = 2;
private const int RetryCount = 25;
private const int SleepTimeMs = 2;

// Allow usage outside of a Horizon context by using standard .NET APIs
Expand Down
81 changes: 79 additions & 2 deletions src/LibHac/FsSystem/LocalFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,28 @@ private static Result DeleteDirectoryInternal(DirectoryInfo dir, bool recursive)

try
{
dir.Delete(recursive);
try
{
dir.Delete(recursive);
}
catch (Exception ex) when (ex.HResult is HResult.COR_E_IO or HResult.ERROR_ACCESS_DENIED)
{
if (recursive)
{
Result rc = DeleteDirectoryRecursivelyWithReadOnly(dir);
if (rc.IsFailure()) return rc.Miss();
}
else
{
// Try to delete read-only directories by first removing the read-only flag
if (dir.Attributes.HasFlag(FileAttributes.ReadOnly))
{
dir.Attributes &= ~FileAttributes.ReadOnly;
}

dir.Delete(false);
}
}
}
catch (Exception ex) when (ex.HResult < 0)
{
Expand All @@ -670,7 +691,20 @@ private static Result DeleteFileInternal(FileInfo file)

try
{
file.Delete();
try
{
file.Delete();
}
catch (UnauthorizedAccessException ex) when (ex.HResult == HResult.ERROR_ACCESS_DENIED)
{
// Try to delete read-only files by first removing the read-only flag.
if (file.Attributes.HasFlag(FileAttributes.ReadOnly))
{
file.Attributes &= ~FileAttributes.ReadOnly;
}

file.Delete();
}
}
catch (Exception ex) when (ex.HResult < 0)
{
Expand All @@ -680,6 +714,49 @@ private static Result DeleteFileInternal(FileInfo file)
return EnsureDeleted(file);
}

private static Result DeleteDirectoryRecursivelyWithReadOnly(DirectoryInfo rootDir)
{
try
{
foreach (FileSystemInfo info in rootDir.EnumerateFileSystemInfos())
{
if (info is FileInfo file)
{
// Check each file for the read-only flag before deleting.
if (file.Attributes.HasFlag(FileAttributes.ReadOnly))
{
file.Attributes &= ~FileAttributes.ReadOnly;
}

file.Delete();
}
else if (info is DirectoryInfo dir)
{
Result rc = DeleteDirectoryRecursivelyWithReadOnly(dir);
if (rc.IsFailure()) return rc.Miss();
}
else
{
return ResultFs.UnexpectedInLocalFileSystemF.Log();
}
}

// The directory should be empty now. Remove any read-only flag and delete it.
if (rootDir.Attributes.HasFlag(FileAttributes.ReadOnly))
{
rootDir.Attributes &= ~FileAttributes.ReadOnly;
}

rootDir.Delete(true);
}
catch (Exception ex) when (ex.HResult < 0)
{
return HResult.HResultToHorizonResult(ex.HResult).Log();
}

return Result.Success;
}

private static Result CreateDirInternal(DirectoryInfo dir, NxFileAttributes attributes)
{
try
Expand Down

0 comments on commit 2540f07

Please sign in to comment.