diff options
author | Russ Cox <rsc@golang.org> | 2009-05-18 10:49:34 -0700 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2009-05-18 10:49:34 -0700 |
commit | df7681858bd5b1294bddfbbef6fb734b35d29dd8 (patch) | |
tree | eb92c3b09fb143cf90ddfe36300008082006c8e0 /src/lib | |
parent | 445e1228300e765463b16c0cc59da0e54a17ad4c (diff) | |
download | golang-df7681858bd5b1294bddfbbef6fb734b35d29dd8.tar.gz |
add Getwd, Fchdir, tests
R=r
DELTA=215 (186 added, 0 deleted, 29 changed)
OCL=28968
CL=28995
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/os/Makefile | 5 | ||||
-rw-r--r-- | src/lib/os/error.go | 1 | ||||
-rw-r--r-- | src/lib/os/file.go | 7 | ||||
-rw-r--r-- | src/lib/os/getwd.go | 94 | ||||
-rw-r--r-- | src/lib/os/os_test.go | 61 | ||||
-rw-r--r-- | src/lib/syscall/file_darwin.go | 15 | ||||
-rw-r--r-- | src/lib/syscall/file_linux.go | 60 | ||||
-rw-r--r-- | src/lib/syscall/types_amd64_linux.go | 2 |
8 files changed, 216 insertions, 29 deletions
diff --git a/src/lib/os/Makefile b/src/lib/os/Makefile index 02863cc97..50a06d92b 100644 --- a/src/lib/os/Makefile +++ b/src/lib/os/Makefile @@ -3,7 +3,7 @@ # license that can be found in the LICENSE file. # DO NOT EDIT. Automatically generated by gobuild. -# gobuild -m dir_${GOARCH}_${GOOS}.go env.go error.go file.go path.go proc_${GOOS}.go stat_${GOARCH}_${GOOS}.go time.go types.go exec.go user.go >Makefile +# gobuild -m dir_${GOARCH}_${GOOS}.go env.go error.go file.go path.go proc_${GOOS}.go stat_${GOARCH}_${GOOS}.go time.go types.go exec.go user.go getwd.go >Makefile D= @@ -56,6 +56,7 @@ O3=\ O4=\ dir_$(GOARCH)_$(GOOS).$O\ exec.$O\ + getwd.$O\ path.$O\ @@ -75,7 +76,7 @@ a3: $(O3) rm -f $(O3) a4: $(O4) - $(AR) grc _obj$D/os.a dir_$(GOARCH)_$(GOOS).$O exec.$O path.$O + $(AR) grc _obj$D/os.a dir_$(GOARCH)_$(GOOS).$O exec.$O getwd.$O path.$O rm -f $(O4) diff --git a/src/lib/os/error.go b/src/lib/os/error.go index 53f58c9ae..778465627 100644 --- a/src/lib/os/error.go +++ b/src/lib/os/error.go @@ -79,5 +79,6 @@ var ( ERANGE Error = Errno(syscall.ERANGE); EADDRINUSE Error = Errno(syscall.EADDRINUSE); ECONNREFUSED Error = Errno(syscall.ECONNREFUSED); + ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG); ) diff --git a/src/lib/os/file.go b/src/lib/os/file.go index d65807326..7aa6632c7 100644 --- a/src/lib/os/file.go +++ b/src/lib/os/file.go @@ -284,6 +284,13 @@ func Chdir(dir string) Error { return ErrnoToError(e); } +// Chdir changes the current working directory to the file, +// which must be a directory. +func (f *File) Chdir() Error { + r, e := syscall.Fchdir(f.fd); + return ErrnoToError(e); +} + // Remove removes the named file or directory. func Remove(name string) Error { // System call interface forces us to know diff --git a/src/lib/os/getwd.go b/src/lib/os/getwd.go new file mode 100644 index 000000000..2d7b754b5 --- /dev/null +++ b/src/lib/os/getwd.go @@ -0,0 +1,94 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import ( + "os"; + "syscall" +) + +// Getwd returns a rooted path name corresponding to the +// current directory. If the current directory can be +// reached via multiple paths (due to symbolic links), +// Getwd may return any one of them. +func Getwd() (string, Error) { + // If the operating system provides a Getwd call, use it. + if syscall.ImplementsGetwd { + s, e := syscall.Getwd(); + return s, ErrnoToError(e); + } + + // Otherwise, we're trying to find our way back to ".". + dot, err := Stat("."); + if err != nil { + return "", err; + } + + // Clumsy but widespread kludge: + // if $PWD is set and matches ".", use it. + pwd, _ := Getenv("PWD"); + if len(pwd) > 0 && pwd[0] == '/' { + d, err := Stat(pwd); + if err == nil && d.Dev == dot.Dev && d.Ino == dot.Ino { + return pwd, nil + } + } + + // Root is a special case because it has no parent + // and ends in a slash. + root, err := Stat("/"); + if err != nil { + // Can't stat root - no hope. + return "", err; + } + if root.Dev == dot.Dev && root.Ino == dot.Ino { + return "/", nil + } + + // General algorithm: find name in parent + // and then find name of parent. Each iteration + // adds /name to the beginning of pwd. + elem := make([]string, 0, 16); + pwd = ""; + for parent := "..";; parent = "../" + parent { + if len(parent) >= 1024 { // Sanity check + return "", ENAMETOOLONG; + } + fd, err := Open(parent, O_RDONLY, 0); + if err != nil { + return "", err; + } + + for { + names, err := fd.Readdirnames(100); + if err != nil { + fd.Close(); + return "", err; + } + for i, name := range names { + d, err := Lstat(parent + "/" + name); + if d.Dev == dot.Dev && d.Ino == dot.Ino { + pwd = "/" + name + pwd; + goto Found; + } + } + } + fd.Close(); + return "", ENOENT; + + Found: + pd, err := fd.Stat(); + if err != nil { + return "", err; + } + fd.Close(); + if pd.Dev == root.Dev && pd.Ino == root.Ino { + break; + } + // Set up for next round. + dot = pd; + } + return pwd, nil +} diff --git a/src/lib/os/os_test.go b/src/lib/os/os_test.go index b291fd85a..e4d115d81 100644 --- a/src/lib/os/os_test.go +++ b/src/lib/os/os_test.go @@ -316,7 +316,7 @@ func TestForkExec(t *testing.T) { if err != nil { t.Fatalf("Pipe: %v", err); } - pid, err := ForkExec("/bin/pwd", []string{"pwd"}, nil, "/", []*File{nil, w, os.Stderr}); + pid, err := ForkExec("/bin/pwd", []string{"pwd"}, nil, "/", []*File{nil, w, Stderr}); if err != nil { t.Fatalf("ForkExec: %v", err); } @@ -345,12 +345,12 @@ func checkMode(t *testing.T, path string, mode uint32) { func TestChmod(t *testing.T) { MkdirAll("_obj", 0777); const Path = "_obj/_TestChmod_"; - fd, err := os.Open(Path, os.O_WRONLY | os.O_CREAT, 0666); + fd, err := Open(Path, O_WRONLY | O_CREAT, 0666); if err != nil { t.Fatalf("create %s: %s", Path, err); } - if err = os.Chmod(Path, 0456); err != nil { + if err = Chmod(Path, 0456); err != nil { t.Fatalf("chmod %s 0456: %s", Path, err); } checkMode(t, Path, 0456); @@ -384,7 +384,7 @@ func TestChown(t *testing.T) { // basically useless. const Path = "/tmp/_TestChown_"; - fd, err := os.Open(Path, os.O_WRONLY | os.O_CREAT, 0666); + fd, err := Open(Path, O_WRONLY | O_CREAT, 0666); if err != nil { t.Fatalf("create %s: %s", Path, err); } @@ -398,7 +398,7 @@ func TestChown(t *testing.T) { // Can't change uid unless root, but can try // changing the group id. First try our current group. gid := Getgid(); - if err = os.Chown(Path, -1, gid); err != nil { + if err = Chown(Path, -1, gid); err != nil { t.Fatalf("chown %s -1 %d: %s", Path, gid, err); } checkUidGid(t, Path, int(dir.Uid), gid); @@ -409,7 +409,7 @@ func TestChown(t *testing.T) { t.Fatalf("getgroups: %s", err); } for i, g := range groups { - if err = os.Chown(Path, -1, g); err != nil { + if err = Chown(Path, -1, g); err != nil { t.Fatalf("chown %s -1 %d: %s", Path, g, err); } checkUidGid(t, Path, int(dir.Uid), g); @@ -435,7 +435,7 @@ func checkSize(t *testing.T, path string, size uint64) { func TestTruncate(t *testing.T) { MkdirAll("_obj", 0777); const Path = "_obj/_TestTruncate_"; - fd, err := os.Open(Path, os.O_WRONLY | os.O_CREAT, 0666); + fd, err := Open(Path, O_WRONLY | O_CREAT, 0666); if err != nil { t.Fatalf("create %s: %s", Path, err); } @@ -454,3 +454,50 @@ func TestTruncate(t *testing.T) { fd.Close(); Remove(Path); } + +func TestChdirAndGetwd(t *testing.T) { + fd, err := Open(".", O_RDONLY, 0); + if err != nil { + t.Fatalf("Open .: %s", err); + } + // These are chosen carefully not to be symlinks on a Mac + // (unlike, say, /var, /etc, and /tmp). + dirs := []string{ "/bin", "/", "/usr/local/bin" }; + for mode := 0; mode < 2; mode++ { + for i, d := range dirs { + if mode == 0 { + err = Chdir(d); + } else { + fd1, err := os.Open(d, os.O_RDONLY, 0); + if err != nil { + t.Errorf("Open %s: %s", d, err); + continue; + } + err = fd1.Chdir(); + fd1.Close(); + } + pwd, err1 := Getwd(); + err2 := fd.Chdir(); + if err2 != nil { + // We changed the current directory and cannot go back. + // Don't let the tests continue; they'll scribble + // all over some other directory. + fmt.Fprintf(Stderr, "fchdir back to dot failed: %s\n", err2); + Exit(1); + } + if err != nil { + fd.Close(); + t.Fatalf("Chdir %s: %s", d, err); + } + if err1 != nil { + fd.Close(); + t.Fatalf("Getwd in %s: %s", d, err1); + } + if pwd != d { + fd.Close(); + t.Fatalf("Getwd returned %q want %q", pwd, d); + } + } + } + fd.Close(); +} diff --git a/src/lib/syscall/file_darwin.go b/src/lib/syscall/file_darwin.go index e3c9567e3..6c4eee6fb 100644 --- a/src/lib/syscall/file_darwin.go +++ b/src/lib/syscall/file_darwin.go @@ -111,6 +111,11 @@ func Chdir(dir string) (ret, errno int64) { return r1, err; } +func Fchdir(fd int64) (ret, errno int64) { + r1, r2, err := Syscall(SYS_FCHDIR, fd, 0, 0); + return r1, err; +} + func Link(oldname, newname string) (ret, errno int64) { oldbuf := StringBytePtr(oldname); newbuf := StringBytePtr(newname); @@ -169,3 +174,13 @@ func Ftruncate(fd, length int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_FTRUNCATE, fd, length, 0); return r1, err; } + +// The const provides a compile-time constant so clients +// can adjust to whether there is a working Getwd and avoid +// even linking this function into the binary. See ../os/getwd.go. +const ImplementsGetwd = false + +func Getwd() (string, int64) { + return "", ENOTSUP; +} + diff --git a/src/lib/syscall/file_linux.go b/src/lib/syscall/file_linux.go index f6b6ea7e6..2f8b9101f 100644 --- a/src/lib/syscall/file_linux.go +++ b/src/lib/syscall/file_linux.go @@ -13,39 +13,39 @@ import ( const nameBufsize = 512 -func Open(name string, mode int64, perm int64) (ret int64, errno int64) { +func Open(name string, mode int64, perm int64) (ret, errno int64) { namebuf := StringBytePtr(name); r1, r2, err := Syscall(SYS_OPEN, int64(uintptr(unsafe.Pointer(namebuf))), mode, perm); return r1, err; } -func Creat(name string, perm int64) (ret int64, errno int64) { +func Creat(name string, perm int64) (ret, errno int64) { namebuf := StringBytePtr(name); r1, r2, err := Syscall(SYS_OPEN, int64(uintptr(unsafe.Pointer(namebuf))), O_CREAT|O_WRONLY|O_TRUNC, perm); return r1, err; } -func Close(fd int64) (ret int64, errno int64) { +func Close(fd int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_CLOSE, fd, 0, 0); return r1, err; } -func Read(fd int64, buf *byte, nbytes int64) (ret int64, errno int64) { +func Read(fd int64, buf *byte, nbytes int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_READ, fd, int64(uintptr(unsafe.Pointer(buf))), nbytes); return r1, err; } -func Write(fd int64, buf *byte, nbytes int64) (ret int64, errno int64) { +func Write(fd int64, buf *byte, nbytes int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_WRITE, fd, int64(uintptr(unsafe.Pointer(buf))), nbytes); return r1, err; } -func Seek(fd int64, offset int64, whence int64) (ret int64, errno int64) { +func Seek(fd int64, offset int64, whence int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_LSEEK, fd, offset, whence); return r1, err; } -func Pipe(fds *[2]int64) (ret int64, errno int64) { +func Pipe(fds *[2]int64) (ret, errno int64) { var t [2] int32; r1, r2, err := Syscall(SYS_PIPE, int64(uintptr(unsafe.Pointer(&t[0]))), 0, 0); if r1 < 0 { @@ -56,77 +56,97 @@ func Pipe(fds *[2]int64) (ret int64, errno int64) { return 0, 0; } -func Stat(name string, buf *Stat_t) (ret int64, errno int64) { +func Stat(name string, buf *Stat_t) (ret, errno int64) { namebuf := StringBytePtr(name); r1, r2, err := Syscall(SYS_STAT, int64(uintptr(unsafe.Pointer(namebuf))), int64(uintptr(unsafe.Pointer(buf))), 0); return r1, err; } -func Lstat(name string, buf *Stat_t) (ret int64, errno int64) { +func Lstat(name string, buf *Stat_t) (ret, errno int64) { namebuf := StringBytePtr(name); r1, r2, err := Syscall(SYS_LSTAT, int64(uintptr(unsafe.Pointer(namebuf))), int64(uintptr(unsafe.Pointer(buf))), 0); return r1, err; } -func Fstat(fd int64, buf *Stat_t) (ret int64, errno int64) { +func Fstat(fd int64, buf *Stat_t) (ret, errno int64) { r1, r2, err := Syscall(SYS_FSTAT, fd, int64(uintptr(unsafe.Pointer(buf))), 0); return r1, err; } -func Unlink(name string) (ret int64, errno int64) { +func Unlink(name string) (ret, errno int64) { namebuf := StringBytePtr(name); r1, r2, err := Syscall(SYS_UNLINK, int64(uintptr(unsafe.Pointer(namebuf))), 0, 0); return r1, err; } -func Rmdir(name string) (ret int64, errno int64) { +func Rmdir(name string) (ret, errno int64) { namebuf := StringBytePtr(name); r1, r2, err := Syscall(SYS_RMDIR, int64(uintptr(unsafe.Pointer(namebuf))), 0, 0); return r1, err; } -func Fcntl(fd, cmd, arg int64) (ret int64, errno int64) { +func Fcntl(fd, cmd, arg int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_FCNTL, fd, cmd, arg); return r1, err } -func Mkdir(name string, perm int64) (ret int64, errno int64) { +func Mkdir(name string, perm int64) (ret, errno int64) { namebuf := StringBytePtr(name); r1, r2, err := Syscall(SYS_MKDIR, int64(uintptr(unsafe.Pointer(namebuf))), perm, 0); return r1, err; } -func Dup2(fd1, fd2 int64) (ret int64, errno int64) { +func Dup2(fd1, fd2 int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_DUP2, fd1, fd2, 0); return r1, err; } -func Getdents(fd int64, buf *Dirent, nbytes int64) (ret int64, errno int64) { +func Getdents(fd int64, buf *Dirent, nbytes int64) (ret, errno int64) { r1, r2, err := Syscall(SYS_GETDENTS64, fd, int64(uintptr(unsafe.Pointer(buf))), nbytes); return r1, err; } -func Chdir(dir string) (ret int64, errno int64) { +func Chdir(dir string) (ret, errno int64) { namebuf := StringBytePtr(dir); r1, r2, err := Syscall(SYS_CHDIR, int64(uintptr(unsafe.Pointer(namebuf))), 0, 0); return r1, err; } -func Link(oldpath, newpath string) (ret int64, errno int64) { +func Fchdir(fd int64) (ret, errno int64) { + r1, r2, err := Syscall(SYS_FCHDIR, fd, 0, 0); + return r1, err; +} + +const ImplementsGetwd = true + +func Getwd() (ret string, errno int64) { + var buf [PathMax]byte; + r1, r2, err := Syscall(SYS_GETCWD, int64(uintptr(unsafe.Pointer(&buf))), int64(len(buf)), 0); + if err != 0 { + return "", err; + } + // SYS_GETCWD returns the number of bytes written to buf, including the NUL. + if r1 < 1 || r1 > int64(len(buf)) || buf[r1-1] != 0 { + return "", EINVAL; + } + return string(buf[0:r1-1]), 0 +} + +func Link(oldpath, newpath string) (ret, errno int64) { oldbuf := StringBytePtr(oldpath); newbuf := StringBytePtr(newpath); r1, r2, err := Syscall(SYS_LINK, int64(uintptr(unsafe.Pointer(oldbuf))), int64(uintptr(unsafe.Pointer(newbuf))), 0); return r1, err; } -func Symlink(oldpath, newpath string) (ret int64, errno int64) { +func Symlink(oldpath, newpath string) (ret, errno int64) { oldbuf := StringBytePtr(oldpath); newbuf := StringBytePtr(newpath); r1, r2, err := Syscall(SYS_SYMLINK, int64(uintptr(unsafe.Pointer(oldbuf))), int64(uintptr(unsafe.Pointer(newbuf))), 0); return r1, err; } -func Readlink(path string, buf *byte, nbytes int64) (ret int64, errno int64) { +func Readlink(path string, buf *byte, nbytes int64) (ret, errno int64) { pathbuf := StringBytePtr(path); r1, r2, err := Syscall(SYS_READLINK, int64(uintptr(unsafe.Pointer(pathbuf))), int64(uintptr(unsafe.Pointer(buf))), nbytes); return r1, err; diff --git a/src/lib/syscall/types_amd64_linux.go b/src/lib/syscall/types_amd64_linux.go index b15f0cbcc..a71606a5b 100644 --- a/src/lib/syscall/types_amd64_linux.go +++ b/src/lib/syscall/types_amd64_linux.go @@ -11,6 +11,8 @@ import "syscall" const OS = "linux" +const PathMax = 4096 + // Time type Timespec struct { |