Example #1
0
        public static Result SetGlobalAccessLogMode(this FileSystemClient fs, GlobalAccessLogMode mode)
        {
            using ReferenceCountedDisposable <IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();

            Result rc = fsProxy.Target.SetGlobalAccessLogMode(mode);

            fs.Impl.AbortIfNeeded(rc);
            return(rc);
        }
Example #2
0
        internal static bool IsEnabledFileSystemAccessorAccessLog(this FileSystemClient fs, string mountName)
        {
            if (fs.MountTable.Find(mountName, out FileSystemAccessor accessor).IsFailure())
            {
                return(true);
            }

            return(accessor.IsAccessLogEnabled);
        }
Example #3
0
        private static Result ExtendSaveDataIfNeeded(FileSystemClient fs, out long requiredSize,
                                                     SaveDataSpaceId spaceId, ulong saveDataId, long requiredDataSize, long requiredJournalSize)
        {
            // Checks the current save data size and extends it if needed.
            // If there is not enough space to do the extension, the amount of space
            // the extension requires will be written to requiredSize.

            requiredSize = 0;
            return(Result.Success);
        }
Example #4
0
 public void Initialize(FileSystemClient fsClient, HorizonClient horizonClient)
 {
     Hos       = horizonClient;
     InitMutex = new object();
     AccessLog.Initialize(fsClient);
     UserMountTable.Initialize(fsClient);
     FsContextHandler.Initialize(fsClient);
     PathUtility.Initialize(fsClient);
     DirectorySaveDataFileSystem.Initialize(fsClient);
 }
Example #5
0
        public static Result TryCreateCacheStorage(this FileSystemClient fs, out long requiredSize,
                                                   SaveDataSpaceId spaceId, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
                                                   long dataSize, long journalSize, bool allowExisting)
        {
            requiredSize = default;
            long requiredSizeLocal = 0;

            var filter = new SaveDataFilter();

            filter.SetProgramId(applicationId);
            filter.SetIndex(index);
            filter.SetSaveDataType(SaveDataType.Cache);

            Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, spaceId, ref filter);

            if (rc.IsFailure())
            {
                if (!ResultFs.TargetNotFound.Includes(rc))
                {
                    return(rc);
                }

                Result CreateCacheFunc() => fs.CreateCacheStorage(applicationId, spaceId, saveDataOwnerId, index,
                                                                  dataSize, journalSize, SaveDataFlags.None);

                rc = CreateSaveData(fs, CreateCacheFunc, ref requiredSizeLocal, 0x4000, dataSize, journalSize);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                requiredSize = requiredSizeLocal;
                return(Result.Success);
            }

            if (!allowExisting)
            {
                return(ResultFs.SaveDataPathAlreadyExists.Log());
            }

            rc = ExtendSaveDataIfNeeded(fs, out requiredSizeLocal, spaceId, info.SaveDataId, dataSize, journalSize);

            if (rc.IsSuccess() || ResultFs.InsufficientFreeSpace.Includes(rc))
            {
                requiredSize = requiredSizeLocal;
                return(Result.Success);
            }

            if (ResultFs.SaveDataIsExtending.Includes(rc))
            {
                return(ResultFs.SaveDataCorrupted.LogConverted(rc));
            }

            return(rc);
        }
        public static void CreateOrOverwriteFile(this FileSystemClient fs, string path, long size, CreateFileOptions options)
        {
            path = PathTools.Normalize(path);

            if (fs.FileExists(path))
            {
                fs.DeleteFile(path);
            }

            fs.CreateFile(path, size, CreateFileOptions.None);
        }
Example #7
0
 private static void SetLocalAccessLogImpl(FileSystemClient fs, bool enabled)
 {
     if (enabled)
     {
         fs.Globals.AccessLog.LocalAccessLogTarget |= AccessLogTarget.Application;
     }
     else
     {
         fs.Globals.AccessLog.LocalAccessLogTarget &= ~AccessLogTarget.Application;
     }
 }
