func (r *Renter) RenameFile(currentName, newName string) error {
	lockID := r.mu.Lock()
	defer r.mu.Unlock(lockID)
	err := validateSiapath(newName)
	if err != nil {
		return err
	}
	file, exists := r.files[currentName]
	if !exists {
		return ErrUnknownPath
	}
	_, exists = r.files[newName]
	if exists {
		return ErrPathOverload
	}
	file.mu.Lock()
	file.name = newName
	err = r.saveFile(file)
	file.mu.Unlock()
	if err != nil {
		return err
	}
	delete(r.files, currentName)
	r.files[newName] = file
	if t, ok := r.persist.Tracking[currentName]; ok {
		delete(r.persist.Tracking, currentName)
		r.persist.Tracking[newName] = t
	}
	err = r.saveSync()
	if err != nil {
		return err
	}
	oldPath := filepath.Join(r.persistDir, currentName+ShareExtension)
	return os.RemoveAll(oldPath)
}
RenameFile takes an existing file and changes the nickname. The original
file must exist, and there must not be any file that already has the
replacement nickname.
package renter
import (
	"errors"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"sync"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/persist"
	"github.com/NebulousLabs/Sia/types"
)
var (
	ErrEmptyFilename = errors.New("filename must be a nonempty string")
	ErrPathOverload = errors.New("a file already exists at that location")
	ErrUnknownPath = errors.New("no file known with that path")
)
type file struct {
	name        string
	size        uint64  
	contracts   map[types.FileContractID]fileContract
	masterKey   crypto.TwofishKey     
	erasureCode modules.ErasureCoder  
	pieceSize   uint64                
	mode        uint32                
	deleted     bool                  
	staticUID string  
	mu sync.RWMutex
}
type fileContract struct {
	ID     types.FileContractID
	IP     modules.NetAddress
	Pieces []pieceData
	WindowStart types.BlockHeight
}
type pieceData struct {
	Chunk      uint64       
	Piece      uint64       
	MerkleRoot crypto.Hash  
}
func deriveKey(masterKey crypto.TwofishKey, chunkIndex, pieceIndex uint64) crypto.TwofishKey {
	return crypto.TwofishKey(crypto.HashAll(masterKey, chunkIndex, pieceIndex))
}
func (f *file) staticChunkSize() uint64 {
	return f.pieceSize * uint64(f.erasureCode.MinPieces())
}
func (f *file) numChunks() uint64 {
	if f.size == 0 {
		return 1
	}
	n := f.size / f.staticChunkSize()
	if f.size%f.staticChunkSize() != 0 {
		n++
	}
	return n
}
func (f *file) available(offline map[types.FileContractID]bool) bool {
	chunkPieces := make([]int, f.numChunks())
	for _, fc := range f.contracts {
		if offline[fc.ID] {
			continue
		}
		for _, p := range fc.Pieces {
			chunkPieces[p.Chunk]++
		}
	}
	for _, n := range chunkPieces {
		if n < f.erasureCode.MinPieces() {
			return false
		}
	}
	return true
}
func (f *file) uploadedBytes() uint64 {
	var uploaded uint64
	for _, fc := range f.contracts {
		uploaded += uint64(len(fc.Pieces)) * modules.SectorSize
	}
	return uploaded
}
func (f *file) uploadProgress() float64 {
	uploaded := f.uploadedBytes()
	desired := modules.SectorSize * uint64(f.erasureCode.NumPieces()) * f.numChunks()
	return math.Min(100*(float64(uploaded)/float64(desired)), 100)
}
func (f *file) redundancy(offlineMap map[types.FileContractID]bool, goodForRenewMap map[types.FileContractID]bool) float64 {
	if f.size == 0 {
		return -1
	}
	piecesPerChunk := make([]int, f.numChunks())
	piecesPerChunkNoRenew := make([]int, f.numChunks())
	if len(piecesPerChunk) == 0 {
		build.Critical("cannot get redundancy of a file with 0 chunks")
		return -1
	}
	pieceRenewMap := make(map[string]bool)
	for _, fc := range f.contracts {
		offline, exists1 := offlineMap[fc.ID]
		goodForRenew, exists2 := goodForRenewMap[fc.ID]
		if exists1 != exists2 {
			build.Critical("contract can't be in one map but not in the other")
		}
		if !exists1 {
			continue
		}
		if offline {
			continue
		}
		for _, p := range fc.Pieces {
			pieceKey := fmt.Sprintf("%v/%v", p.Chunk, p.Piece)
			if gfr, redundant := pieceRenewMap[pieceKey]; redundant && gfr {
				continue
			} else if redundant && !gfr {
				pieceRenewMap[pieceKey] = true
				piecesPerChunk[p.Chunk]++
				continue
			}
			pieceRenewMap[pieceKey] = goodForRenew
			if goodForRenew {
				piecesPerChunk[p.Chunk]++
			}
			piecesPerChunkNoRenew[p.Chunk]++
		}
	}
	minPieces := piecesPerChunk[0]
	for _, numPieces := range piecesPerChunk {
		if numPieces < minPieces {
			minPieces = numPieces
		}
	}
	minPiecesNoRenew := piecesPerChunkNoRenew[0]
	for _, numPieces := range piecesPerChunkNoRenew {
		if numPieces < minPiecesNoRenew {
			minPiecesNoRenew = numPieces
		}
	}
	redundancy := float64(minPieces) / float64(f.erasureCode.MinPieces())
	redundancyNoRenew := float64(minPiecesNoRenew) / float64(f.erasureCode.MinPieces())
	if redundancy < 1 {
		return redundancyNoRenew
	}
	return redundancy
}
func (f *file) expiration() types.BlockHeight {
	if len(f.contracts) == 0 {
		return 0
	}
	lowest := ^types.BlockHeight(0)
	for _, fc := range f.contracts {
		if fc.WindowStart < lowest {
			lowest = fc.WindowStart
		}
	}
	return lowest
}
func newFile(name string, code modules.ErasureCoder, pieceSize, fileSize uint64) *file {
	return &file{
		name:        name,
		size:        fileSize,
		contracts:   make(map[types.FileContractID]fileContract),
		masterKey:   crypto.GenerateTwofishKey(),
		erasureCode: code,
		pieceSize:   pieceSize,
		staticUID: persist.RandomSuffix(),
	}
}
func (r *Renter) DeleteFile(nickname string) error {
	lockID := r.mu.Lock()
	f, exists := r.files[nickname]
	if !exists {
		r.mu.Unlock(lockID)
		return ErrUnknownPath
	}
	delete(r.files, nickname)
	delete(r.persist.Tracking, nickname)
	err := persist.RemoveFile(filepath.Join(r.persistDir, f.name+ShareExtension))
	if err != nil {
		r.log.Println("WARN: couldn't remove file :", err)
	}
	r.saveSync()
	r.mu.Unlock(lockID)
	f.mu.Lock()
	defer f.mu.Unlock()
	f.deleted = true
	return nil
}
func (r *Renter) FileList() []modules.FileInfo {
	var files []*file
	contractIDs := make(map[types.FileContractID]struct{})
	lockID := r.mu.RLock()
	for _, f := range r.files {
		files = append(files, f)
		f.mu.RLock()
		for cid := range f.contracts {
			contractIDs[cid] = struct{}{}
		}
		f.mu.RUnlock()
	}
	r.mu.RUnlock(lockID)
	goodForRenew := make(map[types.FileContractID]bool)
	offline := make(map[types.FileContractID]bool)
	for cid := range contractIDs {
		resolvedKey := r.hostContractor.ResolveIDToPubKey(cid)
		cu, ok := r.hostContractor.ContractUtility(resolvedKey)
		if !ok {
			continue
		}
		goodForRenew[cid] = ok && cu.GoodForRenew
		offline[cid] = r.hostContractor.IsOffline(resolvedKey)
	}
	fileList := []modules.FileInfo{}
	for _, f := range files {
		lockID := r.mu.RLock()
		f.mu.RLock()
		renewing := true
		var localPath string
		tf, exists := r.persist.Tracking[f.name]
		if exists {
			localPath = tf.RepairPath
		}
		fileList = append(fileList, modules.FileInfo{
			SiaPath:        f.name,
			LocalPath:      localPath,
			Filesize:       f.size,
			Renewing:       renewing,
			Available:      f.available(offline),
			Redundancy:     f.redundancy(offline, goodForRenew),
			UploadedBytes:  f.uploadedBytes(),
			UploadProgress: f.uploadProgress(),
			Expiration:     f.expiration(),
		})
		f.mu.RUnlock()
		r.mu.RUnlock(lockID)
	}
	return fileList
}
func (r *Renter) File(siaPath string) (modules.FileInfo, error) {
	var fileInfo modules.FileInfo
	contractIDs := make(map[types.FileContractID]struct{})
	lockID := r.mu.RLock()
	defer r.mu.RUnlock(lockID)
	file, exists := r.files[siaPath]
	if !exists {
		return fileInfo, ErrUnknownPath
	}
	file.mu.RLock()
	defer file.mu.RUnlock()
	for cid := range file.contracts {
		contractIDs[cid] = struct{}{}
	}
	goodForRenew := make(map[types.FileContractID]bool)
	offline := make(map[types.FileContractID]bool)
	for cid := range contractIDs {
		resolvedKey := r.hostContractor.ResolveIDToPubKey(cid)
		cu, ok := r.hostContractor.ContractUtility(resolvedKey)
		if !ok {
			continue
		}
		goodForRenew[cid] = ok && cu.GoodForRenew
		offline[cid] = r.hostContractor.IsOffline(resolvedKey)
	}
	renewing := true
	var localPath string
	tf, exists := r.persist.Tracking[file.name]
	if exists {
		localPath = tf.RepairPath
	}
	fileInfo = modules.FileInfo{
		SiaPath:        file.name,
		LocalPath:      localPath,
		Filesize:       file.size,
		Renewing:       renewing,
		Available:      file.available(offline),
		Redundancy:     file.redundancy(offline, goodForRenew),
		UploadedBytes:  file.uploadedBytes(),
		UploadProgress: file.uploadProgress(),
		Expiration:     file.expiration(),
	}
	return fileInfo, nil
}