...

Source file src/pkg/os/file_plan9.go

     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.
     4	
     5	package os
     6	
     7	import (
     8		"internal/poll"
     9		"io"
    10		"runtime"
    11		"syscall"
    12		"time"
    13	)
    14	
    15	// fixLongPath is a noop on non-Windows platforms.
    16	func fixLongPath(path string) string {
    17		return path
    18	}
    19	
    20	// file is the real representation of *File.
    21	// The extra level of indirection ensures that no clients of os
    22	// can overwrite this data, which could cause the finalizer
    23	// to close the wrong file descriptor.
    24	type file struct {
    25		fd         int
    26		name       string
    27		dirinfo    *dirInfo // nil unless directory being read
    28		appendMode bool     // whether file is opened for appending
    29	}
    30	
    31	// Fd returns the integer Plan 9 file descriptor referencing the open file.
    32	// The file descriptor is valid only until f.Close is called or f is garbage collected.
    33	// On Unix systems this will cause the SetDeadline methods to stop working.
    34	func (f *File) Fd() uintptr {
    35		if f == nil {
    36			return ^(uintptr(0))
    37		}
    38		return uintptr(f.fd)
    39	}
    40	
    41	// NewFile returns a new File with the given file descriptor and
    42	// name. The returned value will be nil if fd is not a valid file
    43	// descriptor.
    44	func NewFile(fd uintptr, name string) *File {
    45		fdi := int(fd)
    46		if fdi < 0 {
    47			return nil
    48		}
    49		f := &File{&file{fd: fdi, name: name}}
    50		runtime.SetFinalizer(f.file, (*file).close)
    51		return f
    52	}
    53	
    54	// Auxiliary information if the File describes a directory
    55	type dirInfo struct {
    56		buf  [syscall.STATMAX]byte // buffer for directory I/O
    57		nbuf int                   // length of buf; return value from Read
    58		bufp int                   // location of next record in buf.
    59	}
    60	
    61	func epipecheck(file *File, e error) {
    62	}
    63	
    64	// DevNull is the name of the operating system's ``null device.''
    65	// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
    66	const DevNull = "/dev/null"
    67	
    68	// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
    69	func syscallMode(i FileMode) (o uint32) {
    70		o |= uint32(i.Perm())
    71		if i&ModeAppend != 0 {
    72			o |= syscall.DMAPPEND
    73		}
    74		if i&ModeExclusive != 0 {
    75			o |= syscall.DMEXCL
    76		}
    77		if i&ModeTemporary != 0 {
    78			o |= syscall.DMTMP
    79		}
    80		return
    81	}
    82	
    83	// openFileNolog is the Plan 9 implementation of OpenFile.
    84	func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
    85		var (
    86			fd     int
    87			e      error
    88			create bool
    89			excl   bool
    90			trunc  bool
    91			append bool
    92		)
    93	
    94		if flag&O_CREATE == O_CREATE {
    95			flag = flag & ^O_CREATE
    96			create = true
    97		}
    98		if flag&O_EXCL == O_EXCL {
    99			excl = true
   100		}
   101		if flag&O_TRUNC == O_TRUNC {
   102			trunc = true
   103		}
   104		// O_APPEND is emulated on Plan 9
   105		if flag&O_APPEND == O_APPEND {
   106			flag = flag &^ O_APPEND
   107			append = true
   108		}
   109	
   110		if (create && trunc) || excl {
   111			fd, e = syscall.Create(name, flag, syscallMode(perm))
   112		} else {
   113			fd, e = syscall.Open(name, flag)
   114			if e != nil && create {
   115				var e1 error
   116				fd, e1 = syscall.Create(name, flag, syscallMode(perm))
   117				if e1 == nil {
   118					e = nil
   119				}
   120			}
   121		}
   122	
   123		if e != nil {
   124			return nil, &PathError{"open", name, e}
   125		}
   126	
   127		if append {
   128			if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil {
   129				return nil, &PathError{"seek", name, e}
   130			}
   131		}
   132	
   133		return NewFile(uintptr(fd), name), nil
   134	}
   135	
   136	// Close closes the File, rendering it unusable for I/O.
   137	// On files that support SetDeadline, any pending I/O operations will
   138	// be canceled and return immediately with an error.
   139	// Close will return an error if it has already been called.
   140	func (f *File) Close() error {
   141		if err := f.checkValid("close"); err != nil {
   142			return err
   143		}
   144		return f.file.close()
   145	}
   146	
   147	func (file *file) close() error {
   148		if file == nil || file.fd == badFd {
   149			return ErrInvalid
   150		}
   151		var err error
   152		if e := syscall.Close(file.fd); e != nil {
   153			err = &PathError{"close", file.name, e}
   154		}
   155		file.fd = badFd // so it can't be closed again
   156	
   157		// no need for a finalizer anymore
   158		runtime.SetFinalizer(file, nil)
   159		return err
   160	}
   161	
   162	// Stat returns the FileInfo structure describing file.
   163	// If there is an error, it will be of type *PathError.
   164	func (f *File) Stat() (FileInfo, error) {
   165		if f == nil {
   166			return nil, ErrInvalid
   167		}
   168		d, err := dirstat(f)
   169		if err != nil {
   170			return nil, err
   171		}
   172		return fileInfoFromStat(d), nil
   173	}
   174	
   175	// Truncate changes the size of the file.
   176	// It does not change the I/O offset.
   177	// If there is an error, it will be of type *PathError.
   178	func (f *File) Truncate(size int64) error {
   179		if f == nil {
   180			return ErrInvalid
   181		}
   182	
   183		var d syscall.Dir
   184		d.Null()
   185		d.Length = size
   186	
   187		var buf [syscall.STATFIXLEN]byte
   188		n, err := d.Marshal(buf[:])
   189		if err != nil {
   190			return &PathError{"truncate", f.name, err}
   191		}
   192		if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
   193			return &PathError{"truncate", f.name, err}
   194		}
   195		return nil
   196	}
   197	
   198	const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
   199	
   200	func (f *File) chmod(mode FileMode) error {
   201		if f == nil {
   202			return ErrInvalid
   203		}
   204		var d syscall.Dir
   205	
   206		odir, e := dirstat(f)
   207		if e != nil {
   208			return &PathError{"chmod", f.name, e}
   209		}
   210		d.Null()
   211		d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
   212	
   213		var buf [syscall.STATFIXLEN]byte
   214		n, err := d.Marshal(buf[:])
   215		if err != nil {
   216			return &PathError{"chmod", f.name, err}
   217		}
   218		if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
   219			return &PathError{"chmod", f.name, err}
   220		}
   221		return nil
   222	}
   223	
   224	// Sync commits the current contents of the file to stable storage.
   225	// Typically, this means flushing the file system's in-memory copy
   226	// of recently written data to disk.
   227	func (f *File) Sync() error {
   228		if f == nil {
   229			return ErrInvalid
   230		}
   231		var d syscall.Dir
   232		d.Null()
   233	
   234		var buf [syscall.STATFIXLEN]byte
   235		n, err := d.Marshal(buf[:])
   236		if err != nil {
   237			return NewSyscallError("fsync", err)
   238		}
   239		if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
   240			return NewSyscallError("fsync", err)
   241		}
   242		return nil
   243	}
   244	
   245	// read reads up to len(b) bytes from the File.
   246	// It returns the number of bytes read and an error, if any.
   247	func (f *File) read(b []byte) (n int, err error) {
   248		n, e := fixCount(syscall.Read(f.fd, b))
   249		if n == 0 && len(b) > 0 && e == nil {
   250			return 0, io.EOF
   251		}
   252		return n, e
   253	}
   254	
   255	// pread reads len(b) bytes from the File starting at byte offset off.
   256	// It returns the number of bytes read and the error, if any.
   257	// EOF is signaled by a zero count with err set to nil.
   258	func (f *File) pread(b []byte, off int64) (n int, err error) {
   259		n, e := fixCount(syscall.Pread(f.fd, b, off))
   260		if n == 0 && len(b) > 0 && e == nil {
   261			return 0, io.EOF
   262		}
   263		return n, e
   264	}
   265	
   266	// write writes len(b) bytes to the File.
   267	// It returns the number of bytes written and an error, if any.
   268	// Since Plan 9 preserves message boundaries, never allow
   269	// a zero-byte write.
   270	func (f *File) write(b []byte) (n int, err error) {
   271		if len(b) == 0 {
   272			return 0, nil
   273		}
   274		return fixCount(syscall.Write(f.fd, b))
   275	}
   276	
   277	// pwrite writes len(b) bytes to the File starting at byte offset off.
   278	// It returns the number of bytes written and an error, if any.
   279	// Since Plan 9 preserves message boundaries, never allow
   280	// a zero-byte write.
   281	func (f *File) pwrite(b []byte, off int64) (n int, err error) {
   282		if len(b) == 0 {
   283			return 0, nil
   284		}
   285		return fixCount(syscall.Pwrite(f.fd, b, off))
   286	}
   287	
   288	// seek sets the offset for the next Read or Write on file to offset, interpreted
   289	// according to whence: 0 means relative to the origin of the file, 1 means
   290	// relative to the current offset, and 2 means relative to the end.
   291	// It returns the new offset and an error, if any.
   292	func (f *File) seek(offset int64, whence int) (ret int64, err error) {
   293		return syscall.Seek(f.fd, offset, whence)
   294	}
   295	
   296	// Truncate changes the size of the named file.
   297	// If the file is a symbolic link, it changes the size of the link's target.
   298	// If there is an error, it will be of type *PathError.
   299	func Truncate(name string, size int64) error {
   300		var d syscall.Dir
   301	
   302		d.Null()
   303		d.Length = size
   304	
   305		var buf [syscall.STATFIXLEN]byte
   306		n, err := d.Marshal(buf[:])
   307		if err != nil {
   308			return &PathError{"truncate", name, err}
   309		}
   310		if err = syscall.Wstat(name, buf[:n]); err != nil {
   311			return &PathError{"truncate", name, err}
   312		}
   313		return nil
   314	}
   315	
   316	// Remove removes the named file or directory.
   317	// If there is an error, it will be of type *PathError.
   318	func Remove(name string) error {
   319		if e := syscall.Remove(name); e != nil {
   320			return &PathError{"remove", name, e}
   321		}
   322		return nil
   323	}
   324	
   325	// HasPrefix from the strings package.
   326	func hasPrefix(s, prefix string) bool {
   327		return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
   328	}
   329	
   330	// LastIndexByte from the strings package.
   331	func lastIndex(s string, sep byte) int {
   332		for i := len(s) - 1; i >= 0; i-- {
   333			if s[i] == sep {
   334				return i
   335			}
   336		}
   337		return -1
   338	}
   339	
   340	func rename(oldname, newname string) error {
   341		dirname := oldname[:lastIndex(oldname, '/')+1]
   342		if hasPrefix(newname, dirname) {
   343			newname = newname[len(dirname):]
   344		} else {
   345			return &LinkError{"rename", oldname, newname, ErrInvalid}
   346		}
   347	
   348		// If newname still contains slashes after removing the oldname
   349		// prefix, the rename is cross-directory and must be rejected.
   350		if lastIndex(newname, '/') >= 0 {
   351			return &LinkError{"rename", oldname, newname, ErrInvalid}
   352		}
   353	
   354		var d syscall.Dir
   355	
   356		d.Null()
   357		d.Name = newname
   358	
   359		buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
   360		n, err := d.Marshal(buf[:])
   361		if err != nil {
   362			return &LinkError{"rename", oldname, newname, err}
   363		}
   364	
   365		// If newname already exists and is not a directory, rename replaces it.
   366		f, err := Stat(dirname + newname)
   367		if err == nil && !f.IsDir() {
   368			Remove(dirname + newname)
   369		}
   370	
   371		if err = syscall.Wstat(oldname, buf[:n]); err != nil {
   372			return &LinkError{"rename", oldname, newname, err}
   373		}
   374		return nil
   375	}
   376	
   377	// See docs in file.go:Chmod.
   378	func chmod(name string, mode FileMode) error {
   379		var d syscall.Dir
   380	
   381		odir, e := dirstat(name)
   382		if e != nil {
   383			return &PathError{"chmod", name, e}
   384		}
   385		d.Null()
   386		d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
   387	
   388		var buf [syscall.STATFIXLEN]byte
   389		n, err := d.Marshal(buf[:])
   390		if err != nil {
   391			return &PathError{"chmod", name, err}
   392		}
   393		if err = syscall.Wstat(name, buf[:n]); err != nil {
   394			return &PathError{"chmod", name, err}
   395		}
   396		return nil
   397	}
   398	
   399	// Chtimes changes the access and modification times of the named
   400	// file, similar to the Unix utime() or utimes() functions.
   401	//
   402	// The underlying filesystem may truncate or round the values to a
   403	// less precise time unit.
   404	// If there is an error, it will be of type *PathError.
   405	func Chtimes(name string, atime time.Time, mtime time.Time) error {
   406		var d syscall.Dir
   407	
   408		d.Null()
   409		d.Atime = uint32(atime.Unix())
   410		d.Mtime = uint32(mtime.Unix())
   411	
   412		var buf [syscall.STATFIXLEN]byte
   413		n, err := d.Marshal(buf[:])
   414		if err != nil {
   415			return &PathError{"chtimes", name, err}
   416		}
   417		if err = syscall.Wstat(name, buf[:n]); err != nil {
   418			return &PathError{"chtimes", name, err}
   419		}
   420		return nil
   421	}
   422	
   423	// Pipe returns a connected pair of Files; reads from r return bytes
   424	// written to w. It returns the files and an error, if any.
   425	func Pipe() (r *File, w *File, err error) {
   426		var p [2]int
   427	
   428		if e := syscall.Pipe(p[0:]); e != nil {
   429			return nil, nil, NewSyscallError("pipe", e)
   430		}
   431	
   432		return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
   433	}
   434	
   435	// not supported on Plan 9
   436	
   437	// Link creates newname as a hard link to the oldname file.
   438	// If there is an error, it will be of type *LinkError.
   439	func Link(oldname, newname string) error {
   440		return &LinkError{"link", oldname, newname, syscall.EPLAN9}
   441	}
   442	
   443	// Symlink creates newname as a symbolic link to oldname.
   444	// If there is an error, it will be of type *LinkError.
   445	func Symlink(oldname, newname string) error {
   446		return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
   447	}
   448	
   449	// Readlink returns the destination of the named symbolic link.
   450	// If there is an error, it will be of type *PathError.
   451	func Readlink(name string) (string, error) {
   452		return "", &PathError{"readlink", name, syscall.EPLAN9}
   453	}
   454	
   455	// Chown 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's target.
   457	// A uid or gid of -1 means to not change that value.
   458	// If there is an error, it will be of type *PathError.
   459	//
   460	// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or
   461	// EPLAN9 error, wrapped in *PathError.
   462	func Chown(name string, uid, gid int) error {
   463		return &PathError{"chown", name, syscall.EPLAN9}
   464	}
   465	
   466	// Lchown changes the numeric uid and gid of the named file.
   467	// If the file is a symbolic link, it changes the uid and gid of the link itself.
   468	// If there is an error, it will be of type *PathError.
   469	func Lchown(name string, uid, gid int) error {
   470		return &PathError{"lchown", name, syscall.EPLAN9}
   471	}
   472	
   473	// Chown changes the numeric uid and gid of the named file.
   474	// If there is an error, it will be of type *PathError.
   475	func (f *File) Chown(uid, gid int) error {
   476		if f == nil {
   477			return ErrInvalid
   478		}
   479		return &PathError{"chown", f.name, syscall.EPLAN9}
   480	}
   481	
   482	func tempDir() string {
   483		dir := Getenv("TMPDIR")
   484		if dir == "" {
   485			dir = "/tmp"
   486		}
   487		return dir
   488	
   489	}
   490	
   491	// Chdir changes the current working directory to the file,
   492	// which must be a directory.
   493	// If there is an error, it will be of type *PathError.
   494	func (f *File) Chdir() error {
   495		if err := f.checkValid("chdir"); err != nil {
   496			return err
   497		}
   498		if e := syscall.Fchdir(f.fd); e != nil {
   499			return &PathError{"chdir", f.name, e}
   500		}
   501		return nil
   502	}
   503	
   504	// setDeadline sets the read and write deadline.
   505	func (f *File) setDeadline(time.Time) error {
   506		if err := f.checkValid("SetDeadline"); err != nil {
   507			return err
   508		}
   509		return poll.ErrNoDeadline
   510	}
   511	
   512	// setReadDeadline sets the read deadline.
   513	func (f *File) setReadDeadline(time.Time) error {
   514		if err := f.checkValid("SetReadDeadline"); err != nil {
   515			return err
   516		}
   517		return poll.ErrNoDeadline
   518	}
   519	
   520	// setWriteDeadline sets the write deadline.
   521	func (f *File) setWriteDeadline(time.Time) error {
   522		if err := f.checkValid("SetWriteDeadline"); err != nil {
   523			return err
   524		}
   525		return poll.ErrNoDeadline
   526	}
   527	
   528	// checkValid checks whether f is valid for use.
   529	// If not, it returns an appropriate error, perhaps incorporating the operation name op.
   530	func (f *File) checkValid(op string) error {
   531		if f == nil {
   532			return ErrInvalid
   533		}
   534		if f.fd == badFd {
   535			return &PathError{op, f.name, ErrClosed}
   536		}
   537		return nil
   538	}
   539	
   540	type rawConn struct{}
   541	
   542	func (c *rawConn) Control(f func(uintptr)) error {
   543		return syscall.EPLAN9
   544	}
   545	
   546	func (c *rawConn) Read(f func(uintptr) bool) error {
   547		return syscall.EPLAN9
   548	}
   549	
   550	func (c *rawConn) Write(f func(uintptr) bool) error {
   551		return syscall.EPLAN9
   552	}
   553	
   554	func newRawConn(file *File) (*rawConn, error) {
   555		return nil, syscall.EPLAN9
   556	}
   557	

View as plain text