Example #8
0
 public static void SetLocalSystemAccessLogForDebug(this FileSystemClient fs, bool enabled)
 {
     if (enabled)
     {
         fs.Globals.AccessLog.LocalAccessLogTarget |= AccessLogTarget.All;
     }
     else
     {
         fs.Globals.AccessLog.LocalAccessLogTarget &= ~AccessLogTarget.All;
     }
 }
Example #9
0
 internal static AbortSpecifier DefaultResultHandler(FileSystemClient fs, Result result)
 {
     if (fs.Globals.FsContextHandler.IsAutoAbortEnabled)
     {
         return(AbortSpecifier.Default);
     }
     else
     {
         return(AbortSpecifier.Return);
     }
 }
Example #10
0
        public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
                                                           out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp)
        {
            if (nacp.CacheStorageSize <= 0)
            {
                requiredSize = default;
                target       = default;
                return(Result.Success);
            }

            return(EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId,
                                                     nacp.SaveDataOwnerId.Value, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true));
        }
Example #11
0
        public static Result CopyDirectory(this FileSystemClient fs, string sourcePath, string destPath,
                                           CreateFileOptions options = CreateFileOptions.None, IProgressReport logger = null)
        {
            Result rc = fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath.ToU8Span(), OpenDirectoryMode.All);

            if (rc.IsFailure())
            {
                return(rc);
            }

            try
            {
                foreach (DirectoryEntryEx entry in fs.EnumerateEntries(sourcePath, "*", SearchOptions.Default))
                {
                    string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name));
                    string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name));

                    if (entry.Type == DirectoryEntryType.Directory)
                    {
                        fs.EnsureDirectoryExists(subDstPath);

                        rc = fs.CopyDirectory(subSrcPath, subDstPath, options, logger);
                        if (rc.IsFailure())
                        {
                            return(rc);
                        }
                    }

                    if (entry.Type == DirectoryEntryType.File)
                    {
                        logger?.LogMessage(subSrcPath);
                        fs.CreateOrOverwriteFile(subDstPath, entry.Size, options);

                        rc = fs.CopyFile(subSrcPath, subDstPath, logger);
                        if (rc.IsFailure())
                        {
                            return(rc);
                        }
                    }
                }
            }
            finally
            {
                if (sourceHandle.IsValid)
                {
                    fs.CloseDirectory(sourceHandle);
                }
            }

            return(Result.Success);
        }
Example #12
0
        public static IEnumerable <DirectoryEntryEx> EnumerateEntries(this FileSystemClient fs, string path, string searchPattern, SearchOptions searchOptions)
        {
            bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive);
            bool recurse    = searchOptions.HasFlag(SearchOptions.RecurseSubdirectories);

            fs.OpenDirectory(out DirectoryHandle sourceHandle, path.ToU8Span(), OpenDirectoryMode.All).ThrowIfFailure();

            try
            {
                while (true)
                {
                    DirectoryEntry dirEntry = default;

                    fs.ReadDirectory(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry), sourceHandle);
                    if (entriesRead == 0)
                    {
                        break;
                    }

                    DirectoryEntryEx entry = FileSystemExtensions.GetDirectoryEntryEx(ref dirEntry, path);

                    if (PathTools.MatchesPattern(searchPattern, entry.Name, ignoreCase))
                    {
                        yield return(entry);
                    }

                    if (entry.Type != DirectoryEntryType.Directory || !recurse)
                    {
                        continue;
                    }

                    IEnumerable <DirectoryEntryEx> subEntries =
                        fs.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern, searchOptions);

                    foreach (DirectoryEntryEx subEntry in subEntries)
                    {
                        yield return(subEntry);
                    }
                }
            }
            finally
            {
                if (sourceHandle.IsValid)
                {
                    fs.CloseDirectory(sourceHandle);
                }
            }
        }
        public static Result CreateOrOverwriteFile(this FileSystemClient fs, string path, long size, CreateFileOptions options)
        {
            path = PathTools.Normalize(path);
            var u8Path = path.ToU8Span();

            if (fs.FileExists(path))
            {
                Result rc = fs.DeleteFile(u8Path);
                if (rc.IsFailure())
                {
                    return(rc);
                }
            }

            return(fs.CreateFile(u8Path, size, CreateFileOptions.None));
        }
        public static void EnsureDirectoryExists(this FileSystemClient fs, string path)
        {
            path = PathTools.Normalize(path);
            if (fs.DirectoryExists(path))
            {
                return;
            }

            PathTools.GetMountNameLength(path, out int mountNameLength).ThrowIfFailure();

            // Find the first subdirectory in the path that doesn't exist
            int i;

            for (i = path.Length - 1; i > mountNameLength + 2; i--)
            {
                if (path[i] == '/')
                {
                    string subPath = path.Substring(0, i);

                    if (fs.DirectoryExists(subPath))
                    {
                        break;
                    }
                }
            }

            // path[i] will be a '/', so skip that character
            i++;

            // loop until `path.Length - 1` so CreateDirectory won't be called multiple
            // times on path if the last character in the path is a '/'
            for (; i < path.Length - 1; i++)
            {
                if (path[i] == '/')
                {
                    string subPath = path.Substring(0, i);

                    fs.CreateDirectory(subPath.ToU8Span());
                }
            }

            fs.CreateDirectory(path.ToU8Span());
        }
