Initial commit
Proof-of-concept implementation. Bugs will occur.
This commit is contained in:
158
internal/domains/filesystem/music_app_metadata.go
Normal file
158
internal/domains/filesystem/music_app_metadata.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hanwen/go-fuse/v2/fs"
|
||||
"github.com/hanwen/go-fuse/v2/fuse"
|
||||
)
|
||||
|
||||
type MusicAppMetadataFile struct {
|
||||
fs.Inode
|
||||
|
||||
f *FS
|
||||
path string
|
||||
}
|
||||
|
||||
var (
|
||||
_ = (fs.NodeGetattrer)((*MusicAppMetadataFile)(nil))
|
||||
_ = (fs.NodeOpener)((*MusicAppMetadataFile)(nil))
|
||||
_ = (fs.NodeCreater)((*MusicAppMetadataFile)(nil))
|
||||
_ = (fs.NodeWriter)((*MusicAppMetadataFile)(nil))
|
||||
_ = (fs.NodeSetattrer)((*MusicAppMetadataFile)(nil))
|
||||
_ = (fs.NodeUnlinker)((*MusicAppMetadataFile)(nil))
|
||||
)
|
||||
|
||||
func (m *MusicAppMetadataFile) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
|
||||
info, err := os.Stat(m.path)
|
||||
if err != nil {
|
||||
out.Mode = fuse.S_IFREG | 0644
|
||||
out.Nlink = 1
|
||||
out.Ino = m.StableAttr().Ino
|
||||
out.Size = 0
|
||||
out.Mtime = uint64(time.Now().Unix())
|
||||
out.Atime = out.Mtime
|
||||
out.Ctime = out.Mtime
|
||||
out.Blocks = 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
out.Mode = fuse.S_IFREG | uint32(info.Mode())
|
||||
out.Nlink = 1
|
||||
out.Ino = m.StableAttr().Ino
|
||||
out.Size = uint64(info.Size())
|
||||
out.Mtime = uint64(info.ModTime().Unix())
|
||||
out.Atime = out.Mtime
|
||||
out.Ctime = out.Mtime
|
||||
out.Blocks = (out.Size + 511) / 512
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *MusicAppMetadataFile) Setattr(ctx context.Context, fh fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
|
||||
info, err := os.Stat(m.path)
|
||||
if err != nil {
|
||||
out.Mode = fuse.S_IFREG | 0644
|
||||
out.Nlink = 1
|
||||
out.Ino = m.StableAttr().Ino
|
||||
out.Size = 0
|
||||
out.Mtime = uint64(time.Now().Unix())
|
||||
out.Atime = out.Mtime
|
||||
out.Ctime = out.Mtime
|
||||
out.Blocks = 1
|
||||
} else {
|
||||
out.Mode = fuse.S_IFREG | uint32(info.Mode())
|
||||
out.Nlink = 1
|
||||
out.Ino = m.StableAttr().Ino
|
||||
out.Size = uint64(info.Size())
|
||||
out.Mtime = uint64(info.ModTime().Unix())
|
||||
out.Atime = out.Mtime
|
||||
out.Ctime = out.Mtime
|
||||
out.Blocks = (out.Size + 511) / 512
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *MusicAppMetadataFile) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *fs.Inode, fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||
file, err := os.Create(m.path)
|
||||
if err != nil {
|
||||
return nil, nil, 0, syscall.EIO
|
||||
}
|
||||
|
||||
ch := m.NewInode(ctx, &MusicAppMetadataFile{path: m.path}, fs.StableAttr{
|
||||
Mode: fuse.S_IFREG,
|
||||
Ino: m.f.nextInode(),
|
||||
})
|
||||
|
||||
out.Mode = fuse.S_IFREG | 0644
|
||||
out.Nlink = 1
|
||||
out.Ino = ch.StableAttr().Ino
|
||||
out.Size = 0
|
||||
out.Mtime = uint64(time.Now().Unix())
|
||||
out.Atime = out.Mtime
|
||||
out.Ctime = out.Mtime
|
||||
out.Blocks = 1
|
||||
|
||||
return ch, &File{file: file}, fuse.FOPEN_DIRECT_IO, 0
|
||||
}
|
||||
|
||||
func (m *MusicAppMetadataFile) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||
if _, err := os.Stat(m.path); os.IsNotExist(err) {
|
||||
if err := os.WriteFile(m.path, []byte{}, 0644); err != nil {
|
||||
return nil, 0, syscall.EIO
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(m.path, int(flags), 0644)
|
||||
if err != nil {
|
||||
return nil, 0, syscall.EIO
|
||||
}
|
||||
|
||||
return &File{file: file}, fuse.FOPEN_DIRECT_IO, 0
|
||||
}
|
||||
|
||||
func (m *MusicAppMetadataFile) Write(ctx context.Context, fh fs.FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno) {
|
||||
handle, ok := fh.(*File)
|
||||
if !ok {
|
||||
return 0, syscall.EBADF
|
||||
}
|
||||
|
||||
n, err := handle.file.WriteAt(data, off)
|
||||
if err != nil {
|
||||
return 0, syscall.EIO
|
||||
}
|
||||
|
||||
return uint32(n), 0
|
||||
}
|
||||
|
||||
func (m *MusicAppMetadataFile) Unlink(ctx context.Context, name string) syscall.Errno {
|
||||
if err := os.Remove(m.path); err != nil {
|
||||
return syscall.ENOENT
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *FS) isiTunesMetadata(name string) bool {
|
||||
name = strings.ToLower(name)
|
||||
|
||||
return strings.HasPrefix(name, ".") ||
|
||||
strings.Contains(name, "albumart") ||
|
||||
strings.Contains(name, "folder") ||
|
||||
strings.Contains(name, "itunes") ||
|
||||
strings.HasSuffix(name, ".itl") ||
|
||||
strings.HasSuffix(name, ".xml") ||
|
||||
strings.HasSuffix(name, ".db")
|
||||
}
|
||||
|
||||
func (f *FS) NewMusicAppMetadataFile(path string) *MusicAppMetadataFile {
|
||||
return &MusicAppMetadataFile{
|
||||
f: f,
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user