webdav: let DeadPropsHolder.DeadProps return an error.

A DeadPropsHolder could be backed by disk, whether directly or
indirectly via a database, so we should allow for I/O to fail.

Change-Id: Id40bcc86eb854212ef254ea1e1c54fd2a2b1960a
Reviewed-on: https://go-review.googlesource.com/10472
Reviewed-by: Robert Stepanek <robert.stepanek@gmail.com>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/webdav/file.go b/webdav/file.go
index 6e3563e..3d95c6c 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -423,17 +423,17 @@
 	}
 }
 
-func (n *memFSNode) DeadProps() map[xml.Name]Property {
+func (n *memFSNode) DeadProps() (map[xml.Name]Property, error) {
 	n.mu.Lock()
 	defer n.mu.Unlock()
 	if len(n.deadProps) == 0 {
-		return nil
+		return nil, nil
 	}
 	ret := make(map[xml.Name]Property, len(n.deadProps))
 	for k, v := range n.deadProps {
 		ret[k] = v
 	}
-	return ret
+	return ret, nil
 }
 
 func (n *memFSNode) Patch(patches []Proppatch) ([]Propstat, error) {
@@ -484,7 +484,7 @@
 // A *memFile implements the optional DeadPropsHolder interface.
 var _ DeadPropsHolder = (*memFile)(nil)
 
-func (f *memFile) DeadProps() map[xml.Name]Property              { return f.n.DeadProps() }
+func (f *memFile) DeadProps() (map[xml.Name]Property, error)     { return f.n.DeadProps() }
 func (f *memFile) Patch(patches []Proppatch) ([]Propstat, error) { return f.n.Patch(patches) }
 
 func (f *memFile) Close() error {
@@ -626,6 +626,27 @@
 	return http.StatusNoContent, nil
 }
 
+func copyProps(dst, src File) error {
+	d, ok := dst.(DeadPropsHolder)
+	if !ok {
+		return nil
+	}
+	s, ok := src.(DeadPropsHolder)
+	if !ok {
+		return nil
+	}
+	m, err := s.DeadProps()
+	if err != nil {
+		return err
+	}
+	props := make([]Property, 0, len(m))
+	for _, prop := range m {
+		props = append(props, prop)
+	}
+	_, err = d.Patch([]Proppatch{{Props: props}})
+	return err
+}
+
 // copyFiles copies files and/or directories from src to dst.
 //
 // See section 9.8.5 for when various HTTP status codes apply.
@@ -702,17 +723,7 @@
 
 		}
 		_, copyErr := io.Copy(dstFile, srcFile)
-		var propsErr error
-		if s, ok := srcFile.(DeadPropsHolder); ok {
-			if d, ok := dstFile.(DeadPropsHolder); ok {
-				m := s.DeadProps()
-				props := make([]Property, 0, len(m))
-				for _, prop := range m {
-					props = append(props, prop)
-				}
-				_, propsErr = d.Patch([]Proppatch{{Props: props}})
-			}
-		}
+		propsErr := copyProps(dstFile, srcFile)
 		closeErr := dstFile.Close()
 		if copyErr != nil {
 			return http.StatusInternalServerError, copyErr
diff --git a/webdav/file_test.go b/webdav/file_test.go
index 2930d60..16c7ac9 100644
--- a/webdav/file_test.go
+++ b/webdav/file_test.go
@@ -825,7 +825,7 @@
 	}
 }
 
-func TestMoveCopyProps(t *testing.T) {
+func TestCopyMoveProps(t *testing.T) {
 	fs := NewMemFS()
 	create := func(name string) error {
 		f, err := fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
@@ -856,8 +856,11 @@
 		if err != nil {
 			return nil, err
 		}
-		m := f.(DeadPropsHolder).DeadProps()
+		m, pErr := f.(DeadPropsHolder).DeadProps()
 		cErr := f.Close()
+		if pErr != nil {
+			return nil, pErr
+		}
 		if cErr != nil {
 			return nil, cErr
 		}
diff --git a/webdav/prop.go b/webdav/prop.go
index a0c2c6d..502f80e 100644
--- a/webdav/prop.go
+++ b/webdav/prop.go
@@ -80,7 +80,7 @@
 // method of DeadPropsHolder implementations.
 type DeadPropsHolder interface {
 	// DeadProps returns a copy of the dead properties held.
-	DeadProps() map[xml.Name]Property
+	DeadProps() (map[xml.Name]Property, error)
 
 	// Patch patches the dead properties held.
 	//
@@ -166,7 +166,10 @@
 
 	var deadProps map[xml.Name]Property
 	if dph, ok := f.(DeadPropsHolder); ok {
-		deadProps = dph.DeadProps()
+		deadProps, err = dph.DeadProps()
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	pstatOK := Propstat{Status: http.StatusOK}
@@ -211,7 +214,10 @@
 
 	var deadProps map[xml.Name]Property
 	if dph, ok := f.(DeadPropsHolder); ok {
-		deadProps = dph.DeadProps()
+		deadProps, err = dph.DeadProps()
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	pnames := make([]xml.Name, 0, len(liveProps)+len(deadProps))