package zfs import ( "fmt" "strings" "github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/pkg/chunk" cmap "github.com/orcaman/concurrent-map/v2" "github.com/winfsp/cgofuse/fuse" "go.uber.org/zap" ) type ZurgFS struct { fuse.FileSystemBase TorrentManager *torrent.TorrentManager Config config.ConfigInterface Chunk *chunk.Manager Log *zap.SugaredLogger } func NewZurgFS(tm *torrent.TorrentManager, cfg config.ConfigInterface, chunk *chunk.Manager, log *zap.SugaredLogger) *ZurgFS { return &ZurgFS{ TorrentManager: tm, Config: cfg, Chunk: chunk, Log: log, } } func (fs *ZurgFS) Open(path string, flags int) (errc int, fh uint64) { segments := splitIntoSegments(path) switch len(segments) { case 0: return 0, 0 case 1: if _, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT, ^uint64(0) } return 0, 0 case 2: if directory, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT, ^uint64(0) } else if _, torFound := directory.Get(segments[1]); !torFound { return -fuse.ENOENT, ^uint64(0) } return 0, 0 case 3: if directory, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT, ^uint64(0) } else if torrent, torFound := directory.Get(segments[1]); !torFound { return -fuse.ENOENT, ^uint64(0) } else if file, fileFound := torrent.SelectedFiles.Get(segments[2]); !fileFound { return -fuse.ENOENT, ^uint64(0) } else { return 0, file.ZurgFS } } return -fuse.ENOENT, ^uint64(0) } func (fs *ZurgFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) { segments := splitIntoSegments(path) switch len(segments) { case 0: stat.Mode = fuse.S_IFDIR | 0555 return 0 case 1: if _, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT } stat.Mode = fuse.S_IFDIR | 0555 return 0 case 2: if directory, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT } else if _, torFound := directory.Get(segments[1]); !torFound { return -fuse.ENOENT } stat.Mode = fuse.S_IFDIR | 0555 return 0 case 3: if directory, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT } else if torrent, torFound := directory.Get(segments[1]); !torFound { return -fuse.ENOENT } else if file, fileFound := torrent.SelectedFiles.Get(segments[2]); !fileFound { return -fuse.ENOENT } else { stat.Mode = fuse.S_IFREG | 0444 stat.Size = file.Bytes return 0 } } return -fuse.ENOENT } func (fs *ZurgFS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) { segments := splitIntoSegments(path) fmt.Println("seg", segments) if len(segments) != 3 { return -fuse.ENOENT } else if directory, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT } else if torrent, torFound := directory.Get(segments[1]); !torFound { return -fuse.ENOENT } else if file, fileFound := torrent.SelectedFiles.Get(segments[2]); !fileFound { return -fuse.ENOENT } else { size := int64(len(buff)) endofst := ofst + size if endofst > file.Bytes { endofst = file.Bytes } if endofst < ofst { return 0 } // let's request a bigger chunk than we need if size < int64(fs.Config.GetNetworkBufferSize()) { size = int64(fs.Config.GetNetworkBufferSize()) } response, err := fs.Chunk.GetChunk(file, ofst, size) if err != nil { return -fuse.ENOENT } n = copy(buff, response[:endofst-ofst]) return n } } func (fs *ZurgFS) Readdir(path string, fill func(name string, stat *fuse.Stat_t, ofst int64) bool, ofst int64, fh uint64) (errc int) { segments := splitIntoSegments(path) switch len(segments) { case 0: fill(".", nil, 0) fill("..", nil, 0) fs.TorrentManager.DirectoryMap.IterCb(func(directoryName string, _ cmap.ConcurrentMap[string, *torrent.Torrent]) { fill(directoryName, nil, 0) }) case 1: fill(".", nil, 0) fill("..", nil, 0) if torrents, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT } else { torrents.IterCb(func(accessKey string, _ *torrent.Torrent) { fill(accessKey, nil, 0) }) } case 2: fill(".", nil, 0) fill("..", nil, 0) if torrents, dirFound := fs.TorrentManager.DirectoryMap.Get(segments[0]); !dirFound { return -fuse.ENOENT } else if tor, torFound := torrents.Get(segments[1]); !torFound { return -fuse.ENOENT } else { tor.SelectedFiles.IterCb(func(filename string, _ *torrent.File) { fill(filename, nil, 0) }) } default: return -fuse.ENOENT } return 0 } func splitIntoSegments(path string) []string { segments := strings.Split(path, "/") // remove empty segments for i := 0; i < len(segments); i++ { if segments[i] == "" { segments = append(segments[:i], segments[i+1:]...) i-- } } return segments }