Some checks failed
Golang test / go-test (push) Failing after 11s
211 lines
5.5 KiB
Go
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)
|
|
}
|