Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend filesystem interface #264

Merged
merged 1 commit into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (d *Disk) CreateFilesystem(spec FilesystemSpec) (filesystem.FileSystem, err
case filesystem.TypeExt4:
return ext4.Create(d.File, size, start, d.LogicalBlocksize, nil)
case filesystem.TypeSquashfs:
return nil, errors.New("squashfs is a read-only filesystem")
return nil, filesystem.ErrReadonlyFilesystem
default:
return nil, errors.New("unknown filesystem type requested")
}
Expand Down
57 changes: 55 additions & 2 deletions filesystem/ext4/ext4.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,9 @@ func Read(file util.File, size, start, sectorsize int64) (*FileSystem, error) {
}, nil
}

// interface guard
var _ filesystem.FileSystem = (*FileSystem)(nil)

// Type returns the type code for the filesystem. Always returns filesystem.TypeExt4
func (fs *FileSystem) Type() filesystem.Type {
return filesystem.TypeExt4
Expand All @@ -699,6 +702,44 @@ func (fs *FileSystem) Mkdir(p string) error {
return err
}

// creates a filesystem node (file, device special file, or named pipe) named pathname,
// with attributes specified by mode and dev
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Mknod(pathname string, mode uint32, dev int) error {
return filesystem.ErrNotImplemented
}

// creates a new link (also known as a hard link) to an existing file.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Link(oldpath, newpath string) error {
return filesystem.ErrNotImplemented
}

// creates a symbolic link named linkpath which contains the string target.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Symlink(oldpath, newpath string) error {
return filesystem.ErrNotImplemented
}

// Chmod changes the mode of the named file to mode. If the file is a symbolic link,
// it changes the mode of the link's target.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Chmod(name string, mode os.FileMode) error {
return filesystem.ErrNotImplemented
}

// Chown changes the numeric uid and gid of the named file. If the file is a symbolic link,
// it changes the uid and gid of the link's target. A uid or gid of -1 means to not change that value
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Chown(name string, uid, gid int) error {
return filesystem.ErrNotImplemented
}

// ReadDir return the contents of a given directory in a given filesystem.
//
// Returns a slice of os.FileInfo with all of the entries in the directory.
Expand Down Expand Up @@ -808,12 +849,24 @@ func (fs *FileSystem) Label() string {
return fs.superblock.volumeLabel
}

// Rm remove file or directory at path.
// Rename renames (moves) oldpath to newpath. If newpath already exists and is not a directory, Rename replaces it.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Rename(oldpath, newpath string) error {
return filesystem.ErrNotImplemented
}

// Deprecated: use filesystem.Remove(p string) instead
func (fs *FileSystem) Rm(p string) error {
return fs.Remove(p)
}

