Skip to content
This repository was archived by the owner on Aug 7, 2025. It is now read-only.

Commit bb3b934

Browse files
author
William Douglas
committed
Improve manifest include subtraction
Instead of giving up on directory subtraction entirely, try to subtract directories that are in an included manifest *and* do not have a non-directory file somewhere in their path. For example: bundle1: /dir/file bundle2: includes bundle1 /dir In this case /dir would be subtracted from bundle2. Also add a special case for subtracting directories if they are in os-core as this reduces a lot of unused content from a bundle's manifest. Signed-off-by: William Douglas <[email protected]>
1 parent 0327999 commit bb3b934

File tree

2 files changed

+125
-11
lines changed

2 files changed

+125
-11
lines changed

swupd/manifest.go

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"os"
2323
"path/filepath"
2424
"runtime"
25+
"slices"
2526
"sort"
2627
"strconv"
2728
"strings"
@@ -757,20 +758,58 @@ func (m *Manifest) addManifestFiles(ui UpdateInfo, c config) error {
757758
// Add files to manifest that do not exist in included bundles.
758759
chrootDir := filepath.Join(c.imageBase, fmt.Sprint(ui.version), "full")
759760
includes := m.GetRecursiveIncludes()
761+
usedDirs := make(map[string]struct{})
762+
bundleDirs := []string{}
763+
bundleFiles := []string{}
760764
for f := range m.BundleInfo.Files {
765+
fullPath := filepath.Join(chrootDir, f)
766+
if fi, err := os.Lstat(fullPath); err == nil {
767+
if !fi.IsDir() {
768+
usedDirs[filepath.Dir(f)] = struct{}{}
769+
bundleFiles = append(bundleFiles, f)
770+
} else {
771+
bundleDirs = append(bundleDirs, f)
772+
}
773+
}
774+
}
775+
for _, f := range bundleFiles {
761776
isIncluded := false
762777
for _, inc := range includes {
763778
// Handle cycles
764779
if inc.Name == m.Name {
765780
continue
766781
}
767-
fullPath := filepath.Join(chrootDir, f)
768-
if fi, err := os.Lstat(fullPath); err == nil {
769-
if !fi.IsDir() {
770-
if _, ok := inc.BundleInfo.Files[f]; ok {
771-
isIncluded = true
772-
break
773-
}
782+
if _, ok := inc.BundleInfo.Files[f]; ok {
783+
isIncluded = true
784+
break
785+
}
786+
}
787+
if !isIncluded {
788+
if err := m.addFile(f, c, ui.version); err != nil {
789+
return err
790+
}
791+
}
792+
}
793+
// Directories are sorted into reverse order to allow usedDirs
794+
// to populate correctly (/a/b/c will be seen before /a/b which
795+
// will allow /a to be set when /a/b is seen)
796+
slices.Sort(bundleDirs)
797+
slices.Reverse(bundleDirs)
798+
for _, f := range bundleDirs {
799+
if _, ok := usedDirs[f]; ok {
800+
usedDirs[filepath.Dir(f)] = struct{}{}
801+
}
802+
}
803+
for _, f := range bundleDirs {
804+
isIncluded := false
805+
for _, inc := range includes {
806+
if _, ok := inc.BundleInfo.Files[f]; ok {
807+
if inc.Name == "os-core" {
808+
isIncluded = true
809+
break
810+
} else if _, ok := usedDirs[f]; !ok {
811+
isIncluded = true
812+
break
774813
}
775814
}
776815
}

swupd/swupd_test.go

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func TestFullRun(t *testing.T) {
9999
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle"), ts.path("www/10/pack-test-bundle-from-0.tar"))
100100
mustHaveDeltaCount(t, infoTestBundle, 0)
101101
// Empty file (bundle file), "foo".
102-
mustHaveFullfileCount(t, infoTestBundle, 3)
102+
mustHaveFullfileCount(t, infoTestBundle, 2)
103103

104104
testBundle := ts.parseManifest(10, "test-bundle")
105105
checkIncludes(t, testBundle, "os-core")
@@ -112,7 +112,6 @@ func TestFullRun(t *testing.T) {
112112
checkFileInManifest(t, osCore, 10, "/usr/share")
113113
checkFileInManifest(t, osCore, 10, "/usr/share/clear")
114114
checkFileInManifest(t, osCore, 10, "/usr/share/clear/bundles")
115-
checkFileInManifest(t, osCore, 10, "/usr")
116115
}
117116

118117
// Imported from swupd-server/test/functional/full-run-delta.
@@ -137,7 +136,7 @@ func TestFullRunDelta(t *testing.T) {
137136

138137
ts.createPack("os-core", 0, 10, ts.path("image"))
139138
info := ts.createPack("test-bundle", 0, 10, ts.path("image"))
140-
mustHaveFullfileCount(t, info, 5) // largefile, foo and foobarbaz and the test-bundle file.
139+
mustHaveFullfileCount(t, info, 4) // largefile, foo and foobarbaz and the test-bundle file.
141140

142141
mustValidateZeroPack(t, ts.path("www/10/Manifest.os-core"), ts.path("www/10/pack-os-core-from-0.tar"))
143142
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle"), ts.path("www/10/pack-test-bundle-from-0.tar"))
@@ -164,7 +163,7 @@ func TestFullRunDelta(t *testing.T) {
164163

165164
ts.createPack("os-core", 0, 20, ts.path("image"))
166165
info = ts.createPack("test-bundle", 0, 20, ts.path("image"))
167-
mustHaveFullfileCount(t, info, 3) // largefile and the test-bundle file.
166+
mustHaveFullfileCount(t, info, 2) // largefile and the test-bundle file.
168167

169168
mustValidateZeroPack(t, ts.path("www/20/Manifest.os-core"), ts.path("www/20/pack-os-core-from-0.tar"))
170169
mustValidateZeroPack(t, ts.path("www/20/Manifest.test-bundle"), ts.path("www/20/pack-test-bundle-from-0.tar"))
@@ -191,6 +190,82 @@ func TestFullRunDelta(t *testing.T) {
191190
// not done by new swupd since it seems the client doesn't take advantage of them.
192191
}
193192

193+
// Test an import cycle for includes subtraction
194+
func TestRunBundleCycle(t *testing.T) {
195+
ts := newTestSwupd(t, "bundle-cycle-")
196+
defer ts.cleanup()
197+
198+
// Version 10.
199+
ts.Bundles = []string{"os-core", "test-bundle1", "test-bundle2"}
200+
201+
ts.mkdir("image/10/os-core/a")
202+
ts.write("image/10/test-bundle1/a/b/t1", "t1")
203+
ts.write("image/10/test-bundle2/a/b/t2", "t2")
204+
ts.write("image/10/noship/test-bundle1-includes", "test-bundle2")
205+
ts.write("image/10/noship/test-bundle2-includes", "test-bundle1")
206+
207+
ts.createManifestsFromChroots(10)
208+
ts.createFullfiles(10)
209+
210+
ts.createPack("os-core", 0, 10, ts.path("image"))
211+
info1 := ts.createPack("test-bundle1", 0, 10, ts.path("image"))
212+
info2 := ts.createPack("test-bundle2", 0, 10, ts.path("image"))
213+
mustHaveFullfileCount(t, info1, 3) // b dir, t1 file and the test-bundle1 file.
214+
mustHaveFullfileCount(t, info2, 3) // b dir, t2 file and the test-bundle2 file.
215+
216+
mustValidateZeroPack(t, ts.path("www/10/Manifest.os-core"), ts.path("www/10/pack-os-core-from-0.tar"))
217+
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle1"), ts.path("www/10/pack-test-bundle1-from-0.tar"))
218+
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle2"), ts.path("www/10/pack-test-bundle2-from-0.tar"))
219+
220+
osCore := ts.parseManifest(10, "os-core")
221+
testBundle1 := ts.parseManifest(10, "test-bundle1")
222+
testBundle2 := ts.parseManifest(10, "test-bundle2")
223+
checkIncludes(t, testBundle1, "os-core", "test-bundle2")
224+
checkIncludes(t, testBundle2, "os-core", "test-bundle1")
225+
checkFileInManifest(t, osCore, 10, "/a")
226+
checkFileInManifest(t, testBundle1, 10, "/a/b")
227+
checkFileInManifest(t, testBundle1, 10, "/a/b/t1")
228+
checkFileInManifest(t, testBundle2, 10, "/a/b")
229+
checkFileInManifest(t, testBundle2, 10, "/a/b/t2")
230+
}
231+
232+
// Test an import without cycle to see directory subtraction outside os-core
233+
func TestRunBundleIncludes(t *testing.T) {
234+
ts := newTestSwupd(t, "bundle-includes-")
235+
defer ts.cleanup()
236+
237+
// Version 10.
238+
ts.Bundles = []string{"os-core", "test-bundle1", "test-bundle2"}
239+
240+
ts.write("image/10/test-bundle1/a/t1", "t1")
241+
ts.write("image/10/test-bundle2/t2", "t2")
242+
// this would have subtracted with a cycle too but
243+
// keeping this test separate in case directory subtraction
244+
// improves in the case of cycles.
245+
ts.mkdir("image/10/test-bundle2/a")
246+
ts.write("image/10/noship/test-bundle2-includes", "test-bundle1")
247+
248+
ts.createManifestsFromChroots(10)
249+
ts.createFullfiles(10)
250+
251+
ts.createPack("os-core", 0, 10, ts.path("image"))
252+
info1 := ts.createPack("test-bundle1", 0, 10, ts.path("image"))
253+
info2 := ts.createPack("test-bundle2", 0, 10, ts.path("image"))
254+
mustHaveFullfileCount(t, info1, 3) // a dir, t1 file and the test-bundle1 file.
255+
mustHaveFullfileCount(t, info2, 2) // t2 file and the test-bundle2 file.
256+
257+
mustValidateZeroPack(t, ts.path("www/10/Manifest.os-core"), ts.path("www/10/pack-os-core-from-0.tar"))
258+
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle1"), ts.path("www/10/pack-test-bundle1-from-0.tar"))
259+
mustValidateZeroPack(t, ts.path("www/10/Manifest.test-bundle2"), ts.path("www/10/pack-test-bundle2-from-0.tar"))
260+
261+
testBundle1 := ts.parseManifest(10, "test-bundle1")
262+
testBundle2 := ts.parseManifest(10, "test-bundle2")
263+
checkIncludes(t, testBundle2, "os-core", "test-bundle1")
264+
checkFileInManifest(t, testBundle1, 10, "/a")
265+
checkFileInManifest(t, testBundle1, 10, "/a/t1")
266+
checkFileInManifest(t, testBundle2, 10, "/t2")
267+
}
268+
194269
func TestAddFilesToBundleInfo(t *testing.T) {
195270
ts := newTestSwupd(t, "extra-files")
196271
defer ts.cleanup()

0 commit comments

Comments
 (0)