Example #15
0
        private static Result GetCacheStorageTargetMediaImpl(this FileSystemClient fs,
                                                             out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId)
        {
            target = CacheStorageTargetMedia.None;

            var filter = new SaveDataFilter();

            filter.SetProgramId(applicationId);
            filter.SetSaveDataType(SaveDataType.Cache);

            if (fs.IsSdCardAccessible())
            {
                Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdCache, ref filter);
                if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc))
                {
                    return(rc);
                }

                if (rc.IsSuccess())
                {
                    target = CacheStorageTargetMedia.SdCard;
                }
            }

            // Not on the SD card. Check it it's in NAND
            if (target == CacheStorageTargetMedia.None)
            {
                Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.User, ref filter);
                if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc))
                {
                    return(rc);
                }

                if (rc.IsSuccess())
                {
                    target = CacheStorageTargetMedia.Nand;
                }
            }

            return(Result.Success);
        }
        public static Result CreateApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
                                                           out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp,
                                                           ushort index, long dataSize, long journalSize)
        {
            UnsafeHelpers.SkipParamInit(out requiredSize, out target);

            if (index > nacp.CacheStorageMaxIndex)
            {
                return(ResultFs.CacheStorageIndexTooLarge.Log());
            }

            if (dataSize + journalSize > nacp.CacheStorageMaxSizeAndMaxJournalSize)
            {
                return(ResultFs.CacheStorageSizeTooLarge.Log());
            }

            Result rc = fs.EnsureApplicationCacheStorage(out requiredSize, out target, applicationId,
                                                         nacp.SaveDataOwnerId.Value, index, dataSize, journalSize, false);

            fs.Impl.AbortIfNeeded(rc);
            return(rc);
        }
 public static Result CreateOrOverwriteFile(this FileSystemClient fs, string path, long size)
 {
     return(fs.CreateOrOverwriteFile(path, size, CreateFileOptions.None));
 }
Example #18
0
 public static Result GetCacheStorageTargetMedia(this FileSystemClient fs, out CacheStorageTargetMedia target,
                                                 Ncm.ApplicationId applicationId)
 {
     return(GetCacheStorageTargetMediaImpl(fs, out target, applicationId));
 }
Example #19
0
 public static Result EnsureApplicationBcatDeliveryCacheStorage(this FileSystemClient fs, out long requiredSize,
                                                                Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp)
 {
     return(EnsureApplicationBcatDeliveryCacheStorageImpl(fs, out requiredSize, applicationId, ref nacp));
 }
Example #20
0
 public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
                                                    Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp)
 {
     return(EnsureApplicationCacheStorageImpl(fs, out requiredSize, out _, applicationId, nacp.SaveDataOwnerId.Value,
                                              0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true));
 }
Example #21
0
 internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient;
 public static IEnumerable <DirectoryEntryEx> EnumerateEntries(this FileSystemClient fs, string path)
 {
     return(fs.EnumerateEntries(path, "*"));
 }
