Files
overlayfs/overlayfs_fuse.go
Matheus Sampaio Queiroga dc9dd1b7c3
Some checks are pending
Golang test / go-test (windows-latest) (push) Waiting to run
Golang test / go-test (ubuntu-latest) (push) Successful in 21s
Update fuse module
Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
2025-07-19 19:40:39 -03:00

210 lines
4.9 KiB
Go

//go:build freebsd || netbsd || openbsd
package overlayfs
// Use fuse to emulate overlayfs
import (
"io"
"os"
"sync"
"syscall"
"time"
"bazil.org/fuse"
)
type internalType = *bazilFuseConn
type bazilFuseConn struct {
Conn *fuse.Conn
FDOpen *sync.Map
}
// Always return true because it is being implemented in fuse
func OverlayfsAvaible() bool { return true }
// Unmount overlayfs fuse
func (overlay *Overlayfs) Unmount() (err error) {
if err = overlay.ProcessInternal.Conn.Close(); err == nil {
err = fuse.Unmount(overlay.Target)
}
return
}
// Attempt mount overlayfs with fuse
func (overlay *Overlayfs) Mount() (err error) {
if overlay.ProcessInternal != nil {
return ErrMounted
}
overlay.ProcessInternal = new(bazilFuseConn)
overlay.ProcessInternal.FDOpen = new(sync.Map)
overlay.ProcessInternal.Conn, err = fuse.Mount(overlay.Target, fuse.AllowOther(), fuse.AllowDev(), fuse.FSName("overlayfs"), fuse.Subtype("overlayfs"))
if err == nil {
go overlay.mountProcess()
}
return
}
func (overlay *Overlayfs) mountProcess() {
for {
req, err := overlay.ProcessInternal.Conn.ReadRequest()
if err != nil {
if err == io.EOF {
break
}
if overlay.Log != nil {
overlay.Log.Println(err)
}
overlay.Err = err
return
}
switch v := req.(type) {
// case *fuse.OpenRequest:
case *fuse.CreateRequest:
fs, err := overlay.OpenFile(v.Name, int(v.Flags), v.Mode.Perm())
if err != nil {
v.RespondError(err)
} else {
overlay.ProcessInternal.FDOpen.Store(fuse.HandleID(fs.Fd()), fs)
stat, _ := fs.Stat()
v.Respond(&fuse.CreateResponse{
LookupResponse: fuse.LookupResponse{Node: v.Node, Attr: fuseAttr(stat)},
OpenResponse: fuse.OpenResponse{
Handle: fuse.HandleID(fs.Fd()),
},
})
}
case *fuse.ReadRequest:
if fs, ok := overlay.ProcessInternal.FDOpen.Load(v.Handle); ok {
if v.Dir {
dirs, err := fs.(*os.File).ReadDir(-1)
if err != nil {
v.RespondError(err)
} else {
var data []byte
for _, dir := range dirs {
data = fuse.AppendDirent(data, fuseDirent(dir))
}
v.Respond(&fuse.ReadResponse{Data: data})
}
continue
}
data := make([]byte, v.Size)
n, err := fs.(*os.File).ReadAt(data, int64(v.Offset))
if err != nil {
v.RespondError(err)
} else {
v.Respond(&fuse.ReadResponse{Data: data[:n]})
}
} else {
v.RespondError(syscall.EIO)
}
case *fuse.WriteRequest:
if fs, ok := overlay.ProcessInternal.FDOpen.Load(v.Handle); ok {
n, err := fs.(*os.File).WriteAt(v.Data, int64(v.Offset))
if err != nil {
v.RespondError(err)
} else {
v.Respond(&fuse.WriteResponse{Size: n})
}
} else {
v.RespondError(syscall.EIO)
}
case *fuse.FlushRequest:
if fs, ok := overlay.ProcessInternal.FDOpen.Load(v.Handle); ok {
err := fs.(*os.File).Sync()
if err != nil {
v.RespondError(err)
} else {
v.Respond()
}
} else {
v.RespondError(syscall.EIO)
}
case *fuse.MkdirRequest:
if err := overlay.Mkdir(v.Name, v.Mode.Perm()); err != nil {
v.RespondError(err)
} else {
stat, _ := overlay.Stat(v.Name)
v.Respond(&fuse.MkdirResponse{
LookupResponse: fuse.LookupResponse{Node: v.Node, Attr: fuseAttr(stat)},
})
}
case *fuse.RenameRequest:
if err := overlay.Rename(v.OldName, v.NewName); err != nil {
v.RespondError(err)
} else {
v.Respond()
}
case *fuse.SymlinkRequest:
if err := overlay.Symlink(v.Target, v.NewName); err != nil {
v.RespondError(err)
} else {
stat, _ := overlay.Stat(v.NewName)
v.Respond(&fuse.SymlinkResponse{
LookupResponse: fuse.LookupResponse{
Node: v.Node,
Attr: fuseAttr(stat),
},
})
}
default:
req.RespondError(syscall.EIO)
}
}
}
func fuseAttr(fsStat os.FileInfo) fuse.Attr {
var attr fuse.Attr
attr.Mode = fsStat.Mode()
attr.Size = uint64(fsStat.Size())
attr.Mtime = fsStat.ModTime()
attr.Ctime = fsStat.ModTime()
if sys, ok := fsStat.Sys().(*syscall.Stat_t); ok {
attr.Atime = time.Unix(sys.Atimespec.Unix())
attr.Mtime = time.Unix(sys.Mtimespec.Unix())
attr.Ctime = time.Unix(sys.Ctimespec.Unix())
attr.Uid = sys.Uid
attr.Gid = sys.Gid
attr.Rdev = uint32(sys.Rdev)
attr.Nlink = uint32(sys.Nlink)
attr.BlockSize = uint32(sys.Blksize)
attr.Blocks = uint64(sys.Blocks)
}
return attr
}
func fuseDirent(stat os.DirEntry) fuse.Dirent {
var dirr fuse.Dirent
dirr.Name = stat.Name()
if stat, err := stat.Info(); err == nil {
if sys, ok := stat.Sys().(*syscall.Stat_t); ok {
dirr.Inode = sys.Ino
}
}
switch stat.Type() {
case os.ModeDir:
dirr.Type = fuse.DT_Dir
case os.ModeSymlink:
dirr.Type = fuse.DT_Link
case os.ModeTemporary:
dirr.Type = fuse.DT_Socket
case os.ModeDevice, os.ModeNamedPipe, os.ModeSetuid, os.ModeSetgid:
dirr.Type = fuse.DT_Unknown
case os.ModeSocket:
dirr.Type = fuse.DT_Socket
case os.ModeCharDevice:
dirr.Type = fuse.DT_Char
default:
dirr.Type = fuse.DT_File
}
return dirr
}