Files
NanoKVM-MIRROR/server/service/application/progress_writer.go
肆月 e5f6dfabaa fix(ota): isolate updates in persistent workspaces (#863)
* fix(ota): isolate updates in persistent workspaces

Stage online and offline update archives under /root/.kvmcache/nanokvm-update-* and validate storage, manifests, and archive contents before changing the installed application.

* fix(ota): harden storage safety and release gates

Preserve the last rollback backup when update storage is insufficient, and verify the actual application mount point before installation.

Move the shared transfer sentinel from /tmp to /run, enforce device package limits in release verification, and run that verification in package CI.
2026-08-10 14:49:29 +08:00

87 lines
1.6 KiB
Go

package application
import (
"fmt"
"io"
"os"
"strings"
"time"
"NanoKVM-Server/utils"
log "github.com/sirupsen/logrus"
)
const (
sentinelPath = utils.TransferSentinelPath
tickerInterval = 2500 * time.Millisecond
sentinelPermission = 0644
)
// progressWriter wraps an io.Writer to track and report upload progress.
type progressWriter struct {
writer io.Writer
written int64
totalSize int64
ticker *time.Ticker
done chan struct{}
}
func newProgressWriter(w io.Writer, totalSize int64) *progressWriter {
pw := &progressWriter{
writer: w,
totalSize: totalSize,
ticker: time.NewTicker(tickerInterval),
done: make(chan struct{}),
}
go pw.reportProgress()
return pw
}
func (pw *progressWriter) reportProgress() {
for {
select {
case <-pw.done:
return
case <-pw.ticker.C:
pw.updateSentinel()
}
}
}
func (pw *progressWriter) updateSentinel() {
if pw.totalSize == 0 {
return
}
content, err := os.ReadFile(sentinelPath)
if err != nil {
log.Error("Failed to read sentinel file")
return
}
parts := strings.SplitN(string(content), ";", 2)
if len(parts) == 0 {
return
}
percentage := float64(pw.written) / float64(pw.totalSize) * 100
newContent := fmt.Sprintf("%s;%.2f%%", parts[0], percentage)
if err := os.WriteFile(sentinelPath, []byte(newContent), sentinelPermission); err != nil {
log.Error("Failed to update sentinel file")
}
}
func (pw *progressWriter) Write(p []byte) (int, error) {
n, err := pw.writer.Write(p)
pw.written += int64(n)
return n, err
}
func (pw *progressWriter) Stop() {
pw.ticker.Stop()
close(pw.done)
}