Пример #1
0
        /// <summary>
        /// Returns MS Office lock file path if such file exists.
        /// </summary>
        /// <param name="msOfficeFilePath">MS Office file path.</param>
        /// <returns>Lock file path.</returns>
        /// <remarks>
        /// mydoc.docx       -> ~$mydoc.docx
        /// mydocfi.docx     -> ~$ydocfi.docx
        /// mydocfile.docx   -> ~$docfile.docx
        /// mydocfile.pptx   -> ~$mydocfile.pptx
        /// mydocfile.ppt    -> ~$mydocfile.ppt
        /// mydocfile.xlsx   -> ~$mydocfile.xlsx
        /// mydocfile.xls    -> null
        /// </remarks>
        private static string GetLockPathFromMsOfficePath(string msOfficeFilePath)
        {
            string msOfficeLockFilePath = null;
            int    separatorIndex       = msOfficeFilePath.LastIndexOf(Path.DirectorySeparatorChar);

            if ((separatorIndex != -1) && !string.IsNullOrEmpty(Path.GetExtension(msOfficeFilePath)))
            {
                msOfficeLockFilePath = msOfficeFilePath.Insert(separatorIndex + 1, "~$");
                if (FsPath.Exists(msOfficeLockFilePath))
                {
                    return(msOfficeLockFilePath);
                }
                int fileNameLength = Path.GetFileNameWithoutExtension(msOfficeFilePath).Length;
                if (fileNameLength > 6)
                {
                    int removeChars = fileNameLength == 7 ? 1 : 2;
                    msOfficeLockFilePath = msOfficeLockFilePath.Remove(separatorIndex + 1 + "~$".Length, removeChars);
                    if (FsPath.Exists(msOfficeLockFilePath))
                    {
                        return(msOfficeLockFilePath);
                    }
                }
            }
            return(null);
        }
Пример #2
0
        /// <inheritdoc/>
        public 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{DateTimeOffset.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {componentName,-26}{message,-45} {sourcePath,-80} {size,7} {att} {targetPath}");
        }
Пример #3
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))
                {
                    if (await engine.ServerNotifications(userFileSystemOldPath).MoveToAsync(userFileSystemNewPath))
                    {
                        // Because of the on-demand population the file or folder placeholder may not exist in the user file system.
                        // In this case the IServerNotifications.MoveToAsync() call is ignored.
                        LogMessage("Renamed succesefully:", userFileSystemOldPath, userFileSystemNewPath);
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", remoteStorageOldPath, remoteStorageNewPath, ex);
            }
        }
Пример #4
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);

                    FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath);
                    if (remoteStorageItem != null)
                    {
                        IFileSystemItemMetadata newItemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);
                        if (await engine.ServerNotifications(userFileSystemParentPath).CreateAsync(new[] { newItemInfo }) > 0)
                        {
                            // Because of the on-demand population, the parent folder placeholder may not exist in the user file system
                            // or the folder may be offline. In this case the IServerNotifications.CreateAsync() call is ignored.
                            LogMessage($"Created succesefully", userFileSystemPath);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex);
            }
        }
Пример #5
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:
            // - IGetChildrenContext.ReturnChildren() method.
            // - IGetChildrenContext.ReportProgress() method.

            LogMessage($"IFolder.GetChildrenAsync({pattern})", this.FullPath);

            string remoreStoragePath = Mapping.MapPath(this.FullPath);

            IEnumerable <FileSystemInfo>    remoteStorageChildren  = new DirectoryInfo(remoreStoragePath).EnumerateFileSystemInfos(pattern);
            List <IFileSystemItemBasicInfo> userFileSystemChildren = new List <IFileSystemItemBasicInfo>();

            foreach (FileSystemInfo remoteStorageItem in remoteStorageChildren)
            {
                string userFileSystemPath = Path.Combine(this.FullPath, Path.GetFileName(remoteStorageItem.Name));
                if (!FsPath.Exists(userFileSystemPath))
                {
                    // Uncomment to simulate slow network access.
                    //Thread.Sleep(10000);
                    //resultContext.ReportProgress(remoteStorageChildren.Count(), userFileSystemChildren.Count());

                    LogMessage("Creating:", Path.GetFileName(remoteStorageItem.Name));
                    FileSystemItemBasicInfo userFileSystemItemInfo = Mapping.GetUserFileSysteItemInfo(remoteStorageItem);
                    userFileSystemChildren.Add(userFileSystemItemInfo);
                }
            }

            // To signal that the children enumeration is completed
            // always call this method in GetChildrenAsync(), even if the folder is empty.
            resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count());
        }
Пример #6
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);
                }
            }

            // To signal that the children enumeration is completed
            // always call ReturnChildren(), even if the folder is empty.
            resultContext.ReturnChildren(userFileSystemChildren.ToArray(), userFileSystemChildren.Count());
        }
Пример #7
0
        ///<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;
            }
        }
Пример #8
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);

            Logger.LogMessage($"{message,-45} {sourcePath,-80} {size,7} {att} {targetPath}");
        }
        /// <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.
                        FileSystemItemMetadataExt itemInfo = Mapping.GetUserFileSysteItemMetadata(remoteStorageItem);
                        if (!await virtualDrive.GetETagManager(userFileSystemPath, this).ETagEqualsAsync(itemInfo))
                        {
                            await virtualDrive.ServerNotifications(userFileSystemPath, this).UpdateAsync(itemInfo);

                            LogMessage("Updated succesefully", userFileSystemPath);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError($"{e.ChangeType} failed", remoteStoragePath, null, ex);
            }
        }
