[cipd] stop doing weird ownership transfer

R=vadimsh@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2877643002
diff --git a/cipd/client/cipd/client.go b/cipd/client/cipd/client.go
index 9d2f8bb..08bbfdc 100644
--- a/cipd/client/cipd/client.go
+++ b/cipd/client/cipd/client.go
@@ -1272,13 +1272,9 @@
 		return err
 	}
 
-	// Make sure to close the file if paniced before OpenInstance call below.
-	owned := false
 	defer func() {
-		if !owned {
-			if err := instanceFile.Close(); err != nil {
-				logging.Warningf(ctx, "cipd: failed to close the package file - %s", err)
-			}
+		if err := instanceFile.Close(); err != nil && err != os.ErrClosed {
+			logging.Warningf(ctx, "cipd: failed to close the package file - %s", err)
 		}
 	}()
 
@@ -1289,14 +1285,8 @@
 		return err
 	}
 
-	owned = true // disarm the defer above, instance.Close closes instanceFile.
-	defer func() {
-		if err := instance.Close(); err != nil {
-			logging.Warningf(ctx, "cipd: failed to close the instance - %s", err)
-		}
-		// Opportunistically clean up trashed files.
-		client.doBatchAwareOp(ctx, batchAwareOpCleanupTrash)
-	}()
+	// Opportunistically clean up trashed files.
+	defer client.doBatchAwareOp(ctx, batchAwareOpCleanupTrash)
 
 	// Deploy it. 'defer' will take care of removing the temp file if needed.
 	_, err = client.deployer.DeployInstance(ctx, subdir, instance)
diff --git a/cipd/client/cipd/client_test.go b/cipd/client/cipd/client_test.go
index a6a2fe4..023512f 100644
--- a/cipd/client/cipd/client_test.go
+++ b/cipd/client/cipd/client_test.go
@@ -147,7 +147,6 @@
 		// Open it for reading.
 		inst, err := local.OpenInstance(ctx, bytes.NewReader(out.Bytes()), "", local.VerifyHash)
 		So(err, ShouldBeNil)