Example #23
0
        private static Result EnsureApplicationCacheStorageImpl(this FileSystemClient fs, out long requiredSize,
                                                                out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
                                                                long dataSize, long journalSize, bool allowExisting)
        {
            requiredSize = default;
            target       = CacheStorageTargetMedia.SdCard;

            Result rc = fs.GetCacheStorageTargetMediaImpl(out CacheStorageTargetMedia targetMedia, applicationId);

            if (rc.IsFailure())
            {
                return(rc);
            }

            long requiredSizeLocal = 0;

            if (targetMedia == CacheStorageTargetMedia.Nand)
            {
                rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.User, applicationId,
                                           saveDataOwnerId, index, dataSize, journalSize, allowExisting);
                if (rc.IsFailure())
                {
                    return(rc);
                }
            }
            else if (targetMedia == CacheStorageTargetMedia.SdCard)
            {
                rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.SdCache, applicationId,
                                           saveDataOwnerId, index, dataSize, journalSize, allowExisting);
                if (rc.IsFailure())
                {
                    return(rc);
                }
            }
            // Savedata doesn't exist. Try to create a new one.
            else
            {
                // Try to create the savedata on the SD card first
                if (fs.IsSdCardAccessible())
                {
                    target = CacheStorageTargetMedia.SdCard;

                    Result CreateFuncSdCard() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache,
                                                                       saveDataOwnerId, index, dataSize, journalSize, SaveDataFlags.None);

                    rc = CreateSaveData(fs, CreateFuncSdCard, ref requiredSizeLocal, 0x4000, dataSize, journalSize);
                    if (rc.IsFailure())
                    {
                        return(rc);
                    }

                    if (requiredSizeLocal == 0)
                    {
                        requiredSize = 0;
                        return(Result.Success);
                    }
                }

                // If the save can't be created on the SD card, try creating it on the User BIS partition
                requiredSizeLocal = 0;
                target            = CacheStorageTargetMedia.Nand;

                Result CreateFuncNand() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, saveDataOwnerId,
                                                                 index, dataSize, journalSize, SaveDataFlags.None);

                rc = CreateSaveData(fs, CreateFuncNand, ref requiredSizeLocal, 0x4000, dataSize, journalSize);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                if (requiredSizeLocal != 0)
                {
                    target       = CacheStorageTargetMedia.None;
                    requiredSize = requiredSizeLocal;
                    return(ResultFs.InsufficientFreeSpace.Log());
                }
            }

            requiredSize = 0;
            return(Result.Success);
        }
Example #24
0
 public static void SetResultHandledByApplication(this FileSystemClient fs, bool isHandledByApplication)
 {
     fs.Globals.ResultHandlingUtility.IsResultHandledByApplication = isHandledByApplication;
 }
