Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 99 additions & 58 deletions src/os/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,119 @@ import (
)

// Portable analogs of some common system call errors.
// Note that these are exported for use in the Filesystem interface.
var (
errUnsupported = errors.New("operation not supported")
notImplemented = errors.New("os: not implemented")
ErrUnsupported = errors.New("operation not supported")
ErrNotImplemented = errors.New("operation not implemented")
ErrNotExist = errors.New("file not found")
ErrExist = errors.New("file exists")
)

// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
var (
Stdin = &File{0, "/dev/stdin"}
Stdout = &File{1, "/dev/stdout"}
Stderr = &File{2, "/dev/stderr"}
)
// Mkdir creates a directory. If the operation fails, it will return an error of
// type *PathError.
func Mkdir(path string, perm FileMode) error {
fs, suffix := findMount(path)
if fs == nil {
return &PathError{"mkdir", path, ErrNotExist}
}
err := fs.Mkdir(suffix, perm)
if err != nil {
return &PathError{"mkdir", path, err}
}
return nil
}

// Remove removes a file or (empty) directory. If the operation fails, it will
// return an error of type *PathError.
func Remove(path string) error {
fs, suffix := findMount(path)
if fs == nil {
return &PathError{"remove", path, ErrNotExist}
}
err := fs.Remove(suffix)
if err != nil {
return &PathError{"remove", path, err}
}
return nil
}

// File represents an open file descriptor.
type File struct {
fd uintptr
name string
handle FileHandle
name string
}

// Name returns the name of the file with which it was opened.
func (f *File) Name() string {
return f.name
}

// OpenFile opens the named file. If the operation fails, the returned error
// will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
fs, suffix := findMount(name)
if fs == nil {
return nil, &PathError{"open", name, ErrNotExist}
}
handle, err := fs.OpenFile(suffix, flag, perm)
if err != nil {
return nil, &PathError{"open", name, err}
}
return &File{name: name, handle: handle}, nil
}

// Open opens the file named for reading.
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}

// Create creates the named file, overwriting it if it already exists.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

// Read reads up to len(b) bytes from the File. It returns the number of bytes
// read and any error encountered. At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
n, err = f.handle.Read(b)
if err != nil {
err = &PathError{"read", f.name, err}
}
return
}

// Write writes len(b) bytes to the File. It returns the number of bytes written
// and an error, if any. Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
n, err = f.handle.Write(b)
if err != nil {
err = &PathError{"write", f.name, err}
}
return
}

// Close closes the File, rendering it unusable for I/O.
func (f *File) Close() (err error) {
err = f.handle.Close()
if err != nil {
err = &PathError{"close", f.name, err}
}
return
}

// Readdir is a stub, not yet implemented
func (f *File) Readdir(n int) ([]FileInfo, error) {
return nil, notImplemented
return nil, &PathError{"readdir", f.name, ErrNotImplemented}
}

// Readdirnames is a stub, not yet implemented
func (f *File) Readdirnames(n int) (names []string, err error) {
return nil, notImplemented
return nil, &PathError{"readdirnames", f.name, ErrNotImplemented}
}

// Stat is a stub, not yet implemented
func (f *File) Stat() (FileInfo, error) {
return nil, notImplemented
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd uintptr, name string) *File {
return &File{fd, name}
}

// Fd returns the integer Unix file descriptor referencing the open file. The
// file descriptor is valid only until f.Close is called.
func (f *File) Fd() uintptr {
return f.fd
return nil, &PathError{"stat", f.name, ErrNotImplemented}
}

const (
Expand All @@ -72,32 +142,8 @@ type PathError struct {
Err error
}

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

// Open is a super simple stub function (for now), only capable of opening stdin, stdout, and stderr
func Open(name string) (*File, error) {
fd := uintptr(999)
switch name {
case "/dev/stdin":
fd = 0
case "/dev/stdout":
fd = 1
case "/dev/stderr":
fd = 2
default:
return nil, &PathError{"open", name, notImplemented}
}
return &File{fd, name}, nil
}

// OpenFile is a stub, passing through to the stub Open() call
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
return Open(name)
}

// Create is a stub, passing through to the stub Open() call
func Create(name string) (*File, error) {
return Open(name)
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}

