private async Task CreateOrUpdateFolderAsync(string userFileSystemPath, bool create) { try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. DirectoryInfo userFileSystemFolder = new DirectoryInfo(userFileSystemPath); DirectoryInfo remoteStorageFolder = new DirectoryInfo(remoteStoragePath); remoteStorageFolder.Create(); // Update ETag/LastWriteTime in user file system, so the synchronyzation or remote storage monitor would not start the new update. byte[] customData = BitConverter.GetBytes(userFileSystemFolder.LastWriteTime.ToBinary()); PlaceholderItem.GetItem(userFileSystemPath).SetCustomData(customData); // Remove Pinned, Unpinned and Offline flags. remoteStorageFolder.Attributes = userFileSystemFolder.Attributes & (FileAttributes) ~FileAttributesExt.Pinned & (FileAttributes) ~FileAttributesExt.Unpinned & ~FileAttributes.Offline; remoteStorageFolder.CreationTimeUtc = userFileSystemFolder.CreationTimeUtc; remoteStorageFolder.LastWriteTimeUtc = userFileSystemFolder.LastWriteTimeUtc; remoteStorageFolder.LastAccessTimeUtc = userFileSystemFolder.LastAccessTimeUtc; remoteStorageFolder.LastWriteTimeUtc = userFileSystemFolder.LastWriteTimeUtc; PlaceholderItem.GetItem(userFileSystemPath).SetInSync(true); } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
///<inheritdoc> public async Task MoveToAsync(string userFileSystemNewPath, IOperationContext operationContext, IConfirmationResultContext resultContext) { // Here we will simply move the file in remote storage and confirm the operation. // In your implementation you may implement a more complex scenario with offline operations support. LogMessage("IFileSystemItem.MoveToAsync()", this.FullPath, userFileSystemNewPath); string userFileSystemOldPath = this.FullPath; try { bool?inSync = null; try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. // When a file is deleted, it is moved to a Recycle Bin, that is why we check for recycle bin here. if (FsPath.Exists(userFileSystemOldPath) && !FsPath.IsRecycleBin(userFileSystemNewPath) && !FsPath.AvoidSync(userFileSystemOldPath) && !FsPath.AvoidSync(userFileSystemNewPath)) { inSync = PlaceholderItem.GetItem(userFileSystemOldPath).GetInSync(); string remoteStorageOldPath = Mapping.MapPath(userFileSystemOldPath); string remoteStorageNewPath = Mapping.MapPath(userFileSystemNewPath); FileSystemInfo remoteStorageOldItem = FsPath.GetFileSystemItem(remoteStorageOldPath); if (remoteStorageOldItem is FileInfo) { (remoteStorageOldItem as FileInfo).MoveTo(remoteStorageNewPath); } else { (remoteStorageOldItem as DirectoryInfo).MoveTo(remoteStorageNewPath); } LogMessage("Moved succesefully:", remoteStorageOldPath, remoteStorageNewPath); } } finally { resultContext.ReturnConfirmationResult(); // 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. if ((inSync != null) && PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem.GetItem(userFileSystemNewPath).SetInSync(inSync.Value); } } } catch (Exception ex) { // remove try-catch when error processing inside CloudProvider is fixed. LogError("Move failed:", $"From: {this.FullPath} to:{userFileSystemNewPath}", ex); } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
/// <summary> /// Recursively updates and creates files and folders in remote storage. /// 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); // Update and create files/folders in remote storage. userFileSystemChildren = Directory.EnumerateFileSystemEntries(userFileSystemFolderPath, "*"); foreach (string userFileSystemPath in userFileSystemChildren) { try { if (!FsPath.AvoidSync(userFileSystemPath)) { if (PlaceholderItem.GetItem(userFileSystemPath).IsNew(virtualDrive)) { // Create a file/folder in the remote storage. FileSystemItemTypeEnum itemType = FsPath.GetItemType(userFileSystemPath); await virtualDrive.GetRemoteStorageRawItem(userFileSystemPath, itemType, this).CreateAsync(); } else if (!PlaceholderItem.GetItem(userFileSystemPath).IsMoved()) { // Update file/folder in the remote storage. Unlock if auto-locked. FileSystemItemTypeEnum itemType = FsPath.GetItemType(userFileSystemPath); await virtualDrive.GetRemoteStorageRawItem(userFileSystemPath, itemType, this).UpdateAsync(); } } } 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); } } }
/// <summary> /// Sends contending to the remote storage if the item is modified. /// If Auto-locking is enabled, automatically locks the file if not locked. Unlocks the file after the update if auto-locked. /// </summary> public async Task UpdateAsync() { Lock fileLock = null; try { if (!PlaceholderItem.GetItem(userFileSystemPath).GetInSync()) { // Lock file if auto-locking is enabled. if (Config.Settings.AutoLock) { // Get existing lock or create a new lock. fileLock = await LockAsync(FileMode.OpenOrCreate, LockMode.Auto); } ServerLockInfo lockInfo = fileLock != null ? await fileLock.GetLockInfoAsync() : null; // Update item in remote storage. logger.LogMessage("Sending to remote storage", userFileSystemPath); await CreateOrUpdateAsync(FileMode.Open, lockInfo); //await new UserFileSystemRawItem(userFileSystemPath).ClearStateAsync(); logger.LogMessage("Sent to remote storage succesefully", userFileSystemPath); } // Unlock if auto-locked. if (await Lock.GetLockModeAsync(userFileSystemPath) == LockMode.Auto) { if (fileLock == null) { // Get existing lock. fileLock = await Lock.LockAsync(userFileSystemPath, FileMode.Open, LockMode.None, logger); } await UnlockAsync(fileLock); } } catch (ClientLockFailedException ex) { // Failed to lock file. Possibly blocked from another thread. This is a normal behaviour. logger.LogMessage(ex.Message, userFileSystemPath); } catch (Exception ex) { // Some error when locking, updating or unlocking occured. await new UserFileSystemRawItem(userFileSystemPath).SetUploadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } finally { if (fileLock != null) { fileLock.Dispose(); } } }
/// <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); } } } }
/// <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(); } }
/// <summary> /// Compares user file system item and remote storage item. Returns true if items are equal. False - otherwise. /// </summary> /// <param name="remoteStorageItem">Remote storage item info.</param> /// <returns>Returns true user file system item and remote storage item are equal. False - otherwise.</returns> public async Task <bool> EqualsAsync(FileSystemInfo remoteStorageItem) { // Here you will typically compare file ETags. // For the sake of simplicity we just compare LastWriteTime. long remoteStorageETag = remoteStorageItem.LastWriteTime.ToBinary(); // We store remote storage LastWriteTime inside custom data when creating and updating files/folders. byte[] userFileSystemItemCustomData = PlaceholderItem.GetItem(userFileSystemPath).GetCustomData(); long userFileSystemETag = BitConverter.ToInt64(userFileSystemItemCustomData); return(remoteStorageETag == userFileSystemETag); }
/// <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); }
/// <inheritdoc/> public async Task CloseAsync(IOperationContext operationContext, IResultContext context) { // Here, if the file in the user file system is modified (not in-sync), you will send the file content, // creation time, modification time and attributes to the remote storage. // We also send ETag, to make sure the changes on the server, if any, are not overwritten. Logger.LogMessage("IFile.CloseAsync()", UserFileSystemPath); string userFileSystemFilePath = UserFileSystemPath; // In case the file is moved it does not exist in user file system when CloseAsync() is called. if (Engine.ChangesProcessingEnabled && FsPath.Exists(userFileSystemFilePath) && !FsPath.AvoidSync(userFileSystemFilePath)) { // In case the file is overwritten it is converted to a regular file prior to CloseAsync(). // we need to convert it back into file/folder placeholder. if (!PlaceholderItem.IsPlaceholder(userFileSystemFilePath)) { PlaceholderItem.ConvertToPlaceholder(userFileSystemFilePath, false); Logger.LogMessage("Converted to placeholder", userFileSystemFilePath); } try { if (PlaceholderItem.GetItem(userFileSystemFilePath).IsNew(VirtualDrive)) { // Create new file in the remote storage. await new RemoteStorageRawItem <IVirtualFile>(userFileSystemFilePath, VirtualDrive, Logger).CreateAsync(); } else if (!PlaceholderItem.GetItem(userFileSystemFilePath).IsMoved()) { // Send content to remote storage. Unlock if auto-locked. await new RemoteStorageRawItem <IVirtualFile>(userFileSystemFilePath, VirtualDrive, Logger).UpdateAsync(); } } catch (IOException ex) { // Either the file is already being synced in another thread or client or server file is blocked by concurrent process. // This is a normal behaviour. // The file must be synched by your synchronyzation service at a later time, when the file becomes available. Logger.LogMessage("Failed to upload file. Possibly in use by an application or blocked for synchronization in another thread:", ex.Message); } } }
/// <inheritdoc/> public async Task CloseAsync(IOperationContext operationContext, IResultContext context) { // Here, if the file in user file system is modified (not in-sync), we send file content, // creation time, modification time and attributes to remote storage. // We also create new ETag, associate it with a file in user file system and send it to the server. LogMessage("IFile.CloseAsync()", this.FullPath); string userFileSystemFilePath = this.FullPath; // In case the file is moved it does not exist in user file system when Close() is called. if (!FsPath.Exists(userFileSystemFilePath) || FsPath.AvoidSync(userFileSystemFilePath)) { return; } // In case the file is overwritten it is converted to a regular file prior to Close(). // we need to convert it back into file/folder placeholder. if (!PlaceholderItem.IsPlaceholder(userFileSystemFilePath)) { PlaceholderItem.ConvertToPlaceholder(userFileSystemFilePath, false); LogMessage("Converted to placeholder:", userFileSystemFilePath); } if (!PlaceholderItem.GetItem(userFileSystemFilePath).GetInSync()) { LogMessage("Changed:", userFileSystemFilePath); string remoteStorageFilePath = Mapping.MapPath(userFileSystemFilePath); try { await new RemoteStorageItem(remoteStorageFilePath).UpdateAsync(userFileSystemFilePath); LogMessage("Updated succesefully:", remoteStorageFilePath); } catch (IOException ex) { // Either the file is already being synced in another thread or client or server file is blocked by concurrent process. // This is a normal behaviour. // The file must be synched by your synchronyzation service at a later time, when the file becomes available. LogMessage("Failed to upload file. Possibly in use by an application or blocked for synchronization in another thread:", ex.Message); } } }
/// <summary> /// Moves a file or folder placeholder in user file system. /// </summary> /// <param name="newUserFileSystemPath">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 MoveAsync(string newUserFileSystemPath) { // Windows 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. PlaceholderItem oldUserFileSystemItem = PlaceholderItem.GetItem(userFileSystemPath); if (oldUserFileSystemItem.GetInSync()) { Directory.Move(userFileSystemPath, newUserFileSystemPath); // The file is marked as not in sync after move/rename. Marking it as in-sync. PlaceholderItem.GetItem(newUserFileSystemPath).SetInSync(true); } else { throw new IOException("File not in-sync."); } }
/// <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); }
//$> //$<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); }
/// <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> /// Gets file system item info from the user file system item. /// Removes Pinned, Unpinned and Offline flags from attributes. /// </summary> /// <param name="userFileSystemItem">User file system item info.</param> /// <returns><see cref="FileSystemItemBasicInfo"/> object.</returns> private static IFileSystemItemBasicInfo GetBasicInfo(FileSystemInfo userFileSystemItem) { FileSystemItemBasicInfo itemInfo; if (userFileSystemItem is FileInfo) { itemInfo = new FileBasicInfo(); } else { itemInfo = new FolderBasicInfo(); } // Remove Pinned, unpinned and offline flags, // so they do not occasionally go into the remote storage. FileAttributes flags = userFileSystemItem.Attributes & (FileAttributes) ~FileAttributesExt.Pinned & (FileAttributes) ~FileAttributesExt.Unpinned & (FileAttributes) ~FileAttributesExt.Offline; itemInfo.Name = userFileSystemItem.Name; itemInfo.Attributes = flags; itemInfo.CreationTime = userFileSystemItem.CreationTime; itemInfo.LastWriteTime = userFileSystemItem.LastWriteTime; itemInfo.LastAccessTime = userFileSystemItem.LastAccessTime; itemInfo.ChangeTime = userFileSystemItem.LastWriteTime; itemInfo.CustomData = PlaceholderItem.GetItem(userFileSystemItem.FullName).GetCustomData(); if (userFileSystemItem is FileInfo) { ((FileBasicInfo)itemInfo).Length = ((FileInfo)userFileSystemItem).Length; } ; return(itemInfo); }
/// <summary> /// Gets file system item info from the user file system item. /// Removes Pinned, Unpinned and Offline flags from attributes. /// </summary> /// <param name="userFileSystemItem">User file system item info.</param> /// <returns>Object implementing <see cref="IFileSystemItemMetadata"/> interface.</returns> private static IFileSystemItemMetadata GetMetadata(FileSystemInfo userFileSystemItem) { FileSystemItemMetadataExt itemInfo; if (userFileSystemItem is FileInfo) { itemInfo = new FileMetadataExt(); } else { itemInfo = new FolderMetadataExt(); } // Remove Pinned, unpinned and offline flags, // so they are not sent to the remote storage by mistake. FileAttributes flags = userFileSystemItem.Attributes & (FileAttributes) ~FileAttributesExt.Pinned & (FileAttributes) ~FileAttributesExt.Unpinned & (FileAttributes) ~FileAttributesExt.Offline; itemInfo.Name = userFileSystemItem.Name; itemInfo.Attributes = flags; itemInfo.CreationTime = userFileSystemItem.CreationTime; itemInfo.LastWriteTime = userFileSystemItem.LastWriteTime; itemInfo.LastAccessTime = userFileSystemItem.LastAccessTime; itemInfo.ChangeTime = userFileSystemItem.LastWriteTime; itemInfo.CustomData = PlaceholderItem.GetItem(userFileSystemItem.FullName).GetCustomData(); if (userFileSystemItem is FileInfo) { ((FileMetadataExt)itemInfo).Length = ((FileInfo)userFileSystemItem).Length; } ; return(itemInfo); }
/// <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="remoteStorageItem">Remote storage item info. The placeholder info will be updated with info provided in this item.</param> internal async Task UpdateAsync(FileSystemInfo remoteStorageItem) { /* * // Process conflicts. File <-> Folder. Folder <-> File. * if (File.Exists(userFileSystemPath) && (remoteStorageItem is DirectoryInfo)) * { * } * else if (Directory.Exists(userFileSystemPath) && (remoteStorageItem is FileInfo)) * { * } */ FileSystemItemBasicInfo userFileSystemItemInfo = Mapping.GetUserFileSysteItemInfo(remoteStorageItem); try { // Dehydrate/hydrate the file, update file size and info. PlaceholderItem.GetItem(userFileSystemPath).SetItemInfo(userFileSystemItemInfo); // Clear download pending icon await SetDownloadPendingIconAsync(false); } catch (System.ComponentModel.Win32Exception ex) { if (ex.NativeErrorCode == 802) { // The file is blocked by the client application or pinned. // "The operation did not complete successfully because it would cause an oplock to be broken. // The caller has requested that existing oplocks not be broken." // Show download pending icon. await SetDownloadPendingIconAsync(true); } // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } }
//$<IFileSystemItem.MoveToAsync ///<inheritdoc> public async Task MoveToAsync(string userFileSystemNewPath, IOperationContext operationContext, IConfirmationResultContext resultContext) { string userFileSystemOldPath = this.UserFileSystemPath; Logger.LogMessage("IFileSystemItem.MoveToAsync()", userFileSystemOldPath, userFileSystemNewPath); // Process move. if (Engine.ChangesProcessingEnabled) { if (FsPath.Exists(userFileSystemOldPath)) { await new RemoteStorageRawItem(userFileSystemOldPath, VirtualDrive, Logger).MoveToAsync(userFileSystemNewPath, resultContext); } } else { resultContext.ReturnConfirmationResult(); } // Restore Original Path and locked icon, lost during MS Office transactional save. if (FsPath.Exists(userFileSystemNewPath) && PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem userFileSystemNewItem = PlaceholderItem.GetItem(userFileSystemNewPath); if (!userFileSystemNewItem.IsNew() && string.IsNullOrEmpty(userFileSystemNewItem.GetOriginalPath())) { // Restore Original Path. Logger.LogMessage("Saving Original Path", userFileSystemNewPath); userFileSystemNewItem.SetOriginalPath(userFileSystemNewPath); // Restore the 'locked' icon. bool isLocked = await Lock.IsLockedAsync(userFileSystemNewPath); ServerLockInfo existingLock = isLocked ? await(await Lock.LockAsync(userFileSystemNewPath, FileMode.Open, LockMode.None, Logger)).GetLockInfoAsync() : null; await new UserFileSystemRawItem(userFileSystemNewPath).SetLockInfoAsync(existingLock); } } }
/// <summary> /// Recursively synchronizes all files and folders moved in user file system with the remote storage. /// Synchronizes only folders already loaded into the user file system. /// </summary> /// <param name="userFileSystemFolderPath">Folder path in user file system.</param> internal async Task SyncronizeMovedAsync(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); foreach (string userFileSystemPath in userFileSystemChildren) { string userFileSystemOldPath = null; try { //$<PlaceholderItem.IsPlaceholder if (!PlaceholderItem.IsPlaceholder(userFileSystemPath)) { // Convert regular file/folder to placeholder. // The file/folder was created or overwritten. PlaceholderItem.ConvertToPlaceholder(userFileSystemPath, false); LogMessage("Converted to placeholder", userFileSystemPath); } //$> if (!FsPath.AvoidSync(userFileSystemPath)) { PlaceholderItem userFileSystemItem = PlaceholderItem.GetItem(userFileSystemPath); if (userFileSystemItem.IsMoved()) { // Process items moved in user file system. userFileSystemOldPath = userFileSystemItem.GetOriginalPath(); await new RemoteStorageRawItem(userFileSystemOldPath, this).MoveToAsync(userFileSystemPath); } else { // Restore Original Path and 'locked' icon that are lost during MS Office transactional save. // We keep Original Path to process moved files when app was not running. userFileSystemOldPath = userFileSystemItem.GetOriginalPath(); if (!userFileSystemItem.IsNew() && string.IsNullOrEmpty(userFileSystemOldPath)) { // Restore Original Path. LogMessage("Saving Original Path", userFileSystemItem.Path); userFileSystemItem.SetOriginalPath(userFileSystemItem.Path); // Restore the 'locked' icon. bool isLocked = await Lock.IsLockedAsync(userFileSystemPath); await new UserFileSystemRawItem(userFileSystemPath).SetLockIconAsync(isLocked); } } } } catch (Exception ex) { LogError("Move in remote storage failed", userFileSystemOldPath, userFileSystemPath, ex); } // Synchronize subfolders. try { if (FsPath.IsFolder(userFileSystemPath)) { await SyncronizeMovedAsync(userFileSystemPath); } } catch (Exception ex) { LogError("Folder move sync failed", userFileSystemPath, 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> /// Creates or updates the item in the remote storage. /// </summary> /// <param name="mode"> /// Indicates if the file should created or updated. /// Supported modes are <see cref="FileMode.CreateNew"/> and <see cref="FileMode.Open"/> /// </param> /// <param name="lockInfo">Information about the lock. Pass null if the item is not locked.</param> private async Task CreateOrUpdateAsync(FileMode mode, ServerLockInfo lockInfo = null) { if ((mode != FileMode.CreateNew) && (mode != FileMode.Open)) { throw new ArgumentOutOfRangeException("mode", $"Must be {FileMode.CreateNew} or {FileMode.Open}"); } FileSystemInfo userFileSystemItem = FsPath.GetFileSystemItem(userFileSystemPath); using (WindowsFileSystemItem userFileSystemWinItem = WindowsFileSystemItem.OpenReadAttributes(userFileSystemPath, FileMode.Open, FileShare.Read)) //await using (FileStream userFileSystemStream = userFileSystemFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read)) { // Create the new file/folder in the remote storage only if the file/folder in the user file system was not moved. // If the file is moved in user file system, move must first be syched to remote storage. if ((mode == FileMode.CreateNew) && PlaceholderItem.GetItem(userFileSystemPath).IsMoved()) { string originalPath = PlaceholderItem.GetItem(userFileSystemPath).GetOriginalPath(); throw new ConflictException(Modified.Client, $"The item was moved. Original path: {originalPath}"); } // Ensures LastWriteTimeUtc is in sync with file content after Open() was called. userFileSystemItem.Refresh(); IFileSystemItemBasicInfo info = GetBasicInfo(userFileSystemItem); // Update remote storage file. FileStream userFileSystemStream = null; try { string eTag = null; if (FsPath.IsFile(userFileSystemPath)) { // File is marked as not in-sync when updated OR moved. // Opening a file for reading triggers hydration, make sure to open only if content is modified. if (PlaceholderFile.GetFileDataSizeInfo(userFileSystemWinItem.SafeHandle).ModifiedDataSize > 0) { //userFileSystemStream = new FileStream(userFileSystemWinItem.SafeHandle, FileAccess.Read); userFileSystemStream = ((FileInfo)userFileSystemItem).Open(FileMode.Open, FileAccess.Read, FileShare.Read); } if (mode == FileMode.CreateNew) { string userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath); IUserFolder userFolder = await virtualDrive.GetItemAsync <IUserFolder>(userFileSystemParentPath); eTag = await userFolder.CreateFileAsync((IFileBasicInfo)info, userFileSystemStream); } else { IUserFile userFile = await virtualDrive.GetItemAsync <IUserFile>(userFileSystemPath); eTag = await userFile.UpdateAsync((IFileBasicInfo)info, userFileSystemStream, lockInfo); } } else { if (mode == FileMode.CreateNew) { string userFileSystemParentPath = Path.GetDirectoryName(userFileSystemPath); IUserFolder userFolder = await virtualDrive.GetItemAsync <IUserFolder>(userFileSystemParentPath); eTag = await userFolder.CreateFolderAsync((IFolderBasicInfo)info); } else { IUserFolder userFolder = await virtualDrive.GetItemAsync <IUserFolder>(userFileSystemPath); eTag = await userFolder.UpdateAsync((IFolderBasicInfo)info, lockInfo); } } await ETag.SetETagAsync(userFileSystemPath, eTag); if (mode == FileMode.CreateNew) { PlaceholderItem.GetItem(userFileSystemPath).SetOriginalPath(userFileSystemPath); } } finally { if (userFileSystemStream != null) { userFileSystemStream.Close(); } } PlaceholderItem.SetInSync(userFileSystemWinItem.SafeHandle, true); } }
/// <inheritdoc/> public async Task UpdateAsync() { // This protects from ocationally calling update on moved items. if (PlaceholderItem.GetItem(userFileSystemPath).IsMoved()) { string originalPath = PlaceholderItem.GetItem(userFileSystemPath).GetOriginalPath(); throw new ConflictException(Modified.Client, $"The item was moved. Original path: {originalPath}"); } LockSync lockSync = null; try { if (!PlaceholderItem.GetItem(userFileSystemPath).GetInSync()) { // This will make sure only one thread can do the lock-update-unlock sequence. lockSync = await lockManager.LockAsync(); IVirtualFileSystemItem userFileSystemItem = await GetItemAsync(userFileSystemPath); ServerLockInfo lockInfo = null; if (userFileSystemItem is IVirtualLock) { // Get lock info in case of manual lock or failed lock/update/unlock if (await lockManager.IsLockedAsync()) { lockInfo = await lockManager.GetLockInfoAsync(); } // Lock file if auto-locking is enabled. else if (virtualDrive.Settings.AutoLock) { // Lock file in remote storage. lockInfo = await LockAsync(lockSync, LockMode.Auto); } } // Update item in remote storage. logger.LogMessage("Sending to remote storage", userFileSystemPath); await CreateOrUpdateAsync(FileMode.Open, lockInfo); //await new UserFileSystemRawItem(userFileSystemPath).ClearStateAsync(); logger.LogMessage("Sent to remote storage succesefully", userFileSystemPath); } // Unlock if auto-locked. if (await lockManager.GetLockModeAsync() == LockMode.Auto) { // Required in case the file failed to unlock during previous update. lockSync ??= await lockManager.LockAsync(); // Send unlock request to remote storage. await UnlockAsync(lockSync); } } catch (ClientLockFailedException ex) { // Failed to lock file. Possibly blocked from another thread. This is a normal behaviour. logger.LogMessage(ex.Message, userFileSystemPath); } catch (Exception ex) { // Some error when locking, updating or unlocking occured. await userFileSystemRawItem.SetUploadErrorStateAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } finally { if (lockSync != null) { lockSync.Dispose(); } // Do not delete lock-token and lock-mode files here, it is required if IUserLock // interfce is implemented and the file was locked manually. } }
/// <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); } } }
/// <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); } } }