521751795a
* Some style fixes and nits on ITimeZoneService * Remove some unneeded usings * Remove the Ryujinx.HLE.OsHle.Handles namespace * Remove hbmenu automatic load on process exit * Rename Ns to Device, rename Os to System, rename SystemState to State * Move Exceptions and Utilities out of OsHle * Rename OsHle to HOS * Rename OsHle folder to HOS * IManagerDisplayService and ISystemDisplayService style fixes * BsdError shouldn't be public * Add a empty new line before using static * Remove unused file * Some style fixes on NPDM * Exit gracefully when the application is closed * Code style fixes on IGeneralService * Add 0x prefix on values printed as hex * Small improvements on finalization code * Move ProcessId and ThreadId out of AThreadState * Rename VFs to FileSystem * FsAccessHeader shouldn't be public. Also fix file names casing * More case changes on NPDM * Remove unused files * Move using to the correct place on NPDM * Use properties on KernelAccessControlMmio * Address PR feedback
412 lines
No EOL
11 KiB
C#
412 lines
No EOL
11 KiB
C#
using Ryujinx.HLE.HOS.Ipc;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
using static Ryujinx.HLE.HOS.ErrorCode;
|
|
|
|
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|
{
|
|
class IFileSystem : IpcService
|
|
{
|
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
|
|
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
|
|
|
private HashSet<string> OpenPaths;
|
|
|
|
private string Path;
|
|
|
|
public IFileSystem(string Path)
|
|
{
|
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
|
{
|
|
{ 0, CreateFile },
|
|
{ 1, DeleteFile },
|
|
{ 2, CreateDirectory },
|
|
{ 3, DeleteDirectory },
|
|
{ 4, DeleteDirectoryRecursively },
|
|
{ 5, RenameFile },
|
|
{ 6, RenameDirectory },
|
|
{ 7, GetEntryType },
|
|
{ 8, OpenFile },
|
|
{ 9, OpenDirectory },
|
|
{ 10, Commit },
|
|
{ 11, GetFreeSpaceSize },
|
|
{ 12, GetTotalSpaceSize },
|
|
{ 13, CleanDirectoryRecursively },
|
|
//{ 14, GetFileTimeStampRaw }
|
|
};
|
|
|
|
OpenPaths = new HashSet<string>();
|
|
|
|
this.Path = Path;
|
|
}
|
|
|
|
public long CreateFile(ServiceCtx Context)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
long Mode = Context.RequestData.ReadInt64();
|
|
int Size = Context.RequestData.ReadInt32();
|
|
|
|
string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (FileName == null)
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (File.Exists(FileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(FileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
using (FileStream NewFile = File.Create(FileName))
|
|
{
|
|
NewFile.SetLength(Size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long DeleteFile(ServiceCtx Context)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (!File.Exists(FileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(FileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
File.Delete(FileName);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long CreateDirectory(ServiceCtx Context)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
string DirName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (DirName == null)
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (Directory.Exists(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
Directory.CreateDirectory(DirName);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long DeleteDirectory(ServiceCtx Context)
|
|
{
|
|
return DeleteDirectory(Context, false);
|
|
}
|
|
|
|
public long DeleteDirectoryRecursively(ServiceCtx Context)
|
|
{
|
|
return DeleteDirectory(Context, true);
|
|
}
|
|
|
|
private long DeleteDirectory(ServiceCtx Context, bool Recursive)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
string DirName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (!Directory.Exists(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
Directory.Delete(DirName, Recursive);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long RenameFile(ServiceCtx Context)
|
|
{
|
|
string OldName = ReadUtf8String(Context, 0);
|
|
string NewName = ReadUtf8String(Context, 1);
|
|
|
|
string OldFileName = Context.Device.FileSystem.GetFullPath(Path, OldName);
|
|
string NewFileName = Context.Device.FileSystem.GetFullPath(Path, NewName);
|
|
|
|
if (!File.Exists(OldFileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (File.Exists(NewFileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(OldFileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
File.Move(OldFileName, NewFileName);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long RenameDirectory(ServiceCtx Context)
|
|
{
|
|
string OldName = ReadUtf8String(Context, 0);
|
|
string NewName = ReadUtf8String(Context, 1);
|
|
|
|
string OldDirName = Context.Device.FileSystem.GetFullPath(Path, OldName);
|
|
string NewDirName = Context.Device.FileSystem.GetFullPath(Path, NewName);
|
|
|
|
if (!Directory.Exists(OldDirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (Directory.Exists(NewDirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(OldDirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
Directory.Move(OldDirName, NewDirName);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long GetEntryType(ServiceCtx Context)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (File.Exists(FileName))
|
|
{
|
|
Context.ResponseData.Write(1);
|
|
}
|
|
else if (Directory.Exists(FileName))
|
|
{
|
|
Context.ResponseData.Write(0);
|
|
}
|
|
else
|
|
{
|
|
Context.ResponseData.Write(0);
|
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long OpenFile(ServiceCtx Context)
|
|
{
|
|
int FilterFlags = Context.RequestData.ReadInt32();
|
|
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (!File.Exists(FileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(FileName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
FileStream Stream = new FileStream(FileName, FileMode.Open);
|
|
|
|
IFile FileInterface = new IFile(Stream, FileName);
|
|
|
|
FileInterface.Disposed += RemoveFileInUse;
|
|
|
|
lock (OpenPaths)
|
|
{
|
|
OpenPaths.Add(FileName);
|
|
}
|
|
|
|
MakeObject(Context, FileInterface);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long OpenDirectory(ServiceCtx Context)
|
|
{
|
|
int FilterFlags = Context.RequestData.ReadInt32();
|
|
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
string DirName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (!Directory.Exists(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
IDirectory DirInterface = new IDirectory(DirName, FilterFlags);
|
|
|
|
DirInterface.Disposed += RemoveDirectoryInUse;
|
|
|
|
lock (OpenPaths)
|
|
{
|
|
OpenPaths.Add(DirName);
|
|
}
|
|
|
|
MakeObject(Context, DirInterface);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long Commit(ServiceCtx Context)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
public long GetFreeSpaceSize(ServiceCtx Context)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
Context.ResponseData.Write(Context.Device.FileSystem.GetDrive().AvailableFreeSpace);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long GetTotalSpaceSize(ServiceCtx Context)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
Context.ResponseData.Write(Context.Device.FileSystem.GetDrive().TotalSize);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public long CleanDirectoryRecursively(ServiceCtx Context)
|
|
{
|
|
string Name = ReadUtf8String(Context);
|
|
|
|
string DirName = Context.Device.FileSystem.GetFullPath(Path, Name);
|
|
|
|
if (!Directory.Exists(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
}
|
|
|
|
if (IsPathAlreadyInUse(DirName))
|
|
{
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
}
|
|
|
|
foreach (string Entry in Directory.EnumerateFileSystemEntries(DirName))
|
|
{
|
|
if (Directory.Exists(Entry))
|
|
{
|
|
Directory.Delete(Entry, true);
|
|
}
|
|
else if (File.Exists(Entry))
|
|
{
|
|
File.Delete(Entry);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private bool IsPathAlreadyInUse(string Path)
|
|
{
|
|
lock (OpenPaths)
|
|
{
|
|
return OpenPaths.Contains(Path);
|
|
}
|
|
}
|
|
|
|
private void RemoveFileInUse(object sender, EventArgs e)
|
|
{
|
|
IFile FileInterface = (IFile)sender;
|
|
|
|
lock (OpenPaths)
|
|
{
|
|
FileInterface.Disposed -= RemoveFileInUse;
|
|
|
|
OpenPaths.Remove(FileInterface.HostPath);
|
|
}
|
|
}
|
|
|
|
private void RemoveDirectoryInUse(object sender, EventArgs e)
|
|
{
|
|
IDirectory DirInterface = (IDirectory)sender;
|
|
|
|
lock (OpenPaths)
|
|
{
|
|
DirInterface.Disposed -= RemoveDirectoryInUse;
|
|
|
|
OpenPaths.Remove(DirInterface.HostPath);
|
|
}
|
|
}
|
|
|
|
private string ReadUtf8String(ServiceCtx Context, int Index = 0)
|
|
{
|
|
long Position = Context.Request.PtrBuff[Index].Position;
|
|
long Size = Context.Request.PtrBuff[Index].Size;
|
|
|
|
using (MemoryStream MS = new MemoryStream())
|
|
{
|
|
while (Size-- > 0)
|
|
{
|
|
byte Value = Context.Memory.ReadByte(Position++);
|
|
|
|
if (Value == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
MS.WriteByte(Value);
|
|
}
|
|
|
|
return Encoding.UTF8.GetString(MS.ToArray());
|
|
}
|
|
}
|
|
}
|
|
} |