Skip to content

Commit 07ce37b

Browse files
committed
os: implement virtual filesystem support
This allows applications to mount filesystems in the os package. This is useful for mounting external flash filesystems, for example. TODO: this is not finished: there is no support yet for paths relative to the current working directory.
1 parent 62e78c0 commit 07ce37b

File tree

8 files changed

+318
-95
lines changed

8 files changed

+318
-95
lines changed

src/os/file.go

Lines changed: 99 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,49 +10,119 @@ import (
1010
)
1111

1212
// Portable analogs of some common system call errors.
13+
// Note that these are exported for use in the Filesystem interface.
1314
var (
14-
errUnsupported = errors.New("operation not supported")
15-
notImplemented = errors.New("os: not implemented")
15+
ErrUnsupported = errors.New("operation not supported")
16+
ErrNotImplemented = errors.New("operation not implemented")
17+
ErrNotExist = errors.New("file not found")
18+
ErrExist = errors.New("file exists")
1619
)
1720

18-
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
19-
// standard output, and standard error file descriptors.
20-
var (
21-
Stdin = &File{0, "/dev/stdin"}
22-
Stdout = &File{1, "/dev/stdout"}
23-
Stderr = &File{2, "/dev/stderr"}
24-
)
21+
// Mkdir creates a directory. If the operation fails, it will return an error of
22+
// type *PathError.
23+
func Mkdir(path string, perm FileMode) error {
24+
fs, suffix := findMount(path)
25+
if fs == nil {
26+
return &PathError{"mkdir", path, ErrNotExist}
27+
}
28+
err := fs.Mkdir(suffix, perm)
29+
if err != nil {
30+
return &PathError{"mkdir", path, err}
31+
}
32+
return nil
33+
}
34+
35+
// Remove removes a file or (empty) directory. If the operation fails, it will
36+
// return an error of type *PathError.
37+
func Remove(path string) error {
38+
fs, suffix := findMount(path)
39+
if fs == nil {
40+
return &PathError{"remove", path, ErrNotExist}
41+
}
42+
err := fs.Remove(suffix)
43+
if err != nil {
44+
return &PathError{"remove", path, err}
45+
}
46+
return nil
47+
}
2548

2649
// File represents an open file descriptor.
2750
type File struct {
28-
fd uintptr
29-
name string
51+
handle FileHandle
52+
name string
53+
}
54+
55+
// Name returns the name of the file with which it was opened.
56+
func (f *File) Name() string {
57+
return f.name
58+
}
59+
60+
// OpenFile opens the named file. If the operation fails, the returned error
61+
// will be of type *PathError.
62+
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
63+
fs, suffix := findMount(name)
64+
if fs == nil {
65+
return nil, &PathError{"open", name, ErrNotExist}
66+
}
67+
handle, err := fs.OpenFile(suffix, flag, perm)
68+
if err != nil {
69+
return nil, &PathError{"open", name, err}
70+
}
71+
return &File{name: name, handle: handle}, nil
72+
}
73+
74+
// Open opens the file named for reading.
75+
func Open(name string) (*File, error) {
76+
return OpenFile(name, O_RDONLY, 0)
77+
}
78+
79+
// Create creates the named file, overwriting it if it already exists.
80+
func Create(name string) (*File, error) {
81+
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
82+
}
83+
84+
// Read reads up to len(b) bytes from the File. It returns the number of bytes
85+
// read and any error encountered. At end of file, Read returns 0, io.EOF.
86+
func (f *File) Read(b []byte) (n int, err error) {
87+
n, err = f.handle.Read(b)
88+
if err != nil {
89+
err = &PathError{"read", f.name, err}
90+
}
91+
return
92+
}
93+
94+
// Write writes len(b) bytes to the File. It returns the number of bytes written
95+
// and an error, if any. Write returns a non-nil error when n != len(b).
96+
func (f *File) Write(b []byte) (n int, err error) {
97+
n, err = f.handle.Write(b)
98+
if err != nil {
99+
err = &PathError{"write", f.name, err}
100+
}
101+
return
102+
}
103+
104+
// Close closes the File, rendering it unusable for I/O.
105+
func (f *File) Close() (err error) {
106+
err = f.handle.Close()
107+
if err != nil {
108+
err = &PathError{"close", f.name, err}
109+
}
110+
return
30111
}
31112

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

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

