diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend/file.go | 6 | ||||
| -rw-r--r-- | internal/fusefrontend/fs.go | 5 | ||||
| -rw-r--r-- | internal/syscallcompat/sys_darwin.go | 87 | ||||
| -rw-r--r-- | internal/syscallcompat/sys_linux.go | 23 | 
4 files changed, 113 insertions, 8 deletions
| diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index a0b82ba..531274a 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -44,8 +44,6 @@ type File struct {  	qIno openfiletable.QIno  	// Entry in the open file table  	fileTableEntry *openfiletable.Entry -	// go-fuse nodefs.loopbackFile -	loopbackFile nodefs.File  	// Store where the last byte was written  	lastWrittenOffset int64  	// The opCount is used to judge whether "lastWrittenOffset" is still @@ -75,7 +73,6 @@ func NewFile(fd *os.File, fs *FS) (*File, fuse.Status) {  		contentEnc:     fs.contentEnc,  		qIno:           qi,  		fileTableEntry: e, -		loopbackFile:   nodefs.NewLoopbackFile(fd),  		fs:             fs,  		File:           nodefs.NewDefaultFile(),  	}, fuse.OK @@ -474,5 +471,6 @@ func (f *File) GetAttr(a *fuse.Attr) fuse.Status {  func (f *File) Utimens(a *time.Time, m *time.Time) fuse.Status {  	f.fdLock.RLock()  	defer f.fdLock.RUnlock() -	return f.loopbackFile.Utimens(a, m) +	err := syscallcompat.FutimesNano(f.intFd(), a, m) +	return fuse.ToStatus(err)  } diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index 4c8af2d..096e663 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -372,10 +372,7 @@ func (fs *FS) Utimens(path string, a *time.Time, m *time.Time, context *fuse.Con  		return fuse.ToStatus(err)  	}  	defer syscall.Close(dirfd) -	ts := make([]unix.Timespec, 2) -	ts[0] = unix.Timespec(fuse.UtimeToTimespec(a)) -	ts[1] = unix.Timespec(fuse.UtimeToTimespec(m)) -	err = unix.UtimesNanoAt(dirfd, cName, ts, unix.AT_SYMLINK_NOFOLLOW) +	err = syscallcompat.UtimesNanoAtNofollow(dirfd, cName, a, m)  	return fuse.ToStatus(err)  } diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go index ebda80f..699e38c 100644 --- a/internal/syscallcompat/sys_darwin.go +++ b/internal/syscallcompat/sys_darwin.go @@ -2,8 +2,11 @@ package syscallcompat  import (  	"log" +	"path/filepath"  	"runtime"  	"syscall" +	"time" +	"unsafe"  	"golang.org/x/sys/unix" @@ -33,6 +36,24 @@ func pthread_setugid_np(uid uint32, gid uint32) (err error) {  	return  } +// Unfortunately fsetattrlist does not have a syscall wrapper yet. +func fsetattrlist(fd int, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) { +	_, _, e1 := syscall.Syscall6(syscall.SYS_FSETATTRLIST, uintptr(fd), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0) +	if e1 != 0 { +		err = e1 +	} +	return +} + +// Setattrlist already has a syscall wrapper, but it is not exported. +func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) { +	_, _, e1 := syscall.Syscall6(syscall.SYS_SETATTRLIST, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0) +	if e1 != 0 { +		err = e1 +	} +	return +} +  // Sorry, fallocate is not available on OSX at all and  // fcntl F_PREALLOCATE is not accessible from Go.  // See https://github.com/rfjakob/gocryptfs/issues/18 if you want to help. @@ -125,6 +146,72 @@ func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (er  	return Mkdirat(dirfd, path, mode)  } +type attrList struct { +	bitmapCount uint16 +	_           uint16 +	CommonAttr  uint32 +	VolAttr     uint32 +	DirAttr     uint32 +	FileAttr    uint32 +	Forkattr    uint32 +} + +func timesToAttrList(a *time.Time, m *time.Time) (attrList attrList, attributes [2]unix.Timespec) { +	attrList.bitmapCount = unix.ATTR_BIT_MAP_COUNT +	attrList.CommonAttr = 0 +	i := 0 +	if m != nil { +		attributes[i] = unix.Timespec(fuse.UtimeToTimespec(m)) +		attrList.CommonAttr |= unix.ATTR_CMN_MODTIME +		i += 1 +	} +	if a != nil { +		attributes[i] = unix.Timespec(fuse.UtimeToTimespec(a)) +		attrList.CommonAttr |= unix.ATTR_CMN_ACCTIME +		i += 1 +	} +	return attrList, attributes +} + +// FutimesNano syscall. +func FutimesNano(fd int, a *time.Time, m *time.Time) (err error) { +	attrList, attributes := timesToAttrList(a, m) +	return fsetattrlist(fd, unsafe.Pointer(&attrList), unsafe.Pointer(&attributes), +		unsafe.Sizeof(attributes), 0) +} + +// UtimesNanoAtNofollow is like UtimesNanoAt but never follows symlinks. +// +// Unfortunately we cannot use unix.UtimesNanoAt since it is broken and just +// ignores the provided 'dirfd'. In addition, it also lacks handling of 'nil' +// pointers (used to preserve one of both timestamps). +func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (err error) { +	if !filepath.IsAbs(path) { +		chdirMutex.Lock() +		defer chdirMutex.Unlock() +		var cwd int +		cwd, err = syscall.Open(".", syscall.O_RDONLY, 0) +		if err != nil { +			return err +		} +		defer syscall.Close(cwd) +		err = syscall.Fchdir(dirfd) +		if err != nil { +			return err +		} +		defer syscall.Fchdir(cwd) +	} + +	_p0, err := syscall.BytePtrFromString(path) +	if err != nil { +		return err +	} + +	attrList, attributes := timesToAttrList(a, m) +	return setattrlist(_p0, unsafe.Pointer(&attrList), unsafe.Pointer(&attributes), +		unsafe.Sizeof(attributes), unix.FSOPT_NOFOLLOW) +} +  func Getdents(fd int) ([]fuse.DirEntry, error) {  	return emulateGetdents(fd)  } diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 625e136..92d8183 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -6,6 +6,7 @@ import (  	"runtime"  	"sync"  	"syscall" +	"time"  	"golang.org/x/sys/unix" @@ -191,6 +192,28 @@ func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (er  	return Mkdirat(dirfd, path, mode)  } +func timesToTimespec(a *time.Time, m *time.Time) []unix.Timespec { +	ts := make([]unix.Timespec, 2) +	ts[0] = unix.Timespec(fuse.UtimeToTimespec(a)) +	ts[1] = unix.Timespec(fuse.UtimeToTimespec(m)) +	return ts +} + +// FutimesNano syscall. +func FutimesNano(fd int, a *time.Time, m *time.Time) (err error) { +	ts := timesToTimespec(a, m) +	// To avoid introducing a separate syscall wrapper for futimens() +	// (as done in go-fuse, for example), we instead use the /proc/self/fd trick. +	procPath := fmt.Sprintf("/proc/self/fd/%d", fd) +	return unix.UtimesNanoAt(unix.AT_FDCWD, procPath, ts, 0) +} + +// UtimesNanoAtNofollow is like UtimesNanoAt but never follows symlinks. +func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (err error) { +	ts := timesToTimespec(a, m) +	return unix.UtimesNanoAt(dirfd, path, ts, unix.AT_SYMLINK_NOFOLLOW) +} +  // Getdents syscall.  func Getdents(fd int) ([]fuse.DirEntry, error) {  	return getdents(fd) | 