Пример #10
0
        /// <inheritdoc/>
        public async Task OpenAsync(IOperationContext operationContext, IResultContext context)
        {
            Logger.LogMessage("IFile.OpenAsync()", UserFileSystemPath);

            // Auto-lock the file.
            string userFileSystemFilePath = UserFileSystemPath;

            if (Engine.ChangesProcessingEnabled && FsPath.Exists(userFileSystemFilePath))
            {
                if (Program.Settings.AutoLock &&
                    !FsPath.AvoidAutoLock(userFileSystemFilePath) &&
                    !await Lock.IsLockedAsync(userFileSystemFilePath) &&
                    FsPath.IsWriteLocked(userFileSystemFilePath) &&
                    !new PlaceholderFile(userFileSystemFilePath).IsNew())
                {
                    try
                    {
                        await new RemoteStorageRawItem(userFileSystemFilePath, Logger).LockAsync(LockMode.Auto);
                    }
                    catch (ClientLockFailedException ex)
                    {
                        // Lock file is blocked by the concurrent thread. This is a normal behaviour.
                        Logger.LogMessage(ex.Message, userFileSystemFilePath);
                    }
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Returns true if the file or folder is marked with Hidden or Temporaty attributes.
        /// </summary>
        /// <param name="path">Path to a file or folder.</param>
        /// <returns>
        /// True if the file or folder is marked with Hidden or Temporaty attributes.
        /// Returns false if no Hidden or Temporaty attributes found or file/folder does not exists.
        /// </returns>
        private static bool IsHiddenOrTemp(string path)
        {
            if (!FsPath.Exists(path))
            {
                return(false);
            }

            FileAttributes att = File.GetAttributes(path);

            return(((att & System.IO.FileAttributes.Hidden) != 0) ||
                   ((att & System.IO.FileAttributes.Temporary) != 0));
        }
Пример #12
0
        //$<IFolder.CloseAsync
        /// <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())
                    {
                        // Create new file in the remote storage.
                        await RemoteStorageRawItem.CreateAsync(userFileSystemFilePath, Logger);
                    }
                    else
                    {
                        // Send content to remote storage. Unlock if auto-locked.
                        await new RemoteStorageRawItem(userFileSystemFilePath, 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);
                }
            }
        }
Пример #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($"IFolder.GetChildrenAsync({pattern})", UserFileSystemPath);

            IEnumerable <FileSystemItemBasicInfo> children = await new UserFolder(UserFileSystemPath).EnumerateChildrenAsync(pattern);

            // Filtering existing files/folders. This is only required to avoid extra errors in the log.
            List <IFileSystemItemBasicInfo> newChildren = new List <IFileSystemItemBasicInfo>();

            foreach (IFileSystemItemBasicInfo child in children)
            {
                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, child.Name);
                if (!FsPath.Exists(userFileSystemItemPath))
                {
                    Logger.LogMessage("Creating", child.Name);
                    newChildren.Add(child);
                }
            }

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


            // Save ETags and set "locked by another user" icon.
            foreach (FileSystemItemBasicInfo child in children)
            {
                string userFileSystemItemPath = Path.Combine(UserFileSystemPath, child.Name);

                // Create ETags.
                // 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.
                ETag.SetETagAsync(userFileSystemItemPath, child.ETag);

                // Set the lock icon and read-only attribute, to indicate that the item is locked by another user.
                new UserFileSystemRawItem(userFileSystemItemPath).SetLockedByAnotherUserAsync(child.LockedByAnotherUser);
            }
        }
Пример #14
0
        /// <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);
                }
            }
        }
Пример #15
0
        ///<inheritdoc>
        public async Task DeleteAsync(IOperationContext operationContext, IConfirmationResultContext resultContext)
        {
            // Here we will simply delete 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.DeleteAsync()", this.FullPath);
            string userFileSystemPath = this.FullPath;
            string remoteStoragePath  = null;

            try
            {
                try
                {
                    Program.RemoteStorageMonitorInstance.Enabled = false; // Disable RemoteStorageMonitor to avoid circular calls.

                    remoteStoragePath = Mapping.MapPath(userFileSystemPath);
                    if (FsPath.Exists(remoteStoragePath) && !FsPath.AvoidSync(userFileSystemPath))
                    {
                        FileSystemInfo remoteStorageItem = FsPath.GetFileSystemItem(remoteStoragePath);
                        remoteStorageItem.Delete();
                        LogMessage("Deleted succesefully:", remoteStoragePath);
                    }
                }
                finally
                {
                    resultContext.ReturnConfirmationResult();
                }
            }
            catch (Exception ex)
            {
                // remove try-catch when error processing inside CloudProvider is fixed.
                LogError("Delete failed:", remoteStoragePath, ex);
            }
            finally
            {
                Program.RemoteStorageMonitorInstance.Enabled = true;
            }
        }
Пример #16
0
        //$<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, 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);

                    await new UserFileSystemRawItem(userFileSystemNewPath).SetLockIconAsync(isLocked);
                }
            }
        }
Пример #17
0
        protected void LogError(string message, string sourcePath = null, Exception ex = null)
        {
            string att = FsPath.Exists(sourcePath) ? FsPath.GetAttString(sourcePath) : null;

            Logger.LogError($"{message,-45} {sourcePath,-80} {att} ", ex);
        }
Пример #18
0
        /// <inheritdoc/>
        public void LogError(string message, string sourcePath = null, string targetPath = null, Exception ex = null)
        {
            string att = FsPath.Exists(sourcePath) ? FsPath.GetAttString(sourcePath) : null;

            Log.Error($"\n{DateTimeOffset.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {componentName,-26}{message,-45} {sourcePath,-80} {att} ", ex);
        }