fs API

fs

package

API reference for the fs package.

F
function

GetFileDiff

GetFileDiff compares the content of two files and returns the changes

Parameters

firstFile
string
secondFile
string

Returns

pkg/v1/fs/diff.go:29-54
func GetFileDiff(firstFile, secondFile string) (types.FileDiffInfo, error)

{
	firstContent, err := os.ReadFile(firstFile)
	if err != nil {
		return types.FileDiffInfo{}, err
	}

	secondContent, err := os.ReadFile(secondFile)
	if err != nil {
		return types.FileDiffInfo{}, err
	}

	diff := types.FileDiffInfo{}

	dmp := diffmatchpatch.New()
	diffs := dmp.DiffMain(string(firstContent), string(secondContent), false)
	for _, d := range diffs {
		switch d.Type {
		case diffmatchpatch.DiffInsert:
			diff.AddedLines = append(diff.AddedLines, d.Text)
		case diffmatchpatch.DiffDelete:
			diff.RemovedLines = append(diff.RemovedLines, d.Text)
		}
	}

	return diff, nil
}

Example

diff, err := fs.GetFileDiff("/tmp/batman", "/tmp/robin")
if err != nil {
	fmt.Printf("Error getting file diff: %v", err)
	return
}
fmt.Printf("Added lines: %v\n", diff.AddedLines)
fmt.Printf("Removed lines: %v\n", diff.RemovedLines)
F
function

GetFilesystemInfo

GetFilesystemInfo returns information about the filesystem of a file or
partition by reading from /etc/mtab.

Parameters

path
string

Returns

map[string]string
pkg/v1/fs/disks_linux.go:21-31
func GetFilesystemInfo(path string) map[string]string

{
	info := make(map[string]string)

	u := udev.Udev{}
	d := u.NewDeviceFromSyspath(filepath.Join("/sys/class/block", filepath.Base(path)))
	info["LABEL"] = d.PropertyValue("ID_FS_LABEL")
	info["TYPE"] = d.PropertyValue("ID_FS_TYPE")
	info["UUID"] = d.PropertyValue("ID_FS_UUID")
	info["PARTUUID"] = d.PropertyValue("ID_PART_ENTRY_UUID")
	return info
}
F
function

GetFileList

GetFileList returns a list of files in the specified directory.
If recursive is true, the function will recursively search for files, if
fullPaths is true, the full path of the file will be returned instead of
the relative path.

Parameters

directory
string
recursive
bool
fullPaths
bool

Returns

error
pkg/v1/fs/fs.go:39-81
func GetFileList(directory string, recursive, fullPaths bool) ([]types.FileInfo, error)

{
	var fileList []types.FileInfo

	walkFunc := func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if !recursive && path != directory {
			return fs.SkipDir
		}
		if d.IsDir() {
			return nil
		}

		info, err := d.Info()
		if err != nil {
			return err
		}

		filePath := path
		if !fullPaths {
			filePath, _ = filepath.Rel(directory, path)
		}

		permissions := convertPermissions(info.Mode())

		fileList = append(fileList, types.FileInfo{
			Path:        filePath,
			ParentPath:  filepath.Dir(filePath),
			IsDirectory: false,
			Size:        info.Size(),
			Permissions: permissions,
			Extension:   GetFileExtension(path),
		})
		return nil
	}

	if err := filepath.WalkDir(directory, walkFunc); err != nil {
		return nil, err
	}

	return fileList, nil
}

Example

fileList, err := fs.GetFileList("/batmans/cave", true, false)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

for _, file := range fileList {
	fmt.Printf("Path: %s\n", file.Path)
	fmt.Printf("Size: %d\n", file.Size)
	fmt.Printf("Permissions: %s\n", file.Permissions.String())
	fmt.Printf("Extension: %s\n", file.Extension)
}
F
function

convertPermissions

convertPermissions converts file mode to Permission type

Parameters

pkg/v1/fs/fs.go:84-96
func convertPermissions(mode os.FileMode) types.Permission

{
	return types.Permission{
		OwnerRead:     mode&0400 != 0,
		OwnerWrite:    mode&0200 != 0,
		OwnerExecute:  mode&0100 != 0,
		GroupRead:     mode&0040 != 0,
		GroupWrite:    mode&0020 != 0,
		GroupExecute:  mode&0010 != 0,
		OthersRead:    mode&0004 != 0,
		OthersWrite:   mode&0002 != 0,
		OthersExecute: mode&0001 != 0,
	}
}
F
function

GetFile

GetFile returns the file info of the specified file.
If fullPath is true, the full path of the file will be returned instead of
the relative path.

Parameters

filePath
string
fullPath
bool

Returns

pkg/v1/fs/fs.go:113-134
func GetFile(filePath string, fullPath bool) (types.FileInfo, error)

{
	info, err := os.Stat(filePath)
	if err != nil {
		return types.FileInfo{}, err
	}

	permissions := convertPermissions(info.Mode())
	file := types.FileInfo{
		Path:        filePath,
		ParentPath:  filepath.Dir(filePath),
		IsDirectory: info.IsDir(),
		Size:        info.Size(),
		Permissions: permissions,
		Extension:   GetFileExtension(filePath),
	}

	if !fullPath {
		file.Path, _ = filepath.Rel(".", filePath)
	}

	return file, nil
}

Example

