forked from TrueCloudLab/rclone
webdav: fix directory creation with 4shared - fixes #4428
When we run MKCOL on 4shared on a directory that already exists, this returns a 409/Conflict error. However this error code usually means that the intermediate collections need creating. The actual error code to return when trying to create a directory that already exists isn't specified in the RFC, only that an error MUST be returned and there are already 3 statuses checked in the code. However using 409 makes rclone's usual strategy for making directories fail and return the 409 error. This patch tries the MKCOL and if it returns an unrecognised error code, then calls PROPFIND on the directory to discover whether the directory really exists or not. This should also cover other WebDAV servers returning other error messages we haven't accounted for in the code yet.
This commit is contained in:
parent
3286d1992b
commit
d35673efc6
1 changed files with 37 additions and 6 deletions
|
@ -686,8 +686,8 @@ func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, opt
|
||||||
|
|
||||||
// mkParentDir makes the parent of the native path dirPath if
|
// mkParentDir makes the parent of the native path dirPath if
|
||||||
// necessary and any directories above that
|
// necessary and any directories above that
|
||||||
func (f *Fs) mkParentDir(ctx context.Context, dirPath string) error {
|
func (f *Fs) mkParentDir(ctx context.Context, dirPath string) (err error) {
|
||||||
// defer log.Trace(dirPath, "")("")
|
// defer log.Trace(dirPath, "")("err=%v", &err)
|
||||||
// chop off trailing / if it exists
|
// chop off trailing / if it exists
|
||||||
if strings.HasSuffix(dirPath, "/") {
|
if strings.HasSuffix(dirPath, "/") {
|
||||||
dirPath = dirPath[:len(dirPath)-1]
|
dirPath = dirPath[:len(dirPath)-1]
|
||||||
|
@ -699,6 +699,27 @@ func (f *Fs) mkParentDir(ctx context.Context, dirPath string) error {
|
||||||
return f.mkdir(ctx, parent)
|
return f.mkdir(ctx, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _dirExists - list dirPath to see if it exists
|
||||||
|
//
|
||||||
|
// dirPath should be a native path ending in a /
|
||||||
|
func (f *Fs) _dirExists(ctx context.Context, dirPath string) (exists bool) {
|
||||||
|
opts := rest.Opts{
|
||||||
|
Method: "PROPFIND",
|
||||||
|
Path: dirPath,
|
||||||
|
ExtraHeaders: map[string]string{
|
||||||
|
"Depth": "0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var result api.Multistatus
|
||||||
|
var resp *http.Response
|
||||||
|
var err error
|
||||||
|
err = f.pacer.Call(func() (bool, error) {
|
||||||
|
resp, err = f.srv.CallXML(ctx, &opts, nil, &result)
|
||||||
|
return f.shouldRetry(resp, err)
|
||||||
|
})
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
// low level mkdir, only makes the directory, doesn't attempt to create parents
|
// low level mkdir, only makes the directory, doesn't attempt to create parents
|
||||||
func (f *Fs) _mkdir(ctx context.Context, dirPath string) error {
|
func (f *Fs) _mkdir(ctx context.Context, dirPath string) error {
|
||||||
// We assume the root is already created
|
// We assume the root is already created
|
||||||
|
@ -719,19 +740,29 @@ func (f *Fs) _mkdir(ctx context.Context, dirPath string) error {
|
||||||
return f.shouldRetry(resp, err)
|
return f.shouldRetry(resp, err)
|
||||||
})
|
})
|
||||||
if apiErr, ok := err.(*api.Error); ok {
|
if apiErr, ok := err.(*api.Error); ok {
|
||||||
// already exists
|
// Check if it already exists. The response code for this isn't
|
||||||
|
// defined in the RFC so the implementations vary wildly.
|
||||||
|
//
|
||||||
// owncloud returns 423/StatusLocked if the create is already in progress
|
// owncloud returns 423/StatusLocked if the create is already in progress
|
||||||
if apiErr.StatusCode == http.StatusMethodNotAllowed || apiErr.StatusCode == http.StatusNotAcceptable || apiErr.StatusCode == http.StatusLocked {
|
if apiErr.StatusCode == http.StatusMethodNotAllowed || apiErr.StatusCode == http.StatusNotAcceptable || apiErr.StatusCode == http.StatusLocked {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// 4shared returns a 409/StatusConflict here which clashes
|
||||||
|
// horribly with the intermediate paths don't exist meaning. So
|
||||||
|
// check to see if actually exists. This will correct other
|
||||||
|
// error codes too.
|
||||||
|
if f._dirExists(ctx, dirPath) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// mkdir makes the directory and parents using native paths
|
// mkdir makes the directory and parents using native paths
|
||||||
func (f *Fs) mkdir(ctx context.Context, dirPath string) error {
|
func (f *Fs) mkdir(ctx context.Context, dirPath string) (err error) {
|
||||||
// defer log.Trace(dirPath, "")("")
|
// defer log.Trace(dirPath, "")("err=%v", &err)
|
||||||
err := f._mkdir(ctx, dirPath)
|
err = f._mkdir(ctx, dirPath)
|
||||||
if apiErr, ok := err.(*api.Error); ok {
|
if apiErr, ok := err.(*api.Error); ok {
|
||||||
// parent does not exist so create it first then try again
|
// parent does not exist so create it first then try again
|
||||||
if apiErr.StatusCode == http.StatusConflict {
|
if apiErr.StatusCode == http.StatusConflict {
|
||||||
|
|
Loading…
Reference in a new issue