public KfsMetaDataThread(KfsShare s, byte[] ticket, KfsPhase1Payload payload) : base(s, ticket, 0) { Phase1Payload = payload; }
/// <summary> /// Begin executing the operation. /// </summary> public void StartOperation() { Debug.Assert(Status == MetaDataManagerStatus.Queued); Debug.Assert(TransferThread == null); Debug.Assert(Phase1Payload != null); Debug.Assert(Ticket != null); try { // Start the transfer. TransferThread = new KfsMetaDataThread(Share, Ticket, Phase1Payload); TransferThread.Start(); Status = MetaDataManagerStatus.Exec; Phase1Payload = null; Ticket = null; } catch (Exception ex) { Share.FatalError(ex); } }
/// <summary> /// Tidy-up the state after a transfer has been completed or when an /// error has occurred. /// </summary> private void GoIdle() { Phase1Payload = null; UploadTree = null; Task = MetaDataTask.None; Status = MetaDataManagerStatus.Idle; CommitIDSet.Clear(); WantedCommitID = 0; Share.NotifyIfKfsIdle(); }
/// <summary> /// Send a phase 1 message to the KCD and retrieve the reply. /// </summary> protected AnpMsg SendPhase1Message(KfsPhase1Payload payload) { AnpMsg m = Share.CreateTransferMsg(KAnpType.KANP_CMD_KFS_PHASE_1); m.AddBin(Ticket); m.AddUInt64(0); payload.AddToMsg(m); SendAnpMsg(m); m = GetAnpMsg(); if (m.Type == KAnpType.KANP_RES_FAIL) throw new Exception(m.Elements[1].String); if (m.Type != KAnpType.KANP_RES_KFS_PHASE_1) throw new Exception("expected RES_KFS_PHASE_1"); return m; }
/// <summary> /// Queue a meta-data operation for execution. /// </summary> public void QueueOperation(KfsPhase1Payload payload, SortedDictionary<UInt64, KfsFileUpload> uploadTree, MetaDataTask task) { Debug.Assert(Status == MetaDataManagerStatus.Idle); Phase1Payload = payload; if (uploadTree == null) UploadTree = new SortedDictionary<UInt64, KfsFileUpload>(); else UploadTree = uploadTree; Task = task; Status = MetaDataManagerStatus.Queued; }
/// <summary> /// Move the selected paths to the destination specified. The selected /// paths must all be rooted in the same directory. dstRelPath must /// not end with a slash. /// </summary> public void MovePath(List<String> pathArray, String dstRelPath) { // Get out early if there is nothing to move. if (pathArray.Count == 0) return; // Disallow all user operations until the pipeline stabilized. Share.DisallowUserOp("MovePath() called", AllowedOpStatus.None); GateEntry("MovePath"); try { // Make sure the destination still exists. if (GetInternalPathType(dstRelPath) != GatePathType.Dir) throw new GateSyncException(); // Check for transfers. List<String> transferPathArray = new List<String>(pathArray); transferPathArray.Add(dstRelPath); ThrowIfContainFileTransfer(pathArray); // Validate the move and extract the source names. String fromLoc = KfsPath.DirName(pathArray[0]); List<String> srcArray = new List<String>(); foreach (String path in pathArray) { Debug.Assert(KfsPath.DirName(path) == fromLoc); if (dstRelPath.StartsWith(path)) throw new GateIllegalException("Sorry, cannot move directory into subdirectory " + "of itself."); srcArray.Add(KfsPath.BaseName(path)); } // Handle the sources selected. String toLoc = dstRelPath; if (toLoc != "") toLoc += "/"; KfsPhase1Payload payload = new KfsPhase1Payload(); String typeConflictAction = "a"; MovePathRecursive(fromLoc, toLoc, srcArray, payload, true, ref typeConflictAction); // Request the move on the server. Debug.Assert(Share.MetaDataManager.Status == MetaDataManagerStatus.Idle, "Share.MetaDataManager.Status = " + Share.MetaDataManager.Status.ToString()); if (payload.OpList.Count > 0) Share.MetaDataManager.QueueOperation(payload, null, MetaDataTask.Move); } catch (Exception ex) { HandleException(ex); } GateExit("MovePath"); }
/// <summary> /// Move a given relative path to a new name, in the same directory. /// </summary> public void RenamePath(string srcPath, string newName) { Debug.Assert(KfsPath.IsValidFileName(newName)); // Disallow all user operations until the pipeline stabilized. Share.DisallowUserOp("RenamePath() called", AllowedOpStatus.None); GateEntry("RenamePath"); try { // Check that the new name has no invalid characters. if (!KfsPath.IsValidFileName(newName)) throw new GateIllegalException("Invalid name (" + newName + "). Note that files and folders cannot contain any of the following characters:\n*, \\, /, ?, :, |, <, >."); // Get the relative path to the source directory. String dirPath = KfsPath.DirName(srcPath); // Make sure the source exists. GatePathType srcType = GetInternalPathType(srcPath); if (srcType != GatePathType.Dir && srcType != GatePathType.File) throw new GateSyncException(); // Make sure the destination does not exist. String dstPath = dirPath + newName; GatePathType dstType = GetInternalPathType(dstPath); if (dstType != GatePathType.None) throw new GateIllegalException("A file or folder with the name you specified " + "already exists. Please specify a different name."); // Check for transfers. List<String> transferPathArray = new List<String>(); transferPathArray.Add(srcPath); transferPathArray.Add(dstPath); ThrowIfContainFileTransfer(transferPathArray); // Get information about the source and the destination. bool fileFlag = (srcType == GatePathType.File); KfsServerObject srcObject = Share.ServerView.GetObjectByPath(srcPath); // The source exists only locally. if (srcObject == null) { // Move the file or directory locally. if (fileFlag) File.Move(Share.MakeAbsolute(srcPath), Share.MakeAbsolute(dstPath)); else Directory.Move(Share.MakeAbsolute(srcPath), Share.MakeAbsolute(dstPath)); } // Ask the server to move the file. else { KfsPhase1Payload payload = new KfsPhase1Payload(); KfsServerObject dirObject = Share.ServerView.GetObjectByPath(dirPath); payload.AddMoveOp(fileFlag, srcObject.Inode, srcObject.CommitID, dirObject.Inode, dirObject.CommitID, newName); // Request the move on the server. Debug.Assert(Share.MetaDataManager.Status == MetaDataManagerStatus.Idle); Share.MetaDataManager.QueueOperation(payload, null, MetaDataTask.Move); } } catch (Exception ex) { HandleException(ex); } GateExit("RenamePath"); }
/// <summary> /// Add the directory having the relative path specified. /// </summary> public void AddDirectory(String relPath) { GateEntry("AddDirectory"); // Disallow all user operations until the pipeline stabilized. Share.DisallowUserOp("AddDirectory() called", AllowedOpStatus.None); try { // Either the target path must exist as a directory, or // the target path must not exist and its parent must exist // as a directory. KfsPhase1Payload payload = new KfsPhase1Payload(); KfsStatusPath ts = Share.StatusView.GetPath(relPath); // The target path exists. if (ts != null && ts.Status != PathStatus.ServerGhost) { // It's not a directory. if (ts.Status != PathStatus.Directory) throw new GateSyncException(); } // The target path does not exist. Check the parent. else { KfsStatusPath ps = Share.StatusView.GetPath(KfsPath.DirName(relPath)); // The parent is not a directory. if (ps == null || ps.Status != PathStatus.Directory) throw new GateSyncException(); } // Create the missing directories. Share.AddMissingLocalDirectories(relPath); AddMissingServerDirectories(relPath, payload); // Request the creation on the server. Debug.Assert(Share.MetaDataManager.Status == MetaDataManagerStatus.Idle); if (payload.OpList.Count > 0) Share.MetaDataManager.QueueOperation(payload, null, MetaDataTask.Mkdir); } catch (Exception ex) { HandleException(ex); } GateExit("AddDirectory"); }
/// <summary> /// Delete the selected relative paths recursively. /// </summary> public void DeletePath(List<String> pathArray) { GateEntry("DeletePath"); // Disallow all user operations until the pipeline stabilized. Share.DisallowUserOp("DeletePath() called", AllowedOpStatus.None); try { // Check for transfers. ThrowIfContainFileTransfer(pathArray); // Get the entries to delete, both locally and remotely. HashedSet<String> seenSet = new HashedSet<String>(); List<String> fileArray = new List<String>(); List<String> dirArray = new List<String>(); KfsPhase1Payload payload = new KfsPhase1Payload(); foreach (String path in pathArray) { // Get the paths in leaf-first order, so that we delete entries in // directories before their parents. foreach (KfsStatusPath sp in Share.StatusView.GetPathArray(path, true)) { // Skip already processed paths. if (seenSet.Contains(sp.Path)) continue; // Remember that we have processed that path. seenSet.Add(sp.Path); // Sanity check. if (sp.IsUndetermined() || sp.IsTypeConflict()) throw new GateSyncException(); // Handle remote deletion. if (sp.HasServerFile()) payload.AddDeleteOp(true, sp.ServerObject.Inode, sp.ServerObject.CommitID); else if (sp.HasServerDir()) payload.AddDeleteOp(false, sp.ServerObject.Inode, sp.ServerObject.CommitID); // Handle local deletion. if (sp.HasLocalFile()) fileArray.Add(sp.Path); else if (sp.HasLocalDir()) dirArray.Add(sp.Path); } } // Delete the files and directories locally. foreach (String path in fileArray) { try { string absPath = Share.MakeAbsolute(path); File.SetAttributes(absPath, FileAttributes.Normal); File.Delete(absPath); } // Silently ignore NotFoundExceptions since we WANT // to have this path removed from the disk. catch (FileNotFoundException) { } catch (DirectoryNotFoundException) { } } foreach (String path in dirArray) { try { string absPath = Share.MakeAbsolute(path); File.SetAttributes(absPath, FileAttributes.Normal); Directory.Delete(Share.MakeAbsolute(path)); } // Silently ignore NotFoundExceptions since we WANT // to have this path removed from the disk. catch (FileNotFoundException) { } catch (DirectoryNotFoundException) { } } // Request the deletion on the server. Debug.Assert(Share.MetaDataManager.Status == MetaDataManagerStatus.Idle); if (payload.OpList.Count > 0) Share.MetaDataManager.QueueOperation(payload, null, MetaDataTask.Delete); } catch (IOException ex) { Misc.KwmTellUser("Unable to delete the selected file or folder : " + Environment.NewLine + Environment.NewLine + ex.Message, "Error Deleting File or Folder", MessageBoxIcon.Error); } catch (Exception ex) { HandleException(ex); } GateExit("DeletePath"); }
/// <summary> /// Add an operation to delete the ghost specified in the payload /// specified if it exists. /// </summary> /// <param name="path"></param> /// <param name="p"></param> private void DeleteGhostIfNeeded(String path, KfsPhase1Payload p) { KfsServerObject o = Share.ServerView.GetObjectByPath(path); if (o != null && o.IsGhost()) DeleteGhost(o, p); }
/// <summary> /// Helper method for MovePath(). 'fromLoc' and 'toLoc' must end with a /// delimiter when non-empty. /// </summary> private bool MovePathRecursive(String fromLoc, String toLoc, List<String> srcArray, KfsPhase1Payload payload, bool topLevelFlag, ref String typeConflictAction) { // Remember whether we moved all the sources we had to move. bool allMovedFlag = true; // Add the missing local directories for the destination. Share.AddMissingLocalDirectories(toLoc); // Move the sources. String fileExistAction = "a"; String dirExistAction = "a"; foreach (String srcName in srcArray) { // Get the objects involved. String srcRelPath = fromLoc + srcName; String dstRelPath = toLoc + srcName; String srcFullPath = Share.MakeAbsolute(srcRelPath); String dstFullPath = Share.MakeAbsolute(dstRelPath); KfsStatusPath srcStatus = Share.StatusView.GetPath(srcRelPath); KfsStatusPath dstStatus = Share.StatusView.GetPath(dstRelPath); Debug.Assert(srcStatus != null, "srcStatus == null"); KfsServerObject srcObject = srcStatus.ServerObject; KfsServerObject dstObject = (dstStatus == null) ? null : dstStatus.ServerObject; GatePathType srcType = GetInternalPathType(srcRelPath); GatePathType dstType = GetInternalPathType(dstRelPath); // Stale types, get out of here. if (srcType == GatePathType.None || srcType == GatePathType.Stale || dstType == GatePathType.Stale) throw new GateSyncException(); // There is a type conflict. else if (dstType != GatePathType.None && srcType != dstType) { String prompt = "The entry " + srcRelPath + " conflicts with the entry " + dstRelPath + "."; ShowChoiceDialog(prompt, "sc", ref typeConflictAction); allMovedFlag = false; continue; } // Add the 'to' directory remotely if the source exists remotely. if (srcObject != null) AddMissingServerDirectories(toLoc, payload); // We are in a directory-directory situation. if (srcType == GatePathType.Dir && dstType == GatePathType.Dir) { if (topLevelFlag) { // Skip or overwrite the directory. String prompt = "This folder already contains a folder named '" + dstRelPath + "'." + Environment.NewLine + "If the files in the existing folder have the same name as files in the folder you are moving, they will be replaced. Do you still want to move this folder?"; String res = ShowChoiceDialog(prompt, "swc", ref dirExistAction); if (res == "s") { allMovedFlag = false; continue; } } // Move all files and directories contained in the source directory. String newFromLoc = srcRelPath + "/"; String newToLoc = dstRelPath + "/"; List<String> newSrcArray = new List<String>(); foreach (String name in srcStatus.ChildTree.Keys) newSrcArray.Add(name); bool subMovedFlag = MovePathRecursive(newFromLoc, newToLoc, newSrcArray, payload, false, ref typeConflictAction); // All subdirectories and files were moved. if (subMovedFlag) { // Ask the server to delete the source if it exists remotely. if (srcStatus.HasServerDir()) { payload.AddDeleteOp(false, srcObject.Inode, srcObject.CommitID); } // Otherwise, delete the source locally since the server won't do it // for us. else { File.SetAttributes(srcFullPath, FileAttributes.Normal); Directory.Delete(srcFullPath); } } // Not all subdirectories and files were moved. else allMovedFlag = false; } // We are in a file-none, file-file or dir-none situation. else { // Remember whether we are in a file-none or file-file situation. bool fileFlag = (srcType == GatePathType.File); // The destination exists. if (dstStatus != null) { // Skip the source or overwrite the destination. if (topLevelFlag && dstStatus.OnServerAndNotGhost()) { String prompt = "The file " + dstRelPath + " already exists."; String res = ShowChoiceDialog(prompt, "swc", ref fileExistAction); if (res == "s") { allMovedFlag = false; continue; } } // Delete the destination file locally if it exists. if (File.Exists(dstFullPath)) { File.SetAttributes(dstFullPath, FileAttributes.Normal); File.Delete(dstFullPath); } } // The source exists only locally. if (srcObject == null) { // The destination exists. if (dstStatus != null) { // If the destination file exists remotely and it is not a ghost, // pretend its current version was downloaded. This will make the // source appear as ModifiedCurrent or UnmodifiedCurrent. if (dstStatus.OnServerAndNotGhost()) { KfsServerFile f = dstObject as KfsServerFile; Debug.Assert(f != null); f.UpdateDownloadVersion(f.CurrentVersion, true); } } // Move the file or directory locally. if (fileFlag) File.Move(srcFullPath, dstFullPath); else Directory.Move(srcFullPath, dstFullPath); } // The source exists remotely. else { // The destination file also exists remotely. Remove it. if (dstObject != null) payload.AddDeleteOp(true, dstObject.Inode, dstObject.CommitID); // Ask the server to move the file. UInt64 parentInode; UInt64 parentCommitID; String parentRelPath; GetServerObjectPath(dstRelPath, out parentInode, out parentCommitID, out parentRelPath); payload.AddMoveOp(fileFlag, srcObject.Inode, srcObject.CommitID, parentInode, parentCommitID, parentRelPath); } } } return allMovedFlag; }
/// <summary> /// Add an operation to delete the ghost specified in the payload /// specified. /// </summary> private void DeleteGhost(KfsServerObject o, KfsPhase1Payload p) { Debug.Assert(o.IsGhost()); p.AddDeleteOp(true, o.Inode, o.CommitID); }
/// <summary> /// Add the missing server directories on the relative path specified in /// the payload specified. /// </summary> private void AddMissingServerDirectories(String path, KfsPhase1Payload payload) { String cur = ""; UInt64 inode = 0; UInt64 commitID = 0; String rel = ""; foreach (String c in KfsPath.SplitRelativePath(path)) { if (cur != "") cur += "/"; cur += c; KfsServerObject o = Share.ServerView.GetObjectByPath(cur); // The current directory doesn't exist. Create it. if (o == null) { if (rel != "") rel += "/"; rel += c; payload.AddCreateOp(false, inode, commitID, rel); } // The current directory is a ghost. Delete the ghost and // replace it by a directory. else if (o.IsGhost()) { Debug.Assert(rel == ""); DeleteGhost(o, payload); rel = c; payload.AddCreateOp(false, inode, commitID, rel); } // The current directory already exists in the server view. else { Debug.Assert(rel == ""); Debug.Assert(o is KfsServerDirectory); inode = o.Inode; commitID = o.CommitID; } } }
/// <summary> /// Helper method for AddExternalFiles() and AddInternalFiles(). /// </summary> /// <param name="fileSet">Set of files to upload</param> /// <param name="dirSet">Set of directories to create remotely</param> private void AddFilesCommon(SortedSet<String> fileSet, SortedSet<String> dirSet) { KfsPhase1Payload payload = new KfsPhase1Payload(); SortedDictionary<UInt64, KfsFileUpload> uploadTree = new SortedDictionary<UInt64, KfsFileUpload>(); // Add the missing server directories. foreach (String path in dirSet) { if (path == "" || KfsPath.IsValidFileName(KfsPath.BaseName(path))) AddMissingServerDirectories(path, payload); else throw new GateIllegalException("Invalid folder name (" + path + "). Note that folders cannot contain any of the following characters:\n*, \\, /, ?, :, |, <, >."); } // Queue the upload of the files. try { foreach (String path in fileSet) { if (!KfsPath.IsValidFileName(KfsPath.BaseName(path))) throw new GateIllegalException("Invalid file name (" + path + "). Note that files cannot contain any of the following characters:\n*, \\, /, ?, :, |, <, >."); // Skip files that are unmodified. KfsServerFile f = Share.ServerView.GetObjectByPath(path) as KfsServerFile; if (f != null) { f.UpdateLocalStatus(); if (f.CurrentLocalStatus == LocalStatus.Unmodified) continue; } // Get rid of the ghost, if needed. DeleteGhostIfNeeded(path, payload); KfsFileUpload upload = Share.UploadManager.QueueUpload(path); // File can't already be queued for upload, // as the UI is frozen on a modal dialog and // we already checked if any files we are // adding is being transfered in AddFiles*. Debug.Assert(upload != null); uploadTree[upload.OrderID] = upload; } } // Cancel the queued uploads. catch (Exception) { foreach (KfsFileUpload upload in uploadTree.Values) Share.UploadManager.CancelUpload(upload.OrderID); throw; } // Ask the meta-data manager to perform the phase 1 operations. Debug.Assert(Share.MetaDataManager.Status == MetaDataManagerStatus.Idle); if (payload.OpList.Count > 0) Share.MetaDataManager.QueueOperation(payload, uploadTree, MetaDataTask.Cleanup); }
public KfsUploadThread(KfsShare share, byte[] ticket, ulong emailID, SortedDictionary<UInt64, KfsUploadBatchFile> orderTree, KfsPhase1Payload phase1Payload) : base(share, ticket, emailID) { Debug.Assert(orderTree.Count > 0); OrderTree = orderTree; Phase1Payload = phase1Payload; FileArray = new KfsUploadBatchFile[OrderTree.Count]; OrderTree.Values.CopyTo(FileArray, 0); }
/// <summary> /// Start a new file transfer batch. /// </summary> public void StartBatch() { Debug.Assert(OrderTree.Count > 0); Debug.Assert(Status == UploadManagerStatus.Idle); Debug.Assert(TransferThread == null); Debug.Assert(Ticket != null); try { ulong emailID = 0; // Build the payload and the tree. KfsPhase1Payload p = new KfsPhase1Payload(); SortedDictionary<UInt64, KfsUploadBatchFile> tree = new SortedDictionary<UInt64, KfsUploadBatchFile>(); int i = -1; foreach (KfsFileUpload f in OrderTree.Values) { Debug.Assert(f.Status == FileTransferStatus.Queued); f.Status = FileTransferStatus.Batched; // Update emailID on first file. // Note: this is not a safe: uploaded files could have bad email IDs // but we didn't care when this was written. if (++i == 0) { emailID = f.EmailID; } // Request creation. if (f.UpdateCommitID == 0) { // Use the tracked inode if it still exists, otherwise use the // last full path. KfsServerObject o = Share.ServerView.GetObjectByInode(f.TrackedInode); if (o == null) { o = Share.ServerView.Root; f.TrackedInode = o.Inode; f.TrackedPath = f.LastFullPath; } Debug.Assert(o is KfsServerDirectory); Debug.Assert(f.TrackedPath != ""); p.AddCreateOp(true, o.Inode, o.CommitID, f.TrackedPath); } // Request update. else { Debug.Assert(f.TrackedPath == ""); p.AddUpdateOp(f.TrackedInode, f.UpdateCommitID); } tree[f.OrderID] = new KfsUploadBatchFile(f.OrderID, f.UploadPath); } // Start the transfer. TransferThread = new KfsUploadThread(Share, Ticket, emailID, tree, p); TransferThread.Start(); Status = UploadManagerStatus.Batch; Ticket = null; } catch (Exception ex) { Share.FatalError(ex); } }