Ejemplo n.º 1
0
 public KfsMetaDataThread(KfsShare s, byte[] ticket, KfsPhase1Payload payload)
     : base(s, ticket, 0)
 {
     Phase1Payload = payload;
 }
Ejemplo n.º 2
0
        /// <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);
            }
        }
Ejemplo n.º 3
0
 /// <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();
 }
Ejemplo n.º 4
0
 /// <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;
 }
Ejemplo n.º 5
0
 /// <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;
 }
Ejemplo n.º 6
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
        /// <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");
        }
Ejemplo n.º 7
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
        /// <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");
        }
Ejemplo n.º 8
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
        /// <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");
        }
Ejemplo n.º 9
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
        /// <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");
        }
Ejemplo n.º 10
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
 /// <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);
 }
Ejemplo n.º 11
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
        /// <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;
        }
Ejemplo n.º 12
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
 /// <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);
 }
Ejemplo n.º 13
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
        /// <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;
                }
            }
        }
Ejemplo n.º 14
0
Archivo: KfsGate.cs Proyecto: tmbx/kwm
        /// <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);
        }
Ejemplo n.º 15
0
 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);
 }
Ejemplo n.º 16
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);
            }
        }