// Removes file or directory at path.
// If path is directory, it only will remove if it is empty.
// If path is a file, it will remove the file.
// Will not remove any parents.
// Error if the file does not exist or is not an empty directory
func (fs *FileSystem) Rm(p string) error {
func (fs *FileSystem) Remove(p string) error {
parentDir, entry, err := fs.getEntryAndParent(p)
if err != nil {
return err
Expand Down
45 changes: 45 additions & 0 deletions filesystem/fat32/fat32.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,9 @@ func (fs *FileSystem) writeFat() error {
return nil
}

// interface guard
var _ filesystem.FileSystem = (*FileSystem)(nil)

// Type returns the type code for the filesystem. Always returns filesystem.TypeFat32
func (fs *FileSystem) Type() filesystem.Type {
return filesystem.TypeFat32
Expand All @@ -485,6 +488,34 @@ func (fs *FileSystem) Mkdir(p string) error {
return err
}

// creates a filesystem node (file, device special file, or named pipe) named pathname,
// with attributes specified by mode and dev
func (fs *FileSystem) Mknod(_ string, _ uint32, _ int) error {
return filesystem.ErrNotSupported
}

// creates a new link (also known as a hard link) to an existing file.
func (fs *FileSystem) Link(_, _ string) error {
return filesystem.ErrNotSupported
}

// creates a symbolic link named linkpath which contains the string target.
func (fs *FileSystem) Symlink(_, _ string) error {
return filesystem.ErrNotSupported
}

// Chmod changes the mode of the named file to mode. If the file is a symbolic link,
// it changes the mode of the link's target.
func (fs *FileSystem) Chmod(_ string, _ os.FileMode) error {
return filesystem.ErrNotSupported
}

// Chown changes the numeric uid and gid of the named file. If the file is a symbolic link,
// it changes the uid and gid of the link's target. A uid or gid of -1 means to not change that value
func (fs *FileSystem) Chown(_ string, _, _ int) error {
return filesystem.ErrNotSupported
}

// ReadDir return the contents of a given directory in a given filesystem.
//
// Returns a slice of os.FileInfo with all of the entries in the directory.
Expand Down Expand Up @@ -606,6 +637,20 @@ func (fs *FileSystem) OpenFile(p string, flag int) (filesystem.File, error) {
}, nil
}

// Rename renames (moves) oldpath to newpath. If newpath already exists and is not a directory, Rename replaces it.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Rename(oldpath, newpath string) error {
return filesystem.ErrNotImplemented
}

// removes the named file or (empty) directory.
//
//nolint:revive // parameters will be used eventually
func (fs *FileSystem) Remove(pathname string) error {
return filesystem.ErrNotImplemented
}

// Label get the label of the filesystem from the secial file in the root directory.
// The label stored in the boot sector is ignored to mimic Windows behavior which
// only stores and reads the label from the special file in the root directory.
Expand Down
4 changes: 2 additions & 2 deletions filesystem/fat32/fat32_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,9 @@ func TestFat32OpenFile(t *testing.T) {
{"/CORTO1.TXT", os.O_RDWR, false, "This is a very long replacement string", "Tenemos un archivo corto\nThis is a very long replacement string", nil},
{"/CORTO1.TXT", os.O_RDWR, false, "Two", "Tenemos un archivo corto\nTwo", nil},
// - open for append file that does exist (write contents, check that appended)
{"/CORTO1.TXT", os.O_APPEND, false, "More", "", fmt.Errorf("cannot write to file opened read-only")},
{"/CORTO1.TXT", os.O_APPEND, false, "More", "", filesystem.ErrReadonlyFilesystem},
{"/CORTO1.TXT", os.O_APPEND | os.O_RDWR, false, "More", "Tenemos un archivo corto\nMore", nil},
{"/CORTO1.TXT", os.O_APPEND, true, "More", "", fmt.Errorf("cannot write to file opened read-only")},
{"/CORTO1.TXT", os.O_APPEND, true, "More", "", filesystem.ErrReadonlyFilesystem},
{"/CORTO1.TXT", os.O_APPEND | os.O_RDWR, true, "More", "Moremos un archivo corto\n", nil},
}
for _, t2 := range tests {
Expand Down
4 changes: 3 additions & 1 deletion filesystem/fat32/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"io"
"os"

"github.com/diskfs/go-diskfs/filesystem"
)

// File represents a single file in a FAT32 filesystem
Expand Down Expand Up @@ -108,7 +110,7 @@ func (fl *File) Write(p []byte) (int, error) {
fs := fl.filesystem
// if the file was not opened RDWR, nothing we can do
if !fl.isReadWrite {
return totalWritten, fmt.Errorf("cannot write to file opened read-only")
return totalWritten, filesystem.ErrReadonlyFilesystem
}
// what is the new file size?
writeSize := len(p)
Expand Down
32 changes: 28 additions & 4 deletions filesystem/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,49 @@
package filesystem

import (
"errors"
"os"
)

var (
ErrNotSupported = errors.New("method not supported by this filesystem")
ErrNotImplemented = errors.New("method not implemented (patches are welcome)")
ErrReadonlyFilesystem = errors.New("read-only filesystem")
)

// FileSystem is a reference to a single filesystem on a disk
type FileSystem interface {
// Type return the type of filesystem
Type() Type
// Mkdir make a directory
Mkdir(string) error
Mkdir(pathname string) error
// creates a filesystem node (file, device special file, or named pipe) named pathname,
// with attributes specified by mode and dev
Mknod(pathname string, mode uint32, dev int) error
// creates a new link (also known as a hard link) to an existing file.
Link(oldpath, newpath string) error
// creates a symbolic link named linkpath which contains the string target.
Symlink(oldpath, newpath string) error
// Chmod changes the mode of the named file to mode. If the file is a symbolic link,
// it changes the mode of the link's target.
Chmod(name string, mode os.FileMode) error
// Chown changes the numeric uid and gid of the named file. If the file is a symbolic link,
// it changes the uid and gid of the link's target. A uid or gid of -1 means to not change that value
Chown(name string, uid, gid int) error
// ReadDir read the contents of a directory
ReadDir(string) ([]os.FileInfo, error)
ReadDir(pathname string) ([]os.FileInfo, error)
// OpenFile open a handle to read or write to a file
OpenFile(string, int) (File, error)
OpenFile(pathname string, flag int) (File, error)
// Rename renames (moves) oldpath to newpath. If newpath already exists and is not a directory, Rename replaces it.
Rename(oldpath, newpath string) error
// removes the named file or (empty) directory.
Remove(pathname string) error
// Label get the label for the filesystem, or "" if none. Be careful to trim it, as it may contain
// leading or following whitespace. The label is passed as-is and not cleaned up at all.
Label() string
// SetLabel changes the label on the writable filesystem. Different file system may hav different
// length constraints.
SetLabel(string) error
SetLabel(label string) error
}

// Type represents the type of disk this is
Expand Down
4 changes: 3 additions & 1 deletion filesystem/iso9660/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"io"
"os"

"github.com/diskfs/go-diskfs/filesystem"
)

// File represents a single file in an iso9660 filesystem
Expand Down Expand Up @@ -65,7 +67,7 @@ func (fl *File) Read(b []byte) (int, error) {
//
// you cannot write to an iso, so this returns an error
func (fl *File) Write(_ []byte) (int, error) {
return 0, fmt.Errorf("cannot write to a read-only iso filesystem")
return 0, filesystem.ErrReadonlyFilesystem
}

// Seek set the offset to a particular point in the file
Expand Down
66 changes: 64 additions & 2 deletions filesystem/iso9660/iso9660.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ func Read(file util.File, size, start, blocksize int64) (*FileSystem, error) {
return fs, nil
}

// interface guard
var _ filesystem.FileSystem = (*FileSystem)(nil)

// Type returns the type code for the filesystem. Always returns filesystem.TypeFat32
func (fsm *FileSystem) Type() filesystem.Type {
return filesystem.TypeISO9660
Expand All @@ -295,7 +298,7 @@ func (fsm *FileSystem) Type() filesystem.Type {
// if readonly and not in workspace, will return an error
func (fsm *FileSystem) Mkdir(p string) error {
if fsm.workspace == "" {
return fmt.Errorf("cannot write to read-only filesystem")
return filesystem.ErrReadonlyFilesystem
}
err := os.MkdirAll(path.Join(fsm.workspace, p), 0o755)
if err != nil {
Expand All @@ -305,6 +308,50 @@ func (fsm *FileSystem) Mkdir(p string) error {
return err
}

// creates a filesystem node (file, device special file, or named pipe) named pathname,
// with attributes specified by mode and dev
//
//nolint:revive // parameters will be used eventually
func (fsm *FileSystem) Mknod(pathname string, mode uint32, dev int) error {
// Rock Ridge has device files support
// https://en.wikipedia.org/wiki/ISO_9660#Rock_Ridge
return filesystem.ErrNotImplemented
}

// creates a new link (also known as a hard link) to an existing file.
func (fsm *FileSystem) Link(_, _ string) error {
return filesystem.ErrNotSupported
}

// creates a symbolic link named linkpath which contains the string target.
//
//nolint:revive // parameters will be used eventually
func (fsm *FileSystem) Symlink(oldpath, newpath string) error {
// Rock Ridge has symlink support
// https://en.wikipedia.org/wiki/ISO_9660#Rock_Ridge
return filesystem.ErrNotImplemented
}

// Chmod changes the mode of the named file to mode. If the file is a symbolic link,
// it changes the mode of the link's target.
//
//nolint:revive // parameters will be used eventually
func (fsm *FileSystem) Chmod(name string, mode os.FileMode) error {
// Rock Ridge has UNIX-style file modes support
// https://en.wikipedia.org/wiki/ISO_9660#Rock_Ridge
return filesystem.ErrNotImplemented
}

// Chown changes the numeric uid and gid of the named file. If the file is a symbolic link,
// it changes the uid and gid of the link's target. A uid or gid of -1 means to not change that value
//
//nolint:revive // parameters will be used eventually
func (fsm *FileSystem) Chown(name string, uid, gid int) error {
// Rock Ridge has user ids and group ids support
// https://en.wikipedia.org/wiki/ISO_9660#Rock_Ridge
return filesystem.ErrNotImplemented
}

// ReadDir return the contents of a given directory in a given filesystem.
//
// Returns a slice of os.FileInfo with all of the entries in the directory.
Expand Down Expand Up @@ -368,7 +415,7 @@ func (fsm *FileSystem) OpenFile(p string, flag int) (filesystem.File, error) {
writeMode := flag&os.O_WRONLY != 0 || flag&os.O_RDWR != 0 || flag&os.O_APPEND != 0 || flag&os.O_CREATE != 0 || flag&os.O_TRUNC != 0 || flag&os.O_EXCL != 0
if fsm.workspace == "" {
if writeMode {
return nil, fmt.Errorf("cannot write to read-only filesystem")
return nil, filesystem.ErrReadonlyFilesystem
}

// get the directory entries
Expand Down Expand Up @@ -414,6 +461,21 @@ func (fsm *FileSystem) OpenFile(p string, flag int) (filesystem.File, error) {
return f, nil
}

// Rename renames (moves) oldpath to newpath. If newpath already exists and is not a directory, Rename replaces it.
func (fsm *FileSystem) Rename(oldpath, newpath string) error {
if fsm.workspace == "" {
return filesystem.ErrReadonlyFilesystem
}
return os.Rename(path.Join(fsm.workspace, oldpath), path.Join(fsm.workspace, newpath))
}

func (fsm *FileSystem) Remove(p string) error {
if fsm.workspace == "" {
return filesystem.ErrReadonlyFilesystem
}
return os.Remove(path.Join(fsm.workspace, p))
}

// readDirectory - read directory entry on iso only (not workspace)
func (fsm *FileSystem) readDirectory(p string) ([]*directoryEntry, error) {
var (
Expand Down
4 changes: 3 additions & 1 deletion filesystem/squashfs/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"io"
"os"

"github.com/diskfs/go-diskfs/filesystem"
)

// File represents a single file in a squashfs filesystem
Expand Down Expand Up @@ -148,7 +150,7 @@ func (fl *File) Read(b []byte) (int, error) {
//
//nolint:unused,revive // but it is important to implement the interface
func (fl *File) Write(p []byte) (int, error) {
return 0, fmt.Errorf("cannot write to a read-only squashfs filesystem")
return 0, filesystem.ErrReadonlyFilesystem
}

// Seek set the offset to a particular point in the file
Expand Down
Loading
Loading