file, err := fs.GetFile("/batmans/cave/batmobile.txt", false)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

fmt.Printf("Path: %s\n", file.Path)
fmt.Printf("Size: %d\n", file.Size)
fmt.Printf("Permissions: %s\n", file.Permissions.String())
F
function

GetFileExtension

GetFileExtension returns the extension of the given file

Parameters

filePath
string

Returns

string
pkg/v1/fs/fs.go:142-148
func GetFileExtension(filePath string) string

{
	ext := filepath.Ext(filePath)
	if ext != "" {
		return strings.TrimPrefix(ext, ".")
	}
	return ""
}

Example

extension := fs.GetFileExtension("/batmans/cave/batmobile.txt")
fmt.Printf("Extension: %s\n", extension)
F
function

IsFile

IsFile checks whether the given path is a regular file

Parameters

filePath
string

Returns

bool
pkg/v1/fs/fs.go:157-163
func IsFile(filePath string) bool

{
	info, err := os.Stat(filePath)
	if err != nil {
		return false
	}
	return info.Mode().IsRegular()
}

Example

if fs.IsFile("/batmans/cave/batmobile.txt") {
	fmt.Println("It's a file!")
}
F
function

IsDirectory

IsDirectory checks whether the given path is a directory

Parameters

dirPath
string

Returns

bool
pkg/v1/fs/fs.go:172-178
func IsDirectory(dirPath string) bool

{
	info, err := os.Stat(dirPath)
	if err != nil {
		return false
	}
	return info.Mode().IsDir()
}

Example

if fs.IsDirectory("/batmans/cave") {
	fmt.Println("It's a directory!")
}
F
function

GetFileSize

GetFileSize returns the size of the specified file in bytes

Parameters

filePath
string

Returns

int64
pkg/v1/fs/fs.go:186-192
func GetFileSize(filePath string) int64

{
	info, err := os.Stat(filePath)
	if err != nil {
		return 0
	}
	return info.Size()
}

Example

size := fs.GetFileSize("/batmans/cave/batmobile.txt")
fmt.Printf("Size: %d\n", size)
F
function

WriteFileContent

WriteFileContent writes content to the specified file

Parameters

filePath
string
content
string

Returns

error
pkg/v1/fs/fs.go:203-209
func WriteFileContent(filePath, content string) error

{
	err := os.WriteFile(filePath, []byte(content), 0644)
	if err != nil {
		return err
	}
	return nil
}

Example

err := fs.WriteFileContent("/tmp/batman", "I'm Batman!")
if err != nil {
	fmt.Printf("Error writing file content: %v", err)
	return
}
F
function

FileExists

FileExists checks if a file exists

Parameters

filePath
string

Returns

bool
pkg/v1/fs/fs.go:218-221
func FileExists(filePath string) bool

{
	_, err := os.Stat(filePath)
	return err == nil
}

Example

if fs.FileExists("/tmp/batman") {
	fmt.Println("The file exists!")
}
F
function

DirectoryExists

DirectoryExists checks if a directory exists

Parameters

directoryPath
string

Returns

bool
pkg/v1/fs/fs.go:230-233
func DirectoryExists(directoryPath string) bool

{
	fileInfo, err := os.Stat(directoryPath)
	return err == nil && fileInfo.IsDir()
}

Example

if fs.DirectoryExists("/tmp/batman") {
	fmt.Println("The directory exists!")
}
F
function

ListDirectories

ListDirectories returns a list of directories in the specified directory

Parameters

directoryPath
string

Returns

[]string
error
pkg/v1/fs/fs.go:248-260
func ListDirectories(directoryPath string) ([]string, error)

{
	var directories []string
	files, err := os.ReadDir(directoryPath)
	if err != nil {
		return nil, err
	}
	for _, file := range files {
		if file.IsDir() {
			directories = append(directories, file.Name())
		}
	}
	return directories, nil
}

Example

directories, err := fs.ListDirectories("/tmp")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

for _, directory := range directories {
	fmt.Printf("Directory: %s\n", directory)
}
S
struct

MountInfo

MountInfo is not supported on non-Linux platforms.

pkg/v1/fs/mountinfo_other.go:16-16
type MountInfo struct
F
function

GetMountInfo

Returns

error
pkg/v1/fs/mountinfo_other.go:18-20
func GetMountInfo() ([]MountInfo, error)

{
	return nil, fmt.Errorf("mountinfo is only supported on Linux")
}
F
function

GetMountpoint

Parameters

source
string

Returns

string
error
pkg/v1/fs/mountinfo_other.go:22-24
func GetMountpoint(source string) (string, error)

{
	return "", fmt.Errorf("mountinfo is only supported on Linux")
}
F
function

GetDiskList

GetDiskList returns a list of disks on the system

Returns

error
pkg/v1/fs/disks.go:46-94
func GetDiskList() ([]types.DiskInfo, error)

{
	var disks []types.DiskInfo

	files, err := os.ReadDir("/sys/class/block")
	if err != nil {
		return nil, err
	}

	diskMap := make(map[string]types.DiskInfo)

	for _, file := range files {
		name := file.Name()

		if isPartition(name) {
			continue
		}

		// Skip non-disk entries
		if strings.HasPrefix(name, "loop") ||
			strings.HasPrefix(name, "ram") ||
			strings.HasPrefix(name, "zram") {
			continue
		}

		diskPath := filepath.Join("/dev", name)
		info, err := GetDiskInfo(diskPath)
		if err != nil {
			return nil, err
		}

		partitions, err := GetPartitionList(diskPath)
		if err != nil {
			return nil, err
		}

		diskInfo := types.DiskInfo{
			BaseInfo:   info,
			Partitions: partitions,
		}

		diskMap[diskPath] = diskInfo
	}

	for _, disk := range diskMap {
		disks = append(disks, disk)
	}

	return disks, nil
}

