Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Plan 9 support #78

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .ci/test-building-binaries-for-supported-os.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ os_archs=(
darwin/amd64
freebsd/amd64
linux/amd64
plan9/386
solaris/amd64
windows/amd64
)
Expand All @@ -18,4 +19,4 @@ do
CGO_ENABLED=0 GOOS=${goos} GOARCH=${goarch} go build -o /dev/null ./...
done

echo "Succeeded building binaries for all supported OS/ARCH pairs!"
echo "Succeeded building binaries for all supported OS/ARCH pairs!"
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ require (
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
)

go 1.13
2 changes: 1 addition & 1 deletion osfs/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (fs *OS) Rename(from, to string) error {
return err
}

return os.Rename(from, to)
return rename(from, to)
}

func (fs *OS) MkdirAll(path string, perm os.FileMode) error {
Expand Down
83 changes: 83 additions & 0 deletions osfs/os_plan9.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package osfs

import (
"io"
"os"
"path/filepath"
"syscall"
)

func (f *file) Lock() error {
// Plan 9 uses a mode bit instead of explicit lock/unlock syscalls.
//
// Per http://man.cat-v.org/plan_9/5/stat: “Exclusive use files may be open
// for I/O by only one fid at a time across all clients of the server. If a
// second open is attempted, it draws an error.”
//
// There is no obvious way to implement this function using the exclusive use bit.
// See https://golang.org/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
// for how file locking is done by the go tool on Plan 9.
return nil
}

func (f *file) Unlock() error {
return nil
}

func rename(from, to string) error {
// If from and to are in different directories, copy the file
// since Plan 9 does not support cross-directory rename.
if filepath.Dir(from) != filepath.Dir(to) {
fi, err := os.Stat(from)
if err != nil {
return &os.LinkError{"rename", from, to, err}
}
if fi.Mode().IsDir() {
return &os.LinkError{"rename", from, to, syscall.EISDIR}
}
fromFile, err := os.Open(from)
if err != nil {
return &os.LinkError{"rename", from, to, err}
}
toFile, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
if err != nil {
return &os.LinkError{"rename", from, to, err}
}
_, err = io.Copy(toFile, fromFile)
if err != nil {
return &os.LinkError{"rename", from, to, err}
}

// Copy mtime and mode from original file.
// We need only one syscall if we avoid os.Chmod and os.Chtimes.
dir := fi.Sys().(*syscall.Dir)
var d syscall.Dir
d.Null()
d.Mtime = dir.Mtime
d.Mode = dir.Mode
if err = dirwstat(to, &d); err != nil {
return &os.LinkError{"rename", from, to, err}
}

// Remove original file.
err = os.Remove(from)
if err != nil {
return &os.LinkError{"rename", from, to, err}
}
return nil
}
return os.Rename(from, to)
}

func dirwstat(name string, d *syscall.Dir) error {
var buf [syscall.STATFIXLEN]byte

n, err := d.Marshal(buf[:])
if err != nil {
return &os.PathError{"dirwstat", name, err}
}
if err = syscall.Wstat(name, buf[:n]); err != nil {
return &os.PathError{"dirwstat", name, err}
}
return nil
}
8 changes: 7 additions & 1 deletion osfs/os_posix.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// +build !windows
// +build !plan9,!windows

package osfs

import (
"os"

"golang.org/x/sys/unix"
)

Expand All @@ -19,3 +21,7 @@ func (f *file) Unlock() error {

return unix.Flock(int(f.File.Fd()), unix.LOCK_UN)
}

func rename(from, to string) error {
return os.Rename(from, to)
}
9 changes: 9 additions & 0 deletions osfs/os_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"testing"

"gopkg.in/src-d/go-billy.v4"
Expand All @@ -23,6 +24,14 @@ var _ = Suite(&OSSuite{})

func (s *OSSuite) SetUpTest(c *C) {
s.path, _ = ioutil.TempDir(os.TempDir(), "go-billy-osfs-test")
if runtime.GOOS == "plan9" {
// On Plan 9, permission mode of newly created files
// or directories are based on the permission mode of
// the containing directory (see http://man.cat-v.org/plan_9/5/open).
// Since TestOpenFileWithModes and TestStat creates files directly
// in the temporary directory, we need to make it more permissive.
c.Assert(os.Chmod(s.path, 0777), IsNil)
}
s.FilesystemSuite = test.NewFilesystemSuite(New(s.path))
}

Expand Down
4 changes: 4 additions & 0 deletions osfs/os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ func (f *file) Unlock() error {
}
return nil
}

func rename(from, to string) error {
return os.Rename(from, to)
}
16 changes: 16 additions & 0 deletions test/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package test

