Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ifneq ($(DEB_BUILD_ARCH), mips64el)
endif

%:
dh $@
dh $@ --buildsystem=makefile

override_dh_install:
dh_install --sourcedir=debian/tmp
Expand Down
28 changes: 0 additions & 28 deletions src/internal/system/apt/apt.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"bytes"
"os"
"os/exec"
"strconv"
"strings"
"syscall"

Expand Down Expand Up @@ -288,30 +287,3 @@ func DownloadPackages(packages []string, environ map[string]string, options map[
}
return tmpPath, nil
}

// In incremental update mode, returns true if all packages are cached, otherwise returns false.
func IsIncrementalUpdateCached(sourceArgs string) bool {
cmd := exec.Command("/usr/sbin/deepin-immutable-ctl", "upgrade", "check")
if sourceArgs != "" {
cmd.Env = append(os.Environ(), "DEEPIN_IMMUTABLE_UPGRADE_APT_OPTION="+sourceArgs)
}
// Need download count: xxx
output, err := cmd.Output()
if err == nil {
matchFlag := "Need download count: "
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
index := strings.Index(line, matchFlag)
if index >= 0 {
count, err := strconv.Atoi(strings.TrimSpace(line[index+len(matchFlag):]))
if err == nil {
if count == 0 {
return true
}
}
}
}
}
return false
}
1 change: 1 addition & 0 deletions src/internal/system/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
const VarLibDir = "/var/lib/lastore"

var IntranetUpdate bool
var IncrementalUpdate bool

type Status string

Expand Down
145 changes: 112 additions & 33 deletions src/internal/system/system_apt.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package system

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -200,8 +201,107 @@ func QueryPackageDownloadSize(updateType UpdateType, packages ...string) (float6
return *downloadSize, *allPackageSize, nil
}

// QuerySourceDownloadSize 根据更新类型(仓库),获取需要的下载量,return arg0:需要下载的量;arg1:所有包的大小;arg2:error
// queryDownloadSizeViaApt queries the download size information by running apt-get dist-upgrade
// with the given source path. It parses the command output to extract package size data.
// Returns: bytes needed to download, total size of all packages, error.
func queryDownloadSizeViaApt(path string, updateType UpdateType, pkgList []string) (float64, float64, error) {
var cmd *exec.Cmd
if utils2.IsDir(path) {
// #nosec G204
cmd = exec.Command("/usr/bin/apt-get",
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", "/dev/null"),
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", path)}, pkgList...)...)
} else {
// #nosec G204
cmd = exec.Command("/usr/bin/apt-get",
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", path),
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", "/dev/null")}, pkgList...)...)
}
cmd.Env = append(os.Environ(), "LC_ALL=C")
logger.Infof("%v download size cmd: %v", updateType.JobType(), cmd.String())
lines, err := utils.FilterExecOutput(cmd, time.Second*120, func(line string) bool {
_, _, _err := parsePackageSize(line)
return _err == nil
})
if err != nil && len(lines) == 0 {
return 0, 0, fmt.Errorf("run:%v failed-->%v", cmd.Args, err)
}
if len(lines) != 0 {
needDownloadSize, allSize, err := parsePackageSize(lines[0])
if err != nil {
logger.Warning(err)
return 0, 0, err
}
return needDownloadSize, allSize, nil
}
return 0, 0, nil
}

// immutableUpgradeCheckOutput represents the JSON output of deepin-immutable-ctl upgrade check -j.
type immutableUpgradeCheckOutput struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
TotalSize uint64 `json:"totalSize"`
CachedOstreePkgCount int `json:"cachedOstreePkgCount"`
NeedDownload struct {
Size uint64 `json:"size"`
} `json:"needDownload"`
} `json:"data"`
}