Example

disks, err := fs.GetDiskList()
if err != nil {
	fmt.Printf("Error getting disk list: %v", err)
	return
}
for _, disk := range disks {
	fmt.Printf("Disk: %s\n", disk.Path)
	fmt.Printf("Size: %d\n", disk.Size)
	fmt.Printf("HumanSize: %s\n", disk.HumanSize)
	for _, partition := range disk.Partitions {
		fmt.Printf("Partition: %s\n", partition.Path)
		fmt.Printf("Size: %d\n", partition.Size)
		fmt.Printf("HumanSize: %s\n", partition.HumanSize)
		fmt.Printf("Filesystem: %s\n", partition.Filesystem)
		fmt.Printf("Mountpoint: %s\n", partition.Mountpoint)
		fmt.Printf("Label: %s\n", partition.Label)
		fmt.Printf("UUID: %s\n", partition.UUID)
		fmt.Printf("PARTUUID: %s\n", partition.PARTUUID)
		fmt.Printf("Flags: %v\n", partition.Flags)
	}
}
F
function

GetPartitionList

GetPartitionList returns a list of disk partitions on the specified disk

Parameters

diskPath
string

Returns

pkg/v1/fs/disks.go:110-139
func GetPartitionList(diskPath string) ([]types.PartitionInfo, error)

{
	var partitions []types.PartitionInfo

	files, err := os.ReadDir(filepath.Join("/sys/class/block", filepath.Base(diskPath)))
	if err != nil {
		return nil, err
	}

	for _, file := range files {
		name := file.Name()

		// Skip non-partition entries
		if !strings.HasPrefix(name, filepath.Base(diskPath)) {
			continue
		}

		partitionPath := filepath.Join("/dev", name)
		info, err := GetDiskInfo(partitionPath)
		if err != nil {
			return nil, err
		}

		partitionInfo := types.PartitionInfo{
			BaseInfo: info,
		}
		partitions = append(partitions, partitionInfo)
	}

	return partitions, nil
}

Example

partitions, err := fs.GetPartitionList("/dev/sda")
if err != nil {
	fmt.Printf("Error getting partition list: %v", err)
	return
}
for _, partition := range partitions {
	fmt.Printf("Partition: %s\n", partition.Path)
	fmt.Printf("Size: %d\n", partition.Size)
	fmt.Printf("HumanSize: %s\n", partition.HumanSize)
}
F
function

GetDiskInfo

GetDiskInfo returns information about a specific disk partition

Parameters

partitionPath
string

Returns

pkg/v1/fs/disks.go:153-179
func GetDiskInfo(partitionPath string) (types.BaseInfo, error)

{
	info := types.BaseInfo{
		Path: partitionPath,
	}

	sizePath := filepath.Join("/sys/class/block", filepath.Base(partitionPath), "size")
	size, err := os.ReadFile(sizePath)
	if err != nil {
		return info, err
	}

	sectorSize := 512
	info.Size = int64(sectorSize) * int64(parseUint64(strings.TrimSpace(string(size))))
	info.HumanSize = GetHumanSize(info.Size)

	fsInfo := GetFilesystemInfo(partitionPath)
	info.Filesystem = fsInfo["TYPE"]
	info.Label = fsInfo["LABEL"]
	info.UUID = fsInfo["UUID"]
	info.PARTUUID = fsInfo["PARTUUID"]

	if mp, err := GetMountpoint(partitionPath); err == nil {
		info.Mountpoint = mp
	}

	return info, nil
}

Example

info, err := fs.GetDiskInfo("/dev/sda1")
if err != nil {
	fmt.Printf("Error getting partition info: %v", err)
	return
}
fmt.Printf("Path: %s\n", info.Path)
fmt.Printf("Size: %d\n", info.Size)
fmt.Printf("HumanSize: %s\n", info.HumanSize)
F
function

parseUint64

parseUint64 parses a string into a uint64 or returns 0 if parsing fails

Parameters

s
string

Returns

uint64
pkg/v1/fs/disks.go:182-188
func parseUint64(s string) uint64

{
	value, err := strconv.ParseUint(s, 10, 64)
	if err != nil {
		return 0
	}
	return value
}
F
function

GetHumanSize

GetHumanSize converts the size from bytes to a human-readable format.
For example, 1024 bytes would be converted to “1 kB”.

Parameters

size
int64

Returns

string
pkg/v1/fs/disks.go:196-218
func GetHumanSize(size int64) string

{
	const (
		kB = 1024.0
		mB = kB * 1024.0
		gB = mB * 1024.0
		tB = gB * 1024.0
	)

	sizeFloat := float64(size)

	switch {
	case size < int64(kB):
		return fmt.Sprintf("%d B", size)
	case size < int64(mB):
		return fmt.Sprintf("%.2f kB", sizeFloat/kB)
	case size < int64(gB):
		return fmt.Sprintf("%.2f MB", sizeFloat/mB)
	case size < int64(tB):
		return fmt.Sprintf("%.2f GB", sizeFloat/gB)
	default:
		return fmt.Sprintf("%.2f TB", sizeFloat/tB)
	}
}

