/// <summary> /// Creates new file and folder placeholders in the user file system in this folder. /// </summary> /// <param name="newItemsInfo">Array of new files and folders.</param> /// <returns>Number of items created.</returns> public async Task <uint> CreateAsync(IFileSystemItemMetadata[] newItemsInfo) { string userFileSystemParentPath = userFileSystemPath; try { // Because of the on-demand population the file or folder placeholder may not exist in the user file system. // Here we also check that the folder content was loaded into user file system (the folder is not offline). if (Directory.Exists(userFileSystemParentPath) && !new DirectoryInfo(userFileSystemParentPath).Attributes.HasFlag(System.IO.FileAttributes.Offline)) { // Here we save current user file system path inside file/folder. // If the file/folder is moved or renamed while this app is not running, // we extract the saved path when this app starts and sync the file/folder to the remote storage. foreach (var newItemInfo in newItemsInfo) { string userFileSystemPath = Path.Combine(userFileSystemParentPath, newItemInfo.Name); newItemInfo.CustomData = new CustomData { OriginalPath = userFileSystemPath }.Serialize(); } // Create placeholders. uint created = await virtualDrive.Engine.ServerNotifications(userFileSystemParentPath).CreateAsync(newItemsInfo); // Create ETags. foreach (IFileSystemItemMetadata newItemInfo in newItemsInfo) { FileSystemItemMetadataExt newItemInfoExt = newItemInfo as FileSystemItemMetadataExt ?? throw new NotImplementedException($"{nameof(FileSystemItemMetadataExt)}"); string userFileSystemNewItemPath = Path.Combine(userFileSystemParentPath, newItemInfoExt.Name); await virtualDrive.GetETagManager(userFileSystemNewItemPath).SetETagAsync(newItemInfoExt.ETag); // Set the read-only attribute and all custom columns data. UserFileSystemRawItem newUserFileSystemRawItem = virtualDrive.GetUserFileSystemRawItem(userFileSystemNewItemPath, logger); await newUserFileSystemRawItem.SetLockedByAnotherUserAsync(newItemInfoExt.LockedByAnotherUser); await newUserFileSystemRawItem.SetCustomColumnsDataAsync(newItemInfoExt.CustomProperties); } return(created); } } catch (ExistsException ex) { // "Cannot create a file when that file already exists." //await new UserFileSystemItem(userFileSystemParentPath).ShowDownloadErrorAsync(ex); // Rethrow the exception preserving stack trace of the original exception. System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw(); } return(0); }
/// <summary> /// Creates instance of this class. /// </summary> /// <param name="userFileSystemPath">File or folder path in the user file system.</param> /// <param name="logger">Logger.</param> internal RemoteStorageRawItem(string userFileSystemPath, VirtualDriveBase virtualDrive, ILogger logger) { if (string.IsNullOrEmpty(userFileSystemPath)) { throw new ArgumentNullException(nameof(userFileSystemPath)); } this.virtualDrive = virtualDrive ?? throw new ArgumentNullException(nameof(virtualDrive)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.userFileSystemPath = userFileSystemPath; this.lockManager = virtualDrive.LockManager(userFileSystemPath, logger); this.userFileSystemRawItem = virtualDrive.GetUserFileSystemRawItem(userFileSystemPath, logger); }
/// <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); } } }