Skip to content

Commit a86ec88

Browse files
committed
Add Plan 9 support
Fixes src-d#52 Signed-off-by: Fazlul Shahriar <[email protected]>
1 parent fd409ff commit a86ec88

9 files changed

+173
-3
lines changed

.ci/test-building-binaries-for-supported-os.sh

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ os_archs=(
88
linux/amd64
99
solaris/amd64
1010
windows/amd64
11+
plan9/386
1112
)
1213

1314
for os_arch in "${os_archs[@]}"
@@ -18,4 +19,4 @@ do
1819
CGO_ENABLED=0 GOOS=${goos} GOARCH=${goarch} go build -o /dev/null ./...
1920
done
2021

21-
echo "Succeeded building binaries for all supported OS/ARCH pairs!"
22+
echo "Succeeded building binaries for all supported OS/ARCH pairs!"

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ require (
55
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e
66
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
77
)
8+
9+
go 1.13

osfs/os.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (fs *OS) Rename(from, to string) error {
7272
return err
7373
}
7474

75-
return os.Rename(from, to)
75+
return rename(from, to)
7676
}
7777

7878
func (fs *OS) MkdirAll(path string, perm os.FileMode) error {

osfs/os_plan9.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package osfs
2+
3+
import (
4+
"io"
5+
"os"
6+
"path/filepath"
7+
"syscall"
8+
)
9+
10+
func (f *file) Lock() error {
11+
// Plan 9 uses a mode bit instead of explicit lock/unlock syscalls.
12+
//
13+
// Per http://man.cat-v.org/plan_9/5/stat: “Exclusive use files may be open
14+
// for I/O by only one fid at a time across all clients of the server. If a
15+
// second open is attempted, it draws an error.”
16+
//
17+
// There is no obvious way to implement this function using the exclusive use bit.
18+
// See https://golang.org/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
19+
// for how file locking is done by the go tool on Plan 9.
20+
return nil
21+
}
22+
23+
func (f *file) Unlock() error {
24+
return nil
25+
}
26+
27+
func rename(from, to string) error {
28+
// If from and to are in different directories, copy the file
29+
// since Plan 9 does not support cross-directory rename.
30+
if filepath.Dir(from) != filepath.Dir(to) {
31+
fi, err := os.Stat(from)
32+
if err != nil {
33+
return &os.LinkError{"rename", from, to, err}
34+
}
35+
if fi.Mode().IsDir() {
36+
return &os.LinkError{"rename", from, to, syscall.EISDIR}
37+
}
38+
fromFile, err := os.Open(from)
39+
if err != nil {
40+
return &os.LinkError{"rename", from, to, err}
41+
}
42+
toFile, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
43+
if err != nil {
44+
return &os.LinkError{"rename", from, to, err}
45+
}
46+
_, err = io.Copy(toFile, fromFile)
47+
if err != nil {
48+
return &os.LinkError{"rename", from, to, err}
49+
}
50+
51+
// Copy mtime and mode from original file.
52+
// We need only one syscall if we avoid os.Chmod and os.Chtimes.
53+
dir := fi.Sys().(*syscall.Dir)
54+
var d syscall.Dir
55+
d.Null()
56+
d.Mtime = dir.Mtime
57+
d.Mode = dir.Mode
58+
if err = dirwstat(to, &d); err != nil {
59+
return &os.LinkError{"rename", from, to, err}
60+
}
61+
62+
// Remove original file.
63+
err = os.Remove(from)
64+
if err != nil {
65+
return &os.LinkError{"rename", from, to, err}
66+
}
67+
return nil
68+
}
69+
return os.Rename(from, to)
70+
}
71+
72+
func dirwstat(name string, d *syscall.Dir) error {
73+
var buf [syscall.STATFIXLEN]byte
74+
75+
n, err := d.Marshal(buf[:])
76+
if err != nil {
77+
return &os.PathError{"dirwstat", name, err}
78+
}
79+
if err = syscall.Wstat(name, buf[:n]); err != nil {
80+
return &os.PathError{"dirwstat", name, err}
81+
}
82+
return nil
83+
}

osfs/os_posix.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
// +build !windows
1+
// +build !plan9,!windows
22

33
package osfs
44

55
import (
6+
"os"
7+
68
"golang.org/x/sys/unix"
79
)
810

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

2022
return unix.Flock(int(f.File.Fd()), unix.LOCK_UN)
2123
}
24+
25+
func rename(from, to string) error {
26+
return os.Rename(from, to)
27+
}

osfs/os_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"io/ioutil"
55
"os"
66
"path/filepath"
7+
"runtime"
78
"testing"
89

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

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

osfs/os_windows.go

+4
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ func (f *file) Unlock() error {
5555
}
5656
return nil
5757
}
58+
59+
func rename(from, to string) error {
60+
return os.Rename(from, to)
61+
}

test/fs.go

+16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package test
22

33
import (
44
"os"
5+
"runtime"
56

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

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

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

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

@@ -85,6 +92,9 @@ func (s *ChrootSuite) TestReadDirWithChroot(c *C) {
8592
}
8693

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

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

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

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

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

test/symlink.go

+49
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package test
33
import (
44
"io/ioutil"
55
"os"
6+
"runtime"
67

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

0 commit comments

Comments
 (0)