Files
overlayfs/mergefs_ro.go
2025-07-28 00:24:34 -03:00

211 lines
5.5 KiB
Go

package overlayfs
import (
"io"
"io/fs"
"maps"
"os"
"path/filepath"
"slices"
"strings"
)
func (over Overlayfs) Open(name string) (file *os.File, err error) {
if !over.isPathInsideOverlay(name) {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
} else if ok, err := over.isDeleted(name); err != nil {
return nil, err
} else if ok {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
for layer := range over.allLayersSeq() {
if file, err = os.Open(filepath.Join(layer, name)); err != nil {
if os.IsNotExist(err) {
err = nil
continue // attemp next layer
}
err = rewriteName(name, err)
}
return // Return file and error if returned
}
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
func (over Overlayfs) Stat(name string) (stat fs.FileInfo, err error) {
if !over.isPathInsideOverlay(name) {
return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrInvalid}
} else if ok, err := over.isDeleted(name); err != nil {
return nil, err
} else if ok {
return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrNotExist}
}
for layer := range over.allLayersSeq() {
if stat, err = os.Stat(filepath.Join(layer, name)); err != nil {
if os.IsNotExist(err) {
err = nil
continue
}
err = rewriteName(name, err)
}
return // Return file
}
return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrNotExist}
}
func (over Overlayfs) Lstat(name string) (stat fs.FileInfo, err error) {
if !over.isPathInsideOverlay(name) {
return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrInvalid}
} else if ok, err := over.isDeleted(name); err != nil {
return nil, err
} else if ok {
return nil, &fs.PathError{Op: "lstat", Path: name, Err: fs.ErrNotExist}
}
for folder := range over.allLayersSeq() {
if stat, err = os.Lstat(filepath.Join(folder, name)); err != nil {
if os.IsNotExist(err) {
err = nil
continue
}
err = rewriteName(name, err)
}
return
}
return nil, &fs.PathError{Op: "lstat", Path: name, Err: fs.ErrNotExist}
}
func (over Overlayfs) Readlink(name string) (string, error) {
if !over.isPathInsideOverlay(name) {
return "", &fs.PathError{Op: "readlink", Path: name, Err: fs.ErrInvalid}
}
for folder := range over.allLayersSeq() {
if folder == "" {
continue
}
target, err := os.Readlink(filepath.Join(folder, name))
if err != nil && os.IsNotExist(err) {
err = nil
continue
}
return target, err
}
return "", &fs.PathError{Op: "readlink", Path: name, Err: fs.ErrNotExist}
}
// func (over Overlayfs) ReadDir(name string) ([]fs.DirEntry, error) {
// if !over.isPathInsideOverlay(name) {
// return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrInvalid}
// } else if ok, err := over.isDeleted(name); err != nil {
// return nil, err
// } else if ok {
// return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrNotExist}
// }
// fileMap := map[string]fs.DirEntry{}
// layers := 0
// for folder := range over.allLayersSeq() {
// layers++
// entrys, err := os.ReadDir(filepath.Join(folder, name))
// if err != nil {
// layers--
// if os.IsNotExist(err) {
// err = nil
// continue
// }
// return nil, rewriteName(name, err)
// }
// for _, entry := range entrys {
// fileMap[filepath.Base(entry.Name())] = entry // Replace if exists
// }
// }
// // return path not exists
// if layers == 0 {
// return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrNotExist}
// }
// // Delete opaque files
// for key := range fileMap {
// if strings.HasPrefix(key, OpaqueWhiteout) {
// delete(fileMap, key[len(OpaqueWhiteout):])
// delete(fileMap, key)
// }
// }
// // Return slice from map values
// return slices.Collect(maps.Values(fileMap)), nil
// }
func (over Overlayfs) ReadDir(name string) ([]fs.DirEntry, error) {
if !over.isPathInsideOverlay(name) {
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrInvalid}
} else if ok, err := over.isDeleted(name); err != nil {
return nil, err
} else if ok {
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrNotExist}
}
checkErr, files, deleted := 0, map[string]fs.DirEntry{}, map[string]bool{}
if over.isRW() {
checkErr++
entrys, err := os.ReadDir(filepath.Join(over.Upper, name))
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
checkErr--
}
for _, entry := range entrys {
if after, ok := strings.CutPrefix(filepath.Base(entry.Name()), OpaqueWhiteout); ok {
deleted[after] = true
}
}
for _, entry := range entrys {
if strings.HasPrefix(filepath.Base(entry.Name()), OpaqueWhiteout) {
continue
} else if deleted[filepath.Base(entry.Name())] {
delete(deleted, filepath.Base(entry.Name()))
}
files[filepath.Join(name, entry.Name())] = entry
}
}
for _, lowPath := range over.Lower {
checkErr++
entrys, err := os.ReadDir(filepath.Join(lowPath, name))
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
checkErr--
}
for _, entry := range entrys {
if strings.HasPrefix(filepath.Base(entry.Name()), OpaqueWhiteout) || deleted[filepath.Base(entry.Name())] {
continue
}
files[filepath.Join(name, entry.Name())] = entry
}
}
if checkErr == 0 {
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrNotExist}
}
return slices.Collect(maps.Values(files)), nil
}
// RO + Extends
func (over Overlayfs) ReadFile(name string) ([]byte, error) {
file, err := over.Open(name)
if err != nil {
return nil, err
}
defer file.Close()
return io.ReadAll(file)
}