From b26cd1bfcd7107d8208595614ba45f54d5efacf6 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Jun 2009 20:24:55 -0700 Subject: Change os.Error convention: echo back context of call in error if likely to be useful. For example, if os.Open("/etc/passwd", os.O_RDONLY) fails with syscall.EPERM, it returns as the os.Error &PathError{ Op: "open", Path: "/etc/passwd" Error: os.EPERM } which formats as open /etc/passwd: permission denied Not converted: datafmt go/... google/... regexp tabwriter template R=r DELTA=1153 (561 added, 156 deleted, 436 changed) OCL=30738 CL=30781 --- src/pkg/os/dir_darwin_386.go | 4 +- src/pkg/os/dir_darwin_amd64.go | 4 +- src/pkg/os/dir_linux_386.go | 6 +- src/pkg/os/dir_linux_amd64.go | 6 +- src/pkg/os/error.go | 39 ++++++++++--- src/pkg/os/exec.go | 14 +++-- src/pkg/os/file.go | 127 +++++++++++++++++++++++++++++------------ src/pkg/os/getwd.go | 2 +- src/pkg/os/os_test.go | 38 ++++++++++++ src/pkg/os/path.go | 36 +++++------- src/pkg/os/proc.go | 2 +- src/pkg/os/time.go | 2 +- 12 files changed, 196 insertions(+), 84 deletions(-) (limited to 'src/pkg/os') diff --git a/src/pkg/os/dir_darwin_386.go b/src/pkg/os/dir_darwin_386.go index 2803ecee2..791499d8f 100644 --- a/src/pkg/os/dir_darwin_386.go +++ b/src/pkg/os/dir_darwin_386.go @@ -38,9 +38,9 @@ func readdirnames(file *File, count int) (names []string, err Error) { d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr)); if errno != 0 { d.nbuf = 0; - return names, ErrnoToError(errno) + return names, NewSyscallError("getdirentries", errno); } - if d.nbuf == 0 { + if d.nbuf <= 0 { break // EOF } } diff --git a/src/pkg/os/dir_darwin_amd64.go b/src/pkg/os/dir_darwin_amd64.go index 2803ecee2..791499d8f 100644 --- a/src/pkg/os/dir_darwin_amd64.go +++ b/src/pkg/os/dir_darwin_amd64.go @@ -38,9 +38,9 @@ func readdirnames(file *File, count int) (names []string, err Error) { d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr)); if errno != 0 { d.nbuf = 0; - return names, ErrnoToError(errno) + return names, NewSyscallError("getdirentries", errno); } - if d.nbuf == 0 { + if d.nbuf <= 0 { break // EOF } } diff --git a/src/pkg/os/dir_linux_386.go b/src/pkg/os/dir_linux_386.go index c4594a52d..d6d700b24 100644 --- a/src/pkg/os/dir_linux_386.go +++ b/src/pkg/os/dir_linux_386.go @@ -47,10 +47,10 @@ func readdirnames(file *File, count int) (names []string, err Error) { if d.bufp >= d.nbuf { var errno int; d.nbuf, errno = syscall.Getdents(file.fd, d.buf); - if d.nbuf < 0 { - return names, ErrnoToError(errno) + if errno != 0 { + return names, NewSyscallError("getdents", errno) } - if d.nbuf == 0 { + if d.nbuf <= 0 { break // EOF } d.bufp = 0; diff --git a/src/pkg/os/dir_linux_amd64.go b/src/pkg/os/dir_linux_amd64.go index 05b3d4c65..8b1664f77 100644 --- a/src/pkg/os/dir_linux_amd64.go +++ b/src/pkg/os/dir_linux_amd64.go @@ -43,10 +43,10 @@ func readdirnames(file *File, count int) (names []string, err Error) { if d.bufp >= d.nbuf { var errno int; d.nbuf, errno = syscall.Getdents(file.fd, d.buf); - if d.nbuf < 0 { - return names, ErrnoToError(errno) + if errno != 0 { + return names, NewSyscallError("getdents", errno) } - if d.nbuf == 0 { + if d.nbuf <= 0 { break // EOF } d.bufp = 0; diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go index 718499b21..10a7d042a 100644 --- a/src/pkg/os/error.go +++ b/src/pkg/os/error.go @@ -30,15 +30,6 @@ func (e Errno) String() string { return syscall.Errstr(int(e)) } -// ErrnoToError converts errno to an Error (underneath, an Errno). -// It returns nil for the "no error" errno. -func ErrnoToError(errno int) Error { - if errno == 0 { - return nil - } - return Errno(errno) -} - // Commonly known Unix errors. var ( EPERM Error = Errno(syscall.EPERM); @@ -81,3 +72,33 @@ var ( ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG); ) +// PathError records an error and the operation and file path that caused it. +type PathError struct { + Op string; + Path string; + Error Error; +} + +func (e *PathError) String() string { + return e.Op + " " + e.Path + ": " + e.Error.String(); +} + +// SyscallError records an error from a specific system call. +type SyscallError struct { + Syscall string; + Errno Errno; +} + +func (e *SyscallError) String() string { + return e.Syscall + ": " + e.Errno.String(); +} + +// NewSyscallError returns, as an os.Error, a new SyscallError +// with the given system call name and error number. +// As a convenience, if errno is 0, NewSyscallError returns nil. +func NewSyscallError(syscall string, errno int) os.Error { + if errno == 0 { + return nil; + } + return &SyscallError{syscall, Errno(errno)} +} diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go index c1551f86d..a7430ef7f 100644 --- a/src/pkg/os/exec.go +++ b/src/pkg/os/exec.go @@ -30,7 +30,10 @@ func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*File } p, e := syscall.ForkExec(argv0, argv, envv, dir, intfd); - return int(p), ErrnoToError(e); + if e != 0 { + return 0, &PathError{"fork/exec", argv0, Errno(e)}; + } + return p, nil; } // Exec replaces the current process with an execution of the program @@ -42,7 +45,10 @@ func Exec(argv0 string, argv []string, envv []string) Error { envv = Environ(); } e := syscall.Exec(argv0, argv, envv); - return ErrnoToError(e); + if e != 0 { + return &PathError{"exec", argv0, Errno(e)}; + } + return nil; } // TODO(rsc): Should os implement its own syscall.WaitStatus @@ -79,10 +85,10 @@ func Wait(pid int, options int) (w *Waitmsg, err Error) { } pid1, e := syscall.Wait4(pid, &status, options, rusage); if e != 0 { - return nil, ErrnoToError(e); + return nil, NewSyscallError("wait", e); } w = new(Waitmsg); - w.Pid = pid; + w.Pid = pid1; w.WaitStatus = status; w.Rusage = rusage; return w, nil; diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go index 5b6115932..8c76735fb 100644 --- a/src/pkg/os/file.go +++ b/src/pkg/os/file.go @@ -37,11 +37,11 @@ func (file *File) Name() string { } // NewFile returns a new File with the given file descriptor and name. -func NewFile(file int, name string) *File { - if file < 0 { +func NewFile(fd int, name string) *File { + if fd < 0 { return nil } - return &File{file, name, nil, 0} + return &File{fd, name, nil, 0} } // Stdin, Stdout, and Stderr are open Files pointing to the standard input, @@ -74,7 +74,7 @@ const ( func Open(name string, flag int, perm int) (file *File, err Error) { r, e := syscall.Open(name, flag | syscall.O_CLOEXEC, perm); if e != 0 { - return nil, ErrnoToError(e); + return nil, &PathError{"open", name, Errno(e)}; } // There's a race here with fork/exec, which we are @@ -83,7 +83,7 @@ func Open(name string, flag int, perm int) (file *File, err Error) { syscall.CloseOnExec(r); } - return NewFile(r, name), ErrnoToError(e) + return NewFile(r, name), nil; } // Close closes the File, rendering it unusable for I/O. @@ -92,7 +92,10 @@ func (file *File) Close() Error { if file == nil { return EINVAL } - err := ErrnoToError(syscall.Close(file.fd)); + var err os.Error; + if e := syscall.Close(file.fd); e != 0 { + err = &PathError{"close", file.name, Errno(e)}; + } file.fd = -1; // so it can't be closed again return err; } @@ -124,7 +127,10 @@ func (file *File) Read(b []byte) (ret int, err Error) { if n == 0 && e == 0 { return 0, EOF } - return n, ErrnoToError(e); + if e != 0 { + err = &PathError{"read", file.name, Errno(e)}; + } + return n, err } // Write writes len(b) bytes to the File. @@ -146,7 +152,10 @@ func (file *File) Write(b []byte) (ret int, err Error) { } else { file.nepipe = 0; } - return n, ErrnoToError(e) + if e != 0 { + err = &PathError{"write", file.name, Errno(e)}; + } + return n, err } // Seek sets the offset for the next Read or Write on file to offset, interpreted @@ -155,11 +164,11 @@ func (file *File) Write(b []byte) (ret int, err Error) { // It returns the new offset and an Error, if any. func (file *File) Seek(offset int64, whence int) (ret int64, err Error) { r, e := syscall.Seek(file.fd, offset, whence); - if e != 0 { - return -1, ErrnoToError(e) + if e == 0 && file.dirinfo != nil && r != 0 { + e = syscall.EISDIR; } - if file.dirinfo != nil && r != 0 { - return -1, ErrnoToError(syscall.EISDIR) + if e != 0 { + return 0, &PathError{"seek", file.name, Errno(e)}; } return r, nil } @@ -172,11 +181,7 @@ func (file *File) WriteString(s string) (ret int, err Error) { } b := syscall.StringByteSlice(s); b = b[0:len(b)-1]; - r, e := syscall.Write(file.fd, b); - if r < 0 { - r = 0 - } - return int(r), ErrnoToError(e) + return file.Write(b); } // Pipe returns a connected pair of Files; reads from r return bytes written to w. @@ -189,7 +194,7 @@ func Pipe() (r *File, w *File, err Error) { e := syscall.Pipe(&p); if e != 0 { syscall.ForkLock.RUnlock(); - return nil, nil, ErrnoToError(e) + return nil, nil, NewSyscallError("pipe", e); } syscall.CloseOnExec(p[0]); syscall.CloseOnExec(p[1]); @@ -201,7 +206,11 @@ func Pipe() (r *File, w *File, err Error) { // Mkdir creates a new directory with the specified name and permission bits. // It returns an error, if any. func Mkdir(name string, perm int) Error { - return ErrnoToError(syscall.Mkdir(name, perm)); + e := syscall.Mkdir(name, perm); + if e != 0 { + return &PathError{"mkdir", name, Errno(e)}; + } + return nil; } // Stat returns a Dir structure describing the named file and an error, if any. @@ -213,7 +222,7 @@ func Stat(name string) (dir *Dir, err Error) { var lstat, stat syscall.Stat_t; e := syscall.Lstat(name, &lstat); if e != 0 { - return nil, ErrnoToError(e); + return nil, &PathError{"stat", name, Errno(e)}; } statp := &lstat; if lstat.Mode & syscall.S_IFMT == syscall.S_IFLNK { @@ -231,7 +240,7 @@ func (file *File) Stat() (dir *Dir, err Error) { var stat syscall.Stat_t; e := syscall.Fstat(file.fd, &stat); if e != 0 { - return nil, ErrnoToError(e) + return nil, &PathError{"stat", file.name, Errno(e)}; } return dirFromStat(file.name, new(Dir), &stat, &stat), nil } @@ -243,7 +252,7 @@ func Lstat(name string) (dir *Dir, err Error) { var stat syscall.Stat_t; e := syscall.Lstat(name, &stat); if e != 0 { - return nil, ErrnoToError(e) + return nil, &PathError{"lstat", name, Errno(e)}; } return dirFromStat(name, new(Dir), &stat, &stat), nil } @@ -290,13 +299,19 @@ func (file *File) Readdir(count int) (dirs []Dir, err Error) { // Chdir changes the current working directory to the named directory. func Chdir(dir string) Error { - return ErrnoToError(syscall.Chdir(dir)); + if e := syscall.Chdir(dir); e != 0 { + return &PathError{"chdir", dir, Errno(e)}; + } + return nil; } // Chdir changes the current working directory to the file, // which must be a directory. func (f *File) Chdir() Error { - return ErrnoToError(syscall.Fchdir(f.fd)); + if e := syscall.Fchdir(f.fd); e != 0 { + return &PathError{"chdir", f.name, Errno(e)}; + } + return nil; } // Remove removes the named file or directory. @@ -326,17 +341,38 @@ func Remove(name string) Error { if e1 != syscall.ENOTDIR { e = e1; } - return ErrnoToError(e); + return &PathError{"remove", name, Errno(e)}; +} + +// LinkError records an error during a link or symlink +// system call and the paths that caused it. +type LinkError struct { + Op string; + Old string; + New string; + Error Error; +} + +func (e *LinkError) String() string { + return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String(); } // Link creates a hard link. func Link(oldname, newname string) Error { - return ErrnoToError(syscall.Link(oldname, newname)); + e := syscall.Link(oldname, newname); + if e != 0 { + return &LinkError{"link", oldname, newname, Errno(e)}; + } + return nil; } // Symlink creates a symbolic link. func Symlink(oldname, newname string) Error { - return ErrnoToError(syscall.Symlink(oldname, newname)); + e := syscall.Symlink(oldname, newname); + if e != 0 { + return &LinkError{"symlink", oldname, newname, Errno(e)}; + } + return nil; } // Readlink reads the contents of a symbolic link: the destination of @@ -346,7 +382,7 @@ func Readlink(name string) (string, Error) { b := make([]byte, len); n, e := syscall.Readlink(name, b); if e != 0 { - return "", ErrnoToError(e); + return "", &PathError{"readlink", name, Errno(e)}; } if n < len { return string(b[0:n]), nil; @@ -359,40 +395,61 @@ func Readlink(name string) (string, Error) { // Chmod changes the mode of the named file to mode. // If the file is a symbolic link, it changes the uid and gid of the link's target. func Chmod(name string, mode int) Error { - return ErrnoToError(syscall.Chmod(name, mode)); + if e := syscall.Chmod(name, mode); e != 0 { + return &PathError{"chmod", name, Errno(e)}; + } + return nil; } // Chmod changes the mode of the file to mode. func (f *File) Chmod(mode int) Error { - return ErrnoToError(syscall.Fchmod(f.fd, mode)); + if e := syscall.Fchmod(f.fd, mode); e != 0 { + return &PathError{"chmod", f.name, Errno(e)}; + } + return nil; } // Chown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link's target. func Chown(name string, uid, gid int) Error { - return ErrnoToError(syscall.Chown(name, uid, gid)); + if e := syscall.Chown(name, uid, gid); e != 0 { + return &PathError{"chown", name, Errno(e)}; + } + return nil; } // Lchown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link itself. func Lchown(name string, uid, gid int) Error { - return ErrnoToError(syscall.Lchown(name, uid, gid)); + if e := syscall.Lchown(name, uid, gid); e != 0 { + return &PathError{"lchown", name, Errno(e)}; + } + return nil; } // Chown changes the numeric uid and gid of the named file. func (f *File) Chown(uid, gid int) Error { - return ErrnoToError(syscall.Fchown(f.fd, uid, gid)); + if e := syscall.Fchown(f.fd, uid, gid); e != 0 { + return &PathError{"chown", f.name, Errno(e)}; + } + return nil; } // Truncate changes the size of the named file. // If the file is a symbolic link, it changes the size of the link's target. func Truncate(name string, size int64) Error { - return ErrnoToError(syscall.Truncate(name, size)); + if e := syscall.Truncate(name, size); e != 0 { + return &PathError{"truncate", name, Errno(e)}; + } + return nil; } // Truncate changes the size of the file. // It does not change the I/O offset. func (f *File) Truncate(size int64) Error { - return ErrnoToError(syscall.Ftruncate(f.fd, size)); + if e := syscall.Ftruncate(f.fd, size); e != 0 { + return &PathError{"truncate", f.name, Errno(e)}; + } + return nil; } diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go index 2d7b754b5..cbc6134a7 100644 --- a/src/pkg/os/getwd.go +++ b/src/pkg/os/getwd.go @@ -17,7 +17,7 @@ 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); + return s, NewSyscallError("getwd", e); } // Otherwise, we're trying to find our way back to ".". diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go index 77b69447d..fb555eb86 100644 --- a/src/pkg/os/os_test.go +++ b/src/pkg/os/os_test.go @@ -549,3 +549,41 @@ func TestSeek(t *testing.T) { } f.Close(); } + +type openErrorTest struct { + path string; + mode int; + error string; +} + +var openErrorTests = []openErrorTest { + openErrorTest { + "/etc/no-such-file", + O_RDONLY, + "open /etc/no-such-file: no such file or directory", + }, + openErrorTest { + "/etc", + O_WRONLY, + "open /etc: is a directory", + }, + openErrorTest { + "/etc/passwd/group", + O_WRONLY, + "open /etc/passwd/group: not a directory", + }, +} + +func TestOpenError(t *testing.T) { + for i, tt := range openErrorTests { + f, err := Open(tt.path, tt.mode, 0); + if err == nil { + t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode); + f.Close(); + continue; + } + if s := err.String(); s != tt.error { + t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, s, tt.error); + } + } +} diff --git a/src/pkg/os/path.go b/src/pkg/os/path.go index 0b86b8f8b..d8efe5183 100644 --- a/src/pkg/os/path.go +++ b/src/pkg/os/path.go @@ -6,16 +6,6 @@ package os import "os" -// PathError reports an error and the file path where it occurred. -type PathError struct { - Path string; - Error Error; -} - -func (p *PathError) String() string { - return p.Path + ": " + p.Error.String(); -} - // MkdirAll creates a directory named path, // along with any necessary parents, and returns nil, // or else returns an error. @@ -30,7 +20,7 @@ func MkdirAll(path string, perm int) Error { if dir.IsDirectory() { return nil; } - return &PathError{path, ENOTDIR}; + return &PathError{"mkdir", path, ENOTDIR}; } // Doesn't already exist; make sure parent does. @@ -61,7 +51,7 @@ func MkdirAll(path string, perm int) Error { if err1 == nil && dir.IsDirectory() { return nil; } - return &PathError{path, err}; + return err; } return nil; } @@ -77,19 +67,19 @@ func RemoveAll(path string) Error { } // Otherwise, is this a directory we need to recurse into? - dir, err1 := os.Lstat(path); - if err1 != nil { - return &PathError{path, err1}; + dir, err := os.Lstat(path); + if err != nil { + return err; } if !dir.IsDirectory() { // Not a directory; return the error from Remove. - return &PathError{path, err}; + return err; } // Directory. fd, err := Open(path, os.O_RDONLY, 0); if err != nil { - return &PathError{path, err}; + return err; } defer fd.Close(); @@ -99,13 +89,13 @@ func RemoveAll(path string) Error { names, err1 := fd.Readdirnames(100); for i, name := range names { err1 := RemoveAll(path + "/" + name); - if err1 != nil && err == nil { + if err == nil { err = err1; } } // If Readdirnames returned an error, use it. - if err1 != nil && err == nil { - err = &PathError{path, err1}; + if err == nil { + err = err1; } if len(names) == 0 { break; @@ -113,9 +103,9 @@ func RemoveAll(path string) Error { } // Remove directory. - err1 = Remove(path); - if err1 != nil && err == nil { - err = &PathError{path, err1}; + err1 := Remove(path); + if err == nil { + err = err1; } return err; } diff --git a/src/pkg/os/proc.go b/src/pkg/os/proc.go index d2fd6493e..9920c1355 100644 --- a/src/pkg/os/proc.go +++ b/src/pkg/os/proc.go @@ -39,7 +39,7 @@ func Getegid() int { // Getgroups returns a list of the numeric ids of groups that the caller belongs to. func Getgroups() ([]int, os.Error) { gids, errno := syscall.Getgroups(); - return gids, ErrnoToError(errno); + return gids, NewSyscallError("getgroups", errno); } // Exit causes the current program to exit with the given status code. diff --git a/src/pkg/os/time.go b/src/pkg/os/time.go index 3eee243cc..e1022066f 100644 --- a/src/pkg/os/time.go +++ b/src/pkg/os/time.go @@ -17,7 +17,7 @@ import ( func Time() (sec int64, nsec int64, err Error) { var tv syscall.Timeval; if errno := syscall.Gettimeofday(&tv); errno != 0 { - return 0, 0, ErrnoToError(errno) + return 0, 0, NewSyscallError("gettimeofday", errno); } return int64(tv.Sec), int64(tv.Usec)*1000, err; } -- cgit v1.2.3