Example

fmt.Println(GetHumanSize(1024)) // 1 kB
F
function

isPartition

isPartition returns true if the specified block device is a partition

Parameters

deviceName
string

Returns

bool
pkg/v1/fs/disks.go:221-228
func isPartition(deviceName string) bool

{
	path := filepath.Join("/sys/class/block", deviceName, "partition")
	info, err := os.Stat(path)
	if err == nil && !info.IsDir() {
		return true
	}
	return false
}
S
struct

MountInfo

MountInfo represents a single entry from /proc/self/mountinfo.

Format reference: https://www.kernel.org/doc/Documentation/filesystems/proc.txt

Fields are kept close to the kernel representation to avoid lossy conversions.
Only a subset is currently exposed as helpers in this package.

pkg/v1/fs/mountinfo_linux.go:28-40
type MountInfo struct

Fields

Name Type Description
MountID int
ParentID int
Major int
Minor int
Root string
MountPoint string
Options []string
OptionalFields []string
FSType string
Source string
SuperOptions []string
F
function

parseMountInfo

Parameters

Returns

error
pkg/v1/fs/mountinfo_linux.go:42-60
func parseMountInfo(r io.Reader) ([]MountInfo, error)

{
	entries := make([]MountInfo, 0)

	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		line := scanner.Text()
		mi, err := parseMountInfoLine(line)
		if err != nil {
			return nil, err
		}
		entries = append(entries, mi)
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return entries, nil
}
F
function

parseMountInfoLine

Parameters

line
string

Returns

error
pkg/v1/fs/mountinfo_linux.go:62-129
func parseMountInfoLine(line string) (MountInfo, error)

{
	// Split on the separator " - " (space, dash, space)
	parts := strings.SplitN(line, " - ", 2)
	if len(parts) != 2 {
		return MountInfo{}, fmt.Errorf("invalid mountinfo line: missing separator")
	}

	pre := strings.Fields(parts[0])
	if len(pre) < 6 {
		return MountInfo{}, fmt.Errorf("invalid mountinfo line: too few fields")
	}

	post := strings.Fields(parts[1])
	if len(post) < 3 {
		return MountInfo{}, fmt.Errorf("invalid mountinfo line: too few post-separator fields")
	}

	mountID, err := strconv.Atoi(pre[0])
	if err != nil {
		return MountInfo{}, err
	}
	parentID, err := strconv.Atoi(pre[1])
	if err != nil {
		return MountInfo{}, err
	}

	majMin := strings.SplitN(pre[2], ":", 2)
	if len(majMin) != 2 {
		return MountInfo{}, fmt.Errorf("invalid mountinfo line: invalid major:minor")
	}
	major, err := strconv.Atoi(majMin[0])
	if err != nil {
		return MountInfo{}, err
	}
	minor, err := strconv.Atoi(majMin[1])
	if err != nil {
		return MountInfo{}, err
	}

	root := unescapeMountField(pre[3])
	mountPoint := unescapeMountField(pre[4])
	options := splitComma(pre[5])

	optional := make([]string, 0)
	if len(pre) > 6 {
		for _, f := range pre[6:] {
			optional = append(optional, unescapeMountField(f))
		}
	}

	fsType := post[0]
	source := unescapeMountField(post[1])
	superOptions := splitComma(post[2])

	return MountInfo{
		MountID:        mountID,
		ParentID:       parentID,
		Major:          major,
		Minor:          minor,
		Root:           root,
		MountPoint:     mountPoint,
		Options:        options,
		OptionalFields: optional,
		FSType:         fsType,
		Source:         source,
		SuperOptions:   superOptions,
	}, nil
}
F
function

splitComma

Parameters

s
string

Returns

[]string
pkg/v1/fs/mountinfo_linux.go:131-136
func splitComma(s string) []string

{
	if s == "" {
		return nil
	}
	return strings.Split(s, ",")
}
F
function

unescapeMountField

Parameters

s
string

Returns

string
pkg/v1/fs/mountinfo_linux.go:138-161
func unescapeMountField(s string) string

{
	// mountinfo uses octal escapes for special characters (e.g. \040 for space)
	if !strings.Contains(s, "\\") {
		return s
	}

	b := make([]byte, 0, len(s))
	for i := 0; i < len(s); i++ {
		if s[i] != '\\' || i+3 >= len(s) {
			b = append(b, s[i])
			continue
		}

		o := s[i+1 : i+4]
		v, err := strconv.ParseInt(o, 8, 32)
		if err != nil {
			b = append(b, s[i])
			continue
		}
		b = append(b, byte(v))
		i += 3
	}
	return string(b)
}
F
function

GetMountInfo

GetMountInfo returns the current mount table by reading /proc/self/mountinfo.

Returns

error
pkg/v1/fs/mountinfo_linux.go:175-182
func GetMountInfo() ([]MountInfo, error)

{
	f, err := os.Open("/proc/self/mountinfo")
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return parseMountInfo(f)
}

Example

entries, err := fs.GetMountInfo()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
for _, e := range entries {
	fmt.Printf("%s -> %s (%s)\n", e.Source, e.MountPoint, e.FSType)
}
F
function

