--- /dev/null
+// 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
+}
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);
}
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);
// 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);
}
// 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);
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);
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);
}
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();
+}
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 {
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;