]> Cypherpunks repositories - gostls13.git/commitdiff
add Getwd, Fchdir, tests
authorRuss Cox <rsc@golang.org>
Mon, 18 May 2009 17:49:34 +0000 (10:49 -0700)
committerRuss Cox <rsc@golang.org>
Mon, 18 May 2009 17:49:34 +0000 (10:49 -0700)
R=r
DELTA=215  (186 added, 0 deleted, 29 changed)
OCL=28968
CL=28995

src/lib/os/Makefile
src/lib/os/error.go
src/lib/os/file.go
src/lib/os/getwd.go [new file with mode: 0644]
src/lib/os/os_test.go
src/lib/syscall/file_darwin.go
src/lib/syscall/file_linux.go
src/lib/syscall/types_amd64_linux.go

index 02863cc97ebf9cf93c27fe3ea82d89e07004374d..50a06d92b5e08f2cac07a8bbabc79b22d50df75f 100644 (file)
@@ -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)
 
 
index 53f58c9aeea12c991762838bd10a6a90d6f91dc4..7784656274df418e7df368d5e427b51b52749815 100644 (file)
@@ -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);
 )
 
index d658073264e3c2908c68526d297a1acf02fb3b25..7aa6632c77d92ae1b1263322597e95aa2bc591fa 100644 (file)
@@ -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 (file)
index 0000000..2d7b754
--- /dev/null
@@ -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
+}
index b291fd85a009549f961784570a3e4528f99262ed..e4d115d81f6d6ee15ac4980cc1088cc0025f584b 100644 (file)
@@ -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();
+}
index e3c9567e39096ea16c66a667d559a946cba956a1..6c4eee6fbb8253a93d86962769f4ff0ce889ef5c 100644 (file)
@@ -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;
+}
+
index f6b6ea7e6f4ea87a80aa0344437fe33313f0e10d..2f8b9101f4faf7ec8401f1cd5ad70426fa3956ba 100644 (file)
@@ -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;
index b15f0cbcc03c1a10d41ce26f14524194c140cb0a..a71606a5bd884a4f161da4a65fb82ec8709c4d57 100644 (file)
@@ -11,6 +11,8 @@ import "syscall"
 
 const OS = "linux"
 
+const PathMax = 4096
+
 // Time
 
 type Timespec struct {