diff --git a/internal/filesystem/cgofuse/stat.go b/internal/filesystem/cgofuse/stat.go index bbf677c6..c0e2c5d9 100644 --- a/internal/filesystem/cgofuse/stat.go +++ b/internal/filesystem/cgofuse/stat.go @@ -16,84 +16,40 @@ func (gw *goWrapper) Statfs(path string, stat *fuse.Statfs_t) int { func (gw *goWrapper) Getattr(path string, stat *fuse.Stat_t, fh uint64) int { defer gw.systemLock.Access(path)() - goPath, err := fuseToGo(path) - if err != nil { - gw.log.Print(err) - // TODO: review; should we return the value raw or send err to a converter? - // ^ send a stacked err to a converter* - // (so that the trace contains both ops, parent-op+path-lexer+reason) - // TODO: re-read spec. This is the closest value that seemed appropriate - // but maybe ACCESS or NOENT makes more sense. - return -fuse.EINVAL - } - + var ( + info fs.FileInfo + err error + ) if fh != errorHandle { - // TODO: fh lookup + info, err = gw.infoFromHandle(fh) + } else { + info, err = gw.infoFromPath(path) } - - // TODO: review - goStat, err := fs.Stat(gw.FS, goPath) if err != nil { - errNo := interpretError(err) - // Don't flood the logs with "not found" errors. - if errNo != -fuse.ENOENT { - // TODO: [DBG] reduce this format - gw.log.Printf("path: %s\ngoPath: %s\nerr:%s", path, goPath, err) - } - return errNo + return interpretError(err) } - - // fsys.log.Printf("stat for %s\n%#v", path, goStat) - - // TODO: don't change stat on the fuse object - // push changes back to fs.FS via extension - // fs.SetAttr, fs.SetAttrFuse(path, someOverlappingAttrType) - - mTime := fuse.NewTimespec(goStat.ModTime()) - - // TODO: The fallback should be (optionally) set within the wrapper's constructor. - // Which itself should have a read-only default like this if not provided. - const fallbackPermissions = readAll | executeAll var ( - goMode = goStat.Mode() - fuseType = goToFuseFileType(goMode) - fusePermissions uint32 + uid, gid, _ = fuse.Getcontext() + fctx = fuseContext{uid: uid, gid: gid} ) - if goPermissions := goMode.Perm(); goPermissions != 0 { - fusePermissions = goToFusePermissions(goPermissions) - } else { - fusePermissions = fallbackPermissions - } - stat.Uid, stat.Gid, _ = fuse.Getcontext() - stat.Mode = fuseType | fusePermissions - stat.Size = goStat.Size() - // TODO: block size - - // TODO: [devel] `File` needs extensions for these times and we should use them conditionally - // something like `if aTimer ok; stat.Atim = aTimer.Time()` - // For now we cheat and use the same value for all - stat.Atim, // XXX: This shouldn't even be legal syntax. - stat.Mtim, - stat.Ctim, - stat.Birthtim = mTime, - mTime, - mTime, - mTime + goToFuseStat(info, fctx, stat) + return operationSuccess +} - /* - if path != "/" { - log.Printf("%s - mode pre conversion: %d, %s", - path, - goStat.Mode(), goStat.Mode()) - log.Printf("%s - mode post conversion (masked): %d %d|%d", - path, - stat.Mode, - stat.Mode&fuselib.S_IFMT, stat.Mode&^fuselib.S_IFMT, - ) - } - */ +func (gw *goWrapper) infoFromHandle(fh uint64) (fs.FileInfo, error) { + file, err := gw.fileTable.get(fh) + if err != nil { + return nil, err + } + return file.goFile.Stat() +} - return operationSuccess +func (gw *goWrapper) infoFromPath(path string) (fs.FileInfo, error) { + goPath, err := fuseToGo(path) + if err != nil { + return nil, err + } + return fs.Stat(gw.FS, goPath) } func (gw *goWrapper) Utimens(path string, tmsp []fuse.Timespec) int { diff --git a/internal/filesystem/cgofuse/translate.go b/internal/filesystem/cgofuse/translate.go index e59980d1..989c2107 100644 --- a/internal/filesystem/cgofuse/translate.go +++ b/internal/filesystem/cgofuse/translate.go @@ -67,6 +67,39 @@ func fuseToGo(path string) (string, error) { return path[1:], nil } +func goToFuseStat(info fs.FileInfo, fctx fuseContext, stat *fuse.Stat_t) { + var ( + goMode = info.Mode() + fuseType = goToFuseFileType(goMode) + fusePermissions uint32 + ) + if goPermissions := goMode.Perm(); goPermissions != 0 { + fusePermissions = goToFusePermissions(goPermissions) + } else { + // TODO: The fallback should be (optionally) set within the wrapper's constructor. + // Which itself should have a read-only default like this if not provided. + const fallbackPermissions = readAll | executeAll + fusePermissions = fallbackPermissions + } + + stat.Mode = fuseType | fusePermissions + stat.Uid = fctx.uid + stat.Gid = fctx.gid + stat.Size = info.Size() + + if atimer, ok := info.(filesystem.AccessTimer); ok { + stat.Atim = fuse.NewTimespec(atimer.AccessTime()) + } + stat.Mtim = fuse.NewTimespec(info.ModTime()) + if ctimer, ok := info.(filesystem.ChangeTimer); ok { + stat.Ctim = fuse.NewTimespec(ctimer.ChangeTime()) + } + // TODO: Block size + others. + if crtimer, ok := info.(filesystem.CreationTimer); ok { + stat.Birthtim = fuse.NewTimespec(crtimer.CreationTime()) + } +} + // [FileMode] to FUSE mode bits. func goToFuseFileType(m fs.FileMode) fileType { switch m.Type() {