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 | |
| 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
| -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 { | 
