diff --git a/cmd/cp-main.go b/cmd/cp-main.go index 8025e78de8..049a3bcd9e 100644 --- a/cmd/cp-main.go +++ b/cmd/cp-main.go @@ -61,6 +61,10 @@ var ( Name: "attr", Usage: "add custom metadata for the object", }, + cli.BoolFlag{ + Name: "continue, c", + Usage: "create or resume copy session.", + }, } ) @@ -127,6 +131,9 @@ EXAMPLES: 13. Copy a text file to an object storage and assign REDUCED_REDUNDANCY storage-class to the uploaded object. {{.Prompt}} {{.HelpName}} --storage-class REDUCED_REDUNDANCY myobject.txt play/mybucket + + 14. Copy a text file to an object storage and create or resume copy session. + $ {{.HelpName}} --recursive --continue myobject.txt play/mybucket `, } @@ -431,7 +438,7 @@ loop: // For critical errors we should exit. Session // can be resumed after the user figures out // the problem. - session.CloseAndDie() + session.copyCloseAndDie(session.Header.CommandBoolFlags["session"]) } } } @@ -497,7 +504,13 @@ func mainCopy(ctx *cli.Context) error { } sse := ctx.String("encrypt") - session := newSessionV8() + sessionID := getHash("cp", ctx.Args()) + if ctx.Bool("continue") && isSessionExists(sessionID) { + resumeSession(sessionID) + return nil + } + + session := newSessionV8(sessionID) session.Header.CommandType = "cp" session.Header.CommandBoolFlags["recursive"] = recursive session.Header.CommandStringFlags["older-than"] = olderThan @@ -505,6 +518,7 @@ func mainCopy(ctx *cli.Context) error { session.Header.CommandStringFlags["storage-class"] = storageClass session.Header.CommandStringFlags["encrypt-key"] = sseKeys session.Header.CommandStringFlags["encrypt"] = sse + session.Header.CommandBoolFlags["session"] = ctx.Bool("continue") session.Header.UserMetaData = userMetaMap var e error diff --git a/cmd/session-v8.go b/cmd/session-v8.go index bd7af95d89..01df67f211 100644 --- a/cmd/session-v8.go +++ b/cmd/session-v8.go @@ -162,7 +162,7 @@ func loadSessionV8(sid string) (*sessionV8, *probe.Error) { } // newSessionV8 provides a new session. -func newSessionV8() *sessionV8 { +func newSessionV8(sessionID string) *sessionV8 { s := &sessionV8{} s.Header = &sessionV8Header{} s.Header.Version = globalSessionConfigVersion @@ -177,7 +177,7 @@ func newSessionV8() *sessionV8 { s.Header.UserMetaData = make(map[string]string) s.Header.When = UTCNow() s.mutex = new(sync.Mutex) - s.SessionID = newRandomID(8) + s.SessionID = sessionID sessionDataFile, err := getSessionDataFile(s.SessionID) fatalIf(err.Trace(s.SessionID), "Unable to create session data file \""+sessionDataFile+"\".") @@ -375,6 +375,18 @@ func (s sessionV8) CloseAndDie() { console.Fatalln("Session safely terminated. To resume session `mc session resume " + s.SessionID + "`") } +func (s sessionV8) copyCloseAndDie(sessionFlag bool) { + if sessionFlag { + s.Close() + console.Fatalln("Command terminated safely. Run this command to resume copy again.") + } else { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.DataFP.Close() // ignore error. + } +} + // Create a factory function to simplify checking if // object was last operated on. func isLastFactory(lastURL string) func(string) bool { diff --git a/cmd/session.go b/cmd/session.go index 81ecd1419f..1b41cd13f8 100644 --- a/cmd/session.go +++ b/cmd/session.go @@ -17,6 +17,8 @@ package cmd import ( + "crypto/sha256" + "encoding/hex" "os" "path/filepath" "strings" @@ -136,3 +138,14 @@ func removeSessionDataFile(sid string) { } os.Remove(dataFile) } + +func getHash(prefix string, args []string) string { + hasher := sha256.New() + for _, arg := range args { + if _, err := hasher.Write([]byte(arg)); err != nil { + panic(err) + } + } + + return prefix + "-" + hex.EncodeToString(hasher.Sum(nil)) +} diff --git a/cmd/session_test.go b/cmd/session_test.go index ffc1c9edd5..d01fb4a088 100644 --- a/cmd/session_test.go +++ b/cmd/session_test.go @@ -35,9 +35,9 @@ func (s *TestSuite) TestSession(c *C) { c.Assert(err, IsNil) c.Assert(isSessionDirExists(), Equals, true) - session := newSessionV8() + session := newSessionV8(getHash("cp", []string{"mybucket", "myminio/mybucket"})) c.Assert(session.Header.CommandArgs, IsNil) - c.Assert(len(session.SessionID), Equals, 8) + c.Assert(len(session.SessionID) >= 8, Equals, true) _, e := os.Stat(session.DataFP.Name()) c.Assert(e, IsNil)