func (pf PseudoFile) Seek(offset int64, whence int) (int64, error) {
	if pf.appendOnly() {
		return 0, ErrAppendOnly
	}
	pf.fs.mu.Lock()
	defer pf.fs.mu.Unlock()
	f, d := pf.lookupFD()
	if f == nil && d == nil {
		return 0, ErrInvalidFileDescriptor
	} else if d != nil {
		return 0, ErrDirectory
	}
	return pf.fs.fileSeek(f, offset, whence)
}
Seek implements io.Seeker.
package renterutil  
import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
	"go.sia.tech/siad/crypto"
	"lukechampine.com/us/hostdb"
	"lukechampine.com/us/renter"
)
const metafileExt = ".usa"
var ErrInvalidFileDescriptor = errors.New("invalid file descriptor")
type pseudoFileInfo struct {
	name string
	m    renter.MetaIndex
}
func (i pseudoFileInfo) Name() string       { return i.name }
func (i pseudoFileInfo) Size() int64        { return i.m.Filesize }
func (i pseudoFileInfo) Mode() os.FileMode  { return i.m.Mode }
func (i pseudoFileInfo) ModTime() time.Time { return i.m.ModTime }
func (i pseudoFileInfo) IsDir() bool        { return false }
func (i pseudoFileInfo) Sys() interface{}   { return i.m }
type PseudoFS struct {
	root           string
	curFD          int
	files          map[int]*openMetaFile
	dirs           map[int]*os.File
	hosts          *HostSet
	sectors        map[hostdb.HostPublicKey]*renter.SectorBuilder
	lastCommitTime time.Time
	mu             sync.RWMutex
}
func (fs *PseudoFS) path(name string) string {
	return filepath.Join(fs.root, name)
}
func isDir(path string) bool {
	stat, err := os.Stat(path)
	return err == nil && stat.IsDir()
}
func exists(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}
func (fs *PseudoFS) Chmod(name string, mode os.FileMode) error {
	path := fs.path(name)
	if isDir(path) {
		return os.Chmod(path, mode)
	}
	path += metafileExt
	for _, of := range fs.files {
		if of.name == name {
			of.m.Mode = mode
			of.m.ModTime = time.Now()
			return nil
		}
	}
	m, err := renter.ReadMetaFile(path)
	if err != nil {
		return fmt.Errorf("chmod %v: %w", path, err)
	}
	m.Mode = mode
	m.ModTime = time.Now()
	if err := renter.WriteMetaFile(path, m); err != nil {
		return fmt.Errorf("chmod %v: %w", path, err)
	}
	return nil
}
func (fs *PseudoFS) Create(name string, minShards int) (*PseudoFile, error) {
	return fs.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666, minShards)
}
func (fs *PseudoFS) Mkdir(name string, perm os.FileMode) error {
	return os.Mkdir(fs.path(name), perm)
}
func (fs *PseudoFS) MkdirAll(path string, perm os.FileMode) error {
	return os.MkdirAll(fs.path(path), perm)
}
func (fs *PseudoFS) Open(name string) (*PseudoFile, error) {
	return fs.OpenFile(name, os.O_RDONLY, 0, 0)
}
func (fs *PseudoFS) OpenFile(name string, flag int, perm os.FileMode, minShards int) (*PseudoFile, error) {
	fs.mu.Lock()
	defer fs.mu.Unlock()
	path := fs.path(name)
	if isDir(path) {
		dir, err := os.OpenFile(path, flag, perm)
		if err != nil {
			return nil, err
		}
		fs.dirs[fs.curFD] = dir
		fs.curFD++
		return &PseudoFile{
			name: name,
			fd:   fs.curFD - 1,
			fs:   fs,
		}, nil
	}
	path += metafileExt
	for fd, of := range fs.files {
		if of.name == name {
			if flag&rwmask == os.O_WRONLY || flag&rwmask == os.O_RDWR {
				var missing []string
				for _, hostKey := range of.m.Hosts {
					if _, ok := fs.hosts.sessions[hostKey]; !ok {
						missing = append(missing, hostKey.ShortKey())
					}
				}
				if len(missing) > 0 {
					return nil, fmt.Errorf("insufficient contracts: need a contract from each of these hosts: %v",
						strings.Join(missing, " "))
				}
			}
			of.closed = false
			of.offset = 0
			if flag&os.O_APPEND == os.O_APPEND {
				of.offset = of.filesize()
			}
			return &PseudoFile{
				name:  name,
				flags: flag,
				fd:    fd,
				fs:    fs,
			}, nil
		}
	}
	var m *renter.MetaFile
	if flag&os.O_CREATE == os.O_CREATE {
		if len(fs.hosts.sessions) < minShards {
			return nil, errors.New("minShards cannot be greater than the number of hosts")
		}
		if flag&os.O_TRUNC == os.O_TRUNC {
			for fd, f := range fs.files {
				if f.name == name && f.closed {
					delete(fs.files, fd)
					break
				}
			}
		}
		hosts := make([]hostdb.HostPublicKey, 0, len(fs.hosts.sessions))
		for hostKey := range fs.hosts.sessions {
			hosts = append(hosts, hostKey)
		}
		m = renter.NewMetaFile(perm, 0, hosts, minShards)
	} else {
		var err error
		m, err = renter.ReadMetaFile(path)
		if err != nil {
			return nil, fmt.Errorf("open %v: %w", path, err)
		}
		var missing []string
		for _, hostKey := range m.Hosts {
			if _, ok := fs.hosts.sessions[hostKey]; !ok {
				missing = append(missing, hostKey.ShortKey())
			}
		}
		if flag&rwmask == os.O_RDONLY {
			if have := len(m.Hosts) - len(missing); have < m.MinShards {
				return nil, fmt.Errorf("insufficient contracts: need a contract from at least %v of these hosts: %v",
					m.MinShards-have, strings.Join(missing, " "))
			}
		} else {
			if len(missing) > 0 {
				return nil, fmt.Errorf("insufficient contracts: need a contract from each of these hosts: %v",
					strings.Join(missing, " "))
			}
		}
	}
	of := &openMetaFile{
		name: name,
		m:    m,
	}
	if flag&os.O_APPEND == os.O_APPEND {
		of.offset = m.Filesize
	}
	fs.files[fs.curFD] = of
	fs.curFD++
	return &PseudoFile{
		name:  name,
		flags: flag,
		fd:    fs.curFD - 1,
		fs:    fs,
	}, nil
}
func (fs *PseudoFS) Remove(name string) error {
	fs.mu.Lock()
	defer fs.mu.Unlock()
	for fd, f := range fs.files {
		if f.name == name && f.closed {
			delete(fs.files, fd)
			break
		}
	}
	path := fs.path(name)
	if !isDir(path) {
		path += metafileExt
	}
	if !exists(path) {
		return nil
	}
	return os.Remove(path)
}
func (fs *PseudoFS) RemoveAll(path string) error {
	for fd, f := range fs.files {
		if strings.HasPrefix(f.name, path) && f.closed {
			delete(fs.files, fd)
		}
	}
	path = fs.path(path)
	if !isDir(path) {
		path += metafileExt
	}
	return os.RemoveAll(path)
}
func (fs *PseudoFS) GC() error {
	fs.mu.Lock()
	defer fs.mu.Unlock()
	hostRoots := make(map[hostdb.HostPublicKey]map[crypto.Hash]struct{})
	for hostKey := range fs.hosts.sessions {
		err := func() error {
			h, err := fs.hosts.acquire(hostKey)
			if err != nil {
				return err
			}
			defer fs.hosts.release(hostKey)
			numRoots := h.Revision().NumSectors()
			rootMap := make(map[crypto.Hash]struct{}, numRoots)
			for offset := 0; offset < numRoots; {
				n := 130000  
				if offset+n > numRoots {
					n = numRoots - offset
				}
				roots, err := h.SectorRoots(offset, n)
				if err != nil {
					return err
				}
				for _, r := range roots {
					rootMap[r] = struct{}{}
				}
				offset += n
			}
			hostRoots[hostKey] = rootMap
			return nil
		}()
		if err != nil {
			return err
		}
	}
	err := filepath.Walk(fs.root, func(path string, info os.FileInfo, err error) error {
		if (info != nil && info.IsDir()) || !strings.HasSuffix(path, ".usa") {
			return nil
		} else if err != nil {
			return err
		}
		m, err := renter.ReadMetaFile(path)
		if err != nil {
			return err
		}
		for i, hostKey := range m.Hosts {
			if roots, ok := hostRoots[hostKey]; ok {
				for _, ss := range m.Shards[i] {
					delete(roots, ss.MerkleRoot)
				}
			}
		}
		return nil
	})
	if err != nil {
		return err
	}
	done := true
	for _, roots := range hostRoots {
		done = done && len(roots) == 0
	}
	if done {
		return nil
	}
	for hostKey, rootsMap := range hostRoots {
		err := func() error {
			h, err := fs.hosts.acquire(hostKey)
			if err != nil {
				return err
			}
			defer fs.hosts.release(hostKey)
			roots := make([]crypto.Hash, 0, len(rootsMap))
			for root := range rootsMap {
				roots = append(roots, root)
			}
			return h.DeleteSectors(roots)
		}()
		if err != nil {
			return err
		}
	}
	return nil
}
func (fs *PseudoFS) Rename(oldname, newname string) error {
	fs.mu.Lock()
	for _, f := range fs.files {
		if f.name == oldname && len(f.pendingWrites) > 0 {
			if err := fs.flushSectors(); err != nil {
				return err
			}
			break
		}
	}
	fs.mu.Unlock()
	oldpath, newpath := fs.path(oldname), fs.path(newname)
	if !isDir(oldpath) {
		oldpath += metafileExt
	}
	if !isDir(newpath) {
		newpath += metafileExt
	}
	return os.Rename(oldpath, newpath)
}
func (fs *PseudoFS) Stat(name string) (os.FileInfo, error) {
	fs.mu.RLock()
	for _, f := range fs.files {
		if f.name == name {
			info := pseudoFileInfo{name: f.name, m: f.m.MetaIndex}
			info.m.Filesize = f.filesize()
			fs.mu.RUnlock()
			return info, nil
		}
	}
	fs.mu.RUnlock()
	path := fs.path(name)
	if isDir(path) {
		return os.Stat(path)
	}
	path += metafileExt
	index, err := renter.ReadMetaIndex(path)
	if err != nil {
		return nil, fmt.Errorf("stat %v: %w", name, err)
	}
	return pseudoFileInfo{name, index}, nil
}
func (fs *PseudoFS) Close() error {
	fs.mu.Lock()
	defer fs.mu.Unlock()
	if err := fs.flushSectors(); err != nil {
		return err
	}
	for fd, f := range fs.files {
		if err := fs.commitChanges(f); err != nil {
			return err
		}
		delete(fs.files, fd)
	}
	for fd, d := range fs.dirs {
		d.Close()
		delete(fs.dirs, fd)
	}
	return fs.hosts.Close()
}
func NewFileSystem(root string, hosts *HostSet) *PseudoFS {
	sectors := make(map[hostdb.HostPublicKey]*renter.SectorBuilder)
	for hostKey := range hosts.sessions {
		sectors[hostKey] = new(renter.SectorBuilder)
	}
	return &PseudoFS{
		root:           root,
		files:          make(map[int]*openMetaFile),
		dirs:           make(map[int]*os.File),
		hosts:          hosts,
		sectors:        sectors,
		lastCommitTime: time.Now(),
	}
}
type PseudoFile struct {
	name  string
	fd    int
	flags int
	fs    *PseudoFS
}
var ErrNotWriteable = errors.New("file is not writeable")
var ErrNotReadable = errors.New("file is not readable")
var ErrAppendOnly = errors.New("file is append-only")
var ErrDirectory = errors.New("file is a directory")
var ErrNotDirectory = errors.New("file is not a directory")
const rwmask = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
func (pf PseudoFile) writeable() bool {
	return pf.flags&rwmask == os.O_WRONLY || pf.flags&rwmask == os.O_RDWR
}
func (pf PseudoFile) readable() bool {
	return pf.flags&rwmask == os.O_RDONLY || pf.flags&rwmask == os.O_RDWR
}
func (pf PseudoFile) appendOnly() bool {
	return pf.flags&os.O_APPEND == os.O_APPEND
}
func (pf PseudoFile) lookupFD() (file *openMetaFile, dir *os.File) {
	file = pf.fs.files[pf.fd]
	if file != nil && file.closed {
		file = nil
	}
	return file, pf.fs.dirs[pf.fd]
}
func (pf PseudoFile) Close() error {
	pf.fs.mu.Lock()
	defer pf.fs.mu.Unlock()
	f, d := pf.lookupFD()
	if f == nil && d == nil {
		return ErrInvalidFileDescriptor
	} else if d != nil {
		delete(pf.fs.dirs, pf.fd)
		return d.Close()
	}
	if len(f.pendingWrites) == 0 {
		delete(pf.fs.files, pf.fd)
	}
	return nil
}
func (pf PseudoFile) Read(p []byte) (int, error) {
	if !pf.readable() {
		return 0, ErrNotReadable
	}
	pf.fs.mu.Lock()
	defer pf.fs.mu.Unlock()
	f, d := pf.lookupFD()
	if f == nil && d == nil {
		return 0, ErrInvalidFileDescriptor
	} else if d != nil {
		return 0, ErrDirectory
	}
	return pf.fs.fileRead(f, p)
}
func (pf PseudoFile) Write(p []byte) (int, error) {
	if !pf.writeable() {
		return 0, ErrNotWriteable
	}
	pf.fs.mu.Lock()
	defer pf.fs.mu.Unlock()
	f, d := pf.lookupFD()
	if f == nil && d == nil {
		return 0, ErrInvalidFileDescriptor
	} else if d != nil {
		return 0, ErrDirectory
	}
	return pf.fs.fileWrite(f, p)
}
func (pf PseudoFile) ReadAt(p []byte, off int64) (int, error) {
	if !pf.readable() {
		return 0, ErrNotReadable
	}
	pf.fs.mu.RLock()
	defer pf.fs.mu.RUnlock()
	f, d := pf.lookupFD()
	if f == nil && d == nil {
		return 0, ErrInvalidFileDescriptor
	} else if d != nil {
		return 0, ErrDirectory
	}
	return pf.fs.fileReadAt(f, p, off)
}
func (pf PseudoFile) ReadAtP(p []byte, off int64) (int, error) {
	if !pf.readable() {
		return 0, ErrNotReadable
	}
	pf.fs.mu.RLock()
	defer pf.fs.mu.RUnlock()
	f, d := pf.lookupFD()
	if f == nil && d == nil {
		return 0, ErrInvalidFileDescriptor
	} else if d != nil {
		return 0, ErrDirectory
	}
	splitSize := len(p) / (len(f.m.Hosts) / f.m.MinShards)
	if splitSize == 0 {
		return pf.fs.fileReadAt(f, p, off)
	}
	type readResult struct {
		n   int
		err error
	}
	resChan := make(chan readResult)
	var numResults int
	for buf := bytes.NewBuffer(p); buf.Len() > 0; {
		numResults++
		suboff := off + int64(len(p)-buf.Len())
		subp := buf.Next(splitSize)
		go func() {
			n, err := pf.fs.fileReadAt(f, subp, suboff)
			resChan <- readResult{n, err}
		}()
	}
	var n int
	var err error
	for i := 0; i < numResults; i++ {
		r := <-resChan
		n += r.n
		if r.err != nil && (err == nil || err == io.EOF) {
			err = r.err
		}
	}
	return n, err
}
func (pf PseudoFile) WriteAt(p []byte, off int64) (int, error) {
	if !pf.writeable() {
		return 0, ErrNotWriteable
	}
	pf.fs.mu.Lock()
	defer pf.fs.mu.Unlock()
	f, d := pf.lookupFD()
	if f == nil && d == nil {
		return 0, ErrInvalidFileDescriptor
	} else if d != nil {
		return 0, ErrDirectory
	}
	if pf.appendOnly() && off != f.filesize() {
		return 0, ErrAppendOnly
	}
	return pf.fs.fileWriteAt(f, p, off)
}