1
0
mirror of https://github.com/golang/go synced 2024-11-19 10:44:47 -07:00
go/src/os/file_posix.go

142 lines
4.0 KiB
Go
Raw Normal View History

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
package os
import (
"syscall"
"time"
)
func sigpipe() // implemented in package runtime
// Readlink returns the destination of the named symbolic link.
// If there is an error, it will be of type *PathError.
func Readlink(name string) (string, error) {
for len := 128; ; len *= 2 {
b := make([]byte, len)
n, e := fixCount(syscall.Readlink(fixLongPath(name), b))
if e != nil {
return "", &PathError{"readlink", name, e}
}
if n < len {
return string(b[0:n]), nil
}
}
}
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm())
if i&ModeSetuid != 0 {
o |= syscall.S_ISUID
}
if i&ModeSetgid != 0 {
o |= syscall.S_ISGID
}
if i&ModeSticky != 0 {
o |= syscall.S_ISVTX
}
// No mapping for Go's ModeTemporary (plan9 only).
return
}
// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
// If there is an error, it will be of type *PathError.
func Chmod(name string, mode FileMode) error {
if e := syscall.Chmod(name, syscallMode(mode)); e != nil {
return &PathError{"chmod", name, e}
}
return nil
}
// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error {
if err := f.checkValid("chmod"); err != nil {
return err
}
if e := syscall.Fchmod(f.fd, syscallMode(mode)); e != nil {
return &PathError{"chmod", f.name, e}
}
return nil
}
// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
// If there is an error, it will be of type *PathError.
func Chown(name string, uid, gid int) error {
if e := syscall.Chown(name, uid, gid); e != nil {
return &PathError{"chown", name, e}
}
return nil
}
// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
// If there is an error, it will be of type *PathError.
func Lchown(name string, uid, gid int) error {
if e := syscall.Lchown(name, uid, gid); e != nil {
return &PathError{"lchown", name, e}
}
return nil
}
// Chown changes the numeric uid and gid of the named file.
// If there is an error, it will be of type *PathError.
func (f *File) Chown(uid, gid int) error {
if err := f.checkValid("chown"); err != nil {
return err
}
if e := syscall.Fchown(f.fd, uid, gid); e != nil {
return &PathError{"chown", f.name, e}
}
return nil
}
// Truncate changes the size of the file.
// It does not change the I/O offset.
// If there is an error, it will be of type *PathError.
func (f *File) Truncate(size int64) error {
if err := f.checkValid("truncate"); err != nil {
return err
}
if e := syscall.Ftruncate(f.fd, size); e != nil {
return &PathError{"truncate", f.name, e}
}
return nil
}
// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
func (f *File) Sync() error {
if err := f.checkValid("sync"); err != nil {
return err
}
if e := syscall.Fsync(f.fd); e != nil {
return &PathError{"sync", f.name, e}
}
return nil
}
// Chtimes changes the access and modification times of the named
// file, similar to the Unix utime() or utimes() functions.
//
// The underlying filesystem may truncate or round the values to a
// less precise time unit.
// If there is an error, it will be of type *PathError.
func Chtimes(name string, atime time.Time, mtime time.Time) error {
os: Improve the accuracy of os.Chtimes I've been writing some code which involves syncing files (like rsync) and it became apparent that under Linux I could read modification times (os.Lstat) with nanosecond precision but only write them with microsecond precision. This difference in precision is rather annoying when trying to discover whether files need syncing or not! I've patched syscall and os to increases the accuracy of of os.Chtimes for Linux and Windows. This involved exposing the utimensat system call under Linux and a bit of extra code under Windows. I decided not to expose the "at" bit of the system call as it is impossible to replicate under Windows, so the patch adds syscall.Utimens() to all architectures along with a ImplementsUtimens flag. If the utimensat syscall isn't available (utimensat was added to Linux in 2.6.22, Released, 8 July 2007) then it silently falls back to the microsecond accuracy version it uses now. The improved accuracy for Windows should be good for all versions of Windows. Unfortunately Darwin doesn't seem to have a utimensat system call that I could find so I couldn't implement it there. The BSDs do, but since they share their syscall implementation with Darwin I couldn't figure out how to define a syscall for *BSD and not Darwin. I've left this as a TODO in the code. In the process I implemented the missing methods for Timespec under Windows which I needed which just happened to round out the Timespec API for all platforms! ------------------------------------------------------------ Test code: http://play.golang.org/p/1xnGuYOi4b Linux Before (1000 ns precision) $ ./utimetest.linux.before z Setting mtime 1344937903123456789: 2012-08-14 10:51:43.123456789 +0100 BST Reading mtime 1344937903123457000: 2012-08-14 10:51:43.123457 +0100 BST Linux After (1 ns precision) $ ./utimetest.linux.after z Setting mtime 1344937903123456789: 2012-08-14 10:51:43.123456789 +0100 BST Reading mtime 1344937903123456789: 2012-08-14 10:51:43.123456789 +0100 BST Windows Before (1000 ns precision) X:\>utimetest.windows.before.exe c:\Test.txt Setting mtime 1344937903123456789: 2012-08-14 10:51:43.123456789 +0100 GMTDT Reading mtime 1344937903123456000: 2012-08-14 10:51:43.123456 +0100 GMTDT Windows After (100 ns precision) X:\>utimetest.windows.after.exe c:\Test.txt Setting mtime 1344937903123456789: 2012-08-14 10:51:43.123456789 +0100 GMTDT Reading mtime 1344937903123456700: 2012-08-14 10:51:43.1234567 +0100 GMTDT R=golang-dev, alex.brainman, rsc, bradfitz CC=golang-dev https://golang.org/cl/6905057
2012-12-13 14:02:39 -07:00
var utimes [2]syscall.Timespec
utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
return &PathError{"chtimes", name, e}
}
return nil
}