-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmkdir_all.go
More file actions
111 lines (96 loc) · 2.55 KB
/
Copy pathmkdir_all.go
File metadata and controls
111 lines (96 loc) · 2.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package fs
import (
"context"
"errors"
"lesiw.io/fs/path"
)
// A MkdirAllFS is a file system with the MkdirAll method.
//
// If not implemented, MkdirAll falls back to recursive creation using
// MkdirFS and StatFS.
type MkdirAllFS interface {
FS
// MkdirAll creates a directory named name, along with any necessary
// parents. If name is already a directory, MkdirAll does nothing and
// returns nil.
MkdirAll(ctx context.Context, name string) error
}
// MkdirAll creates a directory named name, along with any necessary parents.
// Analogous to: [os.MkdirAll], mkdir -p.
//
// The directory mode is obtained from [DirMode](ctx). If not set in the
// context, the default mode 0755 is used:
//
// ctx = fs.WithDirMode(ctx, 0700)
// fs.MkdirAll(ctx, fsys, "a/b/c") // All created with mode 0700
//
// If name is already a directory, MkdirAll does nothing and returns nil.
//
// Requires: [MkdirAllFS] || ([MkdirFS] && [StatFS])
func MkdirAll(ctx context.Context, fsys FS, name string) (err error) {
if name, err = localizePath(ctx, fsys, name); err != nil {
return err
}
mafs, ok := fsys.(MkdirAllFS)
if !ok {
return mkdirAllFallback(ctx, fsys, name)
}
err = mafs.MkdirAll(ctx, name)
if err != nil && !errors.Is(err, ErrUnsupported) {
return err
}
if err == nil {
return nil
}
// Fall through to fallback if ErrUnsupported
return mkdirAllFallback(ctx, fsys, name)
}
// mkdirAllFallback implements MkdirAll using MkdirFS and StatFS.
func mkdirAllFallback(ctx context.Context, fsys FS, name string) error {
// Check if fallback is possible - requires MkdirFS and StatFS
var (
mfs, hasMkdir = fsys.(MkdirFS)
_, hasStat = fsys.(StatFS)
)
if !hasMkdir || !hasStat {
return &PathError{
Op: "mkdir",
Path: name,
Err: ErrUnsupported,
}
}
// Check if it already exists
info, err := Stat(ctx, fsys, name)
if err == nil {
if info.IsDir() {
return nil
}
return &PathError{
Op: "mkdir",
Path: name,
Err: ErrNotDir,
}
}
// Try to create the directory
err = mfs.Mkdir(ctx, name)
if err == nil || errors.Is(err, ErrExist) {
return nil
}
// Mkdir failed - try to create parent directories
// Most commonly this is because the parent doesn't exist,
// but we recurse regardless of the error type
parent := path.Dir(name)
if parent == name {
return err
}
// Recursively create parent
if err := MkdirAll(ctx, fsys, parent); err != nil {
return err
}
// Try again (ignore ErrExist in case created by parent)
err = mfs.Mkdir(ctx, name)
if err == nil || errors.Is(err, ErrExist) {
return nil
}
return err
}