// queryDownloadSizeForIncrementalUpdate queries the download size information for incremental updates
// by invoking deepin-immutable-ctl upgrade check. The source path is passed via the
// DEEPIN_IMMUTABLE_UPGRADE_APT_OPTION environment variable.
// Returns: bytes needed to download, total size of all packages, error.
func queryDownloadSizeForIncrementalUpdate(path string, updateType UpdateType, pkgList []string) (float64, float64, error) {
var sourceArgs string
modeStr := updateType.JobType()
if modeStr == "" {
modeStr = fmt.Sprintf("mode-%d", updateType)
}
cmd := exec.Command(DeepinImmutableCtlPath, "upgrade", "check", "-j", "-m", modeStr)
if path != "" {
if utils2.IsDir(path) {
sourceArgs = "-o Dir::Etc::sourcelist=/dev/null -o Dir::Etc::SourceParts=" + path
} else {
sourceArgs = "-o Dir::Etc::sourcelist=" + path + " -o Dir::Etc::SourceParts=/dev/null"
}
}
cmd.Env = append(os.Environ(), "LC_ALL=C",
"DEEPIN_IMMUTABLE_UPGRADE_APT_OPTION="+sourceArgs)
logger.Infof("%v download size cmd: %v, sourceArgs: %v", updateType.JobType(), cmd.String(), sourceArgs)
out, err := cmd.Output()
logger.Debugf("immutable upgrade check output: %s", out)
if err != nil {
return 0, 0, fmt.Errorf("run %v failed: %v", cmd.Args, err)
}
var result immutableUpgradeCheckOutput
if err := json.Unmarshal(out, &result); err != nil {
return 0, 0, fmt.Errorf("parse immutable upgrade check output failed: %v", err)
}
logger.Debugf("immutable upgrade check result: %+v", result)
if result.Code != 0 {
return 0, 0, fmt.Errorf("immutable upgrade check returned code %d: %s", result.Code, result.Message)
}
needDownload := result.Data.NeedDownload.Size
totalSize := result.Data.TotalSize
// If both needDownload and totalSize are 0 but cached ostree packages exist, the ostree
// transfer stats have likely been discarded. Return a non-zero totalSize (1) as a
// sentinel to prevent the control center upgrade UI from stalling on a zero-size result.
if needDownload == 0 && totalSize == 0 && result.Data.CachedOstreePkgCount > 0 {
return 0, 1, nil
}
return float64(needDownload), float64(totalSize), nil
}

// QuerySourceDownloadSize returns the download size information for the given update type.
// It selects the appropriate backend (apt or immutable-ctl) based on the IncrementalUpdate flag.
// Returns: bytes needed to download, total size of all packages, error.
func QuerySourceDownloadSize(updateType UpdateType, pkgList []string) (float64, float64, error) {
logger.Debugf("QuerySourceDownloadSize updateType: %v, pkgList: %v", updateType, pkgList)
startTime := time.Now()
downloadSize := new(float64)
allPackageSize := new(float64)
Expand All @@ -211,46 +311,25 @@ func QuerySourceDownloadSize(updateType UpdateType, pkgList []string) (float64,
unref()
}
}()
var cmd *exec.Cmd
if utils2.IsDir(path) {
// #nosec G204
cmd = exec.Command("/usr/bin/apt-get",
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", "/dev/null"),
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", path)}, pkgList...)...)
} else {
// #nosec G204
cmd = exec.Command("/usr/bin/apt-get",
append([]string{"dist-upgrade", "-d", "-o", "Debug::NoLocking=1", "-c", LastoreAptV2CommonConfPath, "--assume-no",
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::sourcelist", path),
"-o", fmt.Sprintf("%v=%v", "Dir::Etc::SourceParts", "/dev/null")}, pkgList...)...)
}
cmd.Env = append(os.Environ(), "LC_ALL=C")
logger.Infof("%v download size cmd: %v", updateType.JobType(), cmd.String())
lines, err := utils.FilterExecOutput(cmd, time.Second*120, func(line string) bool {
_, _, _err := parsePackageSize(line)
return _err == nil
})
if err != nil && len(lines) == 0 {
return fmt.Errorf("run:%v failed-->%v", cmd.Args, err)
queryFn := queryDownloadSizeViaApt
if IncrementalUpdate {
queryFn = queryDownloadSizeForIncrementalUpdate
}

if len(lines) != 0 {
needDownloadSize, allSize, err := parsePackageSize(lines[0])
if err != nil {
logger.Warning(err)
return err
}
*downloadSize = needDownloadSize
*allPackageSize = allSize
needDownloadSize, allSize, err := queryFn(path, updateType, pkgList)
if err != nil {
return err
}
*downloadSize = needDownloadSize
*allPackageSize = allSize
return nil
})
if err != nil {
logger.Warning(err)
return SizeDownloaded, SizeDownloaded, err
}
logger.Debug("end QuerySourceDownloadSize duration:", time.Now().Sub(startTime))
logger.Debugf("QuerySourceDownloadSize result, download size: %s, all package size: %s",
utils.FormatSize(*downloadSize), utils.FormatSize(*allPackageSize))
return *downloadSize, *allPackageSize, nil
}

Expand Down Expand Up @@ -329,7 +408,7 @@ func QuerySourceAddSize(updateType UpdateType) (float64, error) {
logger.Warning(err)
return SizeUnknown, err
}
logger.Debug("end QuerySourceDownloadSize duration:", time.Now().Sub(startTime))
logger.Debug("end QuerySourceAddSize duration:", time.Now().Sub(startTime))
return *addSize, nil
}

Expand Down
22 changes: 22 additions & 0 deletions src/internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,25 @@ func doUnsetEnvC(envName string) {
defer C.free(unsafe.Pointer(cname))
C.unsetenv(cname)
}

// FormatSize formats bytes to human readable format (e.g., 1.5 MiB, 2.3 GiB)
// Uses binary units (1 KiB = 1024 B)
func FormatSize(bytes float64) string {
if bytes < 0 {
return "unknown"
}

units := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
unitIndex := 0
size := bytes

for size >= 1024 && unitIndex < len(units)-1 {
size /= 1024
unitIndex++
}

if unitIndex == 0 {
return fmt.Sprintf("%.0f %s", size, units[unitIndex])
}
return fmt.Sprintf("%.2f %s", size, units[unitIndex])
}
62 changes: 62 additions & 0 deletions src/internal/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,65 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package utils

import (
"testing"
)

func TestFormatSize(t *testing.T) {
tests := []struct {
name string
bytes float64
expected string
}{
// 字节 (B)
{"zero bytes", 0, "0 B"},
{"1 byte", 1, "1 B"},
{"500 bytes", 500, "500 B"},
{"999 bytes", 999, "999 B"},

// KiB (1024 bytes)
{"1 KiB", 1024, "1.00 KiB"},
{"1.5 KiB", 1536, "1.50 KiB"},
{"10 KiB", 10240, "10.00 KiB"},
{"100 KiB", 102400, "100.00 KiB"},
{"1023.99 KiB", 1048575, "1024.00 KiB"},

// MiB (1024 KiB = 1,048,576 bytes)
{"1 MiB", 1048576, "1.00 MiB"},
{"1.5 MiB", 1572864, "1.50 MiB"},
{"15.03 MiB", 15754620, "15.02 MiB"},
{"100 MiB", 104857600, "100.00 MiB"},
{"999.99 MiB", 1048575000, "1000.00 MiB"},

// GiB (1024 MiB = 1,073,741,824 bytes)
{"1 GiB", 1073741824, "1.00 GiB"},
{"1.5 GiB", 1610612736, "1.50 GiB"},
{"2.3 GiB", 2469606195, "2.30 GiB"},
{"10 GiB", 10737418240, "10.00 GiB"},
{"100 GiB", 107374182400, "100.00 GiB"},

// TiB (1024 GiB)
{"1 TiB", 1099511627776, "1.00 TiB"},
{"2 TiB", 2199023255552, "2.00 TiB"},

// 边界值测试
{"boundary 1023 bytes", 1023, "1023 B"},
{"boundary 1024 bytes", 1024, "1.00 KiB"},
{"boundary 1048575 bytes", 1048575, "1024.00 KiB"},
{"boundary 1048576 bytes", 1048576, "1.00 MiB"},

// 负数处理
{"negative value", -1, "unknown"},
{"negative large", -1000, "unknown"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := FormatSize(tt.bytes)
if result != tt.expected {
t.Errorf("FormatSize(%v) = %v, want %v", tt.bytes, result, tt.expected)
}
})
}
}
2 changes: 2 additions & 0 deletions src/lastore-daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func main() {
config := NewConfig(path.Join(system.VarLibDir, "config.json"))
logger.Info("intranet update:", config.IntranetUpdate)
system.IntranetUpdate = config.IntranetUpdate
logger.Info("incremental update:", config.IncrementalUpdate)
system.IncrementalUpdate = config.IncrementalUpdate
if config.IntranetUpdate {
go func() {
out, err := exec.Command("/usr/bin/lastore-tools", "gatherinfo", "-type=post").CombinedOutput()
Expand Down
10 changes: 3 additions & 7 deletions src/lastore-daemon/manager_ifc.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
utils2 "github.com/linuxdeepin/go-lib/utils"
"github.com/linuxdeepin/lastore-daemon/src/internal/config"
"github.com/linuxdeepin/lastore-daemon/src/internal/system"
"github.com/linuxdeepin/lastore-daemon/src/internal/system/apt"
"github.com/linuxdeepin/lastore-daemon/src/internal/utils"
)

/*
Expand Down Expand Up @@ -214,10 +214,6 @@ func (m *Manager) PackagesDownloadSize(packages []string) (int64, *dbus.Error) {
logger.Warningf("PackagesDownloadSize(%q)=%0.2f %v\n", strings.Join(packages, " "), size, err)
}

if m.config.IncrementalUpdate && size > 0 && apt.IsIncrementalUpdateCached("") {
size = 0.0
}

return int64(size), dbusutil.ToError(err)
}

Expand Down Expand Up @@ -485,9 +481,9 @@ func (m *Manager) QueryAllSizeWithSource(mode system.UpdateType) (int64, *dbus.E
}
_, allSize, err := system.QuerySourceDownloadSize(mode, pkgList)
if err != nil || allSize == system.SizeUnknown {
logger.Warningf("failed to get %v source size:%v", strings.Join(sourcePathList, " and "), err)
logger.Warningf("failed to query %v all size: %v", strings.Join(sourcePathList, " and "), err)
} else {
logger.Infof("%v size is:%v M", strings.Join(sourcePathList, " and "), int64(allSize/(1000*1000)))
logger.Infof("%v all size: %s", strings.Join(sourcePathList, " and "), utils.FormatSize(allSize))
}

return int64(allSize), dbusutil.ToError(err)
Expand Down
Loading
Loading