mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add a basic SaveDataFileSystemCreator
This commit is contained in:
parent
5011a57d3e
commit
d7f3e94577
13 changed files with 188 additions and 64 deletions
|
@ -189,7 +189,7 @@ namespace LibHac.Fs
|
|||
rc = operation();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLog(rc, startTime, endTime, textGenerator(), caller);
|
||||
OutputAccessLog(rc, startTime, endTime, handle, textGenerator(), caller);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
{
|
||||
public interface IAttributeFileSystem : IFileSystem
|
||||
{
|
||||
NxFileAttributes GetFileAttributes(string path);
|
||||
void SetFileAttributes(string path, NxFileAttributes attributes);
|
||||
Result CreateDirectory(string path, NxFileAttributes archiveAttribute);
|
||||
Result GetFileAttributes(string path, out NxFileAttributes attributes);
|
||||
Result SetFileAttributes(string path, NxFileAttributes attributes);
|
||||
long GetFileSize(string path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
public static Result InvalidIndirectStorageSource => new Result(ModuleFs, 4023);
|
||||
|
||||
public static Result Result4302 => new Result(ModuleFs, 4302);
|
||||
public static Result InvalidSaveDataEntryType => new Result(ModuleFs, 4303);
|
||||
public static Result InvalidSaveDataHeader => new Result(ModuleFs, 4315);
|
||||
public static Result Result4362 => new Result(ModuleFs, 4362);
|
||||
public static Result Result4363 => new Result(ModuleFs, 4363);
|
||||
|
|
|
@ -5,6 +5,11 @@ namespace LibHac.Fs
|
|||
{
|
||||
public static class SaveData
|
||||
{
|
||||
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
{
|
||||
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.EmptyId);
|
||||
}
|
||||
|
||||
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName,
|
||||
SaveDataSpaceId spaceId, ulong saveDataId, UserId userId)
|
||||
{
|
||||
|
@ -67,19 +72,19 @@ namespace LibHac.Fs
|
|||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
|
||||
long journalSize, uint flags)
|
||||
{
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, new UserId(0, 0), ownerId, size, journalSize, flags);
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags);
|
||||
}
|
||||
|
||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
|
||||
long journalSize, uint flags)
|
||||
{
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, new UserId(0, 0), 0, size, journalSize, flags);
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, 0, size, journalSize, flags);
|
||||
}
|
||||
|
||||
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||
ulong ownerId, long size, long journalSize, uint flags)
|
||||
{
|
||||
return CreateSystemSaveData(fs, spaceId, saveDataId, new UserId(0, 0), ownerId, size, journalSize, flags);
|
||||
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags);
|
||||
}
|
||||
|
||||
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)
|
||||
|
|
|
@ -7,6 +7,8 @@ namespace LibHac.Fs
|
|||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
|
||||
{
|
||||
public static readonly UserId EmptyId = new UserId(0, 0);
|
||||
|
||||
public readonly Id128 Id;
|
||||
|
||||
public UserId(ulong high, ulong low)
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace LibHac.FsService.Creators
|
|||
var creators = new FileSystemCreators();
|
||||
|
||||
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
|
||||
creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(keyset);
|
||||
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keyset);
|
||||
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem);
|
||||
creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(rootFileSystem);
|
||||
|
|
70
src/LibHac/FsService/Creators/SaveDataFileSystemCreator.cs
Normal file
70
src/LibHac/FsService/Creators/SaveDataFileSystemCreator.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSystem.Save;
|
||||
|
||||
namespace LibHac.FsService.Creators
|
||||
{
|
||||
public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator
|
||||
{
|
||||
private Keyset Keyset { get; }
|
||||
|
||||
public SaveDataFileSystemCreator(Keyset keyset)
|
||||
{
|
||||
Keyset = keyset;
|
||||
}
|
||||
|
||||
public Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
|
||||
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
|
||||
SaveDataType type, ITimeStampGenerator timeStampGenerator)
|
||||
{
|
||||
fileSystem = default;
|
||||
extraDataAccessor = default;
|
||||
|
||||
string saveDataPath = $"/{saveDataId:x16}";
|
||||
|
||||
Result rc = sourceFileSystem.GetEntryType(out DirectoryEntryType entryType, saveDataPath);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
return rc == ResultFs.PathNotFound ? ResultFs.TargetNotFound : rc;
|
||||
}
|
||||
|
||||
switch (entryType)
|
||||
{
|
||||
case DirectoryEntryType.Directory:
|
||||
if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();
|
||||
|
||||
var subDirFs = new SubdirectoryFileSystem(sourceFileSystem, saveDataPath);
|
||||
fileSystem = new DirectorySaveDataFileSystem(subDirFs);
|
||||
|
||||
// Todo: Dummy ISaveDataExtraDataAccessor
|
||||
|
||||
return Result.Success;
|
||||
|
||||
case DirectoryEntryType.File:
|
||||
rc = sourceFileSystem.OpenFile(out IFile saveDataFile, saveDataPath, OpenMode.ReadWrite);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var saveDataStorage = new FileStorage(saveDataFile);
|
||||
fileSystem = new SaveDataFileSystem(Keyset, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, true);
|
||||
|
||||
// Todo: ISaveDataExtraDataAccessor
|
||||
|
||||
return Result.Success;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -163,7 +163,7 @@ namespace LibHac.FsService
|
|||
|
||||
if (saveFsResult.IsSuccess()) return Result.Success;
|
||||
|
||||
if (saveFsResult == ResultFs.PathNotFound || saveFsResult == ResultFs.TargetNotFound) return saveFsResult;
|
||||
if (saveFsResult != ResultFs.PathNotFound && saveFsResult != ResultFs.TargetNotFound) return saveFsResult;
|
||||
|
||||
if (saveDataId != SaveIndexerId)
|
||||
{
|
||||
|
|
|
@ -54,14 +54,20 @@ namespace LibHac.FsSystem
|
|||
#if CROSS_PLATFORM
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return HasConcatenationFileAttribute(BaseFileSystem.GetFileAttributes(path));
|
||||
Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes);
|
||||
if (rc.IsFailure()) return false;
|
||||
|
||||
return HasConcatenationFileAttribute(attributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsConcatenationFileHeuristic(path);
|
||||
}
|
||||
#else
|
||||
return HasConcatenationFileAttribute(BaseFileSystem.GetFileAttributes(path));
|
||||
Result rc = BaseFileSystem.GetFileAttributes(path, out NxFileAttributes attributes);
|
||||
if (rc.IsFailure()) return false;
|
||||
|
||||
return HasConcatenationFileAttribute(attributes);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -93,11 +99,9 @@ namespace LibHac.FsSystem
|
|||
return (attributes & NxFileAttributes.Directory) != 0 && (attributes & NxFileAttributes.Archive) != 0;
|
||||
}
|
||||
|
||||
private void SetConcatenationFileAttribute(string path)
|
||||
private Result SetConcatenationFileAttribute(string path)
|
||||
{
|
||||
NxFileAttributes attributes = BaseFileSystem.GetFileAttributes(path);
|
||||
attributes |= NxFileAttributes.Archive;
|
||||
BaseFileSystem.SetFileAttributes(path, attributes);
|
||||
return BaseFileSystem.SetFileAttributes(path, NxFileAttributes.Archive);
|
||||
}
|
||||
|
||||
public Result CreateDirectory(string path)
|
||||
|
@ -134,11 +138,9 @@ namespace LibHac.FsSystem
|
|||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
Result rc = BaseFileSystem.CreateDirectory(path);
|
||||
Result rc = BaseFileSystem.CreateDirectory(path, NxFileAttributes.Archive);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SetConcatenationFileAttribute(path);
|
||||
|
||||
long remaining = size;
|
||||
|
||||
for (int i = 0; remaining > 0; i++)
|
||||
|
@ -320,9 +322,7 @@ namespace LibHac.FsSystem
|
|||
{
|
||||
if (queryId != QueryId.MakeConcatFile) return ResultFs.UnsupportedOperationInConcatFsQueryEntry.Log();
|
||||
|
||||
SetConcatenationFileAttribute(path);
|
||||
|
||||
return Result.Success;
|
||||
return SetConcatenationFileAttribute(path);
|
||||
}
|
||||
|
||||
private int GetSubFileCount(string dirPath)
|
||||
|
|
|
@ -213,7 +213,7 @@ namespace LibHac.FsSystem
|
|||
rc = BaseFs.CreateDirectory(dest);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return this.CopyDirectory(this, src, dest);
|
||||
return BaseFs.CopyDirectory(BaseFs, src, dest);
|
||||
}
|
||||
|
||||
internal void NotifyCloseWritableFile()
|
||||
|
|
|
@ -182,10 +182,18 @@ namespace LibHac.FsSystem
|
|||
return (NxFileAttributes)(((int)attributes >> 4) & 3);
|
||||
}
|
||||
|
||||
public static FileAttributes ToFatAttributes(this NxFileAttributes attributes)
|
||||
{
|
||||
return (FileAttributes)(((int)attributes & 3) << 4);
|
||||
}
|
||||
|
||||
public static FileAttributes ApplyNxAttributes(this FileAttributes attributes, NxFileAttributes nxAttributes)
|
||||
{
|
||||
var nxAttributeBits = (FileAttributes)(((int)nxAttributes & 3) << 4);
|
||||
return attributes | nxAttributeBits;
|
||||
// The only 2 bits from FileAttributes that are used in NxFileAttributes
|
||||
const int mask = 3 << 4;
|
||||
|
||||
FileAttributes oldAttributes = attributes & (FileAttributes)mask;
|
||||
return oldAttributes | nxAttributes.ToFatAttributes();
|
||||
}
|
||||
|
||||
public static void SetConcatenationFileAttribute(this IFileSystem fs, string path)
|
||||
|
|
|
@ -34,21 +34,46 @@ namespace LibHac.FsSystem
|
|||
return PathTools.Combine(BasePath, path);
|
||||
}
|
||||
|
||||
public NxFileAttributes GetFileAttributes(string path)
|
||||
public Result GetFileAttributes(string path, out NxFileAttributes attributes)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
return File.GetAttributes(ResolveLocalPath(path)).ToNxAttributes();
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
FileInfo info = GetFileInfo(localPath);
|
||||
|
||||
if (info.Attributes == (FileAttributes)(-1))
|
||||
{
|
||||
attributes = default;
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
attributes = info.Attributes.ToNxAttributes();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void SetFileAttributes(string path, NxFileAttributes attributes)
|
||||
public Result SetFileAttributes(string path, NxFileAttributes attributes)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string localPath = ResolveLocalPath(path);
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
FileAttributes attributesOld = File.GetAttributes(localPath);
|
||||
FileInfo info = GetFileInfo(localPath);
|
||||
|
||||
if (info.Attributes == (FileAttributes)(-1))
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
FileAttributes attributesOld = info.Attributes;
|
||||
FileAttributes attributesNew = attributesOld.ApplyNxAttributes(attributes);
|
||||
|
||||
File.SetAttributes(localPath, attributesNew);
|
||||
try
|
||||
{
|
||||
info.Attributes = attributesNew;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public long GetFileSize(string path)
|
||||
|
@ -60,6 +85,11 @@ namespace LibHac.FsSystem
|
|||
}
|
||||
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
return CreateDirectory(path, NxFileAttributes.None);
|
||||
}
|
||||
|
||||
public Result CreateDirectory(string path, NxFileAttributes archiveAttribute)
|
||||
{
|
||||
string localPath = ResolveLocalPath(PathTools.Normalize(path));
|
||||
|
||||
|
@ -75,7 +105,7 @@ namespace LibHac.FsSystem
|
|||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
return CreateDirInternal(dir);
|
||||
return CreateDirInternal(dir, archiveAttribute);
|
||||
}
|
||||
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
|
@ -394,11 +424,17 @@ namespace LibHac.FsSystem
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result CreateDirInternal(DirectoryInfo dir)
|
||||
private static Result CreateDirInternal(DirectoryInfo dir, NxFileAttributes attributes)
|
||||
{
|
||||
try
|
||||
{
|
||||
dir.Create();
|
||||
dir.Refresh();
|
||||
|
||||
if (attributes.HasFlag(NxFileAttributes.Archive))
|
||||
{
|
||||
dir.Attributes |= FileAttributes.Archive;
|
||||
}
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
|
|
|
@ -21,81 +21,81 @@ namespace LibHac.FsSystem
|
|||
|
||||
public Result CreateDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.CreateDirectory(ResolveFullPath(path));
|
||||
return ParentFileSystem.CreateDirectory(fullPath);
|
||||
}
|
||||
|
||||
public Result CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.CreateFile(ResolveFullPath(path), size, options);
|
||||
return ParentFileSystem.CreateFile(fullPath, size, options);
|
||||
}
|
||||
|
||||
public Result DeleteDirectory(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.DeleteDirectory(ResolveFullPath(path));
|
||||
return ParentFileSystem.DeleteDirectory(fullPath);
|
||||
}
|
||||
|
||||
public Result DeleteDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.DeleteDirectoryRecursively(ResolveFullPath(path));
|
||||
return ParentFileSystem.DeleteDirectoryRecursively(fullPath);
|
||||
}
|
||||
|
||||
public Result CleanDirectoryRecursively(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.CleanDirectoryRecursively(ResolveFullPath(path));
|
||||
return ParentFileSystem.CleanDirectoryRecursively(fullPath);
|
||||
}
|
||||
|
||||
public Result DeleteFile(string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.DeleteFile(ResolveFullPath(path));
|
||||
return ParentFileSystem.DeleteFile(fullPath);
|
||||
}
|
||||
|
||||
public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.OpenDirectory(out directory, ResolveFullPath(path), mode);
|
||||
return ParentFileSystem.OpenDirectory(out directory, fullPath, mode);
|
||||
}
|
||||
|
||||
public Result OpenFile(out IFile file, string path, OpenMode mode)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.OpenFile(out file, ResolveFullPath(path), mode);
|
||||
return ParentFileSystem.OpenFile(out file, fullPath, mode);
|
||||
}
|
||||
|
||||
public Result RenameDirectory(string oldPath, string newPath)
|
||||
{
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
string fullOldPath = ResolveFullPath(PathTools.Normalize(oldPath));
|
||||
string fullNewPath = ResolveFullPath(PathTools.Normalize(newPath));
|
||||
|
||||
return ParentFileSystem.RenameDirectory(oldPath, newPath);
|
||||
return ParentFileSystem.RenameDirectory(fullOldPath, fullNewPath);
|
||||
}
|
||||
|
||||
public Result RenameFile(string oldPath, string newPath)
|
||||
{
|
||||
oldPath = PathTools.Normalize(oldPath);
|
||||
newPath = PathTools.Normalize(newPath);
|
||||
string fullOldPath = ResolveFullPath(PathTools.Normalize(oldPath));
|
||||
string fullNewPath = ResolveFullPath(PathTools.Normalize(newPath));
|
||||
|
||||
return ParentFileSystem.RenameFile(oldPath, newPath);
|
||||
return ParentFileSystem.RenameFile(fullOldPath, fullNewPath);
|
||||
}
|
||||
|
||||
public Result GetEntryType(out DirectoryEntryType entryType, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.GetEntryType(out entryType, ResolveFullPath(path));
|
||||
return ParentFileSystem.GetEntryType(out entryType, fullPath);
|
||||
}
|
||||
|
||||
public Result Commit()
|
||||
|
@ -105,30 +105,30 @@ namespace LibHac.FsSystem
|
|||
|
||||
public Result GetFreeSpaceSize(out long freeSpace, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.GetFreeSpaceSize(out freeSpace, ResolveFullPath(path));
|
||||
return ParentFileSystem.GetFreeSpaceSize(out freeSpace, fullPath);
|
||||
}
|
||||
|
||||
public Result GetTotalSpaceSize(out long totalSpace, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.GetTotalSpaceSize(out totalSpace, ResolveFullPath(path));
|
||||
return ParentFileSystem.GetTotalSpaceSize(out totalSpace, fullPath);
|
||||
}
|
||||
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.GetFileTimeStampRaw(out timeStamp, ResolveFullPath(path));
|
||||
return ParentFileSystem.GetFileTimeStampRaw(out timeStamp, fullPath);
|
||||
}
|
||||
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, string path)
|
||||
{
|
||||
path = PathTools.Normalize(path);
|
||||
string fullPath = ResolveFullPath(PathTools.Normalize(path));
|
||||
|
||||
return ParentFileSystem.QueryEntry(outBuffer, inBuffer, queryId, ResolveFullPath(path));
|
||||
return ParentFileSystem.QueryEntry(outBuffer, inBuffer, queryId, fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue