Пример #1
0
        /// <summary>
        /// Called when a file or folder is renamed in the remote storage.
        /// </summary>
        /// <remarks>In this method we rename corresponding file/folder in user file system.</remarks>
        private async void RenamedAsync(object sender, RenamedEventArgs e)
        {
            LogMessage("Renamed:", e.OldFullPath, e.FullPath);
            string remoteStorageOldPath = e.OldFullPath;
            string remoteStorageNewPath = e.FullPath;

            try
            {
                string userFileSystemOldPath = Mapping.ReverseMapPath(remoteStorageOldPath);
                string userFileSystemNewPath = Mapping.ReverseMapPath(remoteStorageNewPath);

                // This check is only required because we can not prevent circular calls because of the simplicity of this example.
                // In your real-life application you will not sent updates from server back to client that issued the update.
                Thread.Sleep(2000); // This can be removed in a real-life application.
                if (FsPath.Exists(userFileSystemOldPath))
                {
                    // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                    if (await engine.ServerNotifications(userFileSystemOldPath).MoveToAsync(userFileSystemNewPath))
                    {
                        LogMessage("Renamed succesefully:", userFileSystemOldPath, userFileSystemNewPath);
                    }
                }
                await engine.CustomDataManager(userFileSystemOldPath, this).MoveToAsync(userFileSystemNewPath);
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", remoteStorageOldPath, remoteStorageNewPath, ex);
            }
        }
Пример #2
0
        /// <summary>
        /// Called when a file or folder is created in the remote storage.
        /// </summary>
        /// <remarks>In this method we create a new file/folder in the user file system.</remarks>
        private async void CreatedAsync(object sender, FileSystemEventArgs e)
        {
            LogMessage(e.ChangeType.ToString(), e.FullPath);
            string remoteStoragePath = e.FullPath;

            try
            {
                string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath);

                // This check is only required because we can not prevent circular calls because of the simplicity of this example.
                // In your real-life application you will not sent updates from server back to client that issued the update.
                if (!FsPath.Exists(userFileSystemPath))
                {
                    string userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath);

                    // Because of the on-demand population the file or folder placeholder may not exist in the user file system
                    // or the folder may be offline.
                    FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath);
                    if (remoteStorageItem != null)
                    {
                        IFileSystemItemMetadata newItemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);
                        if (await engine.ServerNotifications(userFileSystemParentPath).CreateAsync(new[] { newItemInfo }) > 0)
                        {
                            LogMessage($"Created succesefully", userFileSystemPath);
                            engine.CustomDataManager(userFileSystemPath, this).IsNew = false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex);
            }
        }
Пример #3
0
        /// <summary>
        /// Called when a file or folder is created in the user file system.
        /// </summary>
        /// <remarks>
        /// This method is also called when a file is being moved in user file system, after the IFileSystem.MoveToAsync() call.
        /// </remarks>
        private async void CreatedAsync(object sender, FileSystemEventArgs e)
        {
            LogMessage($"{e.ChangeType}", e.FullPath);
            string userFileSystemPath = e.FullPath;

            try
            {
                // When a file/folder is moved this method is also called. The file/folder move is already processed in IFileSystem.MoveToAsync().
                if (FsPath.Exists(userFileSystemPath) && !PlaceholderItem.IsPlaceholder(userFileSystemPath))
                {
                    // When a new file or folder is created under sync root it is
                    // created as a regular file or folder. Converting to placeholder.
                    PlaceholderItem.ConvertToPlaceholder(userFileSystemPath, false);
                    LogMessage("Converted to placeholder", userFileSystemPath);

                    // Do not create temp MS Office, temp and hidden files in remote storage.
                    if (!FsPath.AvoidSync(userFileSystemPath))
                    {
                        // Create the file/folder in the remote storage.
                        try
                        {
                            await RemoteStorageRawItem.CreateAsync(userFileSystemPath, this);
                        }
                        catch (IOException ex)
                        {
                            LogError("Creation in remote storage failed. Possibly in use by an application", userFileSystemPath, null, ex);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", userFileSystemPath, null, ex);
            }
        }
        /// <summary>
        /// Called when a file content changed or file/folder attributes changed in the remote storage.
        /// </summary>
        /// <remarks>
        /// In this method we update corresponding file/folder information in user file system.
        /// We also dehydrate the file if it is not blocked.
        /// </remarks>
        private async void ChangedAsync(object sender, FileSystemEventArgs e)
        {
            LogMessage(e.ChangeType.ToString(), e.FullPath);
            string remoteStoragePath = e.FullPath;

            try
            {
                string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath);

                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemPath))
                {
                    FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath);

                    if (!FsPath.AvoidSync(remoteStoragePath))
                    {
                        // This check is only required because we can not prevent circular calls because of the simplicity of this example.
                        if (!await new UserFileSystemItem(userFileSystemPath).EqualsAsync(remoteStorageItem))
                        {
                            LogMessage("Item modified:", remoteStoragePath);
                            await new UserFileSystemItem(userFileSystemPath).UpdateAsync(remoteStorageItem);
                            LogMessage("Updated succesefully:", userFileSystemPath);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed:", remoteStoragePath, ex);
            }
        }
Пример #5
0
 internal async Task ClearStateAsync()
 {
     if (FsPath.Exists(userFileSystemPath))
     {
         await SetIconAsync(false);
     }
 }
Пример #6
0
        /// <summary>
        /// Moves the item in the remote storage. This method is called by the platform.
        /// To move item manually use the <see cref="MoveToAsync(string)"/> method instead.
        /// </summary>
        /// <param name="userFileSystemNewPath">Target path in user file system.</param>
        /// <param name="resultContext">Confirms move competeion. Passed by the platform only.</param>
        internal async Task MoveToAsync(string userFileSystemNewPath, IConfirmationResultContext resultContext)
        {
            // In this method you must either confirm or reject the move by calling call either
            // IConfirmationResultContext.ReturnConfirmationResult() or IConfirmationResultContext.ReturnErrorResult().

            string userFileSystemOldPath = userFileSystemPath;

            try
            {
                if (!FsPath.IsRecycleBin(userFileSystemNewPath) && // When a file is deleted, it is moved to a Recycle Bin.
                    !FsPath.AvoidSync(userFileSystemOldPath) && !FsPath.AvoidSync(userFileSystemNewPath))
                {
                    logger.LogMessage("Moving item in remote storage", userFileSystemOldPath, userFileSystemNewPath);

                    /*
                     * // Read In-Sync state before move and set after move.
                     * if (FsPath.Exists(userFileSystemOldPath))
                     * {
                     *  inSync = PlaceholderItem.GetItem(userFileSystemOldPath).GetInSync();
                     * }
                     */

                    IVirtualFileSystemItem userFileSystemItemOld = await GetItemAsync(userFileSystemOldPath);

                    await userFileSystemItemOld.MoveToAsync(userFileSystemNewPath);

                    //updateTargetOnSuccess = true;
                    logger.LogMessage("Moved succesefully in remote storage", userFileSystemOldPath, userFileSystemNewPath);

                    ETagManager eTagManager = virtualDrive.GetETagManager(userFileSystemOldPath);
                    if (FsPath.Exists(eTagManager.ETagFilePath))
                    {
                        await eTagManager.MoveToAsync(userFileSystemNewPath);

                        logger.LogMessage("Moved ETag succesefully", userFileSystemOldPath, userFileSystemNewPath);
                    }
                }

                // Confirm move.
                if (resultContext != null)
                {
                    // Calling ReturnConfirmationResult() moves file in the user file system.
                    logger.LogMessage("Confirming move in user file system", userFileSystemOldPath, userFileSystemNewPath);
                    resultContext.ReturnConfirmationResult();

                    // After ReturnConfirmationResult() call the MoveToCompletionAsync() method is called.
                    // After that, in case of a file, the file handle is closed, triggering IFile.CloseAsync() call
                    // and Windows File Manager move progress window is closed.
                    // In addition to thos, in case the target is the offline folder, the IFolder.GetChildrenAsync() method is called.
                }
            }
            catch (Exception ex)
            {
                logger.LogError("Failed to move item", userFileSystemOldPath, userFileSystemNewPath, ex);
                resultContext.ReturnErrorResult();

                //string userFileSystemExPath = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemOldPath;
                //await virtualDrive.GetUserFileSystemRawItem(userFileSystemExPath, logger).SetUploadErrorStateAsync(ex);
            }
        }
        /// <summary>
        /// Called when a file or folder is renamed in the remote storage.
        /// </summary>
        /// <remarks>In this method we rename corresponding file/folder in user file system.</remarks>
        private async void RenamedAsync(object sender, RenamedEventArgs e)
        {
            LogMessage("Renamed:", e.OldFullPath, e.FullPath);
            string remoteStorageOldPath = e.OldFullPath;
            string remoteStorageNewPath = e.FullPath;

            try
            {
                string userFileSystemOldPath = Mapping.ReverseMapPath(remoteStorageOldPath);

                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemOldPath))
                {
                    if (!FsPath.AvoidSync(remoteStorageOldPath) && !FsPath.AvoidSync(remoteStorageNewPath))
                    {
                        string userFileSystemNewPath = Mapping.ReverseMapPath(remoteStorageNewPath);
                        await new UserFileSystemItem(userFileSystemOldPath).MoveAsync(userFileSystemNewPath);
                        LogMessage("Renamed succesefully:", userFileSystemOldPath, userFileSystemNewPath);
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed:", $"From:{remoteStorageOldPath} To:{remoteStorageNewPath}", ex);
            }
        }
Пример #8
0
 /// <summary>
 /// Called when a file or folder attributes changed in the user file system.
 /// </summary>
 /// <remarks>
 /// Here we monitor pinned and unpinned attributes and hydrate/dehydrate files.
 /// </remarks>
 private async void ChangedAsync(object sender, FileSystemEventArgs e)
 {
     LogMessage($"{e.ChangeType}", e.FullPath);
     try
     {
         string userFileSystemPath = e.FullPath;
         if (FsPath.Exists(userFileSystemPath) && !FsPath.AvoidSync(userFileSystemPath))
         {
             // Hydrate / dehydrate.
             if (new UserFileSystemRawItem(userFileSystemPath).HydrationRequired())
             {
                 LogMessage("Hydrating", userFileSystemPath);
                 new PlaceholderFile(userFileSystemPath).Hydrate(0, -1);
                 LogMessage("Hydrated succesefully", userFileSystemPath);
             }
             else if (new UserFileSystemRawItem(userFileSystemPath).DehydrationRequired())
             {
                 LogMessage("Dehydrating", userFileSystemPath);
                 new PlaceholderFile(userFileSystemPath).Dehydrate(0, -1);
                 LogMessage("Dehydrated succesefully", userFileSystemPath);
             }
         }
     }
     catch (Exception ex)
     {
         LogError("Hydration/dehydration failed", e.FullPath, null, ex);
     }
 }
Пример #9
0
        protected void LogMessage(string message, string sourcePath = null, string targetPath = null)
        {
            string att  = FsPath.Exists(sourcePath) ? FsPath.GetAttString(sourcePath) : null;
            string size = FsPath.Size(sourcePath);

            log.Debug($"\n{DateTime.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {"User File System Monitor: ",-26}{message,-45} {sourcePath,-80} {size,7} {att} {targetPath}");
        }
Пример #10
0
        /// <summary>
        /// Recursively restores OriginalPath and the 'locked' icon.
        /// </summary>
        /// <param name="userFileSystemNewPath">Path in the user file system to start recursive processing.</param>
        private async Task MoveToCompletionRecursiveAsync(string userFileSystemNewPath)
        {
            if (FsPath.IsFolder(userFileSystemNewPath))
            {
                if (!new DirectoryInfo(userFileSystemNewPath).Attributes.HasFlag(System.IO.FileAttributes.Offline))
                {
                    //LogMessage("Folder offline, skipping:", userFileSystemFolderPath);

                    IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemNewPath, "*");

                    foreach (string userFileSystemChildPath in userFileSystemChildren)
                    {
                        try
                        {
                            await MoveToCompletionRecursiveAsync(userFileSystemChildPath);
                        }
                        catch (Exception ex)
                        {
                            logger.LogError("Failed to complete move", userFileSystemChildPath, null, ex);
                        }
                    }
                }
            }

            if (FsPath.Exists(userFileSystemNewPath) &&         // This check is just to avoid extra error in the log.
                !FsPath.IsRecycleBin(userFileSystemNewPath) &&  // When a file with content is deleted, it is moved to a Recycle Bin.
                !FsPath.AvoidAutoLock(userFileSystemNewPath))   // No need to update temp MS Office docs.
            {
                // Open file to prevent reads and changes between GetFileDataSizeInfo() call and SetInSync() call.
                using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemNewPath, FileMode.Open, FileShare.None))
                {
                    // If a file with content is deleted it is moved to a recycle bin and converted
                    // to a regular file, so placeholder features are not available on it, checking if a file is a placeholder.
                    if (/*updateTargetOnSuccess &&*/ PlaceholderItem.IsPlaceholder(userFileSystemNewPath))
                    {
                        PlaceholderItem placeholderNew = PlaceholderItem.GetItem(userFileSystemNewPath);

                        await virtualDrive.GetUserFileSystemRawItem(userFileSystemNewPath, logger).ClearStateAsync();

                        // Update OriginalPath if the item is not new.
                        // Required for pptx and xlsx files Save As operation.
                        if (!placeholderNew.IsNew(virtualDrive))
                        {
                            // Update OriginalPath, so the item does not appear as moved.
                            logger.LogMessage("Setting Original Path", userFileSystemNewPath);
                            placeholderNew.SetOriginalPath(userFileSystemNewPath);
                        }

                        // Restore the 'locked' icon.
                        ServerLockInfo existingLock = await virtualDrive.LockManager(userFileSystemNewPath, logger).GetLockInfoAsync();

                        await virtualDrive.GetUserFileSystemRawItem(userFileSystemNewPath, logger).SetLockInfoAsync(existingLock);
                    }
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Moves a file or folder placeholder in user file system.
        /// </summary>
        /// <param name="userFileSystemNewPath">New path in user file system.</param>
        /// <remarks>
        /// This method failes if the file or folder in user file system is modified (not in sync with the remote storage)
        /// or if the target file exists.
        /// </remarks>
        public async Task MoveToAsync(string userFileSystemNewPath)
        {
            // Cloud Filter API does not provide a function to move a placeholder file only if it is not modified.
            // The file may be modified between InSync call, Move() call and SetInSync() in this method.

            try
            {
                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemPath))
                {
                    bool inSync = PlaceholderItem.GetItem(userFileSystemPath).GetInSync();
                    if (inSync)
                    {
                        string eTag = await ETag.GetETagAsync(userFileSystemPath);

                        ETag.DeleteETag(userFileSystemPath);
                        try
                        {
                            Directory.Move(userFileSystemPath, userFileSystemNewPath);
                        }
                        catch
                        {
                            await ETag.SetETagAsync(userFileSystemPath, eTag);

                            throw;
                        }

                        await ETag.SetETagAsync(userFileSystemNewPath, eTag);

                        // The file is marked as not in sync after move/rename. Marking it as in-sync.
                        PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemNewPath);
                        placeholderItem.SetInSync(true);
                        placeholderItem.SetOriginalPath(userFileSystemNewPath);

                        await new UserFileSystemRawItem(userFileSystemNewPath).ClearStateAsync();
                    }

                    if (!inSync)
                    {
                        throw new ConflictException(Modified.Client, "The item is not in-sync with the cloud.");
                    }
                }
            }
            catch (Exception ex)
            {
                string path = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemPath;
                await new UserFileSystemRawItem(userFileSystemPath).SetDownloadErrorStateAsync(ex);

                // Rethrow the exception preserving stack trace of the original exception.
                System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
            }
        }
Пример #12
0
 internal async Task SetUploadErrorStateAsync(Exception ex)
 {
     if (FsPath.Exists(userFileSystemPath))
     {
         if (ex is ConflictException)
         {
             await SetConflictIconAsync(true);
         }
         else
         {
             await SetUploadPendingIconAsync(true);
         }
     }
 }
Пример #13
0
        /// <inheritdoc/>
        public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext)
        {
            // This method has a 60 sec timeout.
            // To process longer requests and reset the timout timer call one of the following:
            // - resultContext.ReturnChildren() method.
            // - resultContext.ReportProgress() method.

            Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", UserFileSystemPath);

            IEnumerable <FileSystemInfo> remoteStorageChildren = new DirectoryInfo(RemoteStoragePath).EnumerateFileSystemInfos(pattern);

            List <IFileSystemItemMetadata> userFileSystemChildren = new List <IFileSystemItemMetadata>();

            foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren)
            {
                IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);

                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name);

                // Filtering existing files/folders. This is only required to avoid extra errors in the log.
                if (!FsPath.Exists(userFileSystemItemPath))
                {
                    Logger.LogMessage("Creating", userFileSystemItemPath);
                    userFileSystemChildren.Add(itemInfo);
                }

                ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemItemPath);

                // Mark this item as not new, which is required for correct MS Office saving opertions.
                customDataManager.IsNew = false;

                // Save ETag on the client side, to be sent to the remote storage as part of the update.
                await customDataManager.ETagManager.SetETagAsync("1234567890");
            }

            // To signal that the children enumeration is completed
            // always call ReturnChildren(), even if the folder is empty.
            resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count());

            // Show some custom column in file manager for demo purposes.
            foreach (IFileSystemItemMetadata itemInfo in userFileSystemChildren)
            {
                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name);

                FileSystemItemPropertyData eTagColumn = new FileSystemItemPropertyData((int)CustomColumnIds.ETag, "1234567890");
                await Engine.CustomDataManager(userFileSystemItemPath).SetCustomColumnsAsync(new [] { eTagColumn });
            }
        }
Пример #14
0
        /// <summary>
        /// Moves a file or folder placeholder in user file system.
        /// </summary>
        /// <param name="userFileSystemNewPath">New path in user file system.</param>
        /// <remarks>
        /// This method failes if the file or folder in user file system is modified (not in sync with the remote storage)
        /// or if the target file exists.
        /// </remarks>
        /// <returns>True if the item was moved. False - otherwise.</returns>
        public async Task <bool> MoveToAsync(string userFileSystemNewPath)
        {
            // Cloud Filter API does not provide a function to move a placeholder file only if it is not modified.
            // The file may be modified between InSync call, Move() call and SetInSync() in this method.
            bool itemMoved = false;

            try
            {
                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemPath))
                {
                    bool inSync = PlaceholderItem.GetItem(userFileSystemPath).GetInSync();
                    if (inSync)
                    {
                        logger.LogMessage("Moving ETag", userFileSystemPath, userFileSystemNewPath);
                        await eTagManager.MoveToAsync(userFileSystemNewPath);

                        logger.LogMessage("Moving item", userFileSystemPath, userFileSystemNewPath);
                        await virtualDrive.Engine.ServerNotifications(userFileSystemPath).MoveToAsync(userFileSystemNewPath);

                        // The file is marked as not in sync after move/rename. Marking it as in-sync.
                        PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemNewPath);
                        placeholderItem.SetInSync(true);
                        placeholderItem.SetOriginalPath(userFileSystemNewPath);

                        await virtualDrive.GetUserFileSystemRawItem(userFileSystemNewPath, logger).ClearStateAsync();

                        itemMoved = true;
                    }

                    if (!inSync)
                    {
                        throw new ConflictException(Modified.Client, "The item is not in-sync with the cloud.");
                    }
                }
            }
            catch (Exception ex)
            {
                string userFileSystemExPath = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemPath;
                await virtualDrive.GetUserFileSystemRawItem(userFileSystemExPath, logger).SetDownloadErrorStateAsync(ex);

                // Rethrow the exception preserving stack trace of the original exception.
                System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
            }
            return(itemMoved);
        }
