Skip to content

Commit 16cfae3

Browse files
committed
Lchown/Chown, Symlink tests
1 parent 6a66c65 commit 16cfae3

File tree

2 files changed

+140
-69
lines changed

2 files changed

+140
-69
lines changed

boltfs.go

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -596,13 +596,17 @@ func (fs *FileSystem) OpenFile(name string, flag int, perm os.FileMode) (absfs.F
596596

597597
// Stat returns the FileInfo structure describing file. If there is an error, it will be of type *os.PathError.
598598
func (fs *FileSystem) Stat(name string) (os.FileInfo, error) {
599+
599600
dir, filename := fs.cleanPath(name)
600601
node, err := fs.resolve(filepath.Join(dir, filename))
601602
if err != nil {
602603
return nil, err
603604
}
604605

605606
if node.Mode&os.ModeSymlink == 0 {
607+
if filename == "" {
608+
filename = dir
609+
}
606610
return inodeinfo{filename, node}, nil
607611
}
608612

@@ -966,17 +970,36 @@ func (fs *FileSystem) Chtimes(name string, atime time.Time, mtime time.Time) err
966970

967971
//Chown changes the owner and group ids of the named file
968972
func (fs *FileSystem) Chown(name string, uid, gid int) error {
973+
pathErr := &os.PathError{Op: "chown", Path: name}
974+
969975
dir, filename := fs.cleanPath(name)
970976
_, node := fs.loadParentChild(dir, filename)
971977
if node == nil {
972-
return os.ErrNotExist
978+
pathErr.Err = os.ErrNotExist
979+
return pathErr
973980
}
981+
if node.Mode&os.ModeSymlink == 0 {
982+
node.Uid = uint32(uid)
983+
node.Gid = uint32(gid)
974984

975-
node.Uid = uint32(uid)
976-
node.Gid = uint32(gid)
985+
_, err := fs.saveInode(node)
986+
if err != nil {
987+
pathErr.Err = nil
988+
return pathErr
989+
}
990+
return nil
991+
}
977992

978-
_, err := fs.saveInode(node)
979-
return err
993+
link, err := fs.loadSymlink(node.Ino)
994+
if err != nil {
995+
pathErr.Err = nil
996+
return pathErr
997+
}
998+
if !filepath.IsAbs(link) {
999+
link = filepath.Join(name, link)
1000+
}
1001+
1002+
return fs.Chown(link, uid, gid)
9801003
}
9811004

9821005
//Chmod changes the mode of the named file to mode.
@@ -1005,7 +1028,9 @@ func (fs *FileSystem) Lstat(name string) (os.FileInfo, error) {
10051028
pathErr.Err = err
10061029
return nil, pathErr
10071030
}
1008-
1031+
if filename == "" {
1032+
filename = dir
1033+
}
10091034
return inodeinfo{filename, node}, nil
10101035
}
10111036

@@ -1016,8 +1041,21 @@ func (fs *FileSystem) Lstat(name string) (os.FileInfo, error) {
10161041
// On Windows, it always returns the syscall.EWINDOWS error, wrapped in *PathError.
10171042
func (fs *FileSystem) Lchown(name string, uid, gid int) error {
10181043
pathErr := &os.PathError{Op: "lchown", Path: name}
1019-
pathErr.Err = absfs.ErrNotImplemented
1020-
return pathErr
1044+
dir, filename := fs.cleanPath(name)
1045+
_, node := fs.loadParentChild(dir, filename)
1046+
if node == nil {
1047+
pathErr.Err = os.ErrNotExist
1048+
return pathErr
1049+
}
1050+
node.Uid = uint32(uid)
1051+
node.Gid = uint32(gid)
1052+
1053+
_, err := fs.saveInode(node)
1054+
if err != nil {
1055+
pathErr.Err = err
1056+
return pathErr
1057+
}
1058+
return nil
10211059
}
10221060

10231061
// Readlink returns the destination of the named symbolic link. If there is an

boltfs_test.go

Lines changed: 94 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -530,97 +530,137 @@ func TestSymlinks(t *testing.T) {
530530
var fs absfs.SymlinkFileSystem
531531
fs = boltfs
532532

533-
testdata := [][]string{
534-
535-
// abs path
536-
{"/foo/bar/baz/", "/foo/baz"}, // /foo/bar/baz -> /foo/baz
537-
{"/foo/baz/"}, // /foo/baz
533+
type Test struct {
534+
Label string
535+
Path string
536+
Readlink string
537+
Error string
538+
}
538539

539-
// rel path
540-
{"/foo/bar/bat/", "../../bat"}, // /foo/bar/baz -> /foo/baz
541-
{"/foo/bat/"},
540+
testdata := []Test{
541+
{
542+
Label: "abs path",
543+
Path: "/foo/bar/baz",
544+
Readlink: "/foo/baz",
545+
Error: "",
546+
},
547+
{
548+
Label: "rel path",
549+
Path: "/foo/bar/bat",
550+
Readlink: "../../bat",
551+
Error: "",
552+
},
553+
{
554+
Label: "same dir",
555+
Path: "/bat",
556+
Readlink: "/foo",
557+
Error: "",
558+
},
559+
{
560+
Label: "broken",
561+
Path: "/broken",
562+
Readlink: "/nil",
563+
Error: "file does not exist",
564+
},
565+
{
566+
Label: "circular absolute",
567+
Path: "/circular/one/two/three",
568+
Readlink: "/circular/one",
569+
Error: "",
570+
},
571+
{
572+
Label: "circular relative",
573+
Path: "/circular/one/two/four",
574+
Readlink: "../..",
575+
Error: "",
576+
},
577+
}
542578

543-
// same dir
544-
{"/bat/", "/foo"}, // /bat -> /foo
579+
t.Run("symlinks", func(t *testing.T) {
580+
//Path to test map to make testing a little easier
581+
linkmap := make(map[string]Test)
545582

546-
// broken
547-
{"/broken/", "/nil"}, // /broken ->
583+
// build directory structure, with all symlink targets
584+
for _, test := range testdata {
585+
linkmap[test.Path] = test
548586

549-
// circular absolute
550-
{"/circular/one/two/three/", "/circular/one"}, // /broken -> /nil
587+
// folders in which files will be created
588+
fs.MkdirAll(filepath.Dir(test.Path), 0700)
551589

552-
// circular relative
553-
{"/circular/one/two/four/", "../../"}, // /broken -> /nil
590+
link := test.Readlink
591+
if !filepath.IsAbs(test.Readlink) {
592+
link = filepath.Join(test.Path, test.Readlink)
593+
}
554594

555-
}
595+
// folders that symlinks will link to
596+
fs.MkdirAll(link, 0700)
556597

557-
t.Run("symlinks", func(t *testing.T) {
558-
for _, pathset := range testdata {
559-
dir := filepath.Clean(pathset[0])
560-
if len(pathset) > 1 {
561-
fs.MkdirAll(filepath.Clean(pathset[1]), 0700)
562-
dir = filepath.Dir(dir)
563-
}
564-
err := fs.MkdirAll(dir, 0700)
565-
if err != nil {
566-
t.Logf("%s (error: %v)\n", dir, err)
567-
t.Error(err)
568-
}
569598
}
570-
for _, pathset := range testdata {
571-
if len(pathset) != 2 {
572-
continue
573-
}
574-
source := filepath.Clean(pathset[1])
575-
link := filepath.Clean(pathset[0])
576599

577-
// t.Logf("Symlink %q %q\n", source, link)
578-
err := boltfs.Symlink(source, link)
600+
// make symlinks
601+
for _, test := range testdata {
602+
603+
// The result of "fs.Readlink" is esssentally the same as the `source`
604+
// value in a copy, so `test.Readlink` is the 'source', and
605+
// `test.Path` is a symbolic link to it (a.k.a the 'target')
606+
err := boltfs.Symlink(test.Readlink, test.Path)
579607
if err != nil {
580-
t.Log(err)
608+
t.Fatalf("should not error: %s", err)
581609
}
582610
}
583611

612+
// remove the broken link target to break the link
584613
err := fs.Remove("/nil")
585614
if err != nil {
586615
t.Error(err)
587616
}
588617

589-
i := 0
618+
// `count` and `limit` are to prevent infinite walks if implementation fails
619+
// to stop on symlinks as defined by `filepath.Walk`.
620+
var count, limit int
621+
limit = 100
622+
590623
err = boltfs.Walk("/", func(path string, info os.FileInfo, err error) error {
624+
t.Log(path)
591625
link := ""
592626
if info.Mode()&os.ModeSymlink != 0 {
593627
link, err = fs.Readlink(path)
594628
if err != nil {
595629
return err
596630
}
597-
link = "-> " + link
631+
}
632+
if linkmap[path].Readlink != link {
633+
t.Errorf("expected symlink %q -> %q", path, linkmap[path])
598634
}
599635

600-
// t.Logf("%s %s %s\n", info.Mode(), path, link)
601636
stat, err := fs.Stat(path)
602-
if err != nil {
603-
// t.Logf("fs.Stat err=%s", err)
637+
if len(linkmap[path].Error) == 0 {
638+
if err != nil {
639+
t.Errorf("unexpected error %s", err)
640+
}
641+
} else {
642+
if err == nil {
643+
t.Errorf("expected error missing %s", linkmap[path].Error)
644+
}
604645
}
646+
605647
if stat != nil {
606-
// t.Logf("Stat: %s %q %s", stat.Mode(), stat.Name(), link)
648+
t.Logf(" Stat: %s %q %s", stat.Mode(), stat.Name(), link)
607649
} else {
608-
// t.Logf("Stat: <nil>")
650+
t.Logf("Stat: <nil>")
609651
}
610652

611653
lstat, err := fs.Lstat(path)
612654
if err != nil {
613655
t.Errorf("fs.Lstat error=%s", err)
614656
}
615-
_ = lstat
616-
// if lstat != nil {
617-
// t.Logf("Lstat: %s %q %s\n", lstat.Mode(), lstat.Name(), link)
618-
// } else {
619-
// t.Logf("Lstat: <nil>\n")
620-
// }
621-
// fmt.Printf("Lstat: %s %q %s\n\n", lstat.Mode(), lstat.Name(), link)
622-
i++
623-
if i > 100 {
657+
if lstat != nil {
658+
t.Logf("Lstat: %s %q %s\n", lstat.Mode(), lstat.Name(), link)
659+
} else {
660+
t.Logf("Lstat: <nil>\n")
661+
}
662+
count++
663+
if count > limit {
624664
return errors.New("too deep")
625665
}
626666
return nil
@@ -646,10 +686,3 @@ func TestSymlinks(t *testing.T) {
646686
}
647687

648688
}
649-
650-
// func (fs *FileSystem) Symlink(oldname, newname string) error
651-
// func (fs *FileSystem) Readlink(name string) (string, error)
652-
// func (fs *FileSystem) Lstat(name string) (os.FileInfo, error)
653-
// func (fs *FileSystem) Lchown(name string, uid, gid int) error
654-
655-
// func (fs *FileSystem) FastWalk(name string, fn func(string, os.FileMode) error) error

0 commit comments

Comments
 (0)