-		defer inst.Close()
 
 		Convey("RegisterInstance full flow", func(c C) {
 			client := mockClient(c, "", []expectedHTTPCall{
@@ -487,13 +486,13 @@
 		inst := buildInstanceInMemory(ctx, "testing/package", []local.File{
 			local.NewTestFile("file", "test data", false),
 		})
-		defer inst.Close()
 
 		client := mockClientForFetch(c, tempDir, []local.PackageInstance{inst})
 
 		Convey("FetchInstance (no cache)", func() {
 			reader, err := client.FetchInstance(ctx, inst.Pin())
 			So(err, ShouldBeNil)
+			defer reader.Close() // just in case
 
 			// Backed by a temp file.
 			tmpFile := reader.(deleteOnClose)
@@ -503,7 +502,7 @@
 			fetched, err := local.OpenInstance(ctx, reader, "", local.VerifyHash)
 			So(err, ShouldBeNil)
 			So(fetched.Pin(), ShouldResemble, inst.Pin())
-			fetched.Close() // this closes the 'reader' too
+			tmpFile.Close()
 
 			// The temp file is gone.
 			_, err = os.Stat(tmpFile.Name())
@@ -515,6 +514,7 @@
 
 			reader, err := client.FetchInstance(ctx, inst.Pin())
 			So(err, ShouldBeNil)
+			defer reader.Close()
 
 			// Backed by a real file.
 			cachedFile := reader.(*os.File)
@@ -524,7 +524,6 @@
 			fetched, err := local.OpenInstance(ctx, reader, "", local.VerifyHash)
 			So(err, ShouldBeNil)
 			So(fetched.Pin(), ShouldResemble, inst.Pin())
-			fetched.Close() // this closes the 'reader' too
 
 			// The real file is still there, in the cache.
 			_, err = os.Stat(cachedFile.Name())
@@ -553,10 +552,10 @@
 			So(err, ShouldBeNil)
 			out.Close()
 
-			fetched, err := local.OpenInstanceFile(ctx, tempFile, "", local.VerifyHash)
+			fetched, closer, err := local.OpenInstanceFile(ctx, tempFile, "", local.VerifyHash)
 			So(err, ShouldBeNil)
+			defer closer()
 			So(fetched.Pin(), ShouldResemble, inst.Pin())
-			fetched.Close()
 		})
 
 		Convey("FetchInstanceTo (with cache)", func() {
@@ -571,10 +570,10 @@
 			So(err, ShouldBeNil)
 			out.Close()
 
-			fetched, err := local.OpenInstanceFile(ctx, tempFile, "", local.VerifyHash)
+			fetched, closer, err := local.OpenInstanceFile(ctx, tempFile, "", local.VerifyHash)
 			So(err, ShouldBeNil)
+			defer closer()
 			So(fetched.Pin(), ShouldResemble, inst.Pin())
-			fetched.Close()
 		})
 
 		Convey("FetchAndDeployInstance works", func() {
@@ -652,11 +651,8 @@
 		Convey("EnsurePackages full flow", func(c C) {
 			// Prepare a bunch of packages.
 			a1 := buildInstanceInMemory(ctx, "pkg/a", []local.File{local.NewTestFile("file a 1", "test data", false)})
-			defer a1.Close()
 			a2 := buildInstanceInMemory(ctx, "pkg/a", []local.File{local.NewTestFile("file a 2", "test data", false)})
-			defer a2.Close()
 			b := buildInstanceInMemory(ctx, "pkg/b", []local.File{local.NewTestFile("file b", "test data", false)})
-			defer b.Close()
 
 			pil := func(insts ...local.PackageInstance) []local.PackageInstance {
 				return insts
diff --git a/cipd/client/cipd/local/reader.go b/cipd/client/cipd/local/reader.go
index 1340760..8ead5c0 100644
--- a/cipd/client/cipd/local/reader.go
+++ b/cipd/client/cipd/local/reader.go
@@ -39,9 +39,6 @@
 
 // PackageInstance represents a binary CIPD package file (with manifest inside).
 type PackageInstance interface {
-	// Close shuts down the package and its data provider.
-	Close() error
-
 	// Pin identifies package name and concreted instance ID of this package file.
 	Pin() common.Pin
 
@@ -60,12 +57,6 @@
 // If instanceID is not empty and verification mode is VerifyHash,
 // OpenInstance will check that package data matches the given instanceID. It
 // skips this check if verification mode is SkipHashVerification.
-//
-// If the call succeeds, PackageInstance takes ownership of io.ReadSeeker. If it
-// also implements io.Closer, it will be closed when package.Close() is called.
-//
-// If an error is returned, io.ReadSeeker remains unowned and caller is r
-// esponsible for closing it (if required).
 func OpenInstance(ctx context.Context, r io.ReadSeeker, instanceID string, v VerificationMode) (PackageInstance, error) {
 	out := &packageInstance{data: r}
 	if err := out.open(instanceID, v); err != nil {
@@ -75,14 +66,20 @@
 }
 
 // OpenInstanceFile opens a package instance file on disk.
-func OpenInstanceFile(ctx context.Context, path string, instanceID string, v VerificationMode) (inst PackageInstance, err error) {
+//
+// The caller of this function must call closer() if err != nil to close the
+// underlying file.
+func OpenInstanceFile(ctx context.Context, path string, instanceID string, v VerificationMode) (inst PackageInstance, closer func() error, err error) {
 	file, err := os.Open(path)
 	if err != nil {
 		return
 	}
 	inst, err = OpenInstance(ctx, file, instanceID, v)
 	if err != nil {
+		inst = nil
 		file.Close()
+	} else {
+		closer = file.Close
 	}
 	return
 }
@@ -412,20 +409,6 @@
 	return nil
 }
 
-func (inst *packageInstance) Close() (err error) {
-	if inst.data != nil {
-		if closer, ok := inst.data.(io.Closer); ok {
-			err = closer.Close()
-		}
-		inst.data = nil
-	}
-	inst.instanceID = ""
-	inst.zip = nil
-	inst.files = []File{}
-	inst.manifest = Manifest{}
-	return
-}
-
 func (inst *packageInstance) Pin() common.Pin {
 	return common.Pin{
 		PackageName: inst.manifest.PackageName,
diff --git a/cipd/client/cipd/local/reader_test.go b/cipd/client/cipd/local/reader_test.go
index 7ad2b8f..d73688c 100644
--- a/cipd/client/cipd/local/reader_test.go
+++ b/cipd/client/cipd/local/reader_test.go
@@ -64,9 +64,6 @@
 
 		// Open it.
 		inst, err := OpenInstance(ctx, bytes.NewReader(out.Bytes()), "", VerifyHash)
-		if inst != nil {
-			defer inst.Close()
-		}
 		So(inst, ShouldNotBeNil)
 		So(err, ShouldBeNil)
 		So(inst.Pin(), ShouldResemble, Pin{"testing", "23f2c4900785ac8faa2f38e473925b840e574ccc"})
@@ -107,7 +104,6 @@
 		So(err, ShouldBeNil)
 		So(inst, ShouldNotBeNil)
 		So(inst.Pin(), ShouldResemble, Pin{"testing", "23f2c4900785ac8faa2f38e473925b840e574ccc"})
-		inst.Close()
 
 		// Attempt to open it, providing incorrect instance ID.
 		inst, err = OpenInstance(ctx, source, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", VerifyHash)
@@ -119,7 +115,6 @@
 		So(err, ShouldBeNil)
 		So(inst, ShouldNotBeNil)
 		So(inst.Pin(), ShouldResemble, Pin{"testing", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"})
-		inst.Close()
 	})
 
 	Convey("OpenInstanceFile works", t, func() {
@@ -139,12 +134,10 @@
 		tempFile.Close()
 
 		// Read the package.
-		inst, err := OpenInstanceFile(ctx, tempFilePath, "", VerifyHash)
-		if inst != nil {
-			defer inst.Close()
-		}
-		So(inst, ShouldNotBeNil)
+		inst, closer, err := OpenInstanceFile(ctx, tempFilePath, "", VerifyHash)
 		So(err, ShouldBeNil)
+		defer closer()
+		So(inst, ShouldNotBeNil)
 	})
 
 	Convey("ExtractInstance works", t, func() {
@@ -176,9 +169,6 @@
 
 		// Extract files.
 		inst, err := OpenInstance(ctx, bytes.NewReader(out.Bytes()), "", VerifyHash)
-		if inst != nil {
-			defer inst.Close()
-		}
 		So(err, ShouldBeNil)
 		dest := &testDestination{}
 		err = ExtractInstance(ctx, inst, dest, func(f File) bool {
diff --git a/cipd/client/cli/main.go b/cipd/client/cli/main.go
index 0258a09..be1c65f 100644
--- a/cipd/client/cli/main.go
+++ b/cipd/client/cli/main.go
@@ -1545,11 +1545,11 @@
 }
 
 func deployInstanceFile(ctx context.Context, root string, instanceFile string) (common.Pin, error) {
-	inst, err := local.OpenInstanceFile(ctx, instanceFile, "", local.VerifyHash)
+	inst, closer, err := local.OpenInstanceFile(ctx, instanceFile, "", local.VerifyHash)
 	if err != nil {
 		return common.Pin{}, err
 	}
-	defer inst.Close()
+	defer closer()
 	inspectInstance(ctx, inst, false)
 
 	d := local.NewDeployer(root)
@@ -1627,12 +1627,12 @@
 	// the hash.
 	out.Close()
 	ok = true
-	inst, err := local.OpenInstanceFile(ctx, instanceFile, pin.InstanceID, local.SkipHashVerification)
+	inst, closer, err := local.OpenInstanceFile(ctx, instanceFile, pin.InstanceID, local.SkipHashVerification)
 	if err != nil {
 		os.Remove(instanceFile)
 		return common.Pin{}, err
 	}
-	defer inst.Close()
+	defer closer()
 	inspectInstance(ctx, inst, false)
 	return inst.Pin(), nil
 }
@@ -1667,11 +1667,11 @@
 }
 
 func inspectInstanceFile(ctx context.Context, instanceFile string, listFiles bool) (common.Pin, error) {
-	inst, err := local.OpenInstanceFile(ctx, instanceFile, "", local.VerifyHash)
+	inst, closer, err := local.OpenInstanceFile(ctx, instanceFile, "", local.VerifyHash)
 	if err != nil {
 		return common.Pin{}, err
 	}
-	defer inst.Close()
+	defer closer()
 	inspectInstance(ctx, inst, listFiles)
 	return inst.Pin(), nil
 }
@@ -1752,11 +1752,11 @@
 }
 
 func registerInstanceFile(ctx context.Context, instanceFile string, opts *registerOpts) (common.Pin, error) {
-	inst, err := local.OpenInstanceFile(ctx, instanceFile, "", local.VerifyHash)
+	inst, closer, err := local.OpenInstanceFile(ctx, instanceFile, "", local.VerifyHash)
 	if err != nil {
 		return common.Pin{}, err
 	}
-	defer inst.Close()
+	defer closer()
 	client, err := opts.clientOptions.makeCipdClient(ctx, "")
 	if err != nil {
 		return common.Pin{}, err