Пример #15
0
        //$>

        /// <summary>
        /// Deletes a file or folder placeholder in user file system.
        /// </summary>
        /// <remarks>
        /// This method throws <see cref="ConflictException"/> if the file or folder or any file or folder
        /// in the folder hierarchy being deleted in user file system is modified (not in sync with the remote storage).
        /// </remarks>
        /// <returns>True if the file was deleted. False - otherwise.</returns>
        public async Task <bool> DeleteAsync()
        {
            // Cloud Filter API does not provide a function to delete a placeholder file only if it is not modified.
            // Here we check that the file is not modified in user file system, using GetInSync() call.
            // To avoid the file modification between GetInSync() call and Delete() call we
            // open it without FileShare.Write flag.
            try
            {
                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemPath))
                {
                    using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.Open(userFileSystemPath, (FileAccess)0, FileMode.Open, FileShare.Read | FileShare.Delete))
                    {
                        if (PlaceholderItem.GetInSync(userFileSystemWinItem.SafeHandle))
                        {
                            if (FsPath.IsFile(userFileSystemPath))
                            {
                                File.Delete(userFileSystemPath);
                            }
                            else
                            {
                                Directory.Delete(userFileSystemPath, true);
                            }

                            // Delete ETag
                            ETag.DeleteETag(userFileSystemPath);

                            return(true);
                        }
                        else
                        {
                            throw new ConflictException(Modified.Client, "The item is not in-sync with the cloud.");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                await SetDownloadErrorStateAsync(ex);

                // Rethrow the exception preserving stack trace of the original exception.
                System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
            }

            return(false);
        }
Пример #16
0
 private async Task SetDownloadErrorStateAsync(Exception ex)
 {
     if (FsPath.Exists(userFileSystemPath))
     {
         if (ex is ConflictException)
         {
             await SetConflictIconAsync(true);
         }
         if (ex is ExistsException)
         {
             await SetConflictIconAsync(true);
         }
         else
         {
             // Could be BlockedException or other exception.
             await SetDownloadPendingIconAsync(true);
         }
     }
 }
Пример #17
0
        /// <summary>
        /// Updates information about the file or folder placeholder in the user file system.
        /// This method automatically hydrates and dehydrate files.
        /// </summary>
        /// <remarks>This method failes if the file or folder in user file system is modified (not in-sync with the remote storage).</remarks>
        /// <param name="itemInfo">New file or folder info.</param>
        /// <returns>True if the file was updated. False - otherwise.</returns>
        public async Task <bool> UpdateAsync(IFileSystemItemMetadata itemInfo)
        {
            FileSystemItemMetadataExt itemInfoExt = itemInfo as FileSystemItemMetadataExt ?? throw new NotImplementedException($"{nameof(FileSystemItemMetadataExt)}");

            try
            {
                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemPath))
                {
                    PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemPath);

                    // To be able to update the item we need to remove the read-only attribute.
                    if ((FsPath.GetFileSystemItem(userFileSystemPath).Attributes | System.IO.FileAttributes.ReadOnly) != 0)
                    {
                        FsPath.GetFileSystemItem(userFileSystemPath).Attributes &= ~System.IO.FileAttributes.ReadOnly;
                    }

                    // Dehydrate/hydrate the file, update file size, custom data, creation date, modification date, attributes.
                    await virtualDrive.Engine.ServerNotifications(userFileSystemPath).UpdateAsync(itemInfoExt);

                    // Set ETag.
                    await eTagManager.SetETagAsync(itemInfoExt.ETag);

                    // Clear icon.
                    //await ClearStateAsync();

                    // Set the read-only attribute and all custom columns data.
                    await SetLockedByAnotherUserAsync(itemInfoExt.LockedByAnotherUser);
                    await SetCustomColumnsDataAsync(itemInfoExt.CustomProperties);

                    return(true);
                }
            }
            catch (Exception ex)
            {
                await SetDownloadErrorStateAsync(ex);

                // Rethrow the exception preserving stack trace of the original exception.
                System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
            }
            return(false);
        }
Пример #18
0
 /// <summary>
 /// Called when a file or folder attributes changed in the user file system.
 /// </summary>
 /// <remarks>
 /// Here we monitor pinned and unpinned attributes and hydrate/dehydrate files.
 /// </remarks>
 private async void ChangedAsync(object sender, FileSystemEventArgs e)
 {
     LogMessage($"{e.ChangeType}", e.FullPath);
     try
     {
         string userFileSystemPath = e.FullPath;
         if (FsPath.Exists(userFileSystemPath))
         {
             bool?hydrated = await new UserFileSystemItem(userFileSystemPath).UpdateHydrationAsync();
             if (hydrated != null)
             {
                 string hydrationDescrition = (bool)hydrated ? "Hydrated" : "Dehydrated";
                 LogMessage($"{hydrationDescrition} succesefully:", userFileSystemPath);
             }
         }
     }
     catch (Exception ex)
     {
         LogError("Hydration/dehydration failed:", e.FullPath, ex);
     }
 }
Пример #19
0
        //$>

        //$<PlaceholderItem.SetItemInfo
        /// <summary>
        /// Updates information about the file or folder placeholder in the user file system.
        /// This method automatically hydrates and dehydrate files.
        /// </summary>
        /// <remarks>This method failes if the file or folder in user file system is modified (not in-sync with the remote storage).</remarks>
        /// <param name="itemInfo">New file or folder info.</param>
        /// <returns>True if the file was updated. False - otherwise.</returns>
        public async Task <bool> UpdateAsync(FileSystemItemBasicInfo itemInfo)
        {
            try
            {
                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemPath))
                {
                    PlaceholderItem placeholderItem = PlaceholderItem.GetItem(userFileSystemPath);

                    // To be able to update the item we need to remove the read-only attribute.
                    if ((FsPath.GetFileSystemItem(userFileSystemPath).Attributes | System.IO.FileAttributes.ReadOnly) != 0)
                    {
                        FsPath.GetFileSystemItem(userFileSystemPath).Attributes &= ~System.IO.FileAttributes.ReadOnly;
                    }

                    // Dehydrate/hydrate the file, update file size, custom data, creation date, modification date, attributes.
                    placeholderItem.SetItemInfo(itemInfo);

                    // Set ETag.
                    await ETag.SetETagAsync(userFileSystemPath, itemInfo.ETag);

                    // Clear icon.
                    //await ClearStateAsync();

                    // Set the "locked by another user" icon and all custom columns data.
                    await SetLockedByAnotherUserAsync(itemInfo.LockedByAnotherUser);
                    await SetCustomColumnsDataAsync(itemInfo.CustomProperties);

                    return(true);
                }
            }
            catch (Exception ex)
            {
                await SetDownloadErrorStateAsync(ex);

                // Rethrow the exception preserving stack trace of the original exception.
                System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
            }
            return(false);
        }