type FileMode uint32
Expand Down Expand Up @@ -155,12 +201,12 @@ type FileInfo interface {

// Stat is a stub, not yet implemented
func Stat(name string) (FileInfo, error) {
return nil, notImplemented
return nil, &PathError{"stat", name, ErrNotImplemented}
}

// Lstat is a stub, not yet implemented
func Lstat(name string) (FileInfo, error) {
return nil, notImplemented
return nil, &PathError{"lstat", name, ErrNotImplemented}
}

// Getwd is a stub (for now), always returning an empty string
Expand All @@ -178,11 +224,6 @@ func TempDir() string {
return "/tmp"
}

// Mkdir is a stub, not yet implemented
func Mkdir(name string, perm FileMode) error {
return notImplemented
}

// IsExist is a stub (for now), always returning false
func IsExist(err error) bool {
return false
Expand Down
32 changes: 24 additions & 8 deletions src/os/file_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,44 @@ import (
_ "unsafe"
)

// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
var (
Stdin = &File{stdioFileHandle(0), "/dev/stdin"}
Stdout = &File{stdioFileHandle(1), "/dev/stdout"}
Stderr = &File{stdioFileHandle(2), "/dev/stderr"}
)

// isOS indicates whether we're running on a real operating system with
// filesystem support.
const isOS = false

// stdioFileHandle represents one of stdin, stdout, or stderr depending on the
// number. It implements the FileHandle interface.
type stdioFileHandle uint8

// Read is unsupported on this system.
func (f *File) Read(b []byte) (n int, err error) {
return 0, errUnsupported
func (f stdioFileHandle) Read(b []byte) (n int, err error) {
return 0, ErrUnsupported
}

// Write writes len(b) bytes to the output. It returns the number of bytes
// written or an error if this file is not stdout or stderr.
func (f *File) Write(b []byte) (n int, err error) {
switch f.fd {
case Stdout.fd, Stderr.fd:
func (f stdioFileHandle) Write(b []byte) (n int, err error) {
switch f {
case 1, 2: // stdout, stderr
for _, c := range b {
putchar(c)
}
return len(b), nil
default:
return 0, errUnsupported
return 0, ErrUnsupported
}
}

// Close is unsupported on this system.
func (f *File) Close() error {
return errUnsupported
func (f stdioFileHandle) Close() error {
return ErrUnsupported
}

//go:linkname putchar runtime.putchar
Expand Down
98 changes: 92 additions & 6 deletions src/os/file_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,105 @@ import (
"syscall"
)

func init() {
// Mount the host filesystem at the root directory. This is what most
// programs will be expecting.
Mount("/", unixFilesystem{})
}

// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
var (
Stdin = &File{unixFileHandle(0), "/dev/stdin"}
Stdout = &File{unixFileHandle(1), "/dev/stdout"}
Stderr = &File{unixFileHandle(2), "/dev/stderr"}
)

// isOS indicates whether we're running on a real operating system with
// filesystem support.
const isOS = true

// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
// are relative to the current working directory.
type unixFilesystem struct {
}

func (fs unixFilesystem) Mkdir(path string, perm FileMode) error {
return handleSyscallError(syscall.Mkdir(path, uint32(perm)))
}

func (fs unixFilesystem) Remove(path string) error {
return handleSyscallError(syscall.Unlink(path))
}

func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (FileHandle, error) {
// Map os package flags to syscall flags.
syscallFlag := 0
if flag&O_RDONLY != 0 {
syscallFlag |= syscall.O_RDONLY
}
if flag&O_WRONLY != 0 {
syscallFlag |= syscall.O_WRONLY
}
if flag&O_RDWR != 0 {
syscallFlag |= syscall.O_RDWR
}
if flag&O_APPEND != 0 {
syscallFlag |= syscall.O_APPEND
}
if flag&O_CREATE != 0 {
syscallFlag |= syscall.O_CREAT
}
if flag&O_EXCL != 0 {
syscallFlag |= syscall.O_EXCL
}
if flag&O_SYNC != 0 {
syscallFlag |= syscall.O_SYNC
}
if flag&O_TRUNC != 0 {
syscallFlag |= syscall.O_TRUNC
}
fp, err := syscall.Open(path, syscallFlag, uint32(perm))
return unixFileHandle(fp), handleSyscallError(err)
}

// unixFileHandle is a Unix file pointer with associated methods that implement
// the FileHandle interface.
type unixFileHandle uintptr

// Read reads up to len(b) bytes from the File. It returns the number of bytes
// read and any error encountered. At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
return syscall.Read(int(f.fd), b)
func (f unixFileHandle) Read(b []byte) (n int, err error) {
n, err = syscall.Read(int(f), b)
err = handleSyscallError(err)
return
}

// Write writes len(b) bytes to the File. It returns the number of bytes written
// and an error, if any. Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
return syscall.Write(int(f.fd), b)
func (f unixFileHandle) Write(b []byte) (n int, err error) {
n, err = syscall.Write(int(f), b)
err = handleSyscallError(err)
return
}

// Close closes the File, rendering it unusable for I/O.
func (f *File) Close() error {
return syscall.Close(int(f.fd))
func (f unixFileHandle) Close() error {
return handleSyscallError(syscall.Close(int(f)))
}

// handleSyscallError converts syscall errors into regular os package errors.
// The err parameter must be either nil or of type syscall.Errno.
func handleSyscallError(err error) error {
if err == nil {
return nil
}
switch err.(syscall.Errno) {
case syscall.EEXIST:
return ErrExist
case syscall.ENOENT:
return ErrNotExist
default:
return err
}
}
Loading