import (
"os"
"runtime"

. "gopkg.in/check.v1"
. "gopkg.in/src-d/go-billy.v4"
Expand Down Expand Up @@ -33,6 +34,9 @@ func NewFilesystemSuite(fs Filesystem) FilesystemSuite {
}

func (s *FilesystemSuite) TestSymlinkToDir(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := s.FS.MkdirAll("dir", 0755)
c.Assert(err, IsNil)

Expand All @@ -46,6 +50,9 @@ func (s *FilesystemSuite) TestSymlinkToDir(c *C) {
}

func (s *FilesystemSuite) TestSymlinkReadDir(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "dir/file", []byte("foo"), 0644)
c.Assert(err, IsNil)

Expand Down Expand Up @@ -85,6 +92,9 @@ func (s *ChrootSuite) TestReadDirWithChroot(c *C) {
}

func (s *FilesystemSuite) TestSymlinkWithChrootBasic(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
qux, _ := s.FS.Chroot("/qux")

err := util.WriteFile(qux, "file", nil, 0644)
Expand All @@ -103,6 +113,9 @@ func (s *FilesystemSuite) TestSymlinkWithChrootBasic(c *C) {
}

func (s *FilesystemSuite) TestSymlinkWithChrootCrossBounders(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
qux, _ := s.FS.Chroot("/qux")
util.WriteFile(s.FS, "file", []byte("foo"), customMode)

Expand All @@ -115,6 +128,9 @@ func (s *FilesystemSuite) TestSymlinkWithChrootCrossBounders(c *C) {
}

func (s *FilesystemSuite) TestReadDirWithLink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
util.WriteFile(s.FS, "foo/bar", []byte("foo"), customMode)
s.FS.Symlink("bar", "foo/qux")

Expand Down
49 changes: 49 additions & 0 deletions test/symlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package test
import (
"io/ioutil"
"os"
"runtime"

. "gopkg.in/check.v1"
. "gopkg.in/src-d/go-billy.v4"
Expand All @@ -19,6 +20,9 @@ type SymlinkSuite struct {
}

func (s *SymlinkSuite) TestSymlink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "file", nil, 0644)
c.Assert(err, IsNil)

Expand All @@ -31,6 +35,9 @@ func (s *SymlinkSuite) TestSymlink(c *C) {
}

func (s *SymlinkSuite) TestSymlinkCrossDirs(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "foo/file", nil, 0644)
c.Assert(err, IsNil)

Expand All @@ -43,6 +50,9 @@ func (s *SymlinkSuite) TestSymlinkCrossDirs(c *C) {
}

func (s *SymlinkSuite) TestSymlinkNested(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "file", []byte("hello world!"), 0644)
c.Assert(err, IsNil)

Expand All @@ -59,6 +69,9 @@ func (s *SymlinkSuite) TestSymlinkNested(c *C) {
}

func (s *SymlinkSuite) TestSymlinkWithNonExistentdTarget(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := s.FS.Symlink("file", "link")
c.Assert(err, IsNil)

Expand All @@ -67,6 +80,9 @@ func (s *SymlinkSuite) TestSymlinkWithNonExistentdTarget(c *C) {
}

func (s *SymlinkSuite) TestSymlinkWithExistingLink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "link", nil, 0644)
c.Assert(err, IsNil)

Expand All @@ -75,6 +91,9 @@ func (s *SymlinkSuite) TestSymlinkWithExistingLink(c *C) {
}

func (s *SymlinkSuite) TestOpenWithSymlinkToRelativePath(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "dir/file", []byte("foo"), 0644)
c.Assert(err, IsNil)

Expand All @@ -91,6 +110,9 @@ func (s *SymlinkSuite) TestOpenWithSymlinkToRelativePath(c *C) {
}

func (s *SymlinkSuite) TestOpenWithSymlinkToAbsolutePath(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "dir/file", []byte("foo"), 0644)
c.Assert(err, IsNil)

Expand All @@ -107,6 +129,9 @@ func (s *SymlinkSuite) TestOpenWithSymlinkToAbsolutePath(c *C) {
}

func (s *SymlinkSuite) TestReadlink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "file", nil, 0644)
c.Assert(err, IsNil)

Expand All @@ -115,6 +140,9 @@ func (s *SymlinkSuite) TestReadlink(c *C) {
}

func (s *SymlinkSuite) TestReadlinkWithRelativePath(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "dir/file", nil, 0644)
c.Assert(err, IsNil)

Expand All @@ -127,6 +155,9 @@ func (s *SymlinkSuite) TestReadlinkWithRelativePath(c *C) {
}

func (s *SymlinkSuite) TestReadlinkWithAbsolutePath(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "dir/file", nil, 0644)
c.Assert(err, IsNil)

Expand All @@ -139,6 +170,9 @@ func (s *SymlinkSuite) TestReadlinkWithAbsolutePath(c *C) {
}

func (s *SymlinkSuite) TestReadlinkWithNonExistentTarget(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := s.FS.Symlink("file", "link")
c.Assert(err, IsNil)

Expand All @@ -148,11 +182,17 @@ func (s *SymlinkSuite) TestReadlinkWithNonExistentTarget(c *C) {
}

func (s *SymlinkSuite) TestReadlinkWithNonExistentLink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
_, err := s.FS.Readlink("link")
c.Assert(os.IsNotExist(err), Equals, true)
}

func (s *SymlinkSuite) TestStatLink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
util.WriteFile(s.FS, "foo/bar", []byte("foo"), customMode)
s.FS.Symlink("bar", "foo/qux")

Expand All @@ -178,6 +218,9 @@ func (s *SymlinkSuite) TestLstat(c *C) {
}

func (s *SymlinkSuite) TestLstatLink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
util.WriteFile(s.FS, "foo/bar", []byte("fosddddaaao"), customMode)
s.FS.Symlink("bar", "foo/qux")

Expand All @@ -190,6 +233,9 @@ func (s *SymlinkSuite) TestLstatLink(c *C) {
}

func (s *SymlinkSuite) TestRenameWithSymlink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := s.FS.Symlink("file", "link")
c.Assert(err, IsNil)

Expand All @@ -201,6 +247,9 @@ func (s *SymlinkSuite) TestRenameWithSymlink(c *C) {
}

func (s *SymlinkSuite) TestRemoveWithSymlink(c *C) {
if runtime.GOOS == "plan9" {
c.Skip("skipping on Plan 9; symlinks are not supported")
}
err := util.WriteFile(s.FS, "file", []byte("foo"), 0644)
c.Assert(err, IsNil)

Expand Down