Пример #20
0
        /// <summary>
        /// Completes move. This method is called by the platform.
        /// To move item manually use the <see cref="MoveToAsync(string)"/> method instead.
        /// Sets In-Sync state based on file content changes,
        /// Updates OriginalPath so the file does not appear as moved.
        /// </summary>
        /// <returns></returns>
        internal async Task MoveToCompletionAsync()
        {
            string userFileSystemNewPath = userFileSystemPath;

            if (FsPath.Exists(userFileSystemNewPath) &&         // This check is just to avoid extra error in the log.
                !FsPath.IsRecycleBin(userFileSystemNewPath) &&  // When a file with content is deleted, it is moved to a Recycle Bin.
                !FsPath.AvoidAutoLock(userFileSystemNewPath))   // No need to update temp MS Office docs.
            {
                // Open file to prevent reads and changes between GetFileDataSizeInfo() call and SetInSync() call.
                //using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemNewPath, FileMode.Open, FileShare.None))
                {
                    // If a file with content is deleted it is moved to a recycle bin and converted
                    // to a regular file, so placeholder features are not available on it, checking if a file is a placeholder.
                    if (/*updateTargetOnSuccess &&*/ PlaceholderItem.IsPlaceholder(userFileSystemNewPath))
                    {
                        PlaceholderItem placeholderNew = PlaceholderItem.GetItem(userFileSystemNewPath);
                        // Restore In-sync state.

                        /*
                         * if (inSync != null)
                         * {
                         *  placeholderNew.SetInSync(inSync.Value);
                         * }
                         * else
                         */
                        if (((placeholderNew is PlaceholderFile) && ((PlaceholderFile)placeholderNew).GetFileDataSizeInfo().ModifiedDataSize == 0) ||
                            (placeholderNew is PlaceholderFolder))
                        {
                            logger.LogMessage("Setting In-Sync state", userFileSystemNewPath);
                            placeholderNew.SetInSync(true);
                        }
                    }
                }

                // Recursively restore OriginalPath and the 'locked' icon.
                await MoveToCompletionRecursiveAsync(userFileSystemNewPath);
            }
        }
        /// <summary>
        /// Called when a file or folder is deleted in the remote storage.
        /// </summary>
        /// <remarks>In this method we delete corresponding file/folder in user file system.</remarks>
        private async void DeletedAsync(object sender, FileSystemEventArgs e)
        {
            LogMessage(e.ChangeType.ToString(), e.FullPath);
            string remoteStoragePath = e.FullPath;

            try
            {
                string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath);

                // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                if (FsPath.Exists(userFileSystemPath))
                {
                    if (!FsPath.AvoidSync(remoteStoragePath))
                    {
                        await new UserFileSystemItem(userFileSystemPath).DeleteAsync();
                        LogMessage("Deleted succesefully:", userFileSystemPath);
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed:", remoteStoragePath, ex);
            }
        }
        /// <summary>
        /// Called when a file content changed or file/folder attributes changed in the remote storage.
        /// </summary>
        /// <remarks>
        /// In this method we update corresponding file/folder information in user file system.
        /// We also dehydrate the file if it is not blocked.
        /// </remarks>
        private async void ChangedAsync(object sender, FileSystemEventArgs e)
        {
            LogMessage(e.ChangeType.ToString(), e.FullPath);
            string remoteStoragePath = e.FullPath;

            try
            {
                // We do not want to sync MS Office temp files, etc. from remote storage.
                if (!FsPath.AvoidSync(remoteStoragePath))
                {
                    string userFileSystemPath = Mapping.ReverseMapPath(remoteStoragePath);

                    // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                    if (FsPath.Exists(userFileSystemPath))
                    {
                        FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath);

                        // This check is only required because we can not prevent circular calls because of the simplicity of this example.
                        // In your real-life application you will not sent updates from server back to client that issued the update.
                        FileSystemItemBasicInfo itemInfo = Mapping.GetUserFileSysteItemBasicInfo(remoteStorageItem);
                        if (!await ETag.ETagEqualsAsync(userFileSystemPath, itemInfo))
                        {
                            await new UserFileSystemRawItem(userFileSystemPath).UpdateAsync(itemInfo);
                            LogMessage("Updated succesefully", userFileSystemPath);
                        }

                        // Update "locked by another user" icon.
                        await new UserFileSystemRawItem(userFileSystemPath).SetLockedByAnotherUserAsync(itemInfo.LockedByAnotherUser);
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex);
            }
        }
        internal async Task MoveToAsync(string userFileSystemNewPath, IConfirmationResultContext resultContext = null)
        {
            string userFileSystemOldPath = userFileSystemPath;

            try
            {
                bool?  inSync = null;
                bool   updateTargetOnSuccess = false;
                string eTag = null;
                try
                {
                    if (!FsPath.IsRecycleBin(userFileSystemNewPath) && // When a file is deleted, it is moved to a Recycle Bin.
                        !FsPath.AvoidSync(userFileSystemOldPath) && !FsPath.AvoidSync(userFileSystemNewPath))
                    {
                        logger.LogMessage("Moving item in remote storage", userFileSystemOldPath, userFileSystemNewPath);

                        // Read In-Sync state before move and set after move
                        if (FsPath.Exists(userFileSystemOldPath))
                        {
                            inSync = PlaceholderItem.GetItem(userFileSystemOldPath).GetInSync();
                        }

                        eTag = await ETag.GetETagAsync(userFileSystemOldPath);

                        ETag.DeleteETag(userFileSystemOldPath);

                        IUserFileSystemItem userFileSystemItemOld = await virtualDrive.GetItemAsync <IUserFileSystemItem>(userFileSystemOldPath);

                        await userFileSystemItemOld.MoveToAsync(userFileSystemNewPath);

                        updateTargetOnSuccess = true;
                        logger.LogMessage("Moved succesefully in remote storage", userFileSystemOldPath, userFileSystemNewPath);
                    }
                }
                finally
                {
                    if (resultContext != null)
                    {
                        resultContext.ReturnConfirmationResult();
                    }

                    // This check is just to avoid extra error in the log.
                    if (FsPath.Exists(userFileSystemNewPath))
                    {
                        // Open file to preven reads and changes between GetFileDataSizeInfo() call and SetInSync() call.
                        using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemNewPath, FileMode.Open, FileShare.None))
                        {
                            if ((eTag != null) && PlaceholderItem.IsPlaceholder(userFileSystemNewPath))
                            {
                                await ETag.SetETagAsync(userFileSystemNewPath, eTag);
                            }

                            // If a file with content is deleted it is moved to a recycle bin and converted
                            // to a regular file, so placeholder features are not available on it, checking if a file is a placeholder.
                            if (updateTargetOnSuccess && PlaceholderItem.IsPlaceholder(userFileSystemNewPath))
                            {
                                PlaceholderItem placeholderNew = PlaceholderItem.GetItem(userFileSystemNewPath);

                                // Update OriginalPath, so the item does not appear as moved.
                                placeholderNew.SetOriginalPath(userFileSystemNewPath);

                                if (inSync != null)
                                {
                                    placeholderNew.SetInSync(inSync.Value);
                                }
                                else if ((placeholderNew is PlaceholderFile) && ((PlaceholderFile)placeholderNew).GetFileDataSizeInfo().ModifiedDataSize == 0)
                                {
                                    placeholderNew.SetInSync(true);
                                }
                                await new UserFileSystemRawItem(userFileSystemNewPath).ClearStateAsync();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string userFileSystemPath = FsPath.Exists(userFileSystemNewPath) ? userFileSystemNewPath : userFileSystemOldPath;
                await new UserFileSystemRawItem(userFileSystemPath).SetUploadErrorStateAsync(ex);

                // Rethrow the exception preserving stack trace of the original exception.
                System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();
            }
        }
        /// <summary>
        /// Recursively synchronizes all files and folders from server to client.
        /// Synchronizes only folders already loaded into the user file system.
        /// </summary>
        /// <param name="userFileSystemFolderPath">Folder path in user file system.</param>
        internal async Task SyncronizeFolderAsync(string userFileSystemFolderPath)
        {
            // In case of on-demand loading the user file system contains only a subset of the server files and folders.
            // Here we sync folder only if its content already loaded into user file system (folder is not offline).
            // The folder content is loaded inside IFolder.GetChildrenAsync() method.
            if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline))
            {
                // LogMessage("Folder offline, skipping:", userFileSystemFolderPath);
                return;
            }

            IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*");
            //LogMessage("Synchronizing:", userFileSystemFolderPath);

            IEnumerable <FileSystemItemBasicInfo> remoteStorageChildrenItems = await new UserFolder(userFileSystemFolderPath).EnumerateChildrenAsync("*");

            // Create new files/folders in the user file system.
            string remoteStorageFolderPath = Mapping.MapPath(userFileSystemFolderPath);

            foreach (FileSystemItemBasicInfo remoteStorageItem in remoteStorageChildrenItems)
            {
                string userFileSystemPath = Path.Combine(userFileSystemFolderPath, remoteStorageItem.Name);
                try
                {
                    // We do not want to sync MS Office temp files, etc. from remote storage.
                    // We also do not want to create MS Office files during transactional save in user file system.
                    string remoteStorageItemFullPath = Path.Combine(remoteStorageFolderPath, remoteStorageItem.Name);
                    if (!FsPath.AvoidSync(remoteStorageItemFullPath) && !FsPath.AvoidSync(userFileSystemPath))
                    {
                        if (!FsPath.Exists(userFileSystemPath))
                        {
                            LogMessage($"Creating", userFileSystemPath);
                            await UserFileSystemRawItem.CreateAsync(userFileSystemFolderPath, new[] { remoteStorageItem });

                            LogMessage($"Created succesefully", userFileSystemPath);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogError("Creation failed", userFileSystemPath, null, ex);
                }
            }

            // Update files/folders in user file system and sync subfolders.
            userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*");
            foreach (string userFileSystemPath in userFileSystemChildren)
            {
                try
                {
                    string itemName          = Path.GetFileName(userFileSystemPath);
                    string remoteStoragePath = Mapping.MapPath(userFileSystemPath);
                    FileSystemItemBasicInfo remoteStorageItem = remoteStorageChildrenItems.FirstOrDefault(x => x.Name.Equals(itemName, StringComparison.InvariantCultureIgnoreCase));


                    if (!FsPath.AvoidSync(userFileSystemPath) && !FsPath.AvoidSync(remoteStoragePath))
                    {
                        if (remoteStorageItem == null)
                        {
                            if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync())
                            {
                                // Delete the file/folder in user file system.
                                LogMessage("Deleting item", userFileSystemPath);
                                await new UserFileSystemRawItem(userFileSystemPath).DeleteAsync();
                                LogMessage("Deleted succesefully", userFileSystemPath);
                            }
                        }
                        else
                        {
                            if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync() &&
                                !await ETag.ETagEqualsAsync(userFileSystemPath, remoteStorageItem))
                            {
                                // User file system <- remote storage update.
                                LogMessage("Item modified", remoteStoragePath);
                                await new UserFileSystemRawItem(userFileSystemPath).UpdateAsync(remoteStorageItem);
                                LogMessage("Updated succesefully", userFileSystemPath);
                            }

                            // Update "locked by another user" icon.
                            if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync())

                            {
                                await new UserFileSystemRawItem(userFileSystemPath).SetLockedByAnotherUserAsync(remoteStorageItem.LockedByAnotherUser);
                            }

                            // Hydrate / dehydrate the file.
                            if (new UserFileSystemRawItem(userFileSystemPath).HydrationRequired())
                            {
                                LogMessage("Hydrating", userFileSystemPath);
                                new PlaceholderFile(userFileSystemPath).Hydrate(0, -1);
                                LogMessage("Hydrated succesefully", userFileSystemPath);
                            }
                            else if (new UserFileSystemRawItem(userFileSystemPath).DehydrationRequired())
                            {
                                LogMessage("Dehydrating", userFileSystemPath);
                                new PlaceholderFile(userFileSystemPath).Dehydrate(0, -1);
                                LogMessage("Dehydrated succesefully", userFileSystemPath);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogError("Update failed", userFileSystemPath, null, ex);
                }

                // Synchronize subfolders.
                try
                {
                    if (Directory.Exists(userFileSystemPath))
                    {
                        await SyncronizeFolderAsync(userFileSystemPath);
                    }
                }
                catch (Exception ex)
                {
                    LogError("Folder sync failed:", userFileSystemPath, null, ex);
                }
            }
        }
Пример #25
0
        protected void LogError(string message, string sourcePath, Exception ex)
        {
            string att = FsPath.Exists(sourcePath) ? FsPath.GetAttString(sourcePath) : null;

            log.Error($"\n{DateTime.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {"User File System Monitor: ",-26}{message,-45} {sourcePath,-80} {att} ", ex);
        }
Пример #26
0
        /// <inheritdoc/>
        public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext)
        {
            // This method has a 60 sec timeout.
            // To process longer requests and reset the timout timer call one of the following:
            // - resultContext.ReturnChildren() method.
            // - resultContext.ReportProgress() method.

            Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", UserFileSystemPath);

            IHierarchyItemAsync[] remoteStorageChildren = null;
            // Retry the request in case the log-in dialog is shown.
            try
            {
                remoteStorageChildren = await Program.DavClient.GetChildrenAsync(new Uri(RemoteStoragePath), false);
            }
            catch (ITHit.WebDAV.Client.Exceptions.Redirect302Exception)
            {
                remoteStorageChildren = await Program.DavClient.GetChildrenAsync(new Uri(RemoteStoragePath), false);
            }

            List <FileSystemItemMetadataExt> userFileSystemChildren = new List <FileSystemItemMetadataExt>();


            foreach (IHierarchyItemAsync remoteStorageItem in remoteStorageChildren)
            {
                FileSystemItemMetadataExt itemInfo = Mapping.GetUserFileSystemItemMetadata(remoteStorageItem);

                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, itemInfo.Name);

                // Filtering existing files/folders. This is only required to avoid extra errors in the log.
                if (!FsPath.Exists(userFileSystemItemPath))
                {
                    Logger.LogMessage("Creating", userFileSystemItemPath);
                    userFileSystemChildren.Add(itemInfo);
                }

                ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemItemPath);

                // Mark this item as not new, which is required for correct MS Office saving opertions.
                customDataManager.IsNew = false;
            }

            // To signal that the children enumeration is completed
            // always call ReturnChildren(), even if the folder is empty.
            resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count());

            // Save ETags, the read-only attribute and all custom columns data.
            foreach (FileSystemItemMetadataExt child in userFileSystemChildren)
            {
                string userFileSystemItemPath         = Path.Combine(UserFileSystemPath, child.Name);
                ExternalDataManager customDataManager = Engine.CustomDataManager(userFileSystemItemPath);

                // Save ETag on the client side, to be sent to the remote storage as part of the update.
                // Setting ETag also marks an item as not new.

                // ETags must correspond with a server file/folder, NOT with a client placeholder.
                // It should NOT be moved/deleted/updated when a placeholder in the user file system is moved/deleted/updated.
                // It should be moved/deleted when a file/folder in the remote storage is moved/deleted.
                await customDataManager.ETagManager.SetETagAsync(child.ETag);

                // Set the read-only attribute and all custom columns data.
                await customDataManager.SetLockedByAnotherUserAsync(child.LockedByAnotherUser);

                await customDataManager.SetCustomColumnsAsync(child.CustomProperties);
            }
        }
        /// <summary>
        /// Recursively synchronizes all files and folders from server to client.
        /// Synchronizes only folders already loaded into the user file system.
        /// </summary>
        /// <param name="userFileSystemFolderPath">Folder path in user file system.</param>
        internal async Task SyncronizeFolderAsync(string userFileSystemFolderPath)
        {
            // In case of on-demand loading the user file system contains only a subset of the server files and folders.
            // Here we sync folder only if its content already loaded into user file system (folder is not offline).
            // The folder content is loaded inside IFolder.GetChildrenAsync() method.
            if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline))
            {
                // LogMessage("Folder offline, skipping:", userFileSystemFolderPath);
                return;
            }

            IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*");
            //LogMessage("Synchronizing:", userFileSystemFolderPath);

            IVirtualFolder userFolder = await virtualDrive.GetItemAsync <IVirtualFolder>(userFileSystemFolderPath, this);

            IEnumerable <FileSystemItemMetadataExt> remoteStorageChildrenItems = await userFolder.EnumerateChildrenAsync("*");

            // Create new files/folders in the user file system.
            foreach (FileSystemItemMetadataExt remoteStorageItem in remoteStorageChildrenItems)
            {
                string userFileSystemPath = Path.Combine(userFileSystemFolderPath, remoteStorageItem.Name);
                try
                {
                    // We do not want to sync MS Office temp files, etc. from remote storage.
                    // We also do not want to create MS Office files during transactional save in user file system.
                    if (!FsPath.AvoidSync(remoteStorageItem.Name) && !FsPath.AvoidSync(userFileSystemPath))
                    {
                        if (!FsPath.Exists(userFileSystemPath))
                        {
                            LogMessage($"Creating", userFileSystemPath);

                            await virtualDrive.GetUserFileSystemRawItem(userFileSystemFolderPath, this).CreateAsync(new[] { remoteStorageItem });

                            LogMessage($"Created succesefully", userFileSystemPath);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogError("Creation failed", userFileSystemPath, null, ex);
                }
            }

            // Update files/folders in user file system and sync subfolders.
            userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*");
            foreach (string userFileSystemPath in userFileSystemChildren)
            {
                try
                {
                    string itemName = Path.GetFileName(userFileSystemPath);
                    FileSystemItemMetadataExt remoteStorageItem = remoteStorageChildrenItems.FirstOrDefault(x => x.Name.Equals(itemName, StringComparison.InvariantCultureIgnoreCase));


                    if (!FsPath.AvoidSync(userFileSystemPath))
                    {
                        UserFileSystemRawItem userFileSystemRawItem = virtualDrive.GetUserFileSystemRawItem(userFileSystemPath, this);
                        if (remoteStorageItem == null)
                        {
                            if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync()
                                //&& !PlaceholderItem.GetItem(userFileSystemPath).IsNew(virtualDrive) // Extra check to protect from deletion files that were deleted becuse of incorrect folder merging operation.
                                )
                            {
                                // Delete the file/folder in user file system.
                                LogMessage("Deleting item", userFileSystemPath);
                                await userFileSystemRawItem.DeleteAsync();

                                LogMessage("Deleted succesefully", userFileSystemPath);
                            }
                        }
                        else
                        {
                            if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync() &&
                                !await virtualDrive.GetETagManager(userFileSystemPath).ETagEqualsAsync(remoteStorageItem))
                            {
                                // User file system <- remote storage update.
                                LogMessage("Remote item modified", userFileSystemPath);
                                await userFileSystemRawItem.UpdateAsync(remoteStorageItem);

                                LogMessage("Updated succesefully", userFileSystemPath);
                            }

                            // Set the read-only attribute and all custom columns data.
                            if (PlaceholderItem.GetItem(userFileSystemPath).GetInSync())
                            {
                                await userFileSystemRawItem.SetLockedByAnotherUserAsync(remoteStorageItem.LockedByAnotherUser);

                                await userFileSystemRawItem.SetCustomColumnsDataAsync(remoteStorageItem.CustomProperties);
                            }

                            // Hydrate / dehydrate the file.
                            if (userFileSystemRawItem.HydrationRequired())
                            {
                                LogMessage("Hydrating", userFileSystemPath);
                                try
                                {
                                    new PlaceholderFile(userFileSystemPath).Hydrate(0, -1);
                                    LogMessage("Hydrated succesefully", userFileSystemPath);
                                }
                                catch (FileLoadException)
                                {
                                    // Typically this happens if another thread already hydrating the file.
                                    LogMessage("Failed to hydrate. The file is blocked.", userFileSystemPath);
                                }
                            }
                            else if (userFileSystemRawItem.DehydrationRequired())
                            {
                                LogMessage("Dehydrating", userFileSystemPath);
                                new PlaceholderFile(userFileSystemPath).Dehydrate(0, -1);
                                LogMessage("Dehydrated succesefully", userFileSystemPath);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogError("Update failed", userFileSystemPath, null, ex);
                }

                // Synchronize subfolders.
                try
                {
                    if (Directory.Exists(userFileSystemPath))
                    {
                        await SyncronizeFolderAsync(userFileSystemPath);
                    }
                }
                catch (Exception ex)
                {
                    LogError("Folder sync failed:", userFileSystemPath, null, ex);
                }
            }
        }
Пример #28
0
        /// <summary>
        /// Recursively synchronizes all files and folders with the server.
        /// Synchronizes only folders already loaded into the user file system.
        /// </summary>
        /// <param name="userFileSystemFolderPath">Folder path in user file system.</param>
        private async Task SyncronizeFolderAsync(string userFileSystemFolderPath)
        {
            // In case of on-demand loading the user file system contains only a subset of the server files and folders.
            // Here we sync folder only if its content already loaded into user file system (folder is not offline).
            // The folder content is loaded inside IFolder.GetChildrenAsync() method.
            if (new DirectoryInfo(userFileSystemFolderPath).Attributes.HasFlag(System.IO.FileAttributes.Offline))
            {
                LogMessage("Folder offline, skipping:", userFileSystemFolderPath);
                return;
            }

            IEnumerable <string> userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*");

            LogMessage("Synchronizing:", userFileSystemFolderPath);

            string remoteStorageFolderPath = Mapping.MapPath(userFileSystemFolderPath);
            IEnumerable <FileSystemInfo> remoteStorageChildrenItems = new DirectoryInfo(remoteStorageFolderPath).EnumerateFileSystemInfos("*");

            // Delete files/folders in user file system.
            foreach (string userFileSystemPath in userFileSystemChildren)
            {
                try
                {
                    string         remoteStoragePath = Mapping.MapPath(userFileSystemPath);
                    FileSystemInfo remoteStorageItem = remoteStorageChildrenItems.FirstOrDefault(x => x.FullName.Equals(remoteStoragePath, StringComparison.InvariantCultureIgnoreCase));
                    if (remoteStorageItem == null)
                    {
                        LogMessage("Deleting item:", userFileSystemPath);
                        await new UserFileSystemItem(userFileSystemPath).DeleteAsync();
                        LogMessage("Deleted succesefully:", userFileSystemPath);
                    }
                }
                catch (Exception ex)
                {
                    LogError("Delete failed:", userFileSystemPath, ex);
                }
            }

            // Create new files/folders in user file system.
            foreach (FileSystemInfo remoteStorageItem in remoteStorageChildrenItems)
            {
                try
                {
                    string userFileSystemPath = Mapping.ReverseMapPath(remoteStorageItem.FullName);
                    if (!FsPath.Exists(userFileSystemPath))
                    {
                        LogMessage("Creating new item:", userFileSystemPath);
                        await UserFileSystemItem.CreateAsync(userFileSystemFolderPath, remoteStorageItem);

                        LogMessage("Created succesefully:", userFileSystemPath);
                    }
                }
                catch (Exception ex)
                {
                    LogError("Creation failed:", remoteStorageItem.FullName, ex);
                }
            }

            // Update files/folders in user file system and sync subfolders.
            userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*");
            foreach (string userFileSystemPath in userFileSystemChildren)
            {
                try
                {
                    string         remoteStoragePath = Mapping.MapPath(userFileSystemPath);
                    FileSystemInfo remoteStorageItem = remoteStorageChildrenItems.FirstOrDefault(x => x.FullName.Equals(remoteStoragePath, StringComparison.InvariantCultureIgnoreCase));

                    // The item was deleted or moved/renamed on the server, but it may still exists in the user file system.
                    // This is just to avoid extra exceptions in the log.
                    if (remoteStorageItem == null)
                    {
                        continue;
                    }

                    // Update existing files/folders info.
                    if (!await new UserFileSystemItem(userFileSystemPath).EqualsAsync(remoteStorageItem))
                    {
                        LogMessage("Item modified:", remoteStoragePath);
                        await new UserFileSystemItem(userFileSystemPath).UpdateAsync(remoteStorageItem);
                        LogMessage("Updated succesefully:", userFileSystemPath);
                    }

                    // Hydrate / dehydrate the file.
                    else
                    {
                        bool?hydrated = await new UserFileSystemItem(userFileSystemPath).UpdateHydrationAsync();
                        if (hydrated != null)
                        {
                            string hydrationDescrition = (bool)hydrated ? "Hydrated" : "Dehydrated";
                            LogMessage($"{hydrationDescrition} succesefully:", userFileSystemPath);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogError("Update failed:", userFileSystemPath, ex);
                }

                // Synchronize subfolders.
                try
                {
                    if (FsPath.IsFolder(userFileSystemPath))
                    {
                        await SyncronizeFolderAsync(userFileSystemPath);
                    }
                }
                catch (Exception ex)
                {
                    LogError("Folder sync failed:", userFileSystemPath, ex);
                }
            }
        }