Skip to content

Commit 5b4754d

Browse files
committed
webdav: factor out a moveFiles function, and make the tests call that
instead of FileSystem.Rename directly. Dir.Rename's behavior wrt overwriting existing files and directories is OS-dependent. Fixes golang/go#9786 Change-Id: If42728caa6f0f38f8e3d6b1fcdda8c2d272080d6 Reviewed-on: https://go-review.googlesource.com/4341 Reviewed-by: Nick Cooper <[email protected]> Reviewed-by: Alex Brainman <[email protected]>
1 parent f090b05 commit 5b4754d

File tree

3 files changed

+52
-54
lines changed

3 files changed

+52
-54
lines changed

webdav/file.go

+34
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ func slashClean(name string) string {
3030
//
3131
// Each method has the same semantics as the os package's function of the same
3232
// name.
33+
//
34+
// Note that the os.Rename documentation says that "OS-specific restrictions
35+
// might apply". In particular, whether or not renaming a file or directory
36+
// overwriting another existing file or directory is an error is OS-dependent.
3337
type FileSystem interface {
3438
Mkdir(name string, perm os.FileMode) error
3539
OpenFile(name string, flag int, perm os.FileMode) (File, error)
@@ -548,6 +552,36 @@ func (f *memFile) Write(p []byte) (int, error) {
548552
return lenp, nil
549553
}
550554

555+
// moveFiles moves files and/or directories from src to dst.
556+
//
557+
// See section 9.9.4 for when various HTTP status codes apply.
558+
func moveFiles(fs FileSystem, src, dst string, overwrite bool) (status int, err error) {
559+
created := false
560+
if _, err := fs.Stat(dst); err != nil {
561+
if !os.IsNotExist(err) {
562+
return http.StatusForbidden, err
563+
}
564+
created = true
565+
} else if overwrite {
566+
// Section 9.9.3 says that "If a resource exists at the destination
567+
// and the Overwrite header is "T", then prior to performing the move,
568+
// the server must perform a DELETE with "Depth: infinity" on the
569+
// destination resource.
570+
if err := fs.RemoveAll(dst); err != nil {
571+
return http.StatusForbidden, err
572+
}
573+
} else {
574+
return http.StatusPreconditionFailed, os.ErrExist
575+
}
576+
if err := fs.Rename(src, dst); err != nil {
577+
return http.StatusForbidden, err
578+
}
579+
if created {
580+
return http.StatusCreated, nil
581+
}
582+
return http.StatusNoContent, nil
583+
}
584+
551585
// copyFiles copies files and/or directories from src to dst.
552586
//
553587
// See section 9.8.5 for when various HTTP status codes apply.

webdav/file_test.go

+17-23
Original file line numberDiff line numberDiff line change
@@ -321,44 +321,41 @@ func testFS(t *testing.T, fs FileSystem) {
321321
" stat /d want dir",
322322
" stat /d/m want dir",
323323
" find / /a /b /d /d/m",
324-
"rename /b /b want ok",
325-
" stat /b want 2",
326-
" stat /c want errNotExist",
327-
"rename /b /c want ok",
324+
"move__ o=F /b /c want ok",
328325
" stat /b want errNotExist",
329326
" stat /c want 2",
330327
" stat /d/m want dir",
331328
" stat /d/n want errNotExist",
332329
" find / /a /c /d /d/m",
333-
"rename /d/m /d/n want ok",
330+
"move__ o=F /d/m /d/n want ok",
334331
"create /d/n/q QQQQ want ok",
335332
" stat /d/m want errNotExist",
336333
" stat /d/n want dir",
337334
" stat /d/n/q want 4",
338-
"rename /d /d/n/z want err",
339-
"rename /c /d/n/q want ok",
335+
"move__ o=F /d /d/n/z want err",
336+
"move__ o=T /c /d/n/q want ok",
340337
" stat /c want errNotExist",
341338
" stat /d/n/q want 2",
342339
" find / /a /d /d/n /d/n/q",
343340
"create /d/n/r RRRRR want ok",
344341
"mk-dir /u want ok",
345342
"mk-dir /u/v want ok",
346-
"rename /d/n /u want err",
343+
"move__ o=F /d/n /u want errExist",
347344
"create /t TTTTTT want ok",
348-
"rename /d/n /t want err",
345+
"move__ o=F /d/n /t want errExist",
349346
"rm-all /t want ok",
350-
"rename /d/n /t want ok",
347+
"move__ o=F /d/n /t want ok",
351348
" stat /d want dir",
352349
" stat /d/n want errNotExist",
353350
" stat /d/n/r want errNotExist",
354351
" stat /t want dir",
355352
" stat /t/q want 2",
356353
" stat /t/r want 5",
357354
" find / /a /d /t /t/q /t/r /u /u/v",
358-
"rename /t / want err",
359-
"rename /t /u/v want ok",
355+
"move__ o=F /t / want errExist",
356+
"move__ o=T /t /u/v want ok",
360357
" stat /u/v/r want 5",
361-
"rename / /z want err",
358+
"move__ o=F / /z want err",
362359
" find / /a /d /u /u/v /u/v/q /u/v/r",
363360
" stat /a want 1",
364361
" stat /b want errNotExist",
@@ -445,13 +442,13 @@ func testFS(t *testing.T, fs FileSystem) {
445442
t.Fatalf("test case #%d %q:\ngot %s\nwant %s", i, tc, got, want)
446443
}
447444

448-
case "copy__", "mk-dir", "rename", "rm-all", "stat":
445+
case "copy__", "mk-dir", "move__", "rm-all", "stat":
449446
nParts := 3
450447
switch op {
451448
case "copy__":
452449
nParts = 6
453-
case "rename":
454-
nParts = 4
450+
case "move__":
451+
nParts = 5
455452
}
456453
parts := strings.Split(arg, " ")
457454
if len(parts) != nParts {
@@ -461,18 +458,15 @@ func testFS(t *testing.T, fs FileSystem) {
461458
got, opErr := "", error(nil)
462459
switch op {
463460
case "copy__":
464-
overwrite, depth := false, 0
465-
if parts[0] == "o=T" {
466-
overwrite = true
467-
}
461+
depth := 0
468462
if parts[1] == "d=∞" {
469463
depth = infiniteDepth
470464
}
471-
_, opErr = copyFiles(fs, parts[2], parts[3], overwrite, depth, 0)
465+
_, opErr = copyFiles(fs, parts[2], parts[3], parts[0] == "o=T", depth, 0)
472466
case "mk-dir":
473467
opErr = fs.Mkdir(parts[0], 0777)
474-
case "rename":
475-
opErr = fs.Rename(parts[0], parts[1])
468+
case "move__":
469+
_, opErr = moveFiles(fs, parts[1], parts[2], parts[0] == "o=T")
476470
case "rm-all":
477471
opErr = fs.RemoveAll(parts[0])
478472
case "stat":

webdav/webdav.go

+1-31
Original file line numberDiff line numberDiff line change
@@ -247,36 +247,7 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status
247247
return http.StatusBadRequest, errInvalidDepth
248248
}
249249
}
250-
251-
created := false
252-
if _, err := h.FileSystem.Stat(dst); err != nil {
253-
if !os.IsNotExist(err) {
254-
return http.StatusForbidden, err
255-
}
256-
created = true
257-
} else {
258-
switch r.Header.Get("Overwrite") {
259-
case "T":
260-
// Section 9.9.3 says that "If a resource exists at the destination
261-
// and the Overwrite header is "T", then prior to performing the move,
262-
// the server must perform a DELETE with "Depth: infinity" on the
263-
// destination resource.
264-
if err := h.FileSystem.RemoveAll(dst); err != nil {
265-
return http.StatusForbidden, err
266-
}
267-
case "F":
268-
return http.StatusPreconditionFailed, os.ErrExist
269-
default:
270-
return http.StatusBadRequest, errInvalidOverwrite
271-
}
272-
}
273-
if err := h.FileSystem.Rename(src, dst); err != nil {
274-
return http.StatusForbidden, err
275-
}
276-
if created {
277-
return http.StatusCreated, nil
278-
}
279-
return http.StatusNoContent, nil
250+
return moveFiles(h.FileSystem, src, dst, r.Header.Get("Overwrite") == "T")
280251
}
281252

282253
func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus int, retErr error) {
@@ -450,7 +421,6 @@ var (
450421
errInvalidIfHeader = errors.New("webdav: invalid If header")
451422
errInvalidLockInfo = errors.New("webdav: invalid lock info")
452423
errInvalidLockToken = errors.New("webdav: invalid lock token")
453-
errInvalidOverwrite = errors.New("webdav: invalid overwrite")
454424
errInvalidPropfind = errors.New("webdav: invalid propfind")
455425
errInvalidResponse = errors.New("webdav: invalid response")
456426
errInvalidTimeout = errors.New("webdav: invalid timeout")

0 commit comments

Comments
 (0)