42123
// Stat is a stub, not yet implemented
43124
func (f *File) Stat() (FileInfo, error) {
44-
return nil, notImplemented
45-
}
46-
47-
// NewFile returns a new File with the given file descriptor and name.
48-
func NewFile(fd uintptr, name string) *File {
49-
return &File{fd, name}
50-
}
51-
52-
// Fd returns the integer Unix file descriptor referencing the open file. The
53-
// file descriptor is valid only until f.Close is called.
54-
func (f *File) Fd() uintptr {
55-
return f.fd
125+
return nil, &PathError{"stat", f.name, ErrNotImplemented}
56126
}
57127

58128
const (
@@ -72,32 +142,8 @@ type PathError struct {
72142
Err error
73143
}
74144

75-
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
76-
77-
// Open is a super simple stub function (for now), only capable of opening stdin, stdout, and stderr
78-
func Open(name string) (*File, error) {
79-
fd := uintptr(999)
80-
switch name {
81-
case "/dev/stdin":
82-
fd = 0
83-
case "/dev/stdout":
84-
fd = 1
85-
case "/dev/stderr":
86-
fd = 2
87-
default:
88-
return nil, &PathError{"open", name, notImplemented}
89-
}
90-
return &File{fd, name}, nil
91-
}
92-
93-
// OpenFile is a stub, passing through to the stub Open() call
94-
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
95-
return Open(name)
96-
}
97-
98-
// Create is a stub, passing through to the stub Open() call
99-
func Create(name string) (*File, error) {
100-
return Open(name)
145+
func (e *PathError) Error() string {
146+
return e.Op + " " + e.Path + ": " + e.Err.Error()
101147
}
102148

103149
type FileMode uint32
@@ -155,12 +201,12 @@ type FileInfo interface {
155201

156202
// Stat is a stub, not yet implemented
157203
func Stat(name string) (FileInfo, error) {
158-
return nil, notImplemented
204+
return nil, &PathError{"stat", name, ErrNotImplemented}
159205
}
160206

161207
// Lstat is a stub, not yet implemented
162208
func Lstat(name string) (FileInfo, error) {
163-
return nil, notImplemented
209+
return nil, &PathError{"lstat", name, ErrNotImplemented}
164210
}
165211

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

181-
// Mkdir is a stub, not yet implemented
182-
func Mkdir(name string, perm FileMode) error {
183-
return notImplemented
184-
}
185-
186227
// IsExist is a stub (for now), always returning false
187228
func IsExist(err error) bool {
188229
return false

src/os/file_other.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,40 @@ import (
66
_ "unsafe"
77
)
88

9+
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
10+
// standard output, and standard error file descriptors.
11+
var (
12+
Stdin = &File{stdioFileHandle(0), "/dev/stdin"}
13+
Stdout = &File{stdioFileHandle(1), "/dev/stdout"}
14+
Stderr = &File{stdioFileHandle(2), "/dev/stderr"}
15+
)
16+
17+
// stdioFileHandle represents one of stdin, stdout, or stderr depending on the
18+
// number. It implements the FileHandle interface.
19+
type stdioFileHandle uint8
20+
921
// Read is unsupported on this system.
10-
func (f *File) Read(b []byte) (n int, err error) {
11-
return 0, errUnsupported
22+
func (f stdioFileHandle) Read(b []byte) (n int, err error) {
23+
return 0, ErrUnsupported
1224
}
1325

1426
// Write writes len(b) bytes to the output. It returns the number of bytes
1527
// written or an error if this file is not stdout or stderr.
16-
func (f *File) Write(b []byte) (n int, err error) {
17-
switch f.fd {
18-
case Stdout.fd, Stderr.fd:
28+
func (f stdioFileHandle) Write(b []byte) (n int, err error) {
29+
switch f {
30+
case 1, 2: // stdout, stderr
1931
for _, c := range b {
2032
putchar(c)
2133
}
2234
return len(b), nil
2335
default:
24-
return 0, errUnsupported
36+
return 0, ErrUnsupported
2537
}
2638
}
2739

2840
// Close is unsupported on this system.
29-
func (f *File) Close() error {
30-
return errUnsupported
41+
func (f stdioFileHandle) Close() error {
42+
return ErrUnsupported
3143
}
3244

3345
//go:linkname putchar runtime.putchar

src/os/file_unix.go

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,101 @@ import (
66
"syscall"
77
)
88

9+
func init() {
10+
// Mount the host filesystem at the root directory. This is what most
11+
// programs will be expecting.
12+
Mount("/", unixFilesystem{})
13+
}
14+
15+
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
16+
// standard output, and standard error file descriptors.
17+
var (
18+
Stdin = &File{unixFileHandle(0), "/dev/stdin"}
19+
Stdout = &File{unixFileHandle(1), "/dev/stdout"}
20+
Stderr = &File{unixFileHandle(2), "/dev/stderr"}
21+
)
22+
23+
// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
24+
// are relative to the current working directory.
25+
type unixFilesystem struct {
26+
}
27+
28+
func (fs unixFilesystem) Mkdir(path string, perm FileMode) error {
29+
return handleSyscallError(syscall.Mkdir(path, uint32(perm)))
30+
}
31+
32+
func (fs unixFilesystem) Remove(path string) error {
33+
return handleSyscallError(syscall.Unlink(path))
34+
}
35+
36+
func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (FileHandle, error) {
37+
// Map os package flags to syscall flags.
38+
syscallFlag := 0
39+
if flag&O_RDONLY != 0 {
40+
syscallFlag |= syscall.O_RDONLY
41+
}
42+
if flag&O_WRONLY != 0 {
43+
syscallFlag |= syscall.O_WRONLY
44+
}
45+
if flag&O_RDWR != 0 {
46+
syscallFlag |= syscall.O_RDWR
47+
}
48+
if flag&O_APPEND != 0 {
49+
syscallFlag |= syscall.O_APPEND
50+
}
51+
if flag&O_CREATE != 0 {
52+
syscallFlag |= syscall.O_CREAT
53+
}
54+
if flag&O_EXCL != 0 {
55+
syscallFlag |= syscall.O_EXCL
56+
}
57+
if flag&O_SYNC != 0 {
58+
syscallFlag |= syscall.O_SYNC
59+
}
60+
if flag&O_TRUNC != 0 {
61+
syscallFlag |= syscall.O_TRUNC
62+
}
63+
fp, err := syscall.Open(path, syscallFlag, uint32(perm))
64+
return unixFileHandle(fp), handleSyscallError(err)
65+
}
66+
67+
// unixFileHandle is a Unix file pointer with associated methods that implement
68+
// the FileHandle interface.
69+
type unixFileHandle uintptr
70+
971
// Read reads up to len(b) bytes from the File. It returns the number of bytes
1072
// read and any error encountered. At end of file, Read returns 0, io.EOF.
11-
func (f *File) Read(b []byte) (n int, err error) {
12-
return syscall.Read(int(f.fd), b)
73+
func (f unixFileHandle) Read(b []byte) (n int, err error) {
74+
n, err = syscall.Read(int(f), b)
75+
err = handleSyscallError(err)
76+
return
1377
}
1478

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

2187
// Close closes the File, rendering it unusable for I/O.
22-
func (f *File) Close() error {
23-
return syscall.Close(int(f.fd))
88+
func (f unixFileHandle) Close() error {
89+
return handleSyscallError(syscall.Close(int(f)))
90+
}
91+
92+
// handleSyscallError converts syscall errors into regular os package errors.
93+
// The err parameter must be either nil or of type syscall.Errno.
94+
func handleSyscallError(err error) error {
95+
if err == nil {
96+
return nil
97+
}
98+
switch err.(syscall.Errno) {
99+
case syscall.EEXIST:
100+
return ErrExist
101+
case syscall.ENOENT:
102+
return ErrNotExist
103+
default:
104+
return err
105+
}
24106
}

0 commit comments

Comments
 (0)