Home | History | Annotate | Download | only in os
      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 	"runtime"
      9 	"syscall"
     10 	"time"
     11 )
     12 
     13 // File represents an open file descriptor.
     14 type File struct {
     15 	*file
     16 }
     17 
     18 // file is the real representation of *File.
     19 // The extra level of indirection ensures that no clients of os
     20 // can overwrite this data, which could cause the finalizer
     21 // to close the wrong file descriptor.
     22 type file struct {
     23 	fd      int
     24 	name    string
     25 	dirinfo *dirInfo // nil unless directory being read
     26 }
     27 
     28 // Fd returns the integer Plan 9 file descriptor referencing the open file.
     29 // The file descriptor is valid only until f.Close is called or f is garbage collected.
     30 func (f *File) Fd() uintptr {
     31 	if f == nil {
     32 		return ^(uintptr(0))
     33 	}
     34 	return uintptr(f.fd)
     35 }
     36 
     37 // NewFile returns a new File with the given file descriptor and name.
     38 func NewFile(fd uintptr, name string) *File {
     39 	fdi := int(fd)
     40 	if fdi < 0 {
     41 		return nil
     42 	}
     43 	f := &File{&file{fd: fdi, name: name}}
     44 	runtime.SetFinalizer(f.file, (*file).close)
     45 	return f
     46 }
     47 
     48 // Auxiliary information if the File describes a directory
     49 type dirInfo struct {
     50 	buf  [syscall.STATMAX]byte // buffer for directory I/O
     51 	nbuf int                   // length of buf; return value from Read
     52 	bufp int                   // location of next record in buf.
     53 }
     54 
     55 func epipecheck(file *File, e error) {
     56 }
     57 
     58 // DevNull is the name of the operating system's ``null device.''
     59 // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
     60 const DevNull = "/dev/null"
     61 
     62 // syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
     63 func syscallMode(i FileMode) (o uint32) {
     64 	o |= uint32(i.Perm())
     65 	if i&ModeAppend != 0 {
     66 		o |= syscall.DMAPPEND
     67 	}
     68 	if i&ModeExclusive != 0 {
     69 		o |= syscall.DMEXCL
     70 	}
     71 	if i&ModeTemporary != 0 {
     72 		o |= syscall.DMTMP
     73 	}
     74 	return
     75 }
     76 
     77 // OpenFile is the generalized open call; most users will use Open
     78 // or Create instead.  It opens the named file with specified flag
     79 // (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
     80 // methods on the returned File can be used for I/O.
     81 // If there is an error, it will be of type *PathError.
     82 func OpenFile(name string, flag int, perm FileMode) (*File, error) {
     83 	var (
     84 		fd     int
     85 		e      error
     86 		create bool
     87 		excl   bool
     88 		trunc  bool
     89 		append bool
     90 	)
     91 
     92 	if flag&O_CREATE == O_CREATE {
     93 		flag = flag & ^O_CREATE
     94 		create = true
     95 	}
     96 	if flag&O_EXCL == O_EXCL {
     97 		excl = true
     98 	}
     99 	if flag&O_TRUNC == O_TRUNC {
    100 		trunc = true
    101 	}
    102 	// O_APPEND is emulated on Plan 9
    103 	if flag&O_APPEND == O_APPEND {
    104 		flag = flag &^ O_APPEND
    105 		append = true
    106 	}
    107 
    108 	if (create && trunc) || excl {
    109 		fd, e = syscall.Create(name, flag, syscallMode(perm))
    110 	} else {
    111 		fd, e = syscall.Open(name, flag)
    112 		if e != nil && create {
    113 			var e1 error
    114 			fd, e1 = syscall.Create(name, flag, syscallMode(perm))
    115 			if e1 == nil {
    116 				e = nil
    117 			}
    118 		}
    119 	}
    120 
    121 	if e != nil {
    122 		return nil, &PathError{"open", name, e}
    123 	}
    124 
    125 	if append {
    126 		if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil {
    127 			return nil, &PathError{"seek", name, e}
    128 		}
    129 	}
    130 
    131 	return NewFile(uintptr(fd), name), nil
    132 }
    133 
    134 // Close closes the File, rendering it unusable for I/O.
    135 // It returns an error, if any.
    136 func (f *File) Close() error {
    137 	if f == nil {
    138 		return ErrInvalid
    139 	}
    140 	return f.file.close()
    141 }
    142 
    143 func (file *file) close() error {
    144 	if file == nil || file.fd < 0 {
    145 		return ErrInvalid
    146 	}
    147 	var err error
    148 	syscall.ForkLock.RLock()
    149 	if e := syscall.Close(file.fd); e != nil {
    150 		err = &PathError{"close", file.name, e}
    151 	}
    152 	syscall.ForkLock.RUnlock()
    153 	file.fd = -1 // so it can't be closed again
    154 
    155 	// no need for a finalizer anymore
    156 	runtime.SetFinalizer(file, nil)
    157 	return err
    158 }
    159 
    160 // Stat returns the FileInfo structure describing file.
    161 // If there is an error, it will be of type *PathError.
    162 func (f *File) Stat() (FileInfo, error) {
    163 	if f == nil {
    164 		return nil, ErrInvalid
    165 	}
    166 	d, err := dirstat(f)
    167 	if err != nil {
    168 		return nil, err
    169 	}
    170 	return fileInfoFromStat(d), nil
    171 }
    172 
    173 // Truncate changes the size of the file.
    174 // It does not change the I/O offset.
    175 // If there is an error, it will be of type *PathError.
    176 func (f *File) Truncate(size int64) error {
    177 	if f == nil {
    178 		return ErrInvalid
    179 	}
    180 
    181 	var d syscall.Dir
    182 	d.Null()
    183 	d.Length = size
    184 
    185 	var buf [syscall.STATFIXLEN]byte
    186 	n, err := d.Marshal(buf[:])
    187 	if err != nil {
    188 		return &PathError{"truncate", f.name, err}
    189 	}
    190 	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
    191 		return &PathError{"truncate", f.name, err}
    192 	}
    193 	return nil
    194 }
    195 
    196 const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
    197 
    198 // Chmod changes the mode of the file to mode.
    199 // If there is an error, it will be of type *PathError.
    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 	return fixCount(syscall.Read(f.fd, b))
    249 }
    250 
    251 // pread reads len(b) bytes from the File starting at byte offset off.
    252 // It returns the number of bytes read and the error, if any.
    253 // EOF is signaled by a zero count with err set to nil.
    254 func (f *File) pread(b []byte, off int64) (n int, err error) {
    255 	return fixCount(syscall.Pread(f.fd, b, off))
    256 }
    257 
    258 // write writes len(b) bytes to the File.
    259 // It returns the number of bytes written and an error, if any.
    260 // Since Plan 9 preserves message boundaries, never allow
    261 // a zero-byte write.
    262 func (f *File) write(b []byte) (n int, err error) {
    263 	if len(b) == 0 {
    264 		return 0, nil
    265 	}
    266 	return fixCount(syscall.Write(f.fd, b))
    267 }
    268 
    269 // pwrite writes len(b) bytes to the File starting at byte offset off.
    270 // It returns the number of bytes written and an error, if any.
    271 // Since Plan 9 preserves message boundaries, never allow
    272 // a zero-byte write.
    273 func (f *File) pwrite(b []byte, off int64) (n int, err error) {
    274 	if len(b) == 0 {
    275 		return 0, nil
    276 	}
    277 	return fixCount(syscall.Pwrite(f.fd, b, off))
    278 }
    279 
    280 // seek sets the offset for the next Read or Write on file to offset, interpreted
    281 // according to whence: 0 means relative to the origin of the file, 1 means
    282 // relative to the current offset, and 2 means relative to the end.
    283 // It returns the new offset and an error, if any.
    284 func (f *File) seek(offset int64, whence int) (ret int64, err error) {
    285 	return syscall.Seek(f.fd, offset, whence)
    286 }
    287 
    288 // Truncate changes the size of the named file.
    289 // If the file is a symbolic link, it changes the size of the link's target.
    290 // If there is an error, it will be of type *PathError.
    291 func Truncate(name string, size int64) error {
    292 	var d syscall.Dir
    293 
    294 	d.Null()
    295 	d.Length = size
    296 
    297 	var buf [syscall.STATFIXLEN]byte
    298 	n, err := d.Marshal(buf[:])
    299 	if err != nil {
    300 		return &PathError{"truncate", name, err}
    301 	}
    302 	if err = syscall.Wstat(name, buf[:n]); err != nil {
    303 		return &PathError{"truncate", name, err}
    304 	}
    305 	return nil
    306 }
    307 
    308 // Remove removes the named file or directory.
    309 // If there is an error, it will be of type *PathError.
    310 func Remove(name string) error {
    311 	if e := syscall.Remove(name); e != nil {
    312 		return &PathError{"remove", name, e}
    313 	}
    314 	return nil
    315 }
    316 
    317 // HasPrefix from the strings package.
    318 func hasPrefix(s, prefix string) bool {
    319 	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
    320 }
    321 
    322 // LastIndexByte from the strings package.
    323 func lastIndex(s string, sep byte) int {
    324 	for i := len(s) - 1; i >= 0; i-- {
    325 		if s[i] == sep {
    326 			return i
    327 		}
    328 	}
    329 	return -1
    330 }
    331 
    332 func rename(oldname, newname string) error {
    333 	dirname := oldname[:lastIndex(oldname, '/')+1]
    334 	if hasPrefix(newname, dirname) {
    335 		newname = newname[len(dirname):]
    336 	} else {
    337 		return &LinkError{"rename", oldname, newname, ErrInvalid}
    338 	}
    339 
    340 	// If newname still contains slashes after removing the oldname
    341 	// prefix, the rename is cross-directory and must be rejected.
    342 	// This case is caught by d.Marshal below.
    343 
    344 	var d syscall.Dir
    345 
    346 	d.Null()
    347 	d.Name = newname
    348 
    349 	buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
    350 	n, err := d.Marshal(buf[:])
    351 	if err != nil {
    352 		return &LinkError{"rename", oldname, newname, err}
    353 	}
    354 	if err = syscall.Wstat(oldname, buf[:n]); err != nil {
    355 		return &LinkError{"rename", oldname, newname, err}
    356 	}
    357 	return nil
    358 }
    359 
    360 // Chmod changes the mode of the named file to mode.
    361 // If the file is a symbolic link, it changes the mode of the link's target.
    362 // If there is an error, it will be of type *PathError.
    363 func Chmod(name string, mode FileMode) error {
    364 	var d syscall.Dir
    365 
    366 	odir, e := dirstat(name)
    367 	if e != nil {
    368 		return &PathError{"chmod", name, e}
    369 	}
    370 	d.Null()
    371 	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
    372 
    373 	var buf [syscall.STATFIXLEN]byte
    374 	n, err := d.Marshal(buf[:])
    375 	if err != nil {
    376 		return &PathError{"chmod", name, err}
    377 	}
    378 	if err = syscall.Wstat(name, buf[:n]); err != nil {
    379 		return &PathError{"chmod", name, err}
    380 	}
    381 	return nil
    382 }
    383 
    384 // Chtimes changes the access and modification times of the named
    385 // file, similar to the Unix utime() or utimes() functions.
    386 //
    387 // The underlying filesystem may truncate or round the values to a
    388 // less precise time unit.
    389 // If there is an error, it will be of type *PathError.
    390 func Chtimes(name string, atime time.Time, mtime time.Time) error {
    391 	var d syscall.Dir
    392 
    393 	d.Null()
    394 	d.Atime = uint32(atime.Unix())
    395 	d.Mtime = uint32(mtime.Unix())
    396 
    397 	var buf [syscall.STATFIXLEN]byte
    398 	n, err := d.Marshal(buf[:])
    399 	if err != nil {
    400 		return &PathError{"chtimes", name, err}
    401 	}
    402 	if err = syscall.Wstat(name, buf[:n]); err != nil {
    403 		return &PathError{"chtimes", name, err}
    404 	}
    405 	return nil
    406 }
    407 
    408 // Pipe returns a connected pair of Files; reads from r return bytes
    409 // written to w. It returns the files and an error, if any.
    410 func Pipe() (r *File, w *File, err error) {
    411 	var p [2]int
    412 
    413 	syscall.ForkLock.RLock()
    414 	if e := syscall.Pipe(p[0:]); e != nil {
    415 		syscall.ForkLock.RUnlock()
    416 		return nil, nil, NewSyscallError("pipe", e)
    417 	}
    418 	syscall.ForkLock.RUnlock()
    419 
    420 	return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
    421 }
    422 
    423 // not supported on Plan 9
    424 
    425 // Link creates newname as a hard link to the oldname file.
    426 // If there is an error, it will be of type *LinkError.
    427 func Link(oldname, newname string) error {
    428 	return &LinkError{"link", oldname, newname, syscall.EPLAN9}
    429 }
    430 
    431 // Symlink creates newname as a symbolic link to oldname.
    432 // If there is an error, it will be of type *LinkError.
    433 func Symlink(oldname, newname string) error {
    434 	return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
    435 }
    436 
    437 // Readlink returns the destination of the named symbolic link.
    438 // If there is an error, it will be of type *PathError.
    439 func Readlink(name string) (string, error) {
    440 	return "", &PathError{"readlink", name, syscall.EPLAN9}
    441 }
    442 
    443 // Chown changes the numeric uid and gid of the named file.
    444 // If the file is a symbolic link, it changes the uid and gid of the link's target.
    445 // If there is an error, it will be of type *PathError.
    446 func Chown(name string, uid, gid int) error {
    447 	return &PathError{"chown", name, syscall.EPLAN9}
    448 }
    449 
    450 // Lchown changes the numeric uid and gid of the named file.
    451 // If the file is a symbolic link, it changes the uid and gid of the link itself.
    452 // If there is an error, it will be of type *PathError.
    453 func Lchown(name string, uid, gid int) error {
    454 	return &PathError{"lchown", name, syscall.EPLAN9}
    455 }
    456 
    457 // Chown changes the numeric uid and gid of the named file.
    458 // If there is an error, it will be of type *PathError.
    459 func (f *File) Chown(uid, gid int) error {
    460 	if f == nil {
    461 		return ErrInvalid
    462 	}
    463 	return &PathError{"chown", f.name, syscall.EPLAN9}
    464 }
    465 
    466 // TempDir returns the default directory to use for temporary files.
    467 func TempDir() string {
    468 	return "/tmp"
    469 }
    470