Example #25
0
        public static Result EnsureApplicationSaveData(FileSystemClient fs, out long requiredSize, Ncm.ApplicationId applicationId,
                                                       ref ApplicationControlProperty nacp, ref Uid uid)
        {
            requiredSize = default;
            long requiredSizeSum = 0;

            // Create local variable for use in closures
            ProgramId saveDataOwnerId = nacp.SaveDataOwnerId;

            // Ensure the user account save exists
            if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0)
            {
                // More local variables for use in closures
                Uid  uidLocal               = uid;
                long accountSaveDataSize    = nacp.UserAccountSaveDataSize;
                long accountSaveJournalSize = nacp.UserAccountSaveDataJournalSize;

                Result CreateAccountSaveFunc()
                {
                    UserId userId = ConvertAccountUidToFsUserId(uidLocal);

                    return(fs.CreateSaveData(applicationId, userId, saveDataOwnerId.Value, accountSaveDataSize,
                                             accountSaveJournalSize, SaveDataFlags.None));
                }

                var filter = new SaveDataFilter();
                filter.SetProgramId(applicationId);
                filter.SetSaveDataType(SaveDataType.Account);
                filter.SetUserId(new UserId(uid.Id.High, uid.Id.Low));

                // The 0x4c000 includes the save meta and other stuff
                Result rc = EnsureAndExtendSaveData(fs, CreateAccountSaveFunc, ref requiredSizeSum, ref filter, 0x4c000,
                                                    accountSaveDataSize, accountSaveJournalSize);

                if (rc.IsFailure())
                {
                    return(rc);
                }
            }

            // Ensure the device save exists
            if (nacp.DeviceSaveDataSize > 0)
            {
                long deviceSaveDataSize    = nacp.DeviceSaveDataSize;
                long deviceSaveJournalSize = nacp.DeviceSaveDataJournalSize;

                Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId.Value,
                                                                         deviceSaveDataSize, deviceSaveJournalSize, 0);

                var filter = new SaveDataFilter();
                filter.SetProgramId(applicationId);
                filter.SetSaveDataType(SaveDataType.Device);

                Result rc = EnsureAndExtendSaveData(fs, CreateDeviceSaveFunc, ref requiredSizeSum, ref filter, 0x4000,
                                                    deviceSaveDataSize, deviceSaveJournalSize);

                if (rc.IsFailure())
                {
                    return(rc);
                }
            }

            Result bcatRc = EnsureApplicationBcatDeliveryCacheStorageImpl(fs,
                                                                          out long requiredSizeBcat, applicationId, ref nacp);

            if (bcatRc.IsFailure())
            {
                if (!ResultFs.InsufficientFreeSpace.Includes(bcatRc))
                {
                    return(bcatRc);
                }

                requiredSizeSum += requiredSizeBcat;
            }

            if (nacp.TemporaryStorageSize > 0)
            {
                if (requiredSizeSum > 0)
                {
                    // If there was already insufficient space to create the previous saves, check if the temp
                    // save already exists instead of trying to create a new one.
                    var filter = new SaveDataFilter();
                    filter.SetProgramId(applicationId);
                    filter.SetSaveDataType(SaveDataType.Temporary);

                    Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.Temporary, ref filter);

                    if (rc.IsFailure())
                    {
                        if (!ResultFs.TargetNotFound.Includes(rc))
                        {
                            return(rc);
                        }

                        Result queryRc = fs.QuerySaveDataTotalSize(out long tempSaveTotalSize,
                                                                   nacp.TemporaryStorageSize, 0);

                        if (queryRc.IsFailure())
                        {
                            return(queryRc);
                        }

                        requiredSizeSum += Utilities.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000;
                    }
                }
                else
                {
                    Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId.Value,
                                                                nacp.TemporaryStorageSize, 0);

                    if (createRc.IsFailure())
                    {
                        if (ResultFs.InsufficientFreeSpace.Includes(createRc))
                        {
                            Result queryRc = fs.QuerySaveDataTotalSize(out long tempSaveTotalSize,
                                                                       nacp.TemporaryStorageSize, 0);

                            if (queryRc.IsFailure())
                            {
                                return(queryRc);
                            }

                            requiredSizeSum += Utilities.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000;
                        }
                        else if (ResultFs.PathAlreadyExists.Includes(createRc))
                        {
                            requiredSizeSum += 0;
                        }
                        else
                        {
                            return(createRc);
                        }
                    }
                }
            }

            requiredSize = requiredSizeSum;

            return(requiredSize == 0 ? Result.Success : ResultFs.InsufficientFreeSpace.Log());
        }
        private static Result EnsureApplicationBcatDeliveryCacheStorageImpl(FileSystemClient fs, out long requiredSize,
                                                                            TitleId applicationId, ref ApplicationControlProperty nacp)
        {
            const long bcatDeliveryCacheJournalSize = 0x200000;

            requiredSize = default;

            if (nacp.BcatDeliveryCacheStorageSize <= 0)
            {
                requiredSize = 0;
                return(Result.Success);
            }

            var filter = new SaveDataFilter();

            filter.SetTitleId(applicationId);
            filter.SetSaveDataType(SaveDataType.BcatDeliveryCacheStorage);

            Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);

            if (rc.IsSuccess())
            {
                rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeBcat, SaveDataSpaceId.User,
                                            saveDataInfo.SaveDataId, nacp.DeviceSaveDataSize, bcatDeliveryCacheJournalSize);

                if (rc.IsFailure())
                {
                    if (!ResultRangeFs.InsufficientFreeSpace.Contains(rc))
                    {
                        return(rc);
                    }

                    requiredSize = requiredSizeBcat;
                }
            }
            else if (rc != ResultFs.TargetNotFound)
            {
                return(rc);
            }
            else
            {
                Result createRc = fs.CreateBcatSaveData(applicationId, nacp.BcatDeliveryCacheStorageSize);

                if (createRc.IsFailure())
                {
                    if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
                    {
                        // todo: Call QuerySaveDataTotalSize and assign the value to requiredSize
                        requiredSize = 0;
                    }
                    else if (createRc == ResultFs.PathAlreadyExists)
                    {
                        requiredSize = 0;
                    }
                    else
                    {
                        return(createRc);
                    }
                }
            }

            return(requiredSize > 0 ? ResultFs.InsufficientFreeSpace.Log() : Result.Success);
        }
        public static Result EnsureApplicationSaveData(FileSystemClient fs, out long requiredSize, TitleId applicationId,
                                                       ref ApplicationControlProperty nacp, ref Uid uid)
        {
            requiredSize = default;

            long requiredSizeSum = 0;

            if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0)
            {
                var filter = new SaveDataFilter();
                filter.SetTitleId(applicationId);
                filter.SetSaveDataType(SaveDataType.SaveData);
                filter.SetUserId(new UserId(uid.Id.High, uid.Id.Low));

                Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);

                if (rc.IsSuccess())
                {
                    rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeUser, SaveDataSpaceId.User,
                                                saveDataInfo.SaveDataId, nacp.UserAccountSaveDataSize, nacp.UserAccountSaveDataJournalSize);

                    if (rc.IsFailure())
                    {
                        if (!ResultRangeFs.InsufficientFreeSpace.Contains(rc))
                        {
                            return(rc);
                        }

                        requiredSizeSum = requiredSizeUser;
                    }
                }
                else if (rc != ResultFs.TargetNotFound)
                {
                    return(rc);
                }
                else
                {
                    UserId userId = ConvertAccountUidToFsUserId(uid);

                    Result createRc = fs.CreateSaveData(applicationId, userId, nacp.SaveDataOwnerId,
                                                        nacp.UserAccountSaveDataSize, nacp.UserAccountSaveDataJournalSize, 0);

                    if (createRc.IsFailure())
                    {
                        if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
                        {
                            // todo: Call QuerySaveDataTotalSize and assign the value to requiredSizeSum
                            requiredSizeSum = 0;
                        }
                        else if (createRc == ResultFs.PathAlreadyExists)
                        {
                            requiredSizeSum = 0;
                        }
                        else
                        {
                            return(createRc);
                        }
                    }
                }
            }

            if (nacp.DeviceSaveDataSize > 0)
            {
                var filter = new SaveDataFilter();
                filter.SetTitleId(applicationId);
                filter.SetSaveDataType(SaveDataType.DeviceSaveData);

                Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);

                if (rc.IsSuccess())
                {
                    rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeDevice, SaveDataSpaceId.User,
                                                saveDataInfo.SaveDataId, nacp.DeviceSaveDataSize, nacp.DeviceSaveDataJournalSize);

                    if (rc.IsFailure())
                    {
                        if (!ResultRangeFs.InsufficientFreeSpace.Contains(rc))
                        {
                            return(rc);
                        }

                        requiredSizeSum += requiredSizeDevice;
                    }
                }
                else if (rc != ResultFs.TargetNotFound)
                {
                    return(rc);
                }
                else
                {
                    Result createRc = fs.CreateDeviceSaveData(applicationId, nacp.SaveDataOwnerId,
                                                              nacp.DeviceSaveDataSize, nacp.DeviceSaveDataJournalSize, 0);

                    if (createRc.IsFailure())
                    {
                        if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
                        {
                            // todo: Call QuerySaveDataTotalSize and add the value to requiredSizeSum
                            requiredSizeSum += 0;
                        }
                        else if (createRc == ResultFs.PathAlreadyExists)
                        {
                            requiredSizeSum += 0;
                        }
                        else
                        {
                            return(createRc);
                        }
                    }
                }
            }

            Result bcatRc = EnsureApplicationBcatDeliveryCacheStorageImpl(fs,
                                                                          out long requiredSizeBcat, applicationId, ref nacp);

            if (bcatRc.IsFailure())
            {
                if (!ResultRangeFs.InsufficientFreeSpace.Contains(bcatRc))
                {
                    return(bcatRc);
                }

                requiredSizeSum += requiredSizeBcat;
            }

            // Don't actually do this yet because the temp save indexer hasn't been implemented
            // todo: Flip the operator when it is
            if (nacp.TemporaryStorageSize < 0)
            {
                if (requiredSizeSum > 0)
                {
                    var filter = new SaveDataFilter();
                    filter.SetTitleId(applicationId);
                    filter.SetSaveDataType(SaveDataType.TemporaryStorage);

                    Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.User, ref filter);

                    if (rc.IsFailure())
                    {
                        if (rc != ResultFs.TargetNotFound)
                        {
                            return(rc);
                        }

                        // todo: Call QuerySaveDataTotalSize and add the value to requiredSizeSum
                        requiredSizeSum += 0;
                    }
                }
                else
                {
                    Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId,
                                                                nacp.TemporaryStorageSize, 0);

                    if (createRc.IsFailure())
                    {
                        if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
                        {
                            // todo: Call QuerySaveDataTotalSize and assign the value to requiredSizeSum
                            requiredSizeSum += 0;
                        }
                        else if (createRc == ResultFs.PathAlreadyExists)
                        {
                            requiredSizeSum += 0;
                        }
                        else
                        {
                            return(createRc);
                        }
                    }
                }
            }

            requiredSize = requiredSizeSum;

            return(requiredSize == 0 ? Result.Success : ResultFs.InsufficientFreeSpace.Log());
        }
        public static Result CopyFile(this FileSystemClient fs, string sourcePath, string destPath, IProgressReport logger = null)
        {
            Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath.ToU8Span(), OpenMode.Read);

            if (rc.IsFailure())
            {
                return(rc);
            }

            using (sourceHandle)
            {
                rc = fs.OpenFile(out FileHandle destHandle, destPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend);
                if (rc.IsFailure())
                {
                    return(rc);
                }

                using (destHandle)
                {
                    const int maxBufferSize = 0x10000;

                    rc = fs.GetFileSize(out long fileSize, sourceHandle);
                    if (rc.IsFailure())
                    {
                        return(rc);
                    }

                    int bufferSize = (int)Math.Min(maxBufferSize, fileSize);

                    logger?.SetTotal(fileSize);

                    byte[] buffer = ArrayPool <byte> .Shared.Rent(bufferSize);

                    try
                    {
                        for (long offset = 0; offset < fileSize; offset += bufferSize)
                        {
                            int         toRead = (int)Math.Min(fileSize - offset, bufferSize);
                            Span <byte> buf    = buffer.AsSpan(0, toRead);

                            rc = fs.ReadFile(out long _, sourceHandle, offset, buf);
                            if (rc.IsFailure())
                            {
                                return(rc);
                            }

                            rc = fs.WriteFile(destHandle, offset, buf);
                            if (rc.IsFailure())
                            {
                                return(rc);
                            }

                            logger?.ReportAdd(toRead);
                        }
                    }
                    finally
                    {
                        ArrayPool <byte> .Shared.Return(buffer);

                        logger?.SetTotal(0);
                    }

                    rc = fs.FlushFile(destHandle);
                    if (rc.IsFailure())
                    {
                        return(rc);
                    }
                }
            }

            return(Result.Success);
        }
 public static IEnumerable <DirectoryEntryEx> EnumerateEntries(this FileSystemClient fs, string path, string searchPattern)
 {
     return(fs.EnumerateEntries(path, searchPattern, SearchOptions.RecurseSubdirectories));
 }
        public static bool FileExists(this FileSystemClient fs, string path)
        {
            Result rc = fs.GetEntryType(out DirectoryEntryType type, path.ToU8Span());

            return(rc.IsSuccess() && type == DirectoryEntryType.File);
        }