/// <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(); } }
private async Task CreateOrUpdateFileAsync(string userFileSystemPath, bool create) { try { Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls. FileInfo userFileSystemFile = new FileInfo(userFileSystemPath); FileInfo remoteStorageFile = new FileInfo(remoteStoragePath); FileMode fileMode = create ? FileMode.CreateNew : FileMode.Open; await using (FileStream userFileSystemStream = userFileSystemFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read)) { // If another thread is trying to sync the same file, this call will fail in other threads. // In your implemntation you must lock your remote storage file, or block it for reading and writing by other means. await using (FileStream remoteStorageStream = remoteStorageFile.Open(fileMode, FileAccess.Write, FileShare.None)) { userFileSystemFile.Refresh(); // Ensures LastWriteTimeUtc is in sync with file content after Open() was called. // Update ETag/LastWriteTime in user file system, so the synchronyzation or remote storage monitor would not start the new update. byte[] customData = BitConverter.GetBytes(userFileSystemFile.LastWriteTime.ToBinary()); PlaceholderItem.SetCustomData(userFileSystemStream.SafeFileHandle, customData); // Update remote storage file content. await userFileSystemStream.CopyToAsync(remoteStorageStream); remoteStorageStream.SetLength(userFileSystemStream.Length); // Update remote storage file basic info. WindowsFileSystemItem.SetFileInformation(remoteStorageStream.SafeFileHandle, userFileSystemFile.Attributes & (FileAttributes) ~FileAttributesExt.Pinned, // Remove Pinned flag. userFileSystemFile.CreationTimeUtc, userFileSystemFile.LastWriteTimeUtc, userFileSystemFile.LastAccessTimeUtc, userFileSystemFile.LastWriteTimeUtc); // If you are using ETags, here you will also send to the remote storage a new file ETag. PlaceholderItem.SetInSync(userFileSystemStream.SafeFileHandle, true); } } } finally { Program.RemoteStorageMonitorInstance.Enabled = true; } }
/// <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); }
/// <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); } }
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); } }