GetMountpoint

GetMountpoint returns the mountpoint for a given mount source (e.g. /dev/sda1).
If the source is not mounted, an empty string is returned.

Parameters

source
string

Returns

string
error
pkg/v1/fs/mountinfo_linux.go:195-206
func GetMountpoint(source string) (string, error)

{
	entries, err := GetMountInfo()
	if err != nil {
		return "", err
	}
	for _, e := range entries {
		if e.Source == source {
			return e.MountPoint, nil
		}
	}
	return "", nil
}

Example

mp, err := fs.GetMountpoint("/dev/sda1")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Mountpoint: %s\n", mp)
S
struct

CopyTreeOptions

CopyTreeOptions controls CopyTree behavior.

pkg/v1/fs/tree.go:24-31
type CopyTreeOptions struct

Fields

Name Type Description
Workers int
PreserveOwnership bool
PreserveTimestamps bool
PreservePermissions bool
AllowSpecial bool
OnProgress func(CopyTreeProgress)
S
struct

CopyTreeProgress

CopyTreeProgress is emitted via CopyTreeOptions.OnProgress when set.

pkg/v1/fs/tree.go:34-38
type CopyTreeProgress struct

Fields

Name Type Description
SourcePath string
DestinationPath string
BytesCopied int64
S
struct

dirMeta

pkg/v1/fs/tree.go:40-47
type dirMeta struct

Fields

Name Type Description
path string
mode os.FileMode
uid int
gid int
at time.Time
mt time.Time
F
function

CopyTree

CopyTree copies a directory tree from source to destination.

Parameters

source
string
destination
string

Returns

error
pkg/v1/fs/tree.go:63-201
func CopyTree(source, destination string, opts CopyTreeOptions) error

{
	if opts.Workers <= 0 {
		opts.Workers = runtime.GOMAXPROCS(0)
		if opts.Workers < 1 {
			opts.Workers = 1
		}
	}
	if !opts.PreservePermissions {
		opts.PreservePermissions = true
	}
	if !opts.PreserveOwnership {
		opts.PreserveOwnership = true
	}
	if !opts.PreserveTimestamps {
		opts.PreserveTimestamps = true
	}

	if err := os.MkdirAll(destination, 0o755); err != nil {
		return err
	}

	jobs := make(chan fileJob, opts.Workers*2)
	var wg sync.WaitGroup

	var firstErr error
	var errMu sync.Mutex
	setErr := func(err error) {
		errMu.Lock()
		defer errMu.Unlock()
		if firstErr == nil {
			firstErr = err
		}
	}

	progress := func(p CopyTreeProgress) {
		if opts.OnProgress != nil {
			opts.OnProgress(p)
		}
	}

	for i := 0; i < opts.Workers; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := range jobs {
				if err := copyRegularFile(j.src, j.dst, j.mode, j.uid, j.gid, j.at, j.mt, opts, progress); err != nil {
					setErr(err)
				}
			}
		}()
	}

	dirs := make([]dirMeta, 0)

	walkErr := filepath.WalkDir(source, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		rel, err := filepath.Rel(source, path)
		if err != nil {
			return err
		}
		if rel == "." {
			return nil
		}
		dst := filepath.Join(destination, rel)

		info, err := d.Info()
		if err != nil {
			return err
		}

		mode := info.Mode()
		uid, gid, at, mt := extractUnixMeta(info)

		switch {
		case mode.IsDir():
			if err := os.MkdirAll(dst, 0o755); err != nil {
				return err
			}
			dirs = append(dirs, dirMeta{path: dst, mode: mode, uid: uid, gid: gid, at: at, mt: mt})
			return nil

		case mode&os.ModeSymlink != 0:
			link, err := os.Readlink(path)
			if err != nil {
				return err
			}
			if err := os.Symlink(link, dst); err != nil {
				if errors.Is(err, os.ErrExist) {
					_ = os.Remove(dst)
					return os.Symlink(link, dst)
				}
				return err
			}
			if opts.PreserveOwnership {
				_ = os.Lchown(dst, uid, gid)
			}
			return nil

		case mode.IsRegular():
			jobs <- fileJob{src: path, dst: dst, mode: mode, uid: uid, gid: gid, at: at, mt: mt}
			return nil

		default:
			if opts.AllowSpecial {
				// Special files support can be added later via mknod.
				return nil
			}
			return fmt.Errorf("unsupported file type: %s", path)
		}
	})

	close(jobs)
	wg.Wait()

	if walkErr != nil {
		return walkErr
	}
	if firstErr != nil {
		return firstErr
	}

	// Apply dir metadata bottom-up to keep directory mtimes stable.
	for i := len(dirs) - 1; i >= 0; i-- {
		dm := dirs[i]
		if opts.PreservePermissions {
			_ = os.Chmod(dm.path, dm.mode)
		}
		if opts.PreserveOwnership {
			_ = os.Chown(dm.path, dm.uid, dm.gid)
		}
		if opts.PreserveTimestamps {
			_ = os.Chtimes(dm.path, dm.at, dm.mt)
		}
	}

	return nil
}

Example

err := fs.CopyTree("/source", "/destination", fs.CopyTreeOptions{Workers: 4})
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

Notes

  • It is streaming (does not load whole files into memory).
  • It preserves symlinks (does not follow them).
  • It preserves permissions/ownership/timestamps when requested.
