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