2018-02-05 00:08:20 +01:00
|
|
|
using ChocolArm64.Memory;
|
2018-02-20 21:09:23 +01:00
|
|
|
using Ryujinx.Core.OsHle.Ipc;
|
2018-02-21 22:56:52 +01:00
|
|
|
using System;
|
2018-02-10 01:14:55 +01:00
|
|
|
using System.Collections.Generic;
|
2018-02-05 00:08:20 +01:00
|
|
|
using System.IO;
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
using static Ryujinx.Core.OsHle.Objects.ErrorCode;
|
2018-02-20 21:09:23 +01:00
|
|
|
using static Ryujinx.Core.OsHle.Objects.ObjHelper;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-20 21:09:23 +01:00
|
|
|
namespace Ryujinx.Core.OsHle.Objects.FspSrv
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-10 01:14:55 +01:00
|
|
|
class IFileSystem : IIpcInterface
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-10 01:14:55 +01:00
|
|
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
private HashSet<string> OpenPaths;
|
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
private string Path;
|
|
|
|
|
|
|
|
public IFileSystem(string Path)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-10 01:14:55 +01:00
|
|
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
{ 0, CreateFile },
|
|
|
|
{ 1, DeleteFile },
|
|
|
|
{ 2, CreateDirectory },
|
|
|
|
{ 3, DeleteDirectory },
|
2018-02-20 12:03:04 +01:00
|
|
|
{ 4, DeleteDirectoryRecursively },
|
2018-02-21 22:56:52 +01:00
|
|
|
{ 5, RenameFile },
|
|
|
|
{ 6, RenameDirectory },
|
|
|
|
{ 7, GetEntryType },
|
|
|
|
{ 8, OpenFile },
|
|
|
|
{ 9, OpenDirectory },
|
|
|
|
{ 10, Commit },
|
|
|
|
{ 11, GetFreeSpaceSize },
|
|
|
|
{ 12, GetTotalSpaceSize },
|
|
|
|
//{ 13, CleanDirectoryRecursively },
|
|
|
|
//{ 14, GetFileTimeStampRaw }
|
2018-02-10 01:14:55 +01:00
|
|
|
};
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
OpenPaths = new HashSet<string>();
|
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
this.Path = Path;
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
public long CreateFile(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
2018-02-21 22:56:52 +01:00
|
|
|
|
|
|
|
long Mode = Context.RequestData.ReadInt64();
|
|
|
|
int Size = Context.RequestData.ReadInt32();
|
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
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))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
|
|
|
NewFile.SetLength(Size);
|
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
return 0;
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public long DeleteFile(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (!File.Exists(FileName))
|
|
|
|
{
|
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsPathAlreadyInUse(FileName))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
File.Delete(FileName);
|
|
|
|
|
|
|
|
return 0;
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public long CreateDirectory(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
string DirName = Context.Ns.VFs.GetFullPath(Path, Name);
|
|
|
|
|
|
|
|
if (DirName == null)
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (Directory.Exists(DirName))
|
|
|
|
{
|
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
|
|
|
}
|
2018-02-20 12:03:04 +01:00
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (IsPathAlreadyInUse(DirName))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
Directory.CreateDirectory(DirName);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long DeleteDirectory(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
return DeleteDirectory(Context, false);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public long DeleteDirectoryRecursively(ServiceCtx Context)
|
2018-02-21 22:56:52 +01:00
|
|
|
{
|
|
|
|
return DeleteDirectory(Context, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private long DeleteDirectory(ServiceCtx Context, bool Recursive)
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
string DirName = Context.Ns.VFs.GetFullPath(Path, Name);
|
|
|
|
|
|
|
|
if (!Directory.Exists(DirName))
|
|
|
|
{
|
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsPathAlreadyInUse(DirName))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
Directory.Delete(DirName, Recursive);
|
|
|
|
|
|
|
|
return 0;
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public long RenameFile(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long OldPosition = Context.Request.PtrBuff[0].Position;
|
|
|
|
long NewPosition = Context.Request.PtrBuff[0].Position;
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition);
|
|
|
|
string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition);
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string OldFileName = Context.Ns.VFs.GetFullPath(Path, OldName);
|
|
|
|
string NewFileName = Context.Ns.VFs.GetFullPath(Path, NewName);
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (!File.Exists(OldFileName))
|
|
|
|
{
|
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (File.Exists(NewFileName))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (IsPathAlreadyInUse(OldFileName))
|
|
|
|
{
|
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
|
|
|
}
|
|
|
|
|
|
|
|
File.Move(OldFileName, NewFileName);
|
|
|
|
|
|
|
|
return 0;
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public long RenameDirectory(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long OldPosition = Context.Request.PtrBuff[0].Position;
|
|
|
|
long NewPosition = Context.Request.PtrBuff[0].Position;
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition);
|
|
|
|
string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition);
|
2018-02-21 22:56:52 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
string OldDirName = Context.Ns.VFs.GetFullPath(Path, OldName);
|
|
|
|
string NewDirName = Context.Ns.VFs.GetFullPath(Path, NewName);
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (!Directory.Exists(OldDirName))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
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;
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
public long GetEntryType(ServiceCtx Context)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
|
|
|
|
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (File.Exists(FileName))
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
Context.ResponseData.Write(1);
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
2018-02-21 22:56:52 +01:00
|
|
|
else if (Directory.Exists(FileName))
|
|
|
|
{
|
|
|
|
Context.ResponseData.Write(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Context.ResponseData.Write(0);
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
|
|
|
}
|
2018-02-05 00:08:20 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
public long OpenFile(ServiceCtx Context)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
|
|
|
|
|
|
|
int FilterFlags = Context.RequestData.ReadInt32();
|
|
|
|
|
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
string FileName = Context.Ns.VFs.GetFullPath(Path, Name);
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (!File.Exists(FileName))
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (IsPathAlreadyInUse(FileName))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
FileStream Stream = new FileStream(FileName, FileMode.Open);
|
|
|
|
|
|
|
|
MakeObject(Context, new IFile(Stream, FileName));
|
|
|
|
|
|
|
|
return 0;
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-20 12:03:04 +01:00
|
|
|
public long OpenDirectory(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
|
|
|
|
|
|
|
int FilterFlags = Context.RequestData.ReadInt32();
|
|
|
|
|
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
|
|
|
|
|
|
|
string DirName = Context.Ns.VFs.GetFullPath(Path, Name);
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
if (!Directory.Exists(DirName))
|
2018-02-20 12:03:04 +01:00
|
|
|
{
|
2018-02-21 22:56:52 +01:00
|
|
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
2018-02-20 12:03:04 +01:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:56:52 +01:00
|
|
|
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;
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-02-10 01:14:55 +01:00
|
|
|
public long Commit(ServiceCtx Context)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2018-02-21 22:56:52 +01:00
|
|
|
|
|
|
|
public long GetFreeSpaceSize(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
|
|
|
|
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
|
|
|
|
|
|
|
Context.ResponseData.Write(Context.Ns.VFs.GetDrive().AvailableFreeSpace);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long GetTotalSpaceSize(ServiceCtx Context)
|
|
|
|
{
|
|
|
|
long Position = Context.Request.PtrBuff[0].Position;
|
|
|
|
|
|
|
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position);
|
|
|
|
|
|
|
|
Context.ResponseData.Write(Context.Ns.VFs.GetDrive().TotalSize);
|
|
|
|
|
|
|
|
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 -= RemoveDirectoryInUse;
|
|
|
|
|
|
|
|
OpenPaths.Remove(FileInterface.HostPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void RemoveDirectoryInUse(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
IDirectory DirInterface = (IDirectory)sender;
|
|
|
|
|
|
|
|
lock (OpenPaths)
|
|
|
|
{
|
|
|
|
DirInterface.Disposed -= RemoveDirectoryInUse;
|
|
|
|
|
|
|
|
OpenPaths.Remove(DirInterface.HostPath);
|
|
|
|
}
|
|
|
|
}
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
}
|