S
struct

fileJob

pkg/v1/fs/tree.go:203-211
type fileJob struct

Fields

Name Type Description
src string
dst string
mode os.FileMode
uid int
gid int
at time.Time
mt time.Time
F
function

copyRegularFile

Parameters

src
string
dst
string
uid
int
gid
int
progress
func(CopyTreeProgress)

Returns

error
pkg/v1/fs/tree.go:213-251
func copyRegularFile(src, dst string, mode os.FileMode, uid, gid int, at, mt time.Time, opts CopyTreeOptions, progress func(CopyTreeProgress)) error

{
	if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
		return err
	}

	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)
	if err != nil {
		return err
	}

	buf := make([]byte, 1024*1024)
	n, copyErr := io.CopyBuffer(out, in, buf)
	closeErr := out.Close()
	if copyErr != nil {
		return copyErr
	}
	if closeErr != nil {
		return closeErr
	}

	if opts.PreservePermissions {
		_ = os.Chmod(dst, mode)
	}
	if opts.PreserveOwnership {
		_ = os.Chown(dst, uid, gid)
	}
	if opts.PreserveTimestamps {
		_ = os.Chtimes(dst, at, mt)
	}

	progress(CopyTreeProgress{SourcePath: src, DestinationPath: dst, BytesCopied: n})
	return nil
}
F
function

extractUnixMeta

Parameters

Returns

uid
int
gid
int
pkg/v1/fs/tree_meta_linux.go:19-34
func extractUnixMeta(info os.FileInfo) (uid, gid int, at, mt time.Time)

{
	mt = info.ModTime()
	at = mt

	st, ok := info.Sys().(*syscall.Stat_t)
	if !ok {
		return 0, 0, at, mt
	}

	uid = int(st.Uid)
	gid = int(st.Gid)

	at = time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec))
	mt = time.Unix(int64(st.Mtim.Sec), int64(st.Mtim.Nsec))
	return uid, gid, at, mt
}
F
function

GetDeviceByUUID

GetDeviceByUUID resolves /dev/disk/by-uuid/.

Parameters

uuid
string

Returns

string
error
pkg/v1/fs/device_linux.go:43-45
func GetDeviceByUUID(uuid string) (string, error)

{
	return resolveDiskSymlink("/dev/disk/by-uuid", uuid)
}

Example

dev, err := fs.GetDeviceByUUID("5a1b2c3d-4e5f-6789-abcd-ef0123456789")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Device: %s\n", dev)
F
function

GetDeviceByPARTUUID

GetDeviceByPARTUUID resolves /dev/disk/by-partuuid/.

Parameters

partuuid
string

Returns

string
error
pkg/v1/fs/device_linux.go:57-59
func GetDeviceByPARTUUID(partuuid string) (string, error)

{
	return resolveDiskSymlink("/dev/disk/by-partuuid", partuuid)
}

Example

dev, err := fs.GetDeviceByPARTUUID("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Device: %s\n", dev)
F
function

GetDeviceByLabel

GetDeviceByLabel resolves /dev/disk/by-label/

Parameters

label
string

Returns

string
error
pkg/v1/fs/device_linux.go:71-73
func GetDeviceByLabel(label string) (string, error)

{
	return resolveDiskSymlink("/dev/disk/by-label", label)
}

Example

dev, err := fs.GetDeviceByLabel("VANILLA")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Device: %s\n", dev)
F
function

IsRemovableDevice

IsRemovableDevice checks if a block device is marked as removable via sysfs.

Parameters

devicePath
string

Returns

bool
error
pkg/v1/fs/device_linux.go:85-92
func IsRemovableDevice(devicePath string) (bool, error)

{
	name := filepath.Base(devicePath)
	b, err := os.ReadFile(filepath.Join("/sys/class/block", name, "removable"))
	if err != nil {
		return false, err
	}
	return strings.TrimSpace(string(b)) == "1", nil
}

Example

rem, err := fs.IsRemovableDevice("/dev/sda")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Removable: %v\n", rem)
F
function

GetDeviceSysPath

