PR target/82112
[official-gcc.git] / libgo / go / os / file_plan9.go
blob5276a7ec541299c1b2abe677417e7639c0a33e55
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 package os
7 import (
8 "io"
9 "runtime"
10 "syscall"
11 "time"
14 // fixLongPath is a noop on non-Windows platforms.
15 func fixLongPath(path string) string {
16 return path
19 // file is the real representation of *File.
20 // The extra level of indirection ensures that no clients of os
21 // can overwrite this data, which could cause the finalizer
22 // to close the wrong file descriptor.
23 type file struct {
24 fd int
25 name string
26 dirinfo *dirInfo // nil unless directory being read
29 // Fd returns the integer Plan 9 file descriptor referencing the open file.
30 // The file descriptor is valid only until f.Close is called or f is garbage collected.
31 func (f *File) Fd() uintptr {
32 if f == nil {
33 return ^(uintptr(0))
35 return uintptr(f.fd)
38 // NewFile returns a new File with the given file descriptor and name.
39 func NewFile(fd uintptr, name string) *File {
40 fdi := int(fd)
41 if fdi < 0 {
42 return nil
44 f := &File{&file{fd: fdi, name: name}}
45 runtime.SetFinalizer(f.file, (*file).close)
46 return f
49 // Auxiliary information if the File describes a directory
50 type dirInfo struct {
51 buf [syscall.STATMAX]byte // buffer for directory I/O
52 nbuf int // length of buf; return value from Read
53 bufp int // location of next record in buf.
56 func epipecheck(file *File, e error) {
59 // DevNull is the name of the operating system's ``null device.''
60 // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
61 const DevNull = "/dev/null"
63 // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
64 func syscallMode(i FileMode) (o uint32) {
65 o |= uint32(i.Perm())
66 if i&ModeAppend != 0 {
67 o |= syscall.DMAPPEND
69 if i&ModeExclusive != 0 {
70 o |= syscall.DMEXCL
72 if i&ModeTemporary != 0 {
73 o |= syscall.DMTMP
75 return
78 // OpenFile is the generalized open call; most users will use Open
79 // or Create instead. It opens the named file with specified flag
80 // (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
81 // methods on the returned File can be used for I/O.
82 // If there is an error, it will be of type *PathError.
83 func OpenFile(name string, flag int, perm FileMode) (*File, error) {
84 var (
85 fd int
86 e error
87 create bool
88 excl bool
89 trunc bool
90 append bool
93 if flag&O_CREATE == O_CREATE {
94 flag = flag & ^O_CREATE
95 create = true
97 if flag&O_EXCL == O_EXCL {
98 excl = true
100 if flag&O_TRUNC == O_TRUNC {
101 trunc = true
103 // O_APPEND is emulated on Plan 9
104 if flag&O_APPEND == O_APPEND {
105 flag = flag &^ O_APPEND
106 append = true
109 if (create && trunc) || excl {
110 fd, e = syscall.Create(name, flag, syscallMode(perm))
111 } else {
112 fd, e = syscall.Open(name, flag)
113 if e != nil && create {
114 var e1 error
115 fd, e1 = syscall.Create(name, flag, syscallMode(perm))
116 if e1 == nil {
117 e = nil
122 if e != nil {
123 return nil, &PathError{"open", name, e}
126 if append {
127 if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil {
128 return nil, &PathError{"seek", name, e}
132 return NewFile(uintptr(fd), name), nil
135 // Close closes the File, rendering it unusable for I/O.
136 // It returns an error, if any.
137 func (f *File) Close() error {
138 if err := f.checkValid("close"); err != nil {
139 return err
141 return f.file.close()
144 func (file *file) close() error {
145 if file == nil || file.fd == badFd {
146 return ErrInvalid
148 var err error
149 if e := syscall.Close(file.fd); e != nil {
150 err = &PathError{"close", file.name, e}
152 file.fd = badFd // so it can't be closed again
154 // no need for a finalizer anymore
155 runtime.SetFinalizer(file, nil)
156 return err
159 // Stat returns the FileInfo structure describing file.
160 // If there is an error, it will be of type *PathError.
161 func (f *File) Stat() (FileInfo, error) {
162 if f == nil {
163 return nil, ErrInvalid
165 d, err := dirstat(f)
166 if err != nil {
167 return nil, err
169 return fileInfoFromStat(d), nil
172 // Truncate changes the size of the file.
173 // It does not change the I/O offset.
174 // If there is an error, it will be of type *PathError.
175 func (f *File) Truncate(size int64) error {
176 if f == nil {
177 return ErrInvalid
180 var d syscall.Dir
181 d.Null()
182 d.Length = size
184 var buf [syscall.STATFIXLEN]byte
185 n, err := d.Marshal(buf[:])
186 if err != nil {
187 return &PathError{"truncate", f.name, err}
189 if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
190 return &PathError{"truncate", f.name, err}
192 return nil
195 const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
197 // Chmod changes the mode of the file to mode.
198 // If there is an error, it will be of type *PathError.
199 func (f *File) Chmod(mode FileMode) error {
200 if f == nil {
201 return ErrInvalid
203 var d syscall.Dir
205 odir, e := dirstat(f)
206 if e != nil {
207 return &PathError{"chmod", f.name, e}
209 d.Null()
210 d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
212 var buf [syscall.STATFIXLEN]byte
213 n, err := d.Marshal(buf[:])
214 if err != nil {
215 return &PathError{"chmod", f.name, err}
217 if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
218 return &PathError{"chmod", f.name, err}
220 return nil
223 // Sync commits the current contents of the file to stable storage.
224 // Typically, this means flushing the file system's in-memory copy
225 // of recently written data to disk.
226 func (f *File) Sync() error {
227 if f == nil {
228 return ErrInvalid
230 var d syscall.Dir
231 d.Null()
233 var buf [syscall.STATFIXLEN]byte
234 n, err := d.Marshal(buf[:])
235 if err != nil {
236 return NewSyscallError("fsync", err)
238 if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
239 return NewSyscallError("fsync", err)
241 return nil
244 // read reads up to len(b) bytes from the File.
245 // It returns the number of bytes read and an error, if any.
246 func (f *File) read(b []byte) (n int, err error) {
247 return fixCount(syscall.Read(f.fd, b))
250 // pread reads len(b) bytes from the File starting at byte offset off.
251 // It returns the number of bytes read and the error, if any.
252 // EOF is signaled by a zero count with err set to nil.
253 func (f *File) pread(b []byte, off int64) (n int, err error) {
254 return fixCount(syscall.Pread(f.fd, b, off))
257 // write writes len(b) bytes to the File.
258 // It returns the number of bytes written and an error, if any.
259 // Since Plan 9 preserves message boundaries, never allow
260 // a zero-byte write.
261 func (f *File) write(b []byte) (n int, err error) {
262 if len(b) == 0 {
263 return 0, nil
265 return fixCount(syscall.Write(f.fd, b))
268 // pwrite writes len(b) bytes to the File starting at byte offset off.
269 // It returns the number of bytes written and an error, if any.
270 // Since Plan 9 preserves message boundaries, never allow
271 // a zero-byte write.
272 func (f *File) pwrite(b []byte, off int64) (n int, err error) {
273 if len(b) == 0 {
274 return 0, nil
276 return fixCount(syscall.Pwrite(f.fd, b, off))
279 // seek sets the offset for the next Read or Write on file to offset, interpreted
280 // according to whence: 0 means relative to the origin of the file, 1 means
281 // relative to the current offset, and 2 means relative to the end.
282 // It returns the new offset and an error, if any.
283 func (f *File) seek(offset int64, whence int) (ret int64, err error) {
284 return syscall.Seek(f.fd, offset, whence)
287 // Truncate changes the size of the named file.
288 // If the file is a symbolic link, it changes the size of the link's target.
289 // If there is an error, it will be of type *PathError.
290 func Truncate(name string, size int64) error {
291 var d syscall.Dir
293 d.Null()
294 d.Length = size
296 var buf [syscall.STATFIXLEN]byte
297 n, err := d.Marshal(buf[:])
298 if err != nil {
299 return &PathError{"truncate", name, err}
301 if err = syscall.Wstat(name, buf[:n]); err != nil {
302 return &PathError{"truncate", name, err}
304 return nil
307 // Remove removes the named file or directory.
308 // If there is an error, it will be of type *PathError.
309 func Remove(name string) error {
310 if e := syscall.Remove(name); e != nil {
311 return &PathError{"remove", name, e}
313 return nil
316 // HasPrefix from the strings package.
317 func hasPrefix(s, prefix string) bool {
318 return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
321 // LastIndexByte from the strings package.
322 func lastIndex(s string, sep byte) int {
323 for i := len(s) - 1; i >= 0; i-- {
324 if s[i] == sep {
325 return i
328 return -1
331 func rename(oldname, newname string) error {
332 dirname := oldname[:lastIndex(oldname, '/')+1]
333 if hasPrefix(newname, dirname) {
334 newname = newname[len(dirname):]
335 } else {
336 return &LinkError{"rename", oldname, newname, ErrInvalid}
339 // If newname still contains slashes after removing the oldname
340 // prefix, the rename is cross-directory and must be rejected.
341 if lastIndex(newname, '/') >= 0 {
342 return &LinkError{"rename", oldname, newname, ErrInvalid}
345 var d syscall.Dir
347 d.Null()
348 d.Name = newname
350 buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
351 n, err := d.Marshal(buf[:])
352 if err != nil {
353 return &LinkError{"rename", oldname, newname, err}
356 // If newname already exists and is not a directory, rename replaces it.
357 f, err := Stat(dirname + newname)
358 if err == nil && !f.IsDir() {
359 Remove(dirname + newname)
362 if err = syscall.Wstat(oldname, buf[:n]); err != nil {
363 return &LinkError{"rename", oldname, newname, err}
365 return nil
368 // Chmod changes the mode of the named file to mode.
369 // If the file is a symbolic link, it changes the mode of the link's target.
370 // If there is an error, it will be of type *PathError.
371 func Chmod(name string, mode FileMode) error {
372 var d syscall.Dir
374 odir, e := dirstat(name)
375 if e != nil {
376 return &PathError{"chmod", name, e}
378 d.Null()
379 d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
381 var buf [syscall.STATFIXLEN]byte
382 n, err := d.Marshal(buf[:])
383 if err != nil {
384 return &PathError{"chmod", name, err}
386 if err = syscall.Wstat(name, buf[:n]); err != nil {
387 return &PathError{"chmod", name, err}
389 return nil
392 // Chtimes changes the access and modification times of the named
393 // file, similar to the Unix utime() or utimes() functions.
395 // The underlying filesystem may truncate or round the values to a
396 // less precise time unit.
397 // If there is an error, it will be of type *PathError.
398 func Chtimes(name string, atime time.Time, mtime time.Time) error {
399 var d syscall.Dir
401 d.Null()
402 d.Atime = uint32(atime.Unix())
403 d.Mtime = uint32(mtime.Unix())
405 var buf [syscall.STATFIXLEN]byte
406 n, err := d.Marshal(buf[:])
407 if err != nil {
408 return &PathError{"chtimes", name, err}
410 if err = syscall.Wstat(name, buf[:n]); err != nil {
411 return &PathError{"chtimes", name, err}
413 return nil
416 // Pipe returns a connected pair of Files; reads from r return bytes
417 // written to w. It returns the files and an error, if any.
418 func Pipe() (r *File, w *File, err error) {
419 var p [2]int
421 if e := syscall.Pipe(p[0:]); e != nil {
422 return nil, nil, NewSyscallError("pipe", e)
425 return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
428 // not supported on Plan 9
430 // Link creates newname as a hard link to the oldname file.
431 // If there is an error, it will be of type *LinkError.
432 func Link(oldname, newname string) error {
433 return &LinkError{"link", oldname, newname, syscall.EPLAN9}
436 // Symlink creates newname as a symbolic link to oldname.
437 // If there is an error, it will be of type *LinkError.
438 func Symlink(oldname, newname string) error {
439 return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
442 // Readlink returns the destination of the named symbolic link.
443 // If there is an error, it will be of type *PathError.
444 func Readlink(name string) (string, error) {
445 return "", &PathError{"readlink", name, syscall.EPLAN9}
448 // Chown changes the numeric uid and gid of the named file.
449 // If the file is a symbolic link, it changes the uid and gid of the link's target.
450 // If there is an error, it will be of type *PathError.
451 func Chown(name string, uid, gid int) error {
452 return &PathError{"chown", name, syscall.EPLAN9}
455 // Lchown changes the numeric uid and gid of the named file.
456 // If the file is a symbolic link, it changes the uid and gid of the link itself.
457 // If there is an error, it will be of type *PathError.
458 func Lchown(name string, uid, gid int) error {
459 return &PathError{"lchown", name, syscall.EPLAN9}
462 // Chown changes the numeric uid and gid of the named file.
463 // If there is an error, it will be of type *PathError.
464 func (f *File) Chown(uid, gid int) error {
465 if f == nil {
466 return ErrInvalid
468 return &PathError{"chown", f.name, syscall.EPLAN9}
471 // TempDir returns the default directory to use for temporary files.
472 func TempDir() string {
473 return "/tmp"