private void doUpload(object o)
        {
            TcpClient dataSocket = (TcpClient)o;

            byte[] keyBytes = new byte[KEYSIZE];
            int    len, timeout = 5;

            do
            {
                len = dataSocket.GetStream().Read(keyBytes, 0, KEYSIZE);
            } while (len == 0 && (--timeout) > 0);

            if (len != KEYSIZE)
            {
                dataSocket.Close();
                return;
            }
            String      keyStr = System.Text.Encoding.ASCII.GetString(keyBytes);
            UPLOAD_INFO f; //NONNULLABLE POS

            lock (pendingUploads)
            {
                if (pendingUploads.ContainsKey(keyStr))
                {
                    f = pendingUploads[keyStr];
                    pendingUploads.Remove(keyStr);
                }
                else
                {
                    dataSocket.Close();
                    return;
                }
            }

            String tmpFileName = Path.GetTempFileName();

            try
            {
                //int totalBytes = 0;
                //byte[] buffer = new byte [1024 * 1024];
                Console.WriteLine("Saving to file {0}", tmpFileName);

                //StreamReader reader = new StreamReader(dataSocket.GetStream());
                FileStream writer = new FileStream(tmpFileName, FileMode.OpenOrCreate);
                //int nBytes = dataSocket.GetStream().Read(buffer, 0, f.sz);
                //reader.ReadBlock(buffer, 0, Math.Min(buffer.Length, f.sz));
                dataSocket.GetStream().CopyTo(writer);

                /*
                 * while (nBytes > 0)
                 * {
                 *  writer.Write(buffer, 0, nBytes);
                 *  totalBytes += nBytes;
                 *  nBytes = dataSocket.GetStream().Read(buffer, 0, Math.Min(buffer.Length, f.sz));
                 * }*/

                writer.Close();
                //reader.Close();
                dataSocket.Close();
                FileInfo f_info = new FileInfo(tmpFileName);
                String   computedHash;
                using (var md5 = System.Security.Cryptography.MD5.Create()) {
                    using (var stream = System.IO.File.OpenRead(tmpFileName)) {
                        byte[] hashBits = md5.ComputeHash(stream);
                        computedHash = Convert.ToBase64String(hashBits);
                    }
                }

                //f_info.Length
                if (f_info.Length == f.sz && f.curHash.Equals(computedHash))
                {
                    try
                    {
                        azureLink.upload(f, tmpFileName);
                    } catch (Exception e)
                    {
                        if (e is DBLikeExceptions.CloudContainerNotFoundException ||
                            e is DBLikeExceptions.UnauthorizedAccessException)
                        {
                            Console.WriteLine("Unexpected error during uploading. Likely shared folder " +
                                              "configuration changed during upload. \r\n{0}", e.Message);
                        }
                        else if (e is Microsoft.WindowsAzure.Storage.StorageException)
                        {
                            Console.WriteLine("Azure storage exception {0}", e.Message);
                        }
                        else
                        {
                            throw e;
                        }
                    }
                }
                else
                {
                    Console.WriteLine("Invalid file upload: expected {0} bits hash {1}, got {2} bits hash {3}.",
                                      f.sz,
                                      f.curHash,
                                      f_info.Length,
                                      computedHash);
                }
            }
            catch (IOException)
            {
                Console.WriteLine("I/O timeout reading file");
            }
            finally
            {
                Console.WriteLine("Download complete: release global file lock");
                this.globalLocks.releaseWriteLock(f.absPath);
                try
                {
                    //Console.WriteLine("Debug mode: not deleting temp file");
                    System.IO.File.Delete(tmpFileName);
                }
                catch (IOException ex)
                {
                    Console.WriteLine("Warning: unable to delete temp file {0}, cause {1}",
                                      tmpFileName,
                                      ex.Message);
                }
            }
        }