Skip to content

Commit

Permalink
Add more fs methods to Filesystem interface
Browse files Browse the repository at this point in the history
Interface is extended as a part of ext4 improvements effort
#9 (comment)

New methods introduced:

* Mknod
* Link
* Symlink
* Chmod
* Chown
* Rename
* Remove

filesystem.ErrNotSupported is returned if FS does not support a method.

Methods lacking implementation return filesystem.ErrNotImplemented.
  • Loading branch information
aol-nnov committed Nov 10, 2024
1 parent f66ea89 commit c20b1cf
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 17 deletions.
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
45 changes: 43 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,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(_ /*pathname*/ string, _ /*mode*/ uint32, _ /*dev*/ int) error {
return filesystem.ErrNotImplemented
}

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

// creates a symbolic link named linkpath which contains the string target.
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.
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
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 +839,22 @@ 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.
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
40 changes: 40 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,15 @@ 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.
func (fs *FileSystem) Rename(_ /*oldpath*/, _ /*newpath*/ string) error {
return filesystem.ErrNotImplemented
}

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
58 changes: 56 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,42 @@ 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
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.
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.
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
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 +407,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 +453,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

0 comments on commit c20b1cf

Please sign in to comment.