diff --git a/vfs/vfs.go b/vfs/vfs.go index a450c85eb..f12e6c3ad 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -654,18 +654,54 @@ func (vfs *VFS) Chtimes(name string, atime time.Time, mtime time.Time) error { return nil } +// mkdir creates a new directory with the specified name and permission bits +// (before umask) returning the new directory node. +func (vfs *VFS) mkdir(name string, perm os.FileMode) (*Dir, error) { + dir, leaf, err := vfs.StatParent(name) + if err != nil { + return nil, err + } + return dir.Mkdir(leaf) +} + // Mkdir creates a new directory with the specified name and permission bits // (before umask). func (vfs *VFS) Mkdir(name string, perm os.FileMode) error { - dir, leaf, err := vfs.StatParent(name) - if err != nil { - return err + _, err := vfs.mkdir(name, perm) + return err +} + +// mkdirAll creates a new directory with the specified name and +// permission bits (before umask) and all of its parent directories up +// to the root. +func (vfs *VFS) mkdirAll(name string, perm os.FileMode) (dir *Dir, err error) { + name = strings.Trim(name, "/") + // the root directory node already exists even if the directory isn't created yet + if name == "" { + return vfs.root, nil } - _, err = dir.Mkdir(leaf) - if err != nil { - return err + var parent, leaf string + dir, leaf, err = vfs.StatParent(name) + if err == ENOENT { + parent, leaf = path.Split(name) + dir, err = vfs.mkdirAll(parent, perm) } - return nil + if err != nil { + return nil, err + } + dir, err = dir.Mkdir(leaf) + if err != nil { + return nil, err + } + return dir, nil +} + +// MkdirAll creates a new directory with the specified name and +// permission bits (before umask) and all of its parent directories up +// to the root. +func (vfs *VFS) MkdirAll(name string, perm os.FileMode) error { + _, err := vfs.mkdirAll(name, perm) + return err } // ReadDir reads the directory named by dirname and returns diff --git a/vfs/vfs_test.go b/vfs/vfs_test.go index e262e7d2a..d0914ddb3 100644 --- a/vfs/vfs_test.go +++ b/vfs/vfs_test.go @@ -364,6 +364,84 @@ func TestVFSStatfs(t *testing.T) { assert.Equal(t, oldTime, vfs.usageTime) } +func TestVFSMkdir(t *testing.T) { + r, vfs := newTestVFS(t) + + if !r.Fremote.Features().CanHaveEmptyDirectories { + return // can't test if can't have empty directories + } + + r.CheckRemoteListing(t, nil, []string{}) + + // Try making the root + err := vfs.Mkdir("", 0777) + require.NoError(t, err) + r.CheckRemoteListing(t, nil, []string{}) + + // Try making a sub directory + err = vfs.Mkdir("a", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a"}) + + // Try making an existing directory + err = vfs.Mkdir("a", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a"}) + + // Try making a new directory + err = vfs.Mkdir("b/", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a", "b"}) + + // Try making a new directory + err = vfs.Mkdir("/c", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a", "b", "c"}) + + // Try making a new directory + err = vfs.Mkdir("/d/", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a", "b", "c", "d"}) +} + +func TestVFSMkdirAll(t *testing.T) { + r, vfs := newTestVFS(t) + + if !r.Fremote.Features().CanHaveEmptyDirectories { + return // can't test if can't have empty directories + } + + r.CheckRemoteListing(t, nil, []string{}) + + // Try making the root + err := vfs.MkdirAll("", 0777) + require.NoError(t, err) + r.CheckRemoteListing(t, nil, []string{}) + + // Try making a sub directory + err = vfs.MkdirAll("a/b/c/d", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"}) + + // Try making an existing directory + err = vfs.MkdirAll("a/b/c", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"}) + + // Try making an existing directory + err = vfs.MkdirAll("/a/b/c/", 0777) + require.NoError(t, err) + + r.CheckRemoteListing(t, nil, []string{"a", "a/b", "a/b/c", "a/b/c/d"}) +} + func TestFillInMissingSizes(t *testing.T) { const unknownFree = 10 for _, test := range []struct {