mirror of
https://github.com/clearlinux/swupd-server.git
synced 2026-10-04 16:08:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e554a06ea6 | ||
|
|
599ce7cb77 | ||
|
|
b8c5bdd2f3 | ||
|
|
14cef226d1 | ||
|
|
83a9c39556 | ||
|
|
b0c4a1c24e | ||
|
|
302ab343ca | ||
|
|
073b1fe062 | ||
|
|
47addb4fa4 | ||
|
|
557fb493ca | ||
|
|
ded4f3003d | ||
|
|
cb9fe9f208 | ||
|
|
5ecb58edff | ||
|
|
e3ca1cc566 | ||
|
|
330bede498 | ||
|
|
0fe32ce8fb | ||
|
|
31aec4684f | ||
|
|
1fa24df6d6 | ||
|
|
e84600c21d | ||
|
|
350bb54fdf | ||
|
|
15e6b1466f | ||
|
|
f9ec967aa2 | ||
|
|
925a5203f4 | ||
|
|
31bb949b9b | ||
|
|
a0c7025a9b | ||
|
|
06320f2862 | ||
|
|
4f84b66321 | ||
|
|
0d352881e7 | ||
|
|
7eff79397c | ||
|
|
37f84501eb | ||
|
|
850e6b9a92 | ||
|
|
ce594a6529 | ||
|
|
cfb0406b2e | ||
|
|
3a46cb6a9d | ||
|
|
15c045de8d |
+4
-4
@@ -11,9 +11,9 @@ before_install:
|
|||||||
- sudo apt-get install -y libmagic-dev
|
- sudo apt-get install -y libmagic-dev
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz
|
- wget https://github.com/libcheck/check/releases/download/0.11.0/check-0.11.0.tar.gz
|
||||||
- tar -xvf check-0.10.0.tar.gz
|
- tar -xvf check-0.11.0.tar.gz
|
||||||
- pushd check-0.10.0 && ./configure --prefix=/usr && make -j48 && sudo make install && popd
|
- pushd check-0.11.0 && ./configure --prefix=/usr && make -j48 && sudo make install && popd
|
||||||
- wget https://github.com/clearlinux/bsdiff/releases/download/v1.0.2/bsdiff-1.0.2.tar.xz
|
- wget https://github.com/clearlinux/bsdiff/releases/download/v1.0.2/bsdiff-1.0.2.tar.xz
|
||||||
- tar -xvf bsdiff-1.0.2.tar.xz
|
- tar -xvf bsdiff-1.0.2.tar.xz
|
||||||
- pushd bsdiff-1.0.2 && ./configure --prefix=/usr --disable-tests && make -j48 && sudo make install && popd
|
- pushd bsdiff-1.0.2 && ./configure --prefix=/usr --disable-tests && make -j48 && sudo make install && popd
|
||||||
@@ -21,5 +21,5 @@ install:
|
|||||||
# Ubuntu's default umask is 0002, but this break's swupd hash calculations.
|
# Ubuntu's default umask is 0002, but this break's swupd hash calculations.
|
||||||
script:
|
script:
|
||||||
- sudo find test/functional -exec chmod g-w {} \;
|
- sudo find test/functional -exec chmod g-w {} \;
|
||||||
- autoreconf --verbose --warnings=none --install --force && ./configure && make -j48 && sudo sh -c 'umask 0022 && make -j48 check'
|
- autoreconf --verbose --warnings=none --install --force && ./configure --enable-rename-detection && make -j48 && sudo sh -c 'umask 0022 && make -j48 check'
|
||||||
after_failure: cat test-suite.log
|
after_failure: cat test-suite.log
|
||||||
|
|||||||
+13
-1
@@ -108,17 +108,29 @@ TESTS = $(dist_check_SCRIPTS)
|
|||||||
|
|
||||||
dist_check_SCRIPTS = \
|
dist_check_SCRIPTS = \
|
||||||
test/functional/basic/test.bats \
|
test/functional/basic/test.bats \
|
||||||
|
test/functional/contentsize-across-versions-includes/test.bats \
|
||||||
test/functional/delete-no-version-bump/test.bats \
|
test/functional/delete-no-version-bump/test.bats \
|
||||||
test/functional/file-name-blacklisted/test.bats \
|
test/functional/file-name-blacklisted/test.bats \
|
||||||
|
test/functional/file-name-debuginfo/test.bats \
|
||||||
|
test/functional/format-no-decrement/test.bats \
|
||||||
test/functional/full-run-delta/test.bats \
|
test/functional/full-run-delta/test.bats \
|
||||||
test/functional/full-run/test.bats \
|
test/functional/full-run/test.bats \
|
||||||
test/functional/fullfiles/test.bats \
|
test/functional/fullfiles/test.bats \
|
||||||
|
test/functional/ghosting/test.bats \
|
||||||
test/functional/include-version-bump/test.bats \
|
test/functional/include-version-bump/test.bats \
|
||||||
|
test/functional/includes-deduplicate/test.bats \
|
||||||
test/functional/no-delta/test.bats \
|
test/functional/no-delta/test.bats \
|
||||||
test/functional/pack/test.bats \
|
test/functional/pack/test.bats \
|
||||||
test/functional/state-file/test.bats \
|
test/functional/state-file/test.bats \
|
||||||
test/functional/subtract-delete/test.bats \
|
test/functional/subtract-delete/test.bats \
|
||||||
test/functional/update/test.bats
|
test/functional/update/test.bats \
|
||||||
|
test/functional/format-bump/test.bats
|
||||||
|
|
||||||
|
if RENAMES
|
||||||
|
dist_check_SCRIPTS += \
|
||||||
|
test/functional/orphaned-renames/test.bats \
|
||||||
|
test/functional/renames/test.bats
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if COVERAGE
|
if COVERAGE
|
||||||
|
|||||||
+6
-1
@@ -2,7 +2,7 @@
|
|||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
AC_PREREQ([2.66])
|
AC_PREREQ([2.66])
|
||||||
AC_INIT(swupd-server, 3.3.4, tudor.marcu@intel.com)
|
AC_INIT(swupd-server, 3.7.0, matthew.johnson@intel.com)
|
||||||
AM_INIT_AUTOMAKE([foreign -Wall -W subdir-objects])
|
AM_INIT_AUTOMAKE([foreign -Wall -W subdir-objects])
|
||||||
AM_SILENT_RULES([yes])
|
AM_SILENT_RULES([yes])
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
@@ -82,6 +82,11 @@ AS_IF([test "$enable_lzma" != "no"], [
|
|||||||
])
|
])
|
||||||
AM_CONDITIONAL([ENABLE_LZMA], [test "$enable_lzma" != "no"])
|
AM_CONDITIONAL([ENABLE_LZMA], [test "$enable_lzma" != "no"])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([rename-detection], [AS_HELP_STRING([--enable-rename-detection], [enable rename detection feature])])
|
||||||
|
|
||||||
|
AS_IF([test "$enable_rename_detection" = "yes"], [AC_DEFINE(RENAMES,1,[Use rename detection])])
|
||||||
|
AM_CONDITIONAL([RENAMES], [test "$enable_rename_detection" = "yes"])
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_CONFIG_FILES([Makefile])
|
||||||
AC_REQUIRE_AUX_FILE([tap-driver.sh])
|
AC_REQUIRE_AUX_FILE([tap-driver.sh])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|||||||
+31
-10
@@ -65,6 +65,13 @@
|
|||||||
#include <lzma.h>
|
#include <lzma.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Approximatly the smallest size of a pair of input files which
|
||||||
|
* differ by a single bit that bsdiff can produce a more compact
|
||||||
|
* deltafile. Files smaller than this are always marked as different.
|
||||||
|
* See the magic 200 value in the bsdiff/src/diff.c code.
|
||||||
|
*/
|
||||||
|
#define BSDIFFSIZE 200
|
||||||
|
|
||||||
struct manifest {
|
struct manifest {
|
||||||
unsigned long long int format;
|
unsigned long long int format;
|
||||||
int version;
|
int version;
|
||||||
@@ -78,6 +85,8 @@ struct manifest {
|
|||||||
GList *submanifests; /* as struct manifest */
|
GList *submanifests; /* as struct manifest */
|
||||||
|
|
||||||
GList *includes; /* struct manifests for all bundles included into this one */
|
GList *includes; /* struct manifests for all bundles included into this one */
|
||||||
|
|
||||||
|
GList *actions; /* post-update actions */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct file;
|
struct file;
|
||||||
@@ -108,6 +117,10 @@ struct file {
|
|||||||
unsigned int is_file : 1;
|
unsigned int is_file : 1;
|
||||||
unsigned int is_link : 1;
|
unsigned int is_link : 1;
|
||||||
unsigned int is_deleted : 1;
|
unsigned int is_deleted : 1;
|
||||||
|
/* a ghosted file is treated as deleted so that it can be used as a rename,
|
||||||
|
* but is not actually deleted in the client due to a 3rd-party program
|
||||||
|
* cleaning it up. This happens with boot files managed by a boot manager */
|
||||||
|
unsigned int is_ghosted : 1;
|
||||||
unsigned int is_manifest : 1;
|
unsigned int is_manifest : 1;
|
||||||
|
|
||||||
/* and these are modifiers */
|
/* and these are modifiers */
|
||||||
@@ -122,7 +135,7 @@ struct file {
|
|||||||
double rename_score;
|
double rename_score;
|
||||||
struct file *rename_peer;
|
struct file *rename_peer;
|
||||||
char *alpha_only_filename; /* filename minus all numerics/etc */
|
char *alpha_only_filename; /* filename minus all numerics/etc */
|
||||||
char *filetype;
|
char *filetype; /* The output of 'file', truncated */
|
||||||
char *basename;
|
char *basename;
|
||||||
char *dirname;
|
char *dirname;
|
||||||
/* end of rename detection fields */
|
/* end of rename detection fields */
|
||||||
@@ -166,7 +179,9 @@ extern void release_configuration_data(void);
|
|||||||
extern char *config_image_base(void);
|
extern char *config_image_base(void);
|
||||||
extern char *config_output_dir(void);
|
extern char *config_output_dir(void);
|
||||||
extern char *config_empty_dir(void);
|
extern char *config_empty_dir(void);
|
||||||
|
extern char *config_debuginfo_path(const char *path);
|
||||||
extern int config_initial_version(void);
|
extern int config_initial_version(void);
|
||||||
|
extern bool config_ban_debuginfo(void);
|
||||||
|
|
||||||
extern void read_current_version(char *filename);
|
extern void read_current_version(char *filename);
|
||||||
extern void write_new_version(char *filename, int version);
|
extern void write_new_version(char *filename, int version);
|
||||||
@@ -177,13 +192,13 @@ extern GList *get_last_versions_list(int next_version, int max_versions);
|
|||||||
extern char *file_type_to_string(struct file *file);
|
extern char *file_type_to_string(struct file *file);
|
||||||
extern struct manifest *manifest_from_file(int version, char *module);
|
extern struct manifest *manifest_from_file(int version, char *module);
|
||||||
extern void free_manifest(struct manifest *manifest);
|
extern void free_manifest(struct manifest *manifest);
|
||||||
extern struct manifest *alloc_manifest(int version, char *module);
|
extern struct manifest *alloc_manifest(int version, char *module, GList *actions);
|
||||||
extern int match_manifests(struct manifest *m1, struct manifest *m2);
|
extern int match_manifests(struct manifest *m1, struct manifest *m2);
|
||||||
extern void sort_manifest_by_version(struct manifest *manifest);
|
extern void sort_manifest_by_version(struct manifest *manifest);
|
||||||
extern bool manifest_includes(struct manifest *manifest, char *component);
|
extern bool manifest_includes(struct manifest *manifest, char *component);
|
||||||
extern bool changed_includes(struct manifest *old, struct manifest *new);
|
extern bool changed_includes(struct manifest *old, struct manifest *new);
|
||||||
extern int prune_manifest(struct manifest *manifest);
|
extern int prune_manifest(struct manifest *manifest);
|
||||||
extern int remove_old_deleted_files(struct manifest *m1, struct manifest *m2);
|
extern int remove_deprecated_files(struct manifest *m1, struct manifest *m2, bool (*compfunc)(struct file *, struct file *));
|
||||||
extern void create_manifest_delta(int oldversion, int newversion, char *module);
|
extern void create_manifest_delta(int oldversion, int newversion, char *module);
|
||||||
extern void create_manifest_deltas(struct manifest *manifest, GList *last_versions_list);
|
extern void create_manifest_deltas(struct manifest *manifest, GList *last_versions_list);
|
||||||
extern void subtract_manifests_frontend(struct manifest *m1, struct manifest *m2);
|
extern void subtract_manifests_frontend(struct manifest *m1, struct manifest *m2);
|
||||||
@@ -218,8 +233,6 @@ extern void chroot_create_full(int newversion);
|
|||||||
|
|
||||||
extern void read_group_file(char *filename);
|
extern void read_group_file(char *filename);
|
||||||
extern void release_group_file(void);
|
extern void release_group_file(void);
|
||||||
extern char *group_groups(char *group);
|
|
||||||
extern char *group_packages(char *group);
|
|
||||||
extern char *group_status(char *group);
|
extern char *group_status(char *group);
|
||||||
extern char *next_group(void);
|
extern char *next_group(void);
|
||||||
|
|
||||||
@@ -242,8 +255,8 @@ extern int previous_version_manifest(struct manifest *mom, char *name);
|
|||||||
|
|
||||||
extern void type_change_detection(struct manifest *manifest);
|
extern void type_change_detection(struct manifest *manifest);
|
||||||
|
|
||||||
extern void rename_detection(struct manifest *manifest, int last_change, GList *last_versions_list);
|
extern void rename_detection(struct manifest *manifest);
|
||||||
extern void link_renames(GList *newfiles, struct manifest *from_manifest);
|
extern void link_renames(GList *newfiles, int to_version);
|
||||||
extern void __create_delta(struct file *file, int from_version, char *from_hash);
|
extern void __create_delta(struct file *file, int from_version, char *from_hash);
|
||||||
|
|
||||||
extern void account_delta_hit(void);
|
extern void account_delta_hit(void);
|
||||||
@@ -253,10 +266,18 @@ extern FILE *fopen_exclusive(const char *filename); /* no mode, opens for write
|
|||||||
extern void dump_file_info(struct file *file);
|
extern void dump_file_info(struct file *file);
|
||||||
extern void string_or_die(char **strp, const char *fmt, ...);
|
extern void string_or_die(char **strp, const char *fmt, ...);
|
||||||
extern void print_elapsed_time(const char *step, struct timeval *previous_time, struct timeval *current_time);
|
extern void print_elapsed_time(const char *step, struct timeval *previous_time, struct timeval *current_time);
|
||||||
|
extern int system_argv_pipe(char *const lhscmd[], char *const rhscmd[]);
|
||||||
|
extern int system_argv_pipe_fd(int lnewstdinfd, int lnewstderrfd, char *const lhscmd[],
|
||||||
|
int rnewstdoutfd, int rnewstderrfd, char *const rhscmd[]);
|
||||||
|
extern void pipe_monitor(int lnewstdinfd, int lnewstderrfd, char *const lhscmd[],
|
||||||
|
int rnewstdoutfd, int rnewstderrfd, char *const rhscmd[]);
|
||||||
extern int system_argv(char *const argv[]);
|
extern int system_argv(char *const argv[]);
|
||||||
extern int system_argv_fd(char *const argv[], int newstdin, int newstdout, int newstderr);
|
extern int system_argv_fd(int newstdinfd, int newstdoutfd, int newstderrfd, char *const cmd[]);
|
||||||
extern int system_argv_pipe(char *const argvp1[], int stdinp1, int stderrp1,
|
extern pid_t system_argv_fd_nowait(int newstdinfd, int newstdoutfd, int newstderrfd, int closefd, char *const cmd[]);
|
||||||
char *const argvp2[], int stdoutp2, int stderrp2);
|
extern void exec_cmd_fd(int newstdinfd, int newstdoutfd, int newstderrfd, int closefd, char *const cmd[]);
|
||||||
|
extern void move_fd(int oldfd, int newfd);
|
||||||
|
extern int wait_process_terminate(pid_t pid);
|
||||||
extern int num_threads(float scaling);
|
extern int num_threads(float scaling);
|
||||||
|
extern bool file_is_debuginfo(const char *path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
-82
@@ -1,82 +0,0 @@
|
|||||||
#!/usr/bin/perl -w
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# arguments:
|
|
||||||
# perl packfsck.pl <target version>
|
|
||||||
|
|
||||||
|
|
||||||
my $target = $ARGV[0];
|
|
||||||
system("rm /tmp/Manifest");
|
|
||||||
system("wget --quiet --output-document=/tmp/Manifest https://download.clearlinux.org/update/$target/Manifest.os-core");
|
|
||||||
|
|
||||||
|
|
||||||
my $from = $target;
|
|
||||||
while ($from > $target - 100) {
|
|
||||||
$from = $from - 10;
|
|
||||||
print "Testing the $from-$target pack\n";
|
|
||||||
system("rm /tmp/pack.tar");
|
|
||||||
system("wget --quiet ---output-document=/tmp/pack.tar https://download.clearlinux.org/update/$target/pack-os-core-from-$from.tar");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open FILE, "</tmp/Manifest";
|
|
||||||
|
|
||||||
my %expected_hashes;
|
|
||||||
my %files;
|
|
||||||
|
|
||||||
while (<FILE>) {
|
|
||||||
my $line = $_;
|
|
||||||
|
|
||||||
|
|
||||||
if ($line =~ /(^[FDLr\.]+)\s*([0-9a-f]+)\s*([0-9]+)\s*+(.*)\n/) {
|
|
||||||
my $type = $1;
|
|
||||||
my $hash = $2;
|
|
||||||
my $version = $3;
|
|
||||||
my $filename = $4;
|
|
||||||
if ($version > $from) {
|
|
||||||
$expected_hashes{"$hash"} = 1;
|
|
||||||
$files{"$hash"} = $filename;
|
|
||||||
# print "$1 - $2 - $3 - $4\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close FILE;
|
|
||||||
|
|
||||||
|
|
||||||
open FILE2, "-|", "tar -tf /tmp/pack.tar 2> /dev/null";
|
|
||||||
|
|
||||||
while (<FILE2>) {
|
|
||||||
my $line = $_;
|
|
||||||
|
|
||||||
|
|
||||||
if ($line =~ /^\.\/delta\/[0-9]+.[0-9]+.([0-9a-f]+)/) {
|
|
||||||
my $hash = $1;
|
|
||||||
if (!defined($expected_hashes{"$hash"})) {
|
|
||||||
print "\tUnexpected delta hash found $hash\n";
|
|
||||||
}
|
|
||||||
$expected_hashes{"$hash"} = 2;
|
|
||||||
}
|
|
||||||
if ($line =~ /\.\/staged\/([0-9a-f]+)/) {
|
|
||||||
my $hash = $1;
|
|
||||||
if (!defined($expected_hashes{"$hash"})) {
|
|
||||||
print "\tUnexpected staged hash found $hash\n";
|
|
||||||
}
|
|
||||||
$expected_hashes{"$hash"} = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close FILE2;
|
|
||||||
|
|
||||||
my $count = 0;
|
|
||||||
foreach my $key ( keys %expected_hashes )
|
|
||||||
{
|
|
||||||
my $value = $expected_hashes{$key};
|
|
||||||
if ($value eq 1) {
|
|
||||||
my $fn = $files{$key};
|
|
||||||
print "\tFile $fn ($key) is not in the pack\n";
|
|
||||||
$count = $count = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($count < 1) {
|
|
||||||
print "\tThe $from-$target pack has no files missing\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,3 +2,8 @@
|
|||||||
emptydir=/var/lib/update/empty/
|
emptydir=/var/lib/update/empty/
|
||||||
imagebase=/var/lib/update/image/
|
imagebase=/var/lib/update/image/
|
||||||
outputdir=/var/lib/update/www/
|
outputdir=/var/lib/update/www/
|
||||||
|
|
||||||
|
[Debuginfo]
|
||||||
|
banned=true
|
||||||
|
lib=/usr/lib/debug/
|
||||||
|
src=/usr/src/debug/
|
||||||
|
|||||||
+21
-4
@@ -42,6 +42,10 @@
|
|||||||
|
|
||||||
static GThreadPool *threadpool;
|
static GThreadPool *threadpool;
|
||||||
|
|
||||||
|
/* Why not strcpy? Looks like the hash was going to be stored in
|
||||||
|
* binary at one stage. Should use g_string_chunk_insert_const to
|
||||||
|
* change hash_compare to a pointer compare
|
||||||
|
*/
|
||||||
void hash_assign(char *src, char *dst)
|
void hash_assign(char *src, char *dst)
|
||||||
{
|
{
|
||||||
memcpy(dst, src, SWUPD_HASH_LEN - 1);
|
memcpy(dst, src, SWUPD_HASH_LEN - 1);
|
||||||
@@ -147,7 +151,13 @@ void populate_file_struct(struct file *file, char *filename)
|
|||||||
ret = lstat(filename, &stat);
|
ret = lstat(filename, &stat);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG(NULL, "stat error ", "%s: %s", filename, strerror(errno));
|
LOG(NULL, "stat error ", "%s: %s", filename, strerror(errno));
|
||||||
file->is_deleted = 1;
|
/* delete the file if it isn't found and isn't a boot file,
|
||||||
|
* mark as ghosted if this is a boot file */
|
||||||
|
if (file->is_boot) {
|
||||||
|
file->is_ghosted = 1;
|
||||||
|
} else {
|
||||||
|
file->is_deleted = 1;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file->stat.st_mode = stat.st_mode;
|
file->stat.st_mode = stat.st_mode;
|
||||||
@@ -196,7 +206,7 @@ int compute_hash(struct file *file, char *filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_set_zeros(key);
|
hash_set_zeros(key); /* Set to 64 '0' (not '\0') characters */
|
||||||
|
|
||||||
if (file->is_link) {
|
if (file->is_link) {
|
||||||
char link[PATH_MAX];
|
char link[PATH_MAX];
|
||||||
@@ -310,6 +320,13 @@ static struct file *add_file(struct manifest *manifest,
|
|||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
|
|
||||||
|
if (config_ban_debuginfo() && file_is_debuginfo(sub_filename)) {
|
||||||
|
printf("WARNING: File %s is banned ...skipping.\n", sub_filename);
|
||||||
|
free(sub_filename);
|
||||||
|
free(fullname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (illegal_characters(entry_name)) {
|
if (illegal_characters(entry_name)) {
|
||||||
printf("WARNING: Filename %s includes illegal character(s) ...skipping.\n", sub_filename);
|
printf("WARNING: Filename %s includes illegal character(s) ...skipping.\n", sub_filename);
|
||||||
free(sub_filename);
|
free(sub_filename);
|
||||||
@@ -477,7 +494,7 @@ struct manifest *full_manifest_from_directory(int version)
|
|||||||
|
|
||||||
LOG(NULL, "Computing hashes", "for %i/full", version);
|
LOG(NULL, "Computing hashes", "for %i/full", version);
|
||||||
|
|
||||||
manifest = alloc_manifest(version, "full");
|
manifest = alloc_manifest(version, "full", NULL);
|
||||||
|
|
||||||
string_or_die(&dir, "%s/%i/full", image_dir, version);
|
string_or_die(&dir, "%s/%i/full", image_dir, version);
|
||||||
|
|
||||||
@@ -553,7 +570,7 @@ struct manifest *sub_manifest_from_directory(char *component, int version)
|
|||||||
|
|
||||||
LOG(NULL, "Creating component manifest", "for %i/%s", version, component);
|
LOG(NULL, "Creating component manifest", "for %i/%s", version, component);
|
||||||
|
|
||||||
manifest = alloc_manifest(version, component);
|
manifest = alloc_manifest(version, component, NULL);
|
||||||
|
|
||||||
string_or_die(&dir, "%s/%i/%s", image_dir, version, component);
|
string_or_die(&dir, "%s/%i/%s", image_dir, version, component);
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,20 @@ int config_initial_version(void)
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool config_ban_debuginfo(void)
|
||||||
|
{
|
||||||
|
assert(keyfile != NULL);
|
||||||
|
|
||||||
|
return g_key_file_get_boolean(keyfile, "Debuginfo", "banned", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *config_debuginfo_path(const char *comp)
|
||||||
|
{
|
||||||
|
assert(keyfile != NULL);
|
||||||
|
|
||||||
|
return g_key_file_get_value(keyfile, "Debuginfo", comp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
bool read_configuration_file(char *filename)
|
bool read_configuration_file(char *filename)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|||||||
+72
-20
@@ -236,6 +236,16 @@ static int check_group_file(void)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool both_ghosted(struct file *file1, struct file *file2)
|
||||||
|
{
|
||||||
|
return (file1->is_ghosted && file2->is_ghosted);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool both_deleted(struct file *file1, struct file *file2)
|
||||||
|
{
|
||||||
|
return (file1->is_deleted && file2->is_deleted);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct manifest *new_core = NULL;
|
struct manifest *new_core = NULL;
|
||||||
@@ -252,6 +262,7 @@ int main(int argc, char **argv)
|
|||||||
GList *manifests_last_versions_list = NULL;
|
GList *manifests_last_versions_list = NULL;
|
||||||
int newfiles = 0;
|
int newfiles = 0;
|
||||||
int old_deleted = 0;
|
int old_deleted = 0;
|
||||||
|
int old_ghosted = 0;
|
||||||
|
|
||||||
struct timeval current_time;
|
struct timeval current_time;
|
||||||
struct timeval previous_time;
|
struct timeval previous_time;
|
||||||
@@ -325,15 +336,19 @@ int main(int argc, char **argv)
|
|||||||
apply_heuristics(new_full);
|
apply_heuristics(new_full);
|
||||||
match_manifests(old_full, new_full);
|
match_manifests(old_full, new_full);
|
||||||
|
|
||||||
old_deleted = remove_old_deleted_files(old_full, new_full);
|
old_deleted = remove_deprecated_files(old_full, new_full, both_deleted);
|
||||||
if (old_deleted > 0) {
|
if (old_deleted > 0) {
|
||||||
LOG(NULL, "", "Old deleted files (%d) removed from full manifest", old_deleted);
|
LOG(NULL, "", "Old deleted files (%d) removed from full manifest", old_deleted);
|
||||||
printf("Old deleted files (%d) removed from full manifest\n", old_deleted);
|
printf("Old deleted files (%d) removed from full manifest\n", old_deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
old_ghosted = remove_deprecated_files(old_full, new_full, both_ghosted);
|
||||||
|
if (old_ghosted > 0) {
|
||||||
|
LOG(NULL, "", "Old ghosted files (%d) removed from full manifest", old_ghosted);
|
||||||
|
printf("Old ghosted files (%d) removed from full manifest", old_ghosted);
|
||||||
|
}
|
||||||
|
|
||||||
apply_heuristics(new_full);
|
apply_heuristics(new_full);
|
||||||
#warning disabled rename detection for some simplicity
|
|
||||||
// rename_detection(new_full);
|
|
||||||
|
|
||||||
print_elapsed_time("full manifest creation", &previous_time, ¤t_time);
|
print_elapsed_time("full manifest creation", &previous_time, ¤t_time);
|
||||||
|
|
||||||
@@ -342,7 +357,25 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
/* Step 2: Make a manifest for the os-core set */
|
/* Step 2: Make a manifest for the os-core set */
|
||||||
old_MoM = manifest_from_file(current_version, "MoM");
|
old_MoM = manifest_from_file(current_version, "MoM");
|
||||||
new_MoM = alloc_manifest(newversion, "MoM");
|
|
||||||
|
/* Verify that the new format is not older than the previous format. It
|
||||||
|
* never makes sense to decrease the format number for the next build. */
|
||||||
|
if (format < old_MoM->format) {
|
||||||
|
LOG(NULL, "", "Current format (%llu) must be greater than or equal to previous format (%llu). Exiting",
|
||||||
|
format, old_MoM->format);
|
||||||
|
printf("Current format (%llu) must be greater than or equal to previous format (%llu). Exiting\n",
|
||||||
|
format, old_MoM->format);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Detect a format bump and add the "update" action to the manifest
|
||||||
|
* "actions:" field */
|
||||||
|
GList *actions = NULL;
|
||||||
|
if (format > old_MoM->format) {
|
||||||
|
actions = g_list_prepend(actions, "update");
|
||||||
|
}
|
||||||
|
|
||||||
|
new_MoM = alloc_manifest(newversion, "MoM", actions);
|
||||||
old_core = manifest_from_file(manifest_subversion(old_MoM, "os-core"), "os-core");
|
old_core = manifest_from_file(manifest_subversion(old_MoM, "os-core"), "os-core");
|
||||||
new_core = sub_manifest_from_directory("os-core", newversion);
|
new_core = sub_manifest_from_directory("os-core", newversion);
|
||||||
add_component_hashes_to_manifest(new_core, new_full);
|
add_component_hashes_to_manifest(new_core, new_full);
|
||||||
@@ -365,21 +398,21 @@ int main(int argc, char **argv)
|
|||||||
apply_heuristics(new_core);
|
apply_heuristics(new_core);
|
||||||
/* Step 3c: ... else save the manifest */
|
/* Step 3c: ... else save the manifest */
|
||||||
type_change_detection(new_core);
|
type_change_detection(new_core);
|
||||||
|
#ifdef RENAMES
|
||||||
#warning disabled rename detection for some simplicity
|
/* Detect renamed files specifically for os-core */
|
||||||
/* Detect renamed files specifically for each pack */
|
rename_detection(new_core);
|
||||||
// rename_detection(...);
|
#endif
|
||||||
|
old_deleted = remove_deprecated_files(old_core, new_core, both_deleted);
|
||||||
old_deleted = remove_old_deleted_files(old_core, new_core);
|
old_ghosted = remove_deprecated_files(old_core, new_core, both_ghosted);
|
||||||
sort_manifest_by_version(new_core);
|
sort_manifest_by_version(new_core); /* sorts by filename */
|
||||||
newfiles = prune_manifest(new_core);
|
newfiles = prune_manifest(new_core);
|
||||||
if (newfiles <= 0) {
|
if (newfiles <= 0) {
|
||||||
LOG(NULL, "", "Core component has not changed (after pruning), exiting");
|
LOG(NULL, "", "Core component has not changed (after pruning), exiting");
|
||||||
printf("Core component has not changed (after pruning), exiting\n");
|
printf("Core component has not changed (after pruning), exiting\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
LOG(NULL, "", "Core component has changes (%d new, %d deleted), writing out new manifest", newfiles, old_deleted);
|
LOG(NULL, "", "Core component has changes (%d new, %d deleted, %d ghosted), writing out new manifest", newfiles, old_deleted, old_ghosted);
|
||||||
printf("Core component has changes (%d new, %d deleted), writing out new manifest\n", newfiles, old_deleted);
|
printf("Core component has changes (%d new, %d deleted, %d ghosted), writing out new manifest\n", newfiles, old_deleted, old_ghosted);
|
||||||
if (write_manifest(new_core) != 0) {
|
if (write_manifest(new_core) != 0) {
|
||||||
LOG(NULL, "", "Core component manifest write failed");
|
LOG(NULL, "", "Core component manifest write failed");
|
||||||
printf("Core component manifest write failed\n");
|
printf("Core component manifest write failed\n");
|
||||||
@@ -406,25 +439,40 @@ int main(int argc, char **argv)
|
|||||||
GList *name_includes;
|
GList *name_includes;
|
||||||
char *group = next_group();
|
char *group = next_group();
|
||||||
struct manifest *manifest;
|
struct manifest *manifest;
|
||||||
|
struct manifest *current = NULL;
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
manifest = g_hash_table_lookup(new_manifests, group);
|
manifest = g_hash_table_lookup(new_manifests, group);
|
||||||
name_includes = manifest->includes;
|
name_includes = manifest->includes;
|
||||||
|
|
||||||
while (name_includes) {
|
while (name_includes) {
|
||||||
char *name = name_includes->data;
|
char *name = name_includes->data;
|
||||||
name_includes = g_list_next(name_includes);
|
name_includes = g_list_next(name_includes);
|
||||||
manifest_includes = g_list_prepend(manifest_includes, g_hash_table_lookup(new_manifests, name));
|
|
||||||
|
current = g_hash_table_lookup(new_manifests, name);
|
||||||
|
|
||||||
|
// Avoid adding duplicate includes to the list
|
||||||
|
if (g_list_find(manifest_includes, current) == NULL) {
|
||||||
|
manifest_includes = g_list_prepend(manifest_includes, current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
manifest->includes = manifest_includes;
|
manifest->includes = manifest_includes;
|
||||||
manifest_includes = NULL;
|
manifest_includes = NULL;
|
||||||
manifest = g_hash_table_lookup(old_manifests, group);
|
manifest = g_hash_table_lookup(old_manifests, group);
|
||||||
name_includes = manifest->includes;
|
name_includes = manifest->includes;
|
||||||
|
|
||||||
while (name_includes) {
|
while (name_includes) {
|
||||||
char *name = name_includes->data;
|
char *name = name_includes->data;
|
||||||
name_includes = g_list_next(name_includes);
|
name_includes = g_list_next(name_includes);
|
||||||
manifest_includes = g_list_prepend(manifest_includes, g_hash_table_lookup(old_manifests, name));
|
|
||||||
|
current = g_hash_table_lookup(old_manifests, name);
|
||||||
|
|
||||||
|
// Avoid adding duplicate includes to the list
|
||||||
|
if (g_list_find(manifest_includes, current) == NULL) {
|
||||||
|
manifest_includes = g_list_prepend(manifest_includes, current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
manifest->includes = manifest_includes;
|
manifest->includes = manifest_includes;
|
||||||
}
|
}
|
||||||
@@ -471,15 +519,19 @@ int main(int argc, char **argv)
|
|||||||
newm->version = oldm->version;
|
newm->version = oldm->version;
|
||||||
} else {
|
} else {
|
||||||
apply_heuristics(newm);
|
apply_heuristics(newm);
|
||||||
#warning missing rename_detection here
|
#ifdef RENAMES
|
||||||
|
/* Detect renamed files specifically for this bundle */
|
||||||
|
rename_detection(newm);
|
||||||
|
#endif
|
||||||
/* Step 6b: otherwise, write out the manifest */
|
/* Step 6b: otherwise, write out the manifest */
|
||||||
old_deleted = remove_old_deleted_files(oldm, newm);
|
old_deleted = remove_deprecated_files(oldm, newm, both_deleted);
|
||||||
|
old_ghosted = remove_deprecated_files(oldm, newm, both_ghosted);
|
||||||
sort_manifest_by_version(newm);
|
sort_manifest_by_version(newm);
|
||||||
type_change_detection(newm);
|
type_change_detection(newm);
|
||||||
newfiles = prune_manifest(newm);
|
newfiles = prune_manifest(newm);
|
||||||
if (newfiles > 0 || old_deleted > 0 || changed_includes(oldm, newm)) {
|
if (newfiles > 0 || old_deleted > 0 || old_ghosted > 0 || changed_includes(oldm, newm)) {
|
||||||
LOG(NULL, "", "%s component has changes (%d new, %d deleted), writing out new manifest", group, newfiles, old_deleted);
|
LOG(NULL, "", "%s component has changes (%d new, %d deleted, %d ghosted), writing out new manifest", group, newfiles, old_deleted, old_ghosted);
|
||||||
printf("%s component has changes (%d new, %d deleted), writing out new manifest\n", group, newfiles, old_deleted);
|
printf("%s component has changes (%d new, %d deleted, %d ghosted), writing out new manifest\n", group, newfiles, old_deleted, old_ghosted);
|
||||||
if (write_manifest(newm) != 0) {
|
if (write_manifest(newm) != 0) {
|
||||||
LOG(NULL, "", "%s component manifest write failed", group);
|
LOG(NULL, "", "%s component manifest write failed", group);
|
||||||
printf("%s component manifest write failed\n", group);
|
printf("%s component manifest write failed\n", group);
|
||||||
|
|||||||
+12
-14
@@ -50,7 +50,6 @@ static void create_fullfile(struct file *file)
|
|||||||
struct stat sbuf;
|
struct stat sbuf;
|
||||||
char *empty, *indir, *outdir;
|
char *empty, *indir, *outdir;
|
||||||
char *param1, *param2;
|
char *param1, *param2;
|
||||||
int stderrfd;
|
|
||||||
|
|
||||||
if (file->is_deleted) {
|
if (file->is_deleted) {
|
||||||
return; /* file got deleted -> by definition we cannot tar it up */
|
return; /* file got deleted -> by definition we cannot tar it up */
|
||||||
@@ -98,17 +97,13 @@ static void create_fullfile(struct file *file)
|
|||||||
char *const tarcfcmd[] = { TAR_COMMAND, "-C", dir, TAR_PERM_ATTR_ARGS_STRLIST, "-cf", "-", param1, param2, NULL };
|
char *const tarcfcmd[] = { TAR_COMMAND, "-C", dir, TAR_PERM_ATTR_ARGS_STRLIST, "-cf", "-", param1, param2, NULL };
|
||||||
char *const tarxfcmd[] = { TAR_COMMAND, "-C", rename_tmpdir, TAR_PERM_ATTR_ARGS_STRLIST, "-xf", "-", NULL };
|
char *const tarxfcmd[] = { TAR_COMMAND, "-C", rename_tmpdir, TAR_PERM_ATTR_ARGS_STRLIST, "-xf", "-", NULL };
|
||||||
|
|
||||||
stderrfd = open("/dev/null", O_WRONLY);
|
int tarcmdresult = system_argv_pipe(tarcfcmd, tarxfcmd);
|
||||||
if (stderrfd == -1) {
|
if (tarcmdresult != 0) {
|
||||||
LOG(NULL, "Failed to open /dev/null", "");
|
LOG(NULL, "Tar command for copying directory full file failed with code %d", tarcmdresult);
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
if (system_argv_pipe(tarcfcmd, -1, stderrfd, tarxfcmd, -1, stderrfd) != 0) {
|
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
free(param1);
|
free(param1);
|
||||||
free(param2);
|
free(param2);
|
||||||
close(stderrfd);
|
|
||||||
|
|
||||||
string_or_die(&rename_source, "%s/%s", rename_tmpdir, base);
|
string_or_die(&rename_source, "%s/%s", rename_tmpdir, base);
|
||||||
string_or_die(&rename_target, "%s/%s", rename_tmpdir, file->hash);
|
string_or_die(&rename_target, "%s/%s", rename_tmpdir, file->hash);
|
||||||
@@ -249,22 +244,20 @@ static GList *get_deduplicated_fullfile_list(struct manifest *manifest)
|
|||||||
struct file *tmp;
|
struct file *tmp;
|
||||||
|
|
||||||
// presort by hash for easy deduplication
|
// presort by hash for easy deduplication
|
||||||
manifest->files = g_list_sort(manifest->files, file_sort_hash);
|
list = manifest->files = g_list_sort(manifest->files, file_sort_hash);
|
||||||
|
|
||||||
list = g_list_first(manifest->files);
|
for (; list; list = g_list_next(list)) {
|
||||||
while (prev == NULL && list != NULL) {
|
|
||||||
tmp = list->data;
|
tmp = list->data;
|
||||||
list = g_list_next(list);
|
|
||||||
|
|
||||||
// find first new file
|
// find first new file
|
||||||
if (tmp->last_change == manifest->version) {
|
if (tmp->last_change == manifest->version) {
|
||||||
prev = tmp;
|
prev = tmp;
|
||||||
outfiles = g_list_prepend(outfiles, tmp);
|
outfiles = g_list_prepend(outfiles, tmp);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (list) {
|
for (; list; list = g_list_next(list)) {
|
||||||
file = list->data;
|
file = list->data;
|
||||||
list = g_list_next(list);
|
|
||||||
|
|
||||||
// add any new file having a unique hash
|
// add any new file having a unique hash
|
||||||
//FIXME: rename logic will be needed here
|
//FIXME: rename logic will be needed here
|
||||||
@@ -305,6 +298,11 @@ static void submit_fullfile_tasks(GList *files)
|
|||||||
file = item->data;
|
file = item->data;
|
||||||
item = g_list_next(item);
|
item = g_list_next(item);
|
||||||
|
|
||||||
|
/* do not push ghosted files */
|
||||||
|
if (file->is_ghosted) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ret = g_thread_pool_push(threadpool, file, &err);
|
ret = g_thread_pool_push(threadpool, file, &err);
|
||||||
if (ret == FALSE) {
|
if (ret == FALSE) {
|
||||||
printf("GThread create_fullfile_task push error\n");
|
printf("GThread create_fullfile_task push error\n");
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ static char **groups = NULL;
|
|||||||
static gsize groupcount = 0;
|
static gsize groupcount = 0;
|
||||||
static unsigned int groupcursor = 0;
|
static unsigned int groupcursor = 0;
|
||||||
|
|
||||||
|
#if 0
|
||||||
char *group_packages(char *group)
|
char *group_packages(char *group)
|
||||||
{
|
{
|
||||||
assert(groupfile != NULL);
|
assert(groupfile != NULL);
|
||||||
@@ -50,6 +51,7 @@ char *group_groups(char *group)
|
|||||||
|
|
||||||
return g_key_file_get_value(groupfile, group, "groups", NULL);
|
return g_key_file_get_value(groupfile, group, "groups", NULL);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
char *group_status(char *group)
|
char *group_status(char *group)
|
||||||
{
|
{
|
||||||
|
|||||||
+129
-114
@@ -149,130 +149,117 @@ void concat_str_array(char **output, char *const argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int system_argv_pipe(char *const lhscmd[], char *const rhscmd[])
|
||||||
|
{
|
||||||
|
return system_argv_pipe_fd(-1, -1, lhscmd, -1, -1, rhscmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int system_argv_pipe_fd(int lnewstdinfd, int lnewstderrfd, char *const lhscmd[],
|
||||||
|
int rnewstdoutfd, int rnewstderrfd, char *const rhscmd[])
|
||||||
|
{
|
||||||
|
pid_t monitorpid = fork();
|
||||||
|
if (monitorpid == -1) {
|
||||||
|
LOG(NULL, "Failed to create child process to monitor pipe between", "command %s and command %s", lhscmd[0], rhscmd[0]);
|
||||||
|
return -1;
|
||||||
|
} else if (monitorpid == 0) {
|
||||||
|
pipe_monitor(lnewstdinfd, lnewstderrfd, lhscmd, rnewstdoutfd, rnewstderrfd, rhscmd);
|
||||||
|
}
|
||||||
|
return wait_process_terminate(monitorpid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pipe_monitor(int lnewstdinfd, int lnewstderrfd, char *const lhscmd[],
|
||||||
|
int rnewstdoutfd, int rnewstderrfd, char *const rhscmd[])
|
||||||
|
{
|
||||||
|
int pipefd[2];
|
||||||
|
if (pipe(pipefd) == -1) {
|
||||||
|
LOG(NULL, "Failed to create a pipe between", "command %s and command %s", lhscmd[0], rhscmd[0]);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t lhspid = system_argv_fd_nowait(lnewstdinfd, pipefd[1], lnewstderrfd, pipefd[0], lhscmd);
|
||||||
|
pid_t rhspid = system_argv_fd_nowait(pipefd[0], rnewstdoutfd, rnewstderrfd, pipefd[1], rhscmd);
|
||||||
|
|
||||||
|
if (close(pipefd[1]) == -1) {
|
||||||
|
LOG(NULL, "Could not close write end of pipe file descriptor", "%d", pipefd[1]);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
if (close(pipefd[0]) == -1) {
|
||||||
|
LOG(NULL, "Could not close read end of pipe file descriptor", "%d", pipefd[0]);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lhsresult = wait_process_terminate(lhspid);
|
||||||
|
int rhsresult = wait_process_terminate(rhspid);
|
||||||
|
exit(rhsresult != EXIT_SUCCESS ? rhsresult : lhsresult);
|
||||||
|
}
|
||||||
|
|
||||||
int system_argv(char *const argv[])
|
int system_argv(char *const argv[])
|
||||||
{
|
{
|
||||||
int child_exit_status;
|
return system_argv_fd(-1, -1, -1, argv);
|
||||||
pid_t pid;
|
|
||||||
int status = -1;
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
|
|
||||||
if (pid == 0) { /* child */
|
|
||||||
execvp(*argv, argv);
|
|
||||||
LOG(NULL, "This line must not be reached", "");
|
|
||||||
assert(0);
|
|
||||||
} else if (pid < 0) {
|
|
||||||
LOG(NULL, "Failed to fork a child process", "");
|
|
||||||
assert(0);
|
|
||||||
} else {
|
|
||||||
pid_t ws = waitpid(pid, &child_exit_status, 0);
|
|
||||||
|
|
||||||
if (ws == -1) {
|
|
||||||
LOG(NULL, "Failed to wait for child process", "");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WIFEXITED(child_exit_status)) {
|
|
||||||
status = WEXITSTATUS(child_exit_status);
|
|
||||||
} else {
|
|
||||||
LOG(NULL, "Child process didn't exit", "");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != 0) {
|
|
||||||
char *cmdline = NULL;
|
|
||||||
|
|
||||||
concat_str_array(&cmdline, argv);
|
|
||||||
LOG(NULL, "Failed to run command:", "%s", cmdline);
|
|
||||||
free(cmdline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int system_argv_fd(char *const argv[], int newstdin, int newstdout, int newstderr)
|
int system_argv_fd(int newstdinfd, int newstdoutfd, int newstderrfd, char *const cmd[])
|
||||||
{
|
{
|
||||||
int child_exit_status;
|
pid_t cmdpid = system_argv_fd_nowait(newstdinfd, newstdoutfd, newstderrfd, -1, cmd);
|
||||||
pid_t pid;
|
return wait_process_terminate(cmdpid);
|
||||||
int status = -1;
|
|
||||||
|
|
||||||
pid = fork();
|
|
||||||
|
|
||||||
if (pid == 0) { /* child */
|
|
||||||
if (newstdin >= 0) {
|
|
||||||
if (dup2(newstdin, STDIN_FILENO) == -1) {
|
|
||||||
LOG(NULL, "Could not redirect stdin", "");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
close(newstdin);
|
|
||||||
}
|
|
||||||
if (newstdout >= 0) {
|
|
||||||
if (dup2(newstdout, STDOUT_FILENO) == -1) {
|
|
||||||
LOG(NULL, "Could not redirect stdout", "");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
close(newstdout);
|
|
||||||
}
|
|
||||||
if (newstderr >= 0) {
|
|
||||||
if (dup2(newstderr, STDERR_FILENO) == -1) {
|
|
||||||
LOG(NULL, "Could not redirect stderr", "");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
close(newstderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
execvp(*argv, argv);
|
|
||||||
LOG(NULL, "This line must not be reached", "");
|
|
||||||
assert(0);
|
|
||||||
} else if (pid < 0) {
|
|
||||||
LOG(NULL, "Failed to fork a child process", "");
|
|
||||||
assert(0);
|
|
||||||
} else {
|
|
||||||
pid_t ws = waitpid(pid, &child_exit_status, 0);
|
|
||||||
|
|
||||||
if (ws == -1) {
|
|
||||||
LOG(NULL, "Failed to wait for child process", "");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WIFEXITED(child_exit_status)) {
|
|
||||||
status = WEXITSTATUS(child_exit_status);
|
|
||||||
} else {
|
|
||||||
LOG(NULL, "Child process didn't exit", "");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status != 0) {
|
|
||||||
char *cmdline = NULL;
|
|
||||||
|
|
||||||
concat_str_array(&cmdline, argv);
|
|
||||||
LOG(NULL, "Failed to run command:", "%s", cmdline);
|
|
||||||
free(cmdline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int system_argv_pipe(char *const argvp1[], int stdinp1, int stderrp1,
|
pid_t system_argv_fd_nowait(int newstdinfd, int newstdoutfd, int newstderrfd, int closefd, char *const cmd[])
|
||||||
char *const argvp2[], int stdoutp2, int stderrp2)
|
|
||||||
{
|
{
|
||||||
int statusp2;
|
pid_t cmdpid = fork();
|
||||||
int pipefd[2];
|
if (cmdpid == -1) {
|
||||||
|
LOG(NULL, "Failed to fork to execute command", "%s", cmd[0]);
|
||||||
if (pipe(pipefd)) {
|
assert(0);
|
||||||
LOG(NULL, "Failed to create a pipe", "");
|
} else if (cmdpid == 0) {
|
||||||
return -1;
|
exec_cmd_fd(newstdinfd, newstdoutfd, newstderrfd, closefd, cmd);
|
||||||
}
|
}
|
||||||
system_argv_fd(argvp1, stdinp1, pipefd[1], stderrp1);
|
return cmdpid;
|
||||||
close(pipefd[1]);
|
}
|
||||||
statusp2 = system_argv_fd(argvp2, pipefd[0], stdoutp2, stderrp2);
|
|
||||||
close(pipefd[0]);
|
|
||||||
|
|
||||||
/* Returns the status of the failed process if any
|
void exec_cmd_fd(int newstdinfd, int newstdoutfd, int newstderrfd, int closefd, char *const cmd[])
|
||||||
If both processes failed returns the status of first one */
|
{
|
||||||
return statusp2;
|
move_fd(newstdinfd, STDIN_FILENO);
|
||||||
|
move_fd(newstdoutfd, STDOUT_FILENO);
|
||||||
|
move_fd(newstderrfd, STDERR_FILENO);
|
||||||
|
if (closefd >= 0 && close(closefd) == -1) {
|
||||||
|
LOG(NULL, "Could not close file descriptor", "%d", closefd);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
execvp(*cmd, cmd);
|
||||||
|
LOG(NULL, "Command", "%s failed", cmd[0]);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_fd(int oldfd, int newfd)
|
||||||
|
{
|
||||||
|
if (oldfd < 0 || newfd < 0 || oldfd == newfd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dup2(oldfd, newfd) == -1) {
|
||||||
|
LOG(NULL, "Could not create duplicate file descriptor", "%d from %d", newfd, oldfd);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
if (close(oldfd) == -1) {
|
||||||
|
LOG(NULL, "Could not close file descriptor", "%d", oldfd);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int wait_process_terminate(pid_t pid)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
do {
|
||||||
|
if (waitpid(pid, &status, 0) == -1) {
|
||||||
|
LOG(NULL, "Failed to wait for PID", "%d", pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
||||||
|
// Exit statuses fall in the range of [0, 255]. Make signal statuses fall in a non-overlapping range starting with 256.
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
} else {
|
||||||
|
return 256 + WTERMSIG(status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_root(void)
|
void check_root(void)
|
||||||
@@ -309,3 +296,31 @@ int num_threads(float scaling)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is called when configuration specifies a ban on debuginfo files
|
||||||
|
* from manifests. Returns true if the passed file path matches the src or lib
|
||||||
|
* debuginfo configuration. */
|
||||||
|
bool file_is_debuginfo(const char *path)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
char *lib;
|
||||||
|
char *src;
|
||||||
|
|
||||||
|
lib = config_debuginfo_path("lib");
|
||||||
|
src = config_debuginfo_path("src");
|
||||||
|
|
||||||
|
if (lib && (strncmp(path, lib, strlen(lib)) == 0)) {
|
||||||
|
ret = true;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src && (strncmp(path, src, strlen(src)) == 0)) {
|
||||||
|
ret = true;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(lib);
|
||||||
|
free(src);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <getopt.h>
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|||||||
+81
-29
@@ -51,6 +51,10 @@ int file_sort_hash(gconstpointer a, gconstpointer b)
|
|||||||
return memcmp(A->hash, B->hash, SWUPD_HASH_LEN - 1);
|
return memcmp(A->hash, B->hash, SWUPD_HASH_LEN - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Standard sort compare function which sorts
|
||||||
|
* first on the version where the file changed and then
|
||||||
|
* on the name
|
||||||
|
*/
|
||||||
int file_sort_version(gconstpointer a, gconstpointer b)
|
int file_sort_version(gconstpointer a, gconstpointer b)
|
||||||
{
|
{
|
||||||
struct file *A, *B;
|
struct file *A, *B;
|
||||||
@@ -88,7 +92,7 @@ int file_sort_filename(gconstpointer a, gconstpointer b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct manifest *alloc_manifest(int version, char *component)
|
struct manifest *alloc_manifest(int version, char *component, GList *actions)
|
||||||
{
|
{
|
||||||
struct manifest *manifest;
|
struct manifest *manifest;
|
||||||
|
|
||||||
@@ -100,6 +104,7 @@ struct manifest *alloc_manifest(int version, char *component)
|
|||||||
manifest->version = version;
|
manifest->version = version;
|
||||||
manifest->component = strdup(component);
|
manifest->component = strdup(component);
|
||||||
manifest->format = format;
|
manifest->format = format;
|
||||||
|
manifest->actions = actions;
|
||||||
|
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
@@ -129,7 +134,7 @@ struct manifest *manifest_from_file(int version, char *component)
|
|||||||
if (infile == NULL) {
|
if (infile == NULL) {
|
||||||
LOG(NULL, "Cannot read manifest", "%s (%s)\n", filename, strerror(errno));
|
LOG(NULL, "Cannot read manifest", "%s (%s)\n", filename, strerror(errno));
|
||||||
free(filename);
|
free(filename);
|
||||||
return alloc_manifest(version, component);
|
return alloc_manifest(version, component, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* line 1: MANIFEST\t<version> */
|
/* line 1: MANIFEST\t<version> */
|
||||||
@@ -189,7 +194,7 @@ struct manifest *manifest_from_file(int version, char *component)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest = alloc_manifest(version, component);
|
manifest = alloc_manifest(version, component, NULL);
|
||||||
manifest->format = format_number;
|
manifest->format = format_number;
|
||||||
manifest->prevversion = previous;
|
manifest->prevversion = previous;
|
||||||
manifest->includes = includes;
|
manifest->includes = includes;
|
||||||
@@ -235,10 +240,22 @@ struct manifest *manifest_from_file(int version, char *component)
|
|||||||
assert(0); /* unknown file type */
|
assert(0); /* unknown file type */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c[1] == 'd') {
|
switch (c[1]) {
|
||||||
|
case 'd':
|
||||||
|
/* file is deleted */
|
||||||
file->is_deleted = 1;
|
file->is_deleted = 1;
|
||||||
} else if (c[1] != '.') {
|
break;
|
||||||
assert(0); /* unknown deleted status */
|
case 'g':
|
||||||
|
/* file is ghosted */
|
||||||
|
file->is_ghosted = 1;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
/* no flag at this index */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unknown deleted status */
|
||||||
|
LOG(NULL, "Invalid flag at index 1", "%s", c[1]);
|
||||||
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c[2] == 'C') {
|
if (c[2] == 'C') {
|
||||||
@@ -423,15 +440,20 @@ int match_manifests(struct manifest *m1, struct manifest *m2)
|
|||||||
file3->is_config = file1->is_config;
|
file3->is_config = file1->is_config;
|
||||||
file3->is_state = file1->is_state;
|
file3->is_state = file1->is_state;
|
||||||
file3->is_boot = file1->is_boot;
|
file3->is_boot = file1->is_boot;
|
||||||
|
/* ghost deleted boot files */
|
||||||
|
file3->is_ghosted = file1->is_boot && file1->is_deleted;
|
||||||
|
|
||||||
if (!file1->is_deleted) {
|
if (file3->is_ghosted || file1->is_deleted) {
|
||||||
file3->last_change = m2->version;
|
/* if the new file is ghosted or the file was deleted, preserve
|
||||||
} else {
|
* hash (all zeros if file1->is_deleted) and rename status */
|
||||||
file3->last_change = file1->last_change;
|
|
||||||
file3->is_rename = file1->is_rename;
|
file3->is_rename = file1->is_rename;
|
||||||
hash_assign(file1->hash, file3->hash);
|
hash_assign(file1->hash, file3->hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* for deleted files last_change remains the same.
|
||||||
|
* otherwise last change is now */
|
||||||
|
file3->last_change = file1->is_deleted ? file1->last_change : m2->version;
|
||||||
|
|
||||||
file3->peer = file1;
|
file3->peer = file1;
|
||||||
file1->peer = file3;
|
file1->peer = file3;
|
||||||
|
|
||||||
@@ -653,6 +675,10 @@ char *file_type_to_string(struct file *file)
|
|||||||
type[1] = 'd';
|
type[1] = 'd';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file->is_ghosted) {
|
||||||
|
type[1] = 'g';
|
||||||
|
}
|
||||||
|
|
||||||
if (file->is_config) {
|
if (file->is_config) {
|
||||||
type[2] = 'C';
|
type[2] = 'C';
|
||||||
}
|
}
|
||||||
@@ -670,19 +696,29 @@ char *file_type_to_string(struct file *file)
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate the contentsize for the manifest based on file sizes.
|
||||||
|
*
|
||||||
|
* This should calculate the files uniquely included in this manifest, but none
|
||||||
|
* of its submanifests, which will allow calculation of sizes of all bundles on
|
||||||
|
* a system by adding all manifest->contentsizes of installed bundles.
|
||||||
|
*
|
||||||
|
* However, if two bundles not in the same include chain have overlapping
|
||||||
|
* content, summing the include chain of each bundle in the client will result
|
||||||
|
* in an over-estimation of the total size on the system. The more content is
|
||||||
|
* shared, the higher the over-estimation. In reality this overlap will not be
|
||||||
|
* large, but it is currently impossible to calculate the exact installed size
|
||||||
|
* using just the contentsize.
|
||||||
|
*/
|
||||||
static void compute_content_size(struct manifest *manifest)
|
static void compute_content_size(struct manifest *manifest)
|
||||||
{
|
{
|
||||||
/* FIXME: this is a temporary implementation based on worst case */
|
|
||||||
|
|
||||||
GList *list;
|
GList *list;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
struct manifest *submanifest;
|
|
||||||
|
|
||||||
list = g_list_first(manifest->files);
|
list = g_list_first(manifest->files);
|
||||||
while (list) {
|
while (list) {
|
||||||
file = list->data;
|
file = list->data;
|
||||||
list = g_list_next(list);
|
list = g_list_next(list);
|
||||||
if (!file->is_deleted && (file->last_change == manifest->version)) {
|
if (!file->is_deleted) {
|
||||||
if (file->is_file) {
|
if (file->is_file) {
|
||||||
manifest->contentsize += file->stat.st_size;
|
manifest->contentsize += file->stat.st_size;
|
||||||
} else if (file->is_link) {
|
} else if (file->is_link) {
|
||||||
@@ -692,17 +728,6 @@ static void compute_content_size(struct manifest *manifest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list = g_list_first(manifest->submanifests);
|
|
||||||
while (list) {
|
|
||||||
submanifest = list->data;
|
|
||||||
list = g_list_next(list);
|
|
||||||
|
|
||||||
/* Do not take into account groups not included in download content */
|
|
||||||
if (create_download_content_for_group(submanifest->component)) {
|
|
||||||
manifest->contentsize += submanifest->contentsize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 0 == success, -1 == failure */
|
/* Returns 0 == success, -1 == failure */
|
||||||
@@ -710,6 +735,7 @@ static int write_manifest_plain(struct manifest *manifest)
|
|||||||
{
|
{
|
||||||
GList *includes;
|
GList *includes;
|
||||||
GList *list;
|
GList *list;
|
||||||
|
GList *actions;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
FILE *out = NULL;
|
FILE *out = NULL;
|
||||||
char *base = NULL, *dir;
|
char *base = NULL, *dir;
|
||||||
@@ -759,6 +785,14 @@ static int write_manifest_plain(struct manifest *manifest)
|
|||||||
includes = g_list_next(includes);
|
includes = g_list_next(includes);
|
||||||
fprintf(out, "includes:\t%s\n", sub->component);
|
fprintf(out, "includes:\t%s\n", sub->component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actions = manifest->actions;
|
||||||
|
while (actions) {
|
||||||
|
char *action = actions->data;
|
||||||
|
fprintf(out, "actions:\t%s\n", action);
|
||||||
|
actions = g_list_next(actions);
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(out, "\n");
|
fprintf(out, "\n");
|
||||||
|
|
||||||
list = g_list_first(manifest->files);
|
list = g_list_first(manifest->files);
|
||||||
@@ -955,7 +989,7 @@ bool changed_includes(struct manifest *old, struct manifest *new)
|
|||||||
*
|
*
|
||||||
* Note: this function should be called after match_manifests().
|
* Note: this function should be called after match_manifests().
|
||||||
*/
|
*/
|
||||||
int remove_old_deleted_files(struct manifest *m1, struct manifest *m2)
|
int remove_deprecated_files(struct manifest *m1, struct manifest *m2, bool (*compfunc)(struct file *file1, struct file *file2))
|
||||||
{
|
{
|
||||||
GList *list1, *list2;
|
GList *list1, *list2;
|
||||||
struct file *file1, *file2;
|
struct file *file1, *file2;
|
||||||
@@ -993,7 +1027,9 @@ int remove_old_deleted_files(struct manifest *m1, struct manifest *m2)
|
|||||||
|
|
||||||
ret = strcmp(file1->filename, file2->filename);
|
ret = strcmp(file1->filename, file2->filename);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
if (file1->is_deleted && file2->is_deleted) {
|
/* use the comparison function passed in to determine if this file
|
||||||
|
* should be removed */
|
||||||
|
if (compfunc(file1, file2)) {
|
||||||
GList *to_delete = list2;
|
GList *to_delete = list2;
|
||||||
list1 = g_list_next(list1);
|
list1 = g_list_next(list1);
|
||||||
list2 = g_list_next(list2);
|
list2 = g_list_next(list2);
|
||||||
@@ -1032,14 +1068,30 @@ int prune_manifest(struct manifest *manifest)
|
|||||||
next = g_list_next(list);
|
next = g_list_next(list);
|
||||||
file = list->data;
|
file = list->data;
|
||||||
|
|
||||||
|
/* if the file is marked as deleted and renamed it is a renamed_from
|
||||||
|
* file. If the rename_peer field is still NULL then it has been
|
||||||
|
* orphaned. Prune these files */
|
||||||
|
if (file->is_deleted && file->is_rename && !file->rename_peer) {
|
||||||
|
manifest->files = g_list_delete_link(manifest->files, list);
|
||||||
|
manifest->count--;
|
||||||
|
}
|
||||||
|
|
||||||
if (OS_IS_STATELESS && (!file->is_deleted) && (file->is_config)) {
|
if (OS_IS_STATELESS && (!file->is_deleted) && (file->is_config)) {
|
||||||
// toward being a stateless OS
|
// toward being a stateless OS
|
||||||
LOG(file, "Skipping config file in manifest write", "component %s", manifest->component);
|
LOG(file, "Skipping config file in manifest write", "component %s", manifest->component);
|
||||||
manifest->files = g_list_delete_link(manifest->files, list);
|
manifest->files = g_list_delete_link(manifest->files, list);
|
||||||
manifest->count--;
|
manifest->count--;
|
||||||
} else if (file->is_boot && file->is_deleted) {
|
} else if (file->is_boot && file->is_deleted) {
|
||||||
// only expose the current best boot files, a client side entity can manage /boot's actual contents
|
/* mark boot files that are going away as ghosted, these will be
|
||||||
// LOG(file, "Skipping deleted boot file in manifest write", "component %s", manifest->component);
|
* cleaned up with the next update */
|
||||||
|
file->is_deleted = 0;
|
||||||
|
file->is_ghosted = 1;
|
||||||
|
} else if (config_ban_debuginfo() && file_is_debuginfo(file->filename)) {
|
||||||
|
/* The configuration option to ban debuginfo from the manifests was
|
||||||
|
* set in server.ini via the [Debuginfo][banned] option. Although
|
||||||
|
* debuginfo additions are banned via analyze_fs, prune it here
|
||||||
|
* to insure mistakenly included debuginfo from old versions is
|
||||||
|
* removed from the manifests. */
|
||||||
manifest->files = g_list_delete_link(manifest->files, list);
|
manifest->files = g_list_delete_link(manifest->files, list);
|
||||||
manifest->count--;
|
manifest->count--;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -58,11 +58,11 @@ static void empty_pack_stage(int full, int from_version, int to_version, char *m
|
|||||||
// (re)create module/version/{delta,staged}
|
// (re)create module/version/{delta,staged}
|
||||||
string_or_die(&path, "%s/%s/%i_to_%i/delta", packstage_dir, module,
|
string_or_die(&path, "%s/%s/%i_to_%i/delta", packstage_dir, module,
|
||||||
from_version, to_version);
|
from_version, to_version);
|
||||||
g_mkdir_with_parents(path, S_IRWXU | S_IRWXG);
|
g_mkdir_with_parents(path, S_IRWXU);
|
||||||
free(path);
|
free(path);
|
||||||
string_or_die(&path, "%s/%s/%i_to_%i/staged", packstage_dir, module,
|
string_or_die(&path, "%s/%s/%i_to_%i/staged", packstage_dir, module,
|
||||||
from_version, to_version);
|
from_version, to_version);
|
||||||
g_mkdir_with_parents(path, S_IRWXU | S_IRWXG);
|
g_mkdir_with_parents(path, S_IRWXU);
|
||||||
free(path);
|
free(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ static void explode_pack_stage(int from_version, int to_version, char *module)
|
|||||||
|
|
||||||
string_or_die(&path, "%s/%s/%i_to_%i/staged", packstage_dir, module,
|
string_or_die(&path, "%s/%s/%i_to_%i/staged", packstage_dir, module,
|
||||||
from_version, to_version);
|
from_version, to_version);
|
||||||
g_mkdir_with_parents(path, S_IRWXU | S_IRWXG);
|
g_mkdir_with_parents(path, S_IRWXU);
|
||||||
dir = opendir(path);
|
dir = opendir(path);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
fprintf(stderr, "There are problems accessing %s, exiting\n", path);
|
fprintf(stderr, "There are problems accessing %s, exiting\n", path);
|
||||||
@@ -144,7 +144,7 @@ static void prepare_pack(struct packdata *pack)
|
|||||||
|
|
||||||
match_manifests(manifest, pack->end_manifest);
|
match_manifests(manifest, pack->end_manifest);
|
||||||
|
|
||||||
link_renames(pack->end_manifest->files, manifest);
|
link_renames(pack->end_manifest->files, pack->to);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void make_pack_full_files(struct packdata *pack)
|
static void make_pack_full_files(struct packdata *pack)
|
||||||
|
|||||||
+217
-153
@@ -21,6 +21,9 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Rename detection and support.
|
||||||
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -37,8 +40,55 @@
|
|||||||
|
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
|
|
||||||
static magic_t mcookie;
|
static bool samefiletype(char *t1, char *t2)
|
||||||
|
{
|
||||||
|
if (t1 && (t1 == t2) && *t1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* For an elf binary, we get the BuildID in it, which
|
||||||
|
* is essentially a hash of the original loaded sections
|
||||||
|
* and hence is unique. This hardly makes for a 'type'
|
||||||
|
*/
|
||||||
|
static char *getmagic(char *filename)
|
||||||
|
{
|
||||||
|
static magic_t mcookie;
|
||||||
|
static GStringChunk *typestore;
|
||||||
|
char *c2;
|
||||||
|
char *c1;
|
||||||
|
if (mcookie == NULL) {
|
||||||
|
mcookie = magic_open(MAGIC_NO_CHECK_COMPRESS);
|
||||||
|
magic_load(mcookie, NULL);
|
||||||
|
typestore = g_string_chunk_new(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
c1 = (char *)magic_file(mcookie, filename);
|
||||||
|
if (!c1) {
|
||||||
|
LOG(NULL, "Cannot find file type", "%s", filename);
|
||||||
|
c1 = "";
|
||||||
|
}
|
||||||
|
c1 = strdup(c1);
|
||||||
|
c2 = strstr(c1, ", BuildID[");
|
||||||
|
if (c2) {
|
||||||
|
*c2 = 0;
|
||||||
|
}
|
||||||
|
c2 = strstr(c1, "not stripped");
|
||||||
|
if (c2) {
|
||||||
|
*c2 = 0;
|
||||||
|
}
|
||||||
|
c2 = strstr(c1, "stripped");
|
||||||
|
if (c2) {
|
||||||
|
*c2 = 0;
|
||||||
|
}
|
||||||
|
c2 = g_string_chunk_insert_const(typestore, c1);
|
||||||
|
free(c1);
|
||||||
|
return c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assign a score roughly in the range -100 to 1000 to express how similar
|
||||||
|
* two files are.
|
||||||
|
*/
|
||||||
double rename_score(struct file *old, struct file *new)
|
double rename_score(struct file *old, struct file *new)
|
||||||
{
|
{
|
||||||
double score = 0.0;
|
double score = 0.0;
|
||||||
@@ -50,6 +100,16 @@ double rename_score(struct file *old, struct file *new)
|
|||||||
score += 400;
|
score += 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the files are smaller than about 200 bytes then even a
|
||||||
|
* single byte change using bsdiff is going to work out as
|
||||||
|
* bigger than just shipping the new file, so stop if they are
|
||||||
|
* not the same. No point in running up bsdiff just for the
|
||||||
|
* sake of it.
|
||||||
|
*/
|
||||||
|
if (new->stat.st_size < BSDIFFSIZE) {
|
||||||
|
return -99.0;
|
||||||
|
}
|
||||||
|
|
||||||
/* points for being in the same directory */
|
/* points for being in the same directory */
|
||||||
if (strcmp(old->dirname, new->dirname) == 0) {
|
if (strcmp(old->dirname, new->dirname) == 0) {
|
||||||
score += 10;
|
score += 10;
|
||||||
@@ -86,12 +146,14 @@ double rename_score(struct file *old, struct file *new)
|
|||||||
in--;
|
in--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* if both start with /boot/vmlinuz give it a boost; this is a local hack due to vmlinuz being very short */
|
/* if both start with /boot/vmlinuz give it a boost; this is a local hack due to vmlinuz being very short */
|
||||||
if (strncmp(old->filename, "/boot/vmlinuz", 13) == 0 && strncmp(new->filename, "/boot/vmlinuz", 13) == 0) {
|
if (strncmp(old->filename, "/boot/vmlinuz", 13) == 0 && strncmp(new->filename, "/boot/vmlinuz", 13) == 0) {
|
||||||
score += 80;
|
score += 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if ELF, points for sharing the same soname to the first dot */
|
/* if ELF, points for sharing the same soname to the first dot */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* negative points for not being within 25%+/-1Kb of the same file size */
|
/* negative points for not being within 25%+/-1Kb of the same file size */
|
||||||
if (old->stat.st_size > ((new->stat.st_size * 1.25) + 1024)) {
|
if (old->stat.st_size > ((new->stat.st_size * 1.25) + 1024)) {
|
||||||
@@ -115,66 +177,40 @@ double rename_score(struct file *old, struct file *new)
|
|||||||
}
|
}
|
||||||
/* negative points for not having the same 'file' type */
|
/* negative points for not having the same 'file' type */
|
||||||
|
|
||||||
if (old->filetype && new->filetype && strcmp(old->filetype, new->filetype) != 0) {
|
if (!samefiletype(old->filetype, new->filetype)) {
|
||||||
score -= 60;
|
score -= 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void precompute_file_data(struct manifest *manifest, struct file *file, int old_rename, GList *last_versions_list)
|
static void precompute_file_data(int version, const char *component, struct file *file)
|
||||||
{
|
{
|
||||||
GList *item;
|
|
||||||
char *c1, *c2;
|
char *c1, *c2;
|
||||||
char *filename = NULL;
|
char *filename = NULL;
|
||||||
int last_change;
|
|
||||||
struct stat buf;
|
|
||||||
|
|
||||||
|
assert(file);
|
||||||
/* fill in the filename-minus-the-numbers field */
|
/* fill in the filename-minus-the-numbers field */
|
||||||
file->alpha_only_filename = calloc(strlen(file->filename) + 1, sizeof(char));
|
file->alpha_only_filename = calloc(strlen(file->filename) + 1, sizeof(char));
|
||||||
|
|
||||||
c1 = file->filename;
|
c1 = file->filename;
|
||||||
c2 = file->alpha_only_filename;
|
c2 = file->alpha_only_filename;
|
||||||
while (*c1) {
|
if (c2) {
|
||||||
while (*c1 && !isalpha(*c1))
|
for (; *c1; c1++) {
|
||||||
c1++;
|
if (isalpha(*c1)) { /* Only copy letters */
|
||||||
if (!*c1) {
|
*c2++ = *c1;
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (c2 == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*c2 = *c1;
|
|
||||||
c1++;
|
|
||||||
c2++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (manifest) {
|
|
||||||
string_or_die(&filename, "%s/%i/%s/%s", image_dir, manifest->version, manifest->component, file->filename);
|
|
||||||
} else if (old_rename) {
|
|
||||||
item = g_list_first(last_versions_list);
|
|
||||||
while (item) {
|
|
||||||
last_change = GPOINTER_TO_INT(item->data);
|
|
||||||
item = g_list_next(item);
|
|
||||||
|
|
||||||
free(filename);
|
|
||||||
string_or_die(&filename, "%s/%i/full/%s", image_dir, last_change, file->filename);
|
|
||||||
if (!lstat(filename, &buf)) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
/* alpha_only_filename is NUL terminated by calloc */
|
||||||
string_or_die(&filename, "%s/%i/full/%s", image_dir, file->last_change, file->filename);
|
|
||||||
}
|
}
|
||||||
|
string_or_die(&filename, "%s/%i/%s/%s", image_dir, version, component, file->filename);
|
||||||
|
|
||||||
/* make sure file->stat.st_size is valid */
|
/* make sure file->stat.st_size is valid */
|
||||||
if (file->stat.st_size == 0) {
|
if (file->stat.st_size == 0) {
|
||||||
int ret;
|
int ret;
|
||||||
|
struct stat buf;
|
||||||
|
|
||||||
if (filename == NULL) {
|
assert(filename);
|
||||||
printf("filename is null...impossible to stat\n");
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
ret = lstat(filename, &buf);
|
ret = lstat(filename, &buf);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
file->stat.st_size = buf.st_size;
|
file->stat.st_size = buf.st_size;
|
||||||
@@ -182,27 +218,19 @@ static void precompute_file_data(struct manifest *manifest, struct file *file, i
|
|||||||
printf("Stat failure on %s\n", filename);
|
printf("Stat failure on %s\n", filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (file->stat.st_size < BSDIFFSIZE) {
|
||||||
c1 = (char *)magic_file(mcookie, filename);
|
/* thing is too small, always will be regenerated
|
||||||
if (c1) {
|
* so no point in trying to figure out what kind
|
||||||
char *c2;
|
* of file it is
|
||||||
file->filetype = strdup(c1);
|
*/
|
||||||
c2 = strstr(file->filetype, "not stripped");
|
|
||||||
if (c2) {
|
|
||||||
*c2 = 0;
|
|
||||||
}
|
|
||||||
c2 = strstr(file->filetype, "stripped");
|
|
||||||
if (c2) {
|
|
||||||
*c2 = 0;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LOG(file, "Cannot find file type", "%s", filename);
|
file->filetype = getmagic(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(filename);
|
free(filename);
|
||||||
|
|
||||||
file->basename = strdup(basename(file->filename));
|
file->basename = g_path_get_basename(file->filename);
|
||||||
file->dirname = strdup(dirname(file->filename));
|
file->dirname = g_path_get_dirname(file->filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
int file_sort_score(gconstpointer a, gconstpointer b)
|
int file_sort_score(gconstpointer a, gconstpointer b)
|
||||||
@@ -222,6 +250,10 @@ int file_sort_score(gconstpointer a, gconstpointer b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* compare file to each deleted file.
|
||||||
|
* set file->rename_peer to best matched deleted file
|
||||||
|
* set file->rename_score to the score
|
||||||
|
*/
|
||||||
static void score_file(GList *deleted_files, struct file *file)
|
static void score_file(GList *deleted_files, struct file *file)
|
||||||
{
|
{
|
||||||
GList *list2;
|
GList *list2;
|
||||||
@@ -246,61 +278,107 @@ static void score_file(GList *deleted_files, struct file *file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rename_detection(struct manifest *manifest, int last_change, GList *last_versions_list)
|
/* delete the first element of the list and return the new head */
|
||||||
|
static GList *del_first(GList *list)
|
||||||
{
|
{
|
||||||
GList *new_files = NULL;
|
/* The first list is the pointer to the list, the second is
|
||||||
GList *deleted_files = NULL;
|
* the pointer to what to delete */
|
||||||
|
return g_list_delete_link(list, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take a list, return a new list where the filter function returns true */
|
||||||
|
static GList *new_filtered_list(GList *list, int version, int (*f)(struct file *file, int version))
|
||||||
|
{
|
||||||
|
/* make a list of new files, no peer */
|
||||||
|
GList *newlist = NULL;
|
||||||
|
list = g_list_first(list);
|
||||||
|
for (; list; list = g_list_next(list)) {
|
||||||
|
struct file *file = list->data;
|
||||||
|
if (f(file, version)) {
|
||||||
|
newlist = g_list_prepend(newlist, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int renamed_file_p(struct file *file, int unused __attribute__((unused)))
|
||||||
|
{
|
||||||
|
return file->is_rename;
|
||||||
|
}
|
||||||
|
/* Return a new list of renamed files */
|
||||||
|
static GList *new_list_renamed_files(GList *infiles)
|
||||||
|
{
|
||||||
|
return new_filtered_list(infiles, 0, renamed_file_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Predicate that returns true if this is a new file in the stated version */
|
||||||
|
static int new_file_p(struct file *file, int version)
|
||||||
|
{
|
||||||
|
if ((file->last_change != version) ||
|
||||||
|
(file->is_deleted) ||
|
||||||
|
(!file->is_file) ||
|
||||||
|
(file->peer)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return a new list of the new files */
|
||||||
|
static GList *list_new_files(struct manifest *manifest)
|
||||||
|
{
|
||||||
|
GList *list = new_filtered_list(manifest->files, manifest->version, new_file_p);
|
||||||
|
/* call precompute_file_data for each file on list, return the list */
|
||||||
|
GList *ret = list;
|
||||||
|
for (list = g_list_first(list); list; list = g_list_next(list)) {
|
||||||
|
struct file *file = list->data;
|
||||||
|
precompute_file_data(manifest->version, manifest->component, file);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int deleted_p(struct file *file, int version)
|
||||||
|
{
|
||||||
|
if ((!file->is_deleted) ||
|
||||||
|
(!file->peer) ||
|
||||||
|
(file->last_change != version) ||
|
||||||
|
(file->peer->is_dir || file->peer->is_link)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GList *list_deleted_files(struct manifest *manifest)
|
||||||
|
{
|
||||||
|
GList *list = new_filtered_list(manifest->files, manifest->version, deleted_p);
|
||||||
|
GList *ret = list;
|
||||||
|
/* call precompute_file_data for each peer of file on list */
|
||||||
|
for (list = g_list_first(list); list; list = g_list_next(list)) {
|
||||||
|
struct file *file = list->data;
|
||||||
|
struct file *peer = file->peer;
|
||||||
|
/* Need to get things from the /full/ as we do not know
|
||||||
|
* which component may be coming from? */
|
||||||
|
precompute_file_data(peer->last_change, "full", peer);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rename_detection(struct manifest *manifest)
|
||||||
|
{
|
||||||
|
GList *new_files;
|
||||||
|
GList *deleted_files;
|
||||||
|
|
||||||
GList *list;
|
GList *list;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
int old_rename = 0;
|
|
||||||
|
|
||||||
if (last_change != manifest->version) {
|
new_files = list_new_files(manifest);
|
||||||
old_rename = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mcookie == NULL) {
|
/* no new files --> no renames -- early exit */
|
||||||
mcookie = magic_open(MAGIC_NO_CHECK_COMPRESS);
|
|
||||||
magic_load(mcookie, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make a list of new files, no peer */
|
|
||||||
list = g_list_first(manifest->files);
|
|
||||||
while (list) {
|
|
||||||
file = list->data;
|
|
||||||
list = g_list_next(list);
|
|
||||||
if ((file->last_change != manifest->version) ||
|
|
||||||
(file->is_deleted) ||
|
|
||||||
(!file->is_file) ||
|
|
||||||
(file->peer)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_files = g_list_prepend(new_files, file);
|
|
||||||
precompute_file_data(manifest, file, old_rename, last_versions_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if there are no new files, we're not having any renames -- early exit */
|
|
||||||
if (!new_files) {
|
if (!new_files) {
|
||||||
LOG(NULL, "No new files, no rename detection", "%s", manifest->component);
|
LOG(NULL, "No new files, no rename detection", "%s", manifest->component);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* make a list of newly deleted files that have a peer */
|
|
||||||
|
|
||||||
list = g_list_first(manifest->files);
|
deleted_files = list_deleted_files(manifest);
|
||||||
while (list) {
|
|
||||||
file = list->data;
|
|
||||||
list = g_list_next(list);
|
|
||||||
if ((!file->is_deleted) ||
|
|
||||||
(!file->peer) ||
|
|
||||||
(file->last_change != last_change) ||
|
|
||||||
(file->peer->is_dir || file->peer->is_link)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
deleted_files = g_list_prepend(deleted_files, file);
|
|
||||||
precompute_file_data(NULL, file->peer, old_rename, last_versions_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nothing got deleted --> no renames --> early exit */
|
/* nothing got deleted --> no renames --> early exit */
|
||||||
if (!deleted_files) {
|
if (!deleted_files) {
|
||||||
@@ -309,7 +387,8 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for each new file, find the deleted file with the highest score, and store the score */
|
/* for each new file, find the deleted file with the highest score,
|
||||||
|
* store it in file->rename_peer and store the score */
|
||||||
list = g_list_first(new_files);
|
list = g_list_first(new_files);
|
||||||
while (list) {
|
while (list) {
|
||||||
file = list->data;
|
file = list->data;
|
||||||
@@ -317,20 +396,24 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
|
|||||||
score_file(deleted_files, file);
|
score_file(deleted_files, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
redo:
|
||||||
/* sort all new files by score */
|
/* sort all new files by score */
|
||||||
|
/* walk the sorted score list.
|
||||||
|
* pick the top score,
|
||||||
|
* check if the score is still valid,
|
||||||
|
* if not, recompute the score and resort
|
||||||
|
* This is probably an O(n^3).
|
||||||
|
*/
|
||||||
new_files = g_list_sort(new_files, file_sort_score);
|
new_files = g_list_sort(new_files, file_sort_score);
|
||||||
|
|
||||||
/* pick the top score, check if the score is still valid, if not, recompute the score and resort */
|
for (; new_files; new_files = del_first(new_files)) {
|
||||||
|
|
||||||
while (new_files) {
|
|
||||||
file = new_files->data;
|
file = new_files->data;
|
||||||
|
if (file->rename_peer == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (file->rename_score < 15.0 || file->rename_peer == NULL) {
|
if (file->rename_score < 15.0) {
|
||||||
new_files = g_list_delete_link(new_files, new_files);
|
LOG(NULL, "Rename not done due to insufficient high score", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
|
||||||
if (file->rename_peer) {
|
|
||||||
LOG(NULL, "Rename not done due to insufficient high score", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,10 +421,8 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
|
|||||||
/* the candidate peer got already taken by another file! */
|
/* the candidate peer got already taken by another file! */
|
||||||
LOG(NULL, "Rename not done due to target already taken", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
|
LOG(NULL, "Rename not done due to target already taken", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
|
||||||
file->rename_peer = NULL;
|
file->rename_peer = NULL;
|
||||||
file->rename_score = -100;
|
|
||||||
score_file(deleted_files, file);
|
score_file(deleted_files, file);
|
||||||
new_files = g_list_sort(new_files, file_sort_score);
|
goto redo;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if valid and score is high enough, make the link by setting the flag and storing the hash */
|
/* if valid and score is high enough, make the link by setting the flag and storing the hash */
|
||||||
@@ -349,13 +430,14 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
|
|||||||
LOG(NULL, "Rename detected!", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
|
LOG(NULL, "Rename detected!", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
|
||||||
|
|
||||||
file->rename_peer->rename_peer = file;
|
file->rename_peer->rename_peer = file;
|
||||||
/* must delete the file from the deleted list */
|
/* must remove the file from the deleted list */
|
||||||
deleted_files = g_list_remove(deleted_files, file->rename_peer);
|
deleted_files = g_list_remove(deleted_files, file->rename_peer);
|
||||||
hash_assign(file->hash, file->rename_peer->hash);
|
hash_assign(file->hash, file->rename_peer->hash);
|
||||||
file->is_rename = 1;
|
file->is_rename = 1;
|
||||||
file->rename_peer->is_rename = 1;
|
file->rename_peer->is_rename = 1;
|
||||||
|
if (!deleted_files) {
|
||||||
new_files = g_list_delete_link(new_files, new_files);
|
break; /* No more deleted files to rename */
|
||||||
|
}
|
||||||
|
|
||||||
} /* lather, rinse, repeat until all files have a target */
|
} /* lather, rinse, repeat until all files have a target */
|
||||||
|
|
||||||
@@ -364,65 +446,47 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
|
|||||||
g_list_free(deleted_files);
|
g_list_free(deleted_files);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int file_found_in_older_manifest(struct manifest *from_manifest, struct file *searched_file)
|
/* What do we need this for?
|
||||||
{
|
*
|
||||||
GList *list;
|
* rename_detection has already set up the links in the manifest it
|
||||||
struct file *file;
|
* was given.
|
||||||
|
*
|
||||||
list = g_list_first(from_manifest->files);
|
*/
|
||||||
while (list) {
|
void link_renames(GList *newfiles, int to_version)
|
||||||
file = list->data;
|
|
||||||
list = g_list_next(list);
|
|
||||||
|
|
||||||
if (file->is_deleted) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(file->filename, searched_file->filename)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void link_renames(GList *newfiles, struct manifest *from_manifest)
|
|
||||||
{
|
{
|
||||||
GList *list1, *list2;
|
GList *list1, *list2;
|
||||||
GList *targets;
|
GList *targets;
|
||||||
struct file *file1, *file2;
|
struct file *file1, *file2;
|
||||||
|
|
||||||
targets = newfiles = g_list_sort(newfiles, file_sort_version);
|
targets = new_list_renamed_files(newfiles);
|
||||||
|
/* TODO: Check that g_list_sort is reasonable speed */
|
||||||
|
targets = newfiles = g_list_sort(targets, file_sort_version);
|
||||||
|
|
||||||
list1 = g_list_first(newfiles);
|
for (list1 = newfiles; list1; list1 = g_list_next(list1)) {
|
||||||
|
|
||||||
/* todo: sort newfiles and targets by hash */
|
|
||||||
|
|
||||||
while (list1) {
|
|
||||||
file1 = list1->data;
|
file1 = list1->data;
|
||||||
list1 = g_list_next(list1);
|
|
||||||
|
|
||||||
if ((file1->peer || !file1->is_rename) ||
|
if (file1->peer || file1->is_deleted) {
|
||||||
(file1->is_deleted)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* now, file1 is the new file that got renamed. time to search the rename targets */
|
/* now, file1 is the new file that got renamed.
|
||||||
|
* time to search the rename targets */
|
||||||
list2 = g_list_first(targets);
|
list2 = g_list_first(targets);
|
||||||
while (list2) {
|
for (; list2; list2 = g_list_next(list2)) {
|
||||||
file2 = list2->data;
|
file2 = list2->data;
|
||||||
list2 = g_list_next(list2);
|
/* This is like deleted_p but not quite */
|
||||||
|
/* deleted_p returns false for directories and links */
|
||||||
if ((!file2->peer || !file2->is_rename) ||
|
if (!file2->peer ||
|
||||||
(!file2->is_deleted) ||
|
(!file2->is_deleted) ||
|
||||||
(!file_found_in_older_manifest(from_manifest, file2))) {
|
(file2->last_change != to_version)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (hash_compare(file2->hash, file1->hash)) {
|
if (hash_compare(file2->hash, file1->hash)) {
|
||||||
file1->rename_peer = file2->peer;
|
file1->rename_peer = file2->peer;
|
||||||
file1->peer = file2->peer;
|
file1->peer = file2->peer;
|
||||||
file2->peer->rename_peer = file1;
|
file2->peer->rename_peer = file1;
|
||||||
list2 = NULL;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(from_manifest);
|
g_list_free(targets);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -235,8 +235,7 @@ GList *get_last_versions_list(int next_version, int max_versions)
|
|||||||
DIR *dir;
|
DIR *dir;
|
||||||
GList *list = NULL;
|
GList *list = NULL;
|
||||||
GList *cur_item, *next_item;
|
GList *cur_item, *next_item;
|
||||||
struct dirent entry;
|
struct dirent *entry;
|
||||||
struct dirent *result;
|
|
||||||
struct stat stat;
|
struct stat stat;
|
||||||
char *filename = NULL;
|
char *filename = NULL;
|
||||||
int idx, build_num, build_type, jump_point;
|
int idx, build_num, build_type, jump_point;
|
||||||
@@ -249,13 +248,13 @@ GList *get_last_versions_list(int next_version, int max_versions)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
|
while ((entry = readdir(dir))) {
|
||||||
if (strspn(entry.d_name, "0123456789") != strlen(entry.d_name)) {
|
if (strspn(entry->d_name, "0123456789") != strlen(entry->d_name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(filename);
|
free(filename);
|
||||||
string_or_die(&filename, "%s/%s", staging_dir, entry.d_name);
|
string_or_die(&filename, "%s/%s", staging_dir, entry->d_name);
|
||||||
|
|
||||||
if (lstat(filename, &stat)) {
|
if (lstat(filename, &stat)) {
|
||||||
LOG(NULL, "lstat failed", "path= %s, strerror= %s",
|
LOG(NULL, "lstat failed", "path= %s, strerror= %s",
|
||||||
@@ -267,11 +266,11 @@ GList *get_last_versions_list(int next_version, int max_versions)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (atoi(entry.d_name) >= next_version) {
|
if (atoi(entry->d_name) >= next_version) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
list = g_list_prepend(list, GINT_TO_POINTER(atoi(entry.d_name)));
|
list = g_list_prepend(list, GINT_TO_POINTER(atoi(entry->d_name)));
|
||||||
}
|
}
|
||||||
free(filename);
|
free(filename);
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
|||||||
+1
-2
@@ -60,8 +60,7 @@ static int xattr_get_value(const char *path, const char *name, char **blob,
|
|||||||
|
|
||||||
/* realloc needed len + 1 in case we need to add final zero
|
/* realloc needed len + 1 in case we need to add final zero
|
||||||
* to ensure consistent blob */
|
* to ensure consistent blob */
|
||||||
value = realloc(*blob, *blob_len + len +
|
value = realloc(*blob, *blob_len + len + (action == XATTRS_ACTION_GET_BLOB ? 1 : 0));
|
||||||
(action == XATTRS_ACTION_GET_BLOB ? 1 : 0));
|
|
||||||
assert(value);
|
assert(value);
|
||||||
|
|
||||||
*blob = value;
|
*blob = value;
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
init_groups_ini os-core test-bundle1 test-bundle2
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle1
|
||||||
|
track_bundle 10 test-bundle2
|
||||||
|
gen_file_plain 10 test-bundle1 foo
|
||||||
|
gen_file_plain 10 test-bundle1 foobar
|
||||||
|
gen_file_plain 10 test-bundle2 foo2
|
||||||
|
gen_includes_file test-bundle2 10 test-bundle1
|
||||||
|
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle1
|
||||||
|
track_bundle 20 test-bundle2
|
||||||
|
gen_file_plain 20 test-bundle1 foo
|
||||||
|
gen_file_plain 20 test-bundle1 foobar
|
||||||
|
gen_file_plain 20 test-bundle1 foobarbaz
|
||||||
|
gen_file_plain 20 test-bundle2 foo2
|
||||||
|
gen_file_plain 20 test-bundle2 foo2bar
|
||||||
|
gen_includes_file test-bundle2 20 test-bundle1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "correct contentsize" {
|
||||||
|
# create a couple updates to both check that contentsize does not add included
|
||||||
|
# bundles and to verify that files changed in previous updates are counted.
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
set_latest_ver 10
|
||||||
|
|
||||||
|
# contentsize for test-bundle2 should not include test-bundle1's contentsize
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 11$' $DIR/www/10/Manifest.test-bundle1 | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 5$' $DIR/www/10/Manifest.test-bundle2 | wc -l) ]]
|
||||||
|
# os-core is large because it includes /usr/*
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 5134$' $DIR/www/10/Manifest.os-core | wc -l) ]]
|
||||||
|
# 5134 + 11 + 5 = 5150
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 5150$' $DIR/www/10/Manifest.full | wc -l) ]]
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
set_latest_ver 20
|
||||||
|
|
||||||
|
# one new file: foobarbaz (10 bytes)
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 21$' $DIR/www/20/Manifest.test-bundle1 | wc -l) ]]
|
||||||
|
# one new file: foo2bar (8 bytes)
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 13$' $DIR/www/20/Manifest.test-bundle2 | wc -l) ]]
|
||||||
|
# os-core should not change size
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 5134$' $DIR/www/10/Manifest.os-core | wc -l) ]]
|
||||||
|
# contentsize for full should be all files, including ones not changed in this release
|
||||||
|
# two new files: foo2bar (8 bytes) and foobarbaz (10 bytes)
|
||||||
|
# 5150 + 10 + 8 = 5168
|
||||||
|
# 5134 + 21 + 13 = 5168
|
||||||
|
[[ 1 -eq $(grep '^contentsize: 5168$' $DIR/www/20/Manifest.full | wc -l) ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
# unban debuginfo
|
||||||
|
sed -i "s|banned=true|banned=false|" $DIR/server.ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core
|
||||||
|
init_groups_ini test-bundle
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
set_os_release 10 test-bundle
|
||||||
|
track_bundle 10 test-bundle
|
||||||
|
|
||||||
|
gen_file_plain 10 test-bundle "/usr/lib/debug/foo"
|
||||||
|
gen_file_plain 10 test-bundle "/usr/src/debug/bar"
|
||||||
|
gen_file_plain 10 test-bundle "/usr/bin/foobar"
|
||||||
|
|
||||||
|
track_bundle 20 os-core
|
||||||
|
set_os_release 20 test-bundle
|
||||||
|
track_bundle 20 test-bundle
|
||||||
|
|
||||||
|
gen_file_plain 20 test-bundle "/usr/foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "debuginfo files pruned" {
|
||||||
|
run sudo sh -c "$CREATE_UPDATE --osversion 10 --statedir $DIR --format 3"
|
||||||
|
echo "$output"
|
||||||
|
# This should not be pruned
|
||||||
|
[[ 1 -eq $(grep '/usr/bin/foobar$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '/usr/lib/debug$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '/usr/src/debug$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
||||||
|
# These should not be pruned right now, but should be pruned with the next
|
||||||
|
# update after being banned in server.ini again
|
||||||
|
[[ 1 -eq $(grep '/usr/src/debug/bar$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '/usr/lib/debug/foo$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
||||||
|
|
||||||
|
# grab the server.ini that bans debuginfo again
|
||||||
|
init_server_ini
|
||||||
|
set_os_release 20 os-core
|
||||||
|
run sudo sh -c "$CREATE_UPDATE --osversion 20 --statedir $DIR --format 3"
|
||||||
|
# This should not be pruned
|
||||||
|
[[ 1 -eq $(grep '/usr/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
# These should be pruned
|
||||||
|
[[ 0 -eq $(grep '/usr/src/debug/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 0 -eq $(grep '/usr/lib/debug/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
Executable
+60
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
|
||||||
|
set_os_release 30 os-core
|
||||||
|
track_bundle 30 os-core
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "full run update creation with delta packs over format bump" {
|
||||||
|
# build the first version
|
||||||
|
echo $CREATE_UPDATE
|
||||||
|
echo $DIR
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
|
||||||
|
set_latest_ver 10
|
||||||
|
|
||||||
|
# then the second version...
|
||||||
|
echo $CREATE_UPDATE
|
||||||
|
echo $DIR
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
|
||||||
|
|
||||||
|
set_latest_ver 20
|
||||||
|
|
||||||
|
# then the third version...
|
||||||
|
echo $CREATE_UPDATE
|
||||||
|
echo $DIR
|
||||||
|
sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 4
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 30
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 30 os-core
|
||||||
|
|
||||||
|
# zero packs should exist (non-zero size) for all versions
|
||||||
|
[ -s $DIR/www/10/pack-os-core-from-0.tar ]
|
||||||
|
[ -s $DIR/www/20/pack-os-core-from-0.tar ]
|
||||||
|
[ -s $DIR/www/30/pack-os-core-from-0.tar ]
|
||||||
|
|
||||||
|
[[ 0 -eq $(grep '^actions: update$' $DIR/www/10/Manifest.MoM | wc -l) ]]
|
||||||
|
[[ 0 -eq $(grep '^actions: update$' $DIR/www/20/Manifest.MoM | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^actions: update$' $DIR/www/30/Manifest.MoM | wc -l) ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
|
||||||
|
gen_file_plain 10 os-core foo
|
||||||
|
gen_file_plain 10 os-core bar
|
||||||
|
gen_file_plain 20 os-core foo
|
||||||
|
gen_file_plain 20 os-core baz
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ensure format numbers cannot be decremented" {
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
set_latest_ver 10
|
||||||
|
|
||||||
|
# Now, decrement the format number. Should result in an EXIT_FAILURE.
|
||||||
|
run sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 2
|
||||||
|
echo "$output"
|
||||||
|
[ $status -eq 1 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
@@ -40,6 +40,12 @@ setup() {
|
|||||||
[[ 1 -eq $(grep '/usr/share/clear/bundles$' $DIR/www/10/Manifest.os-core | wc -l) ]]
|
[[ 1 -eq $(grep '/usr/share/clear/bundles$' $DIR/www/10/Manifest.os-core | wc -l) ]]
|
||||||
[[ 4 -eq $(tar -tf $DIR/www/10/pack-test-bundle-from-0.tar | wc -l) ]]
|
[[ 4 -eq $(tar -tf $DIR/www/10/pack-test-bundle-from-0.tar | wc -l) ]]
|
||||||
[[ 5 -eq $(tar -tf $DIR/www/10/pack-os-core-from-0.tar | wc -l) ]]
|
[[ 5 -eq $(tar -tf $DIR/www/10/pack-os-core-from-0.tar | wc -l) ]]
|
||||||
|
|
||||||
|
# extract test-bundle pack to make sure staged and delta are created with the
|
||||||
|
# correct permissions
|
||||||
|
sudo tar -xf $DIR/www/10/pack-test-bundle-from-0.tar --directory $DIR/www/10/
|
||||||
|
[[ $(stat -c %a $DIR/www/10/staged) -eq 700 ]]
|
||||||
|
[[ $(stat -c %a $DIR/www/10/delta) -eq 700 ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
|
|||||||
Executable
+60
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core test-bundle
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
set_os_release 10 test-bundle
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle
|
||||||
|
|
||||||
|
set_os_release 20 os-core
|
||||||
|
set_os_release 20 test-bundle
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle
|
||||||
|
|
||||||
|
set_os_release 30 os-core
|
||||||
|
set_os_release 30 test-bundle
|
||||||
|
track_bundle 30 os-core
|
||||||
|
track_bundle 30 test-bundle
|
||||||
|
|
||||||
|
gen_file_plain_with_content 10 test-bundle usr/lib/kernel/bar testfile_contents
|
||||||
|
gen_file_plain_with_content 20 test-bundle usr/lib/kernel/baz new_testfile_contents
|
||||||
|
# make sure /usr/lib/kernel stays around
|
||||||
|
mkdir $DIR/image/30/test-bundle/usr/lib/kernel
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "create updates while ghosting boot files before removing them" {
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
|
||||||
|
set_latest_ver 10
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
|
||||||
|
set_latest_ver 20
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 30
|
||||||
|
# version 10: add boot file to 10
|
||||||
|
[ 1 -eq $(grep 'F\.b\. .* 10 /usr/lib/kernel/bar' $DIR/www/10/Manifest.full | wc -l) ]
|
||||||
|
# version 20: ghost boot file instead of deleting it when it doesn't exist
|
||||||
|
[ 1 -eq $(grep '\.gb\. .* 20 /usr/lib/kernel/bar' $DIR/www/20/Manifest.full | wc -l) ]
|
||||||
|
# version 20: add a new boot file
|
||||||
|
[ 1 -eq $(grep 'F\.b\. .* 20 /usr/lib/kernel/baz' $DIR/www/20/Manifest.full | wc -l) ]
|
||||||
|
# version 30: old ghosted file /usr/lib/kernel/bar cleaned up
|
||||||
|
[ 0 -eq $(grep '10 /usr/lib/kernel/bar' $DIR/www/30/Manifest.full | wc -l) ]
|
||||||
|
# version 30: boot file added in version 20 ghosted
|
||||||
|
[ 1 -eq $(grep '\.gb\. .* 30 /usr/lib/kernel/baz' $DIR/www/30/Manifest.full | wc -l) ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core test-bundle1 test-bundle2
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle1
|
||||||
|
track_bundle 10 test-bundle2
|
||||||
|
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle1
|
||||||
|
track_bundle 20 test-bundle2
|
||||||
|
|
||||||
|
gen_file_plain 10 os-core test0
|
||||||
|
gen_file_plain 10 test-bundle1 test1
|
||||||
|
gen_file_plain 10 test-bundle2 test2
|
||||||
|
|
||||||
|
gen_includes_file test-bundle2 10 test-bundle1 test-bundle1
|
||||||
|
gen_includes_file test-bundle2 20 test-bundle1 test-bundle1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "deduplicate bundle includes" {
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
set_latest_ver 10
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
|
||||||
|
# includes list should be deduplicated in both the old and new manifests
|
||||||
|
[[ 1 -eq $(grep '^includes: test-bundle1$' $DIR/www/10/Manifest.test-bundle2 | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^includes: test-bundle1$' $DIR/www/20/Manifest.test-bundle2 | wc -l) ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core test-bundle
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
set_os_release 10 test-bundle
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle
|
||||||
|
|
||||||
|
set_os_release 20 os-core
|
||||||
|
set_os_release 20 test-bundle
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle
|
||||||
|
|
||||||
|
set_os_release 30 os-core
|
||||||
|
set_os_release 30 test-bundle
|
||||||
|
track_bundle 30 os-core
|
||||||
|
track_bundle 30 test-bundle
|
||||||
|
|
||||||
|
gen_file_plain_with_content 10 test-bundle /usr/lib/bar "$(seq 100)"
|
||||||
|
gen_file_plain_with_content 20 test-bundle /usr/lib/baz "$(seq 100)"
|
||||||
|
# different content just to make sure this works with delta renames as well as
|
||||||
|
# direct renames
|
||||||
|
gen_file_plain_with_content 30 test-bundle /usr/lib/foo "$(seq 100) new"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "create updates with renamed-to file getting deleted" {
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
|
||||||
|
set_latest_ver 10
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
|
||||||
|
set_latest_ver 20
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 30
|
||||||
|
# version 10: add file to 10
|
||||||
|
[ 1 -eq $(grep 'F\.\.\. .* 10 /usr/lib/bar' $DIR/www/10/Manifest.test-bundle | wc -l) ]
|
||||||
|
# version 20: rename bar to baz
|
||||||
|
[ 1 -eq $(grep '\.d\.r .* 20 /usr/lib/bar' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep 'F\.\.r .* 20 /usr/lib/baz' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
# version 30: prune original renamed-from file (bar), baz is now a renamed-from file
|
||||||
|
# Check for the new renamed-to file (foo)
|
||||||
|
[ 0 -eq $(grep '/usr/lib/bar' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep '\.d\.r .* 30 /usr/lib/baz' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep 'F\.\.r .* 30 /usr/lib/foo' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export BATS_TEST_DIRNAME=$PWD
|
||||||
|
export TIMEFORMAT
|
||||||
|
TIMEFORMAT="time %R %P%% (%U+%S)"
|
||||||
|
source ../swupdlib.bash
|
||||||
|
exec 3< rundata
|
||||||
|
|
||||||
|
while read -r -u 3
|
||||||
|
do
|
||||||
|
read -i "$REPLY" -e CMD
|
||||||
|
eval "set -x ; time ( $CMD ) ; set +x "
|
||||||
|
done
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
sudo rm -r logs/ web-dir/
|
||||||
|
tar xf ~/data/test.tar
|
||||||
|
init_test_dir
|
||||||
|
init_server_ini
|
||||||
|
set_latest-ver 0
|
||||||
|
init_groups_ini os-core test-bundle
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
time sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
set_latest-ver 10
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
Executable
+246
@@ -0,0 +1,246 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
maybeskip
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core test-bundle
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate data files of 3 types in test-bundle
|
||||||
|
# All files are big enough that they get rename detection
|
||||||
|
gendataA() {
|
||||||
|
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 100)"
|
||||||
|
}
|
||||||
|
gendataB() {
|
||||||
|
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 50) $(seq 52 101)"
|
||||||
|
}
|
||||||
|
gendataC() {
|
||||||
|
# cache string
|
||||||
|
[ -z "$dataC" ] || dataC="$(seq 1000 | gzip | uuencode wombat)"
|
||||||
|
gen_file_plain_with_content "$1" test-bundle "$2" "$dataC"
|
||||||
|
}
|
||||||
|
# Generate small data files
|
||||||
|
gendataAs() {
|
||||||
|
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 50)"
|
||||||
|
}
|
||||||
|
gendataBs() {
|
||||||
|
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 24) $(seq 26 49)"
|
||||||
|
}
|
||||||
|
gendataCs() {
|
||||||
|
# cache string
|
||||||
|
[ -z "$dataCs" ] || dataC="$(seq 50 | gzip | uuencode wombat)"
|
||||||
|
gen_file_plain_with_content "$1" test-bundle "$2" "$dataCs"
|
||||||
|
}
|
||||||
|
|
||||||
|
checkrenamed(){
|
||||||
|
local flags sh1 ver name fromsha1="bad" tosha1
|
||||||
|
# Check that $1 is renamed to $2
|
||||||
|
exec 9< $DIR/www/20/Manifest.test-bundle
|
||||||
|
# skip the header
|
||||||
|
while read -u9
|
||||||
|
do
|
||||||
|
[ -z "$REPLY" ] && break
|
||||||
|
done
|
||||||
|
while read -r -u9 flags sha1 ver name
|
||||||
|
do
|
||||||
|
case "$flags" in
|
||||||
|
(?"dr"?) [ "$name" = "$1" ] && fromsha1=$sha1 ;;
|
||||||
|
(?".r"?) [ "$name" = "$2" ] && tosha1=$sha1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [ "$fromsh1" = "$tosha1" ] ; then return 0 ; else return 1 ; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Guts of doing an update
|
||||||
|
do_an_update() {
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
|
||||||
|
set_latest_ver 10
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "basic rename detection support" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 20 bar
|
||||||
|
do_an_update
|
||||||
|
checkrenamed foo bar
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ignore rename detection for small files" {
|
||||||
|
gendataAs 10 foo
|
||||||
|
gendataAs 20 bar
|
||||||
|
do_an_update
|
||||||
|
# A renamed file comprises a new file and a deleted file
|
||||||
|
[[ 0 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 0 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
@test "ignore rename detection for large to small files" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataAs 20 bar
|
||||||
|
do_an_update
|
||||||
|
# A renamed file comprises a new file and a deleted file
|
||||||
|
[[ 0 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 0 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
@test "ignore rename detection for small to large files" {
|
||||||
|
gendataAs 10 foo
|
||||||
|
gendataA 20 bar
|
||||||
|
do_an_update
|
||||||
|
# A renamed file comprises a new file and a deleted file
|
||||||
|
[[ 0 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 0 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rename one file to two" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 20 bar
|
||||||
|
gendataA 20 baz
|
||||||
|
do_an_update
|
||||||
|
|
||||||
|
# A renamed file comprises a new file and a deleted file
|
||||||
|
[[ 1 -eq $(grep '^F\.\.r.*/ba[rz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
@test "rename two file to one" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 10 foz
|
||||||
|
gendataA 20 baz
|
||||||
|
do_an_update
|
||||||
|
|
||||||
|
# A renamed file comprises a new file and a deleted file
|
||||||
|
[[ 1 -eq $(grep '^F\.\.r.*/baz$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^\.d\.\..*/fo[oz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^\.d\.r.*/fo[oz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
@test "rename two files to two" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 10 foz
|
||||||
|
gendataA 20 bar
|
||||||
|
gendataA 20 baz
|
||||||
|
do_an_update
|
||||||
|
checkrenamed foo bar
|
||||||
|
checkrenamed foz baz
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rename two files to two, one slightly different" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 10 foz
|
||||||
|
gendataA 20 bar
|
||||||
|
gendataB 20 baz
|
||||||
|
do_an_update
|
||||||
|
# we don't actually know how the client we do this rename, but don't care
|
||||||
|
checkrenamed foo bar
|
||||||
|
checkrenamed foz baz
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rename two files to two, each pair slightly different" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataB 10 foz
|
||||||
|
gendataA 20 bar
|
||||||
|
gendataB 20 baz
|
||||||
|
do_an_update
|
||||||
|
checkrenamed foo bar
|
||||||
|
checkrenamed foz baz
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rename two files to two, one very different" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 10 foz
|
||||||
|
gendataA 20 bar
|
||||||
|
gendataC 20 baz
|
||||||
|
do_an_update
|
||||||
|
# A renamed file comprises a new file and a deleted file
|
||||||
|
[[ 1 -eq $(grep '^F\.\.r.*/ba[rz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^\.d\.r.*/fo[oz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
@test "rename two files to two, one small" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 10 foz
|
||||||
|
gendataA 20 bar
|
||||||
|
gendataCs 20 baz
|
||||||
|
do_an_update
|
||||||
|
run checkrenamed foo bar
|
||||||
|
if [ $status -eq 1 ] ; then
|
||||||
|
checkrenamed foz bar
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "directory name changes" {
|
||||||
|
gendataA 10 dir1/foo
|
||||||
|
gendataA 20 dir2/foo
|
||||||
|
do_an_update
|
||||||
|
checkrenamed /dir1/foo /dir2/foo
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "directory name changes small files" {
|
||||||
|
gendataAs 10 dir1/foo
|
||||||
|
gendataAs 20 dir2/foo
|
||||||
|
do_an_update
|
||||||
|
if ! checkrenamed /dir1/foo /dir2/foo ; then false ; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "directory name and small data changes" {
|
||||||
|
gendataA 10 dir1/foo
|
||||||
|
gendataB 20 dir2/foz
|
||||||
|
do_an_update
|
||||||
|
checkrenamed /dir1/foo /dir2/foz
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "directory name and small data changes, choose same name" {
|
||||||
|
gendataA 10 dir1/foo
|
||||||
|
gendataA 10 dir1/foz
|
||||||
|
gendataB 20 dir2/foz
|
||||||
|
do_an_update
|
||||||
|
checkrenamed /dir1/foz /dir2/foz
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "same basename test" {
|
||||||
|
gendataA 10 dir1/foo.so.1
|
||||||
|
gendataA 10 dir1/foz.so.1
|
||||||
|
gendataB 20 dir2/foo.so.2
|
||||||
|
do_an_update
|
||||||
|
checkrenamed /dir1/foo.so.1 /dir2/foo.so.2
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rename file to dir/file" {
|
||||||
|
gendataA 10 foo
|
||||||
|
gendataA 20 foo/bar
|
||||||
|
do_an_update
|
||||||
|
checkrenamed /foo /foo/bar
|
||||||
|
}
|
||||||
|
# @test "rename foo/foo to foo" {
|
||||||
|
# gendataA 10 foo/foo
|
||||||
|
# gendataA 20 foo
|
||||||
|
# do_an_update
|
||||||
|
# checkrenamed /foo/foo /foo
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Emacs and vi support
|
||||||
|
# Local variables:
|
||||||
|
# sh-indentation: 2
|
||||||
|
# End:
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export BATS_TEST_DIRNAME=$PWD
|
||||||
|
export TIMEFORMAT
|
||||||
|
TIMEFORMAT="time %R %P%% (%U+%S)"
|
||||||
|
source ../swupdlib.bash
|
||||||
|
exec 3< rundata
|
||||||
|
|
||||||
|
while read -r -u 3
|
||||||
|
do
|
||||||
|
read -i "$REPLY" -e CMD
|
||||||
|
eval "set -x ; time ( $CMD ) ; set +x "
|
||||||
|
done
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
sudo rm -r logs/ web-dir/
|
||||||
|
tar xf ~/data/test.tar
|
||||||
|
init_test_dir
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core test-bundle
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
time sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
set_latest_ver 10
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
Executable
+144
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
# common functions
|
||||||
|
load "../swupdlib"
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
clean_test_dir
|
||||||
|
init_test_dir
|
||||||
|
tar x -C $DIR/.. -f ~/data/test.tar
|
||||||
|
mv $DIR/image/20/test-bundle/usr/share/bash-completion/completions{,.old}
|
||||||
|
|
||||||
|
init_server_ini
|
||||||
|
set_latest_ver 0
|
||||||
|
init_groups_ini os-core test-bundle
|
||||||
|
|
||||||
|
set_os_release 10 os-core
|
||||||
|
track_bundle 10 os-core
|
||||||
|
track_bundle 10 test-bundle
|
||||||
|
set_os_release 20 os-core
|
||||||
|
track_bundle 20 os-core
|
||||||
|
track_bundle 20 test-bundle
|
||||||
|
|
||||||
|
# Files have different names ("foo" vs "bar"), but have the same content
|
||||||
|
gen_file_plain_with_content 10 test-bundle "foo" "data"
|
||||||
|
gen_file_plain_with_content 20 test-bundle "bar" "data"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rename detection support" {
|
||||||
|
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
|
||||||
|
set_latest_ver 10
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
|
|
||||||
|
# A renamed file comprises a new file and a deleted file
|
||||||
|
[[ 1 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
[[ 1 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# @test "rename detection support 1 to 2" {
|
||||||
|
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
|
||||||
|
# set_latest_ver 10
|
||||||
|
|
||||||
|
# gen_file_plain_with_content 20 test-bundle "baz" "data"
|
||||||
|
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
|
|
||||||
|
# # A renamed file comprises a new file and a deleted file.
|
||||||
|
# # Should be one file that is not renamed
|
||||||
|
# # Need to figure out what happens to hard links
|
||||||
|
# [[ 1 -eq $(grep -c '^F\.\.r.*/ba[rz]$' $DIR/www/20/Manifest.test-bundle) ]]
|
||||||
|
# [[ 1 -eq $(grep -c '^F\.\.\..*/ba[rz]$' $DIR/www/20/Manifest.test-bundle) ]]
|
||||||
|
# [[ 1 -eq $(grep -c '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle) ]]
|
||||||
|
# }
|
||||||
|
|
||||||
|
# @test "rename detection support 2 to 2" {
|
||||||
|
# gen_file_plain_with_content 10 test-bundle "fee" "data"
|
||||||
|
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
|
||||||
|
# set_latest_ver 10
|
||||||
|
|
||||||
|
# gen_file_plain_with_content 20 test-bundle "foo~" "data"
|
||||||
|
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
|
|
||||||
|
# # A renamed file comprises a new file and a deleted file.
|
||||||
|
# # Should be one file that is not renamed
|
||||||
|
# # Need to figure out what happens to hard links
|
||||||
|
# [[ 1 -eq $(grep -c '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle) ]]
|
||||||
|
# [[ 1 -eq $(grep -c '^F\.\.r.*/foo~$' $DIR/www/20/Manifest.test-bundle) ]]
|
||||||
|
# [[ 2 -eq $(grep -c '^\.d\.r.*/f..$' $DIR/www/20/Manifest.test-bundle) ]]
|
||||||
|
# }
|
||||||
|
|
||||||
|
# @test "rename detection support binary files" {
|
||||||
|
# sudo mkdir -p $DIR/image/10/test-bundle/bin/
|
||||||
|
# sudo cp /bin/gcc-ar $DIR/image/10/test-bundle/bin/gcc-ar.1
|
||||||
|
# sudo cp /bin/gcc-ar $DIR/image/20/test-bundle/gcc-ar.1
|
||||||
|
# strip $DIR/image/20/test-bundle/gcc-ar.1
|
||||||
|
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
|
||||||
|
# set_latest_ver 10
|
||||||
|
|
||||||
|
# gen_file_plain_with_content 20 test-bundle "foo~" "And now not the same"
|
||||||
|
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
|
|
||||||
|
# # A renamed file comprises a new file and a deleted file.
|
||||||
|
# # Should be one file that is not renamed
|
||||||
|
# # Need to figure out what happens to hard links
|
||||||
|
# }
|
||||||
|
|
||||||
|
# @test "rename detection support binary files 2" {
|
||||||
|
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 10
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
|
||||||
|
|
||||||
|
# set_latest_ver 10
|
||||||
|
|
||||||
|
# gen_file_plain_with_content 20 test-bundle "foo" "And now not the same"
|
||||||
|
# gen_file_plain_with_content 20 test-bundle "foo.1" "data"
|
||||||
|
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
|
||||||
|
# sudo $MAKE_FULLFILES --statedir $DIR 20
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
|
||||||
|
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
|
||||||
|
|
||||||
|
# # A renamed file comprises a new file and a deleted file.
|
||||||
|
# # Should be one file that is not renamed
|
||||||
|
# # Need to figure out what happens to hard links
|
||||||
|
# }
|
||||||
|
|
||||||
|
# # vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
@@ -40,6 +40,11 @@ EOF
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# If the variable RUN_JUST_ONE is set then only run that test
|
||||||
|
maybeskip() {
|
||||||
|
[ -z "$RUN_JUST_ONE" ] || [ "$RUN_JUST_ONE" -eq "$BATS_TEST_NUMBER" ] || skip
|
||||||
|
}
|
||||||
|
|
||||||
set_os_release() {
|
set_os_release() {
|
||||||
local ver=$1
|
local ver=$1
|
||||||
local bundle=$2
|
local bundle=$2
|
||||||
@@ -92,7 +97,10 @@ gen_file_plain() {
|
|||||||
local name="$3"
|
local name="$3"
|
||||||
|
|
||||||
# Add plain text file into a bundle
|
# Add plain text file into a bundle
|
||||||
mkdir -p $DIR/image/$ver/$bundle/$(dirname "$name")
|
case "$name" in
|
||||||
|
(*"/"*) mkdir -p "$DIR/image/$ver/$bundle/${name%/*}" ;;
|
||||||
|
(*) mkdir -p $DIR/image/$ver/$bundle ;;
|
||||||
|
esac
|
||||||
echo "$name" > $DIR/image/$ver/$bundle/"$name"
|
echo "$name" > $DIR/image/$ver/$bundle/"$name"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +110,10 @@ gen_file_plain_change() {
|
|||||||
local name="$3"
|
local name="$3"
|
||||||
|
|
||||||
# Add plain text file into a bundle
|
# Add plain text file into a bundle
|
||||||
mkdir -p $DIR/image/$ver/$bundle/$(dirname "$name")
|
case "$name" in
|
||||||
|
(*"/"*) mkdir -p "$DIR/image/$ver/$bundle/${name%/*}" ;;
|
||||||
|
(*) mkdir -p $DIR/image/$ver/$bundle ;;
|
||||||
|
esac
|
||||||
echo "$ver $name" > $DIR/image/$ver/$bundle/"$name"
|
echo "$ver $name" > $DIR/image/$ver/$bundle/"$name"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,4 +148,16 @@ hash_for() {
|
|||||||
awk -F'\t' -v NAME="$name" 'NF == 4 && $4 == NAME { print $2 }' $DIR/www/$ver/Manifest.$bundle
|
awk -F'\t' -v NAME="$name" 'NF == 4 && $4 == NAME { print $2 }' $DIR/www/$ver/Manifest.$bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gen_file_plain_with_content() {
|
||||||
|
local ver=$1
|
||||||
|
local bundle=$2
|
||||||
|
local name="$3"
|
||||||
|
local content="$4"
|
||||||
|
case "$name" in
|
||||||
|
(*"/"*) mkdir -p "$DIR/image/$ver/$bundle/${name%/*}" ;;
|
||||||
|
(*) mkdir -p $DIR/image/$ver/$bundle ;;
|
||||||
|
esac
|
||||||
|
echo "$content" > $DIR/image/$ver/$bundle/"$name"
|
||||||
|
}
|
||||||
|
|
||||||
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
|
|||||||
Reference in New Issue
Block a user