package squashfs // Could just be using bare object Open with RangeRequest which // would transfer the minimum amount of data but may be slower. import ( "errors" "fmt" "os" "sync" "github.com/rclone/rclone/vfs" ) // Cache file handles for accessing the file type cache struct { node vfs.Node fhsMu sync.Mutex fhs []cacheHandle } // A cached file handle type cacheHandle struct { offset int64 fh vfs.Handle } // Make a new cache func newCache(node vfs.Node) *cache { return &cache{ node: node, } } // Get a vfs.Handle from the pool or open one // // This tries to find an open file handle which doesn't require seeking. func (c *cache) open(off int64) (fh vfs.Handle, err error) { c.fhsMu.Lock() defer c.fhsMu.Unlock() if len(c.fhs) > 0 { // Look for exact match first for i, cfh := range c.fhs { if cfh.offset == off { // fs.Debugf(nil, "CACHE MATCH") c.fhs = append(c.fhs[:i], c.fhs[i+1:]...) return cfh.fh, nil } } // fs.Debugf(nil, "CACHE MISS") // Just take the first one if not found cfh := c.fhs[0] c.fhs = c.fhs[1:] return cfh.fh, nil } fh, err = c.node.Open(os.O_RDONLY) if err != nil { return nil, fmt.Errorf("failed to open squashfs archive: %w", err) } return fh, nil } // Close a vfs.Handle or return it to the pool // // off should be the offset the file handle would read from without seeking func (c *cache) close(fh vfs.Handle, off int64) { c.fhsMu.Lock() defer c.fhsMu.Unlock() c.fhs = append(c.fhs, cacheHandle{ offset: off, fh: fh, }) } // ReadAt reads len(p) bytes into p starting at offset off in the underlying // input source. It returns the number of bytes read (0 <= n <= len(p)) and any // error encountered. // // When ReadAt returns n < len(p), it returns a non-nil error explaining why // more bytes were not returned. In this respect, ReadAt is stricter than Read. // // Even if ReadAt returns n < len(p), it may use all of p as scratch // space during the call. If some data is available but not len(p) bytes, // ReadAt blocks until either all the data is available or an error occurs. // In this respect ReadAt is different from Read. // // If the n = len(p) bytes returned by ReadAt are at the end of the input // source, ReadAt may return either err == EOF or err == nil. // // If ReadAt is reading from an input source with a seek offset, ReadAt should // not affect nor be affected by the underlying seek offset. // // Clients of ReadAt can execute parallel ReadAt calls on the same input // source. // // Implementations must not retain p. func (c *cache) ReadAt(p []byte, off int64) (n int, err error) { fh, err := c.open(off) if err != nil { return n, err } defer func() { c.close(fh, off+int64(len(p))) }() // fs.Debugf(nil, "ReadAt(p[%d], off=%d, fh=%p)", len(p), off, fh) return fh.ReadAt(p, off) } var errCacheNotImplemented = errors.New("internal error: squashfs cache doesn't implement method") // WriteAt method dummy stub to satisfy interface func (c *cache) WriteAt(p []byte, off int64) (n int, err error) { return 0, errCacheNotImplemented } // Seek method dummy stub to satisfy interface func (c *cache) Seek(offset int64, whence int) (int64, error) { return 0, errCacheNotImplemented }