summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/fusefrontend/file.go6
-rw-r--r--internal/fusefrontend/fs.go5
-rw-r--r--internal/syscallcompat/sys_darwin.go87
-rw-r--r--internal/syscallcompat/sys_linux.go23
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)