GetDeviceSysPath returns the sysfs path for a /dev/* device.

Parameters

devicePath
string

Returns

string
error
pkg/v1/fs/device_linux.go:104-111
func GetDeviceSysPath(devicePath string) (string, error)

{
	name := filepath.Base(devicePath)
	p := filepath.Join("/sys/class/block", name)
	if _, err := os.Stat(p); err != nil {
		return "", fmt.Errorf("device not found in sysfs: %w", err)
	}
	return p, nil
}

Example

sp, err := fs.GetDeviceSysPath("/dev/sda")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Sysfs path: %s\n", sp)
F
function

GetFilesystemInfo

GetFilesystemInfo returns information about the filesystem of a file or
partition by reading from /etc/mtab.

Parameters

path
string

Returns

map[string]string
pkg/v1/fs/disks_other.go:15-22
func GetFilesystemInfo(path string) map[string]string

{
	return map[string]string{
		"LABEL":    "",
		"TYPE":     "",
		"UUID":     "",
		"PARTUUID": "",
	}
}
F
function

CopyFile

CopyFile copies the contents of one file to another

Parameters

sourcePath
string
destinationPath
string

Returns

error
pkg/v1/fs/io.go:34-44
func CopyFile(sourcePath, destinationPath string) error

{
	source, err := os.ReadFile(sourcePath)
	if err != nil {
		return err
	}
	err = os.WriteFile(destinationPath, source, 0644)
	if err != nil {
		return err
	}
	return nil
}

Example

err := fs.CopyFile("/tmp/batman", "/tmp/robin")
if err != nil {
	fmt.Printf("Error copying file: %v", err)
	return
}
F
function

MoveFile

MoveFile moves a file from one location to another

Parameters

sourcePath
string
destinationPath
string

Returns

error
pkg/v1/fs/io.go:55-61
func MoveFile(sourcePath, destinationPath string) error

{
	err := os.Rename(sourcePath, destinationPath)
	if err != nil {
		return err
	}
	return nil
}

Example

err := fs.MoveFile("/tmp/batman", "/tmp/robin")
if err != nil {
	fmt.Printf("Error moving file: %v", err)
	return
}
F
function

DeleteFile

DeleteFile deletes a file

Parameters

filePath
string

Returns

error
pkg/v1/fs/io.go:72-78
func DeleteFile(filePath string) error

{
	err := os.Remove(filePath)
	if err != nil {
		return err
	}
	return nil
}

Example

err := fs.DeleteFile("/tmp/batman")
if err != nil {
	fmt.Printf("Error deleting file: %v", err)
	return
}
F
function

CreateDirectory

CreateDirectory creates a new directory

Parameters

directoryPath
string

Returns

error
pkg/v1/fs/io.go:89-95
func CreateDirectory(directoryPath string) error

{
	err := os.Mkdir(directoryPath, 0755)
	if err != nil {
		return err
	}
	return nil
}

Example

err := fs.CreateDirectory("/tmp/batman")
if err != nil {
	fmt.Printf("Error creating directory: %v", err)
	return
}
F
function

DeleteDirectory

DeleteDirectory deletes a directory and its contents

Parameters

directoryPath
string

Returns

error
pkg/v1/fs/io.go:106-112
func DeleteDirectory(directoryPath string) error

{
	err := os.RemoveAll(directoryPath)
	if err != nil {
		return err
	}
	return nil
}

Example

err := fs.DeleteDirectory("/tmp/batman")
if err != nil {
	fmt.Printf("Error deleting directory: %v", err)
	return
}
F
function

IsMounted

IsMounted checks if the given source path is mounted in the given
destination path. It does so by reading the /proc/mounts file.

Parameters

source
string
destination
string

Returns

bool
error
pkg/v1/fs/mount.go:28-39
func IsMounted(source string, destination string) (bool, error)

{
	entries, err := GetMountInfo()
	if err != nil {
		return false, err
	}
	for _, e := range entries {
		if e.Source == source && e.MountPoint == destination {
			return true, nil
		}
	}
	return false, nil
}

Example

mounted, err := fs.IsMounted("tmpfs", "/tmp")
if err != nil {
	fmt.Printf("Error checking if /tmp is mounted: %v", err)
	return
}
fmt.Printf("/tmp is mounted: %v", mounted)
F
function

Mount

Mount mounts the given source path in the given destination path. It also
creates the destination path if it does not exist. An error is returned if
the source path does not exist.

Notes (for developers):

Avoid mapping the fsType into a custom type, as it would require further
maintenance once a new filesystem type is added to Vanilla OS.

Parameters

source
string
destination
string
fsType
string
data
string
mode
uintptr

Returns

error
pkg/v1/fs/mount.go:57-72
func Mount(source, destination, fsType, data string, mode uintptr) error

{
	info, err := os.Stat(source)
	if err != nil {
		return err
	}

	if info.IsDir() {
		_ = os.MkdirAll(destination, 0o755)
	} else {
		file, _ := os.Create(destination)

		defer func() { _ = file.Close() }()
	}

	return syscall.Mount(source, destination, fsType, mode, data)
}

Example

err := fs.Mount("/dev/sda1", "/mnt", "ext4", "", syscall.MS_RDONLY)
if err != nil {
	fmt.Printf("Error mounting /dev/sda1: %v", err)
	return
}
F
function

MountBind

MountBind mounts bind the given source path in the given destination path.

Parameters

src
string
dest
string

Returns

error
pkg/v1/fs/mount.go:87-101
func MountBind(src, dest string) error

{
	return Mount(
		src,
		dest,
		"bind",
		"",
		syscall.MS_BIND|
			syscall.MS_REC|
			syscall.MS_RDONLY|
			syscall.MS_NOSUID|
			syscall.MS_NOEXEC|
			syscall.MS_NODEV|
			syscall.MS_PRIVATE,
	)
}

Example

err := fs.MountBind("/bruce", "/batman")
if err != nil {
	fmt.Printf("Error bind mounting /batman: %v", err)
	return
}

Notes

This is just a wrapper of the Mount function, for convenience.

F
function

MountOverlay

MountOverlay mounts the given lower, upper and work directories in the
given destination path as an overlay filesystem.

Parameters

lowerDir
string
upperDir
string
workDir
string

Returns

error
pkg/v1/fs/mount.go:117-128
func MountOverlay(lowerDir, upperDir, workDir string) error

{
	return Mount(
		"overlay",
		lowerDir,
		"overlay",
		fmt.Sprintf(
			"lowerdir=%s,upperdir=%s,workdir=%s,userxattr",
			lowerDir, upperDir, workDir,
		),
		0,
	)
}

Example

err := fs.MountOverlay("/batman/lower", "/batman/upper", "/batman/work")
if err != nil {
	fmt.Printf("Error overlay mounting /batman: %v", err)
	return
}

Notes

This is just a wrapper of the Mount function, for convenience.

F
function

MountFuseOverlay

MountFuseOverlay mounts the given lower, upper and work directories in the
given destination path as an overlay filesystem using fuse-overlayfs.

Parameters

targetDir
string
lowerDir
string
upperDir
string
workDir
string

Returns

error
pkg/v1/fs/mount.go:145-157
func MountFuseOverlay(targetDir, lowerDir, upperDir, workDir string) error

{
	// Note: implemented via kernel overlayfs to avoid spawning external commands.
	return Mount(
		"overlay",
		targetDir,
		"overlay",
		fmt.Sprintf(
			"lowerdir=%s,upperdir=%s,workdir=%s,userxattr",
			lowerDir, upperDir, workDir,
		),
		0,
	)
}

Example

err := fs.MountFuseOverlay("/batman", "/batman/lower", "/batman/upper", "/batman/work")
if err != nil {
	fmt.Printf("Error fuse-overlayfs mounting /batman: %v", err)
	return
}

Notes

This implementation uses the fuse-overlayfs command-line tool, if that
is not available in the system, this function will return an error.

F
function

Unmount

Unmount unmounts the given path. An error is returned if the path is not
mounted.

Parameters

target
string

Returns

error
pkg/v1/fs/mount.go:169-171
func Unmount(target string) error

{
	return syscall.Unmount(target, 0)
}

Example

err := fs.Unmount("/mnt")
if err != nil {
	fmt.Printf("Error unmounting /mnt: %v", err)
	return
}
F
function

UnmountFuseOverlay

UnmountFuseOverlay unmounts the given path using the fuse-overlayfs command-
line tool. An error is returned if the path is not mounted.

Parameters

targetDir
string

Returns

error
pkg/v1/fs/mount.go:188-190
func UnmountFuseOverlay(targetDir string) error

{
	return Unmount(targetDir)
}

Example

err := fs.UnmountFuseOverlay("/batman")
if err != nil {
	fmt.Printf("Error fuse-overlayfs unmounting /batman: %v", err)
	return
}

Notes

This implementation uses the fuse-overlayfs command-line tool, if that
is not available in the system, this function will return an error.

F
function

AtomicSwap

AtomicSwap atomically swaps two files or directories

Parameters

sourcePath
string
destinationPath
string

Returns

error
pkg/v1/fs/atomic.go:26-43
func AtomicSwap(sourcePath, destinationPath string) error

{
	orig, err := os.Open(sourcePath)
	if err != nil {
		return err
	}

	newfile, err := os.Open(destinationPath)
	if err != nil {
		return err
	}

	err = unix.Renameat2(int(orig.Fd()), sourcePath, int(newfile.Fd()), destinationPath, unix.RENAME_EXCHANGE)
	if err != nil {
		return err
	}

	return nil
}

Example

err := fs.AtomicSwap("/tmp/file1", "/tmp/file2")
if err != nil {
	fmt.Printf("Error swapping files: %v", err)
	return
}
F
function

GetFreeSpaceBytes

GetFreeSpaceBytes returns the available free space for the filesystem hosting path.

Parameters

path
string

Returns

uint64
error
pkg/v1/fs/space_linux.go:29-36
func GetFreeSpaceBytes(path string) (uint64, error)

{
	var st syscall.Statfs_t
	err := syscall.Statfs(path, &st)
	if err != nil {
		return 0, err
	}
	return st.Bavail * uint64(st.Bsize), nil
}

Example

free, err := fs.GetFreeSpaceBytes("/")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Free bytes: %d\n", free)
F
function

GetTotalSpaceBytes

GetTotalSpaceBytes returns the total size for the filesystem hosting path.

Parameters

path
string

Returns

uint64
error
pkg/v1/fs/space_linux.go:48-55
func GetTotalSpaceBytes(path string) (uint64, error)

{
	var st syscall.Statfs_t
	err := syscall.Statfs(path, &st)
	if err != nil {
		return 0, err
	}
	return st.Blocks * uint64(st.Bsize), nil
}

Example

total, err := fs.GetTotalSpaceBytes("/")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Total bytes: %d\n", total)
F
function

IsWritableDir

IsWritableDir checks if dir is writable by attempting to create a temporary file.

Parameters

dir
string

Returns

bool
error
pkg/v1/fs/space_linux.go:67-79
func IsWritableDir(dir string) (bool, error)

{
	testPath := filepath.Join(dir, ".vos-sdk-writable-check")
	f, err := os.OpenFile(testPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
	if err != nil {
		if os.IsPermission(err) {
			return false, nil
		}
		return false, err
	}
	_ = f.Close()
	_ = os.Remove(testPath)
	return true, nil
}

Example

writable, err := fs.IsWritableDir("/tmp")
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Writable: %v\n", writable)
F
function

extractUnixMeta

Parameters

Returns

uid
int
gid
int
pkg/v1/fs/tree_meta_other.go:18-22
func extractUnixMeta(info os.FileInfo) (uid, gid int, at, mt time.Time)

{
	mt = info.ModTime()
	at = mt
	return 0, 0, at, mt
}