Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
210 lines
4.9 KiB
Go
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
|
|
}
|