mirror of
https://github.com/clearlinux/swupd-server.git
synced 2026-10-04 16:08:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d3a813859 | ||
|
|
5aa1bf9188 | ||
|
|
de04b8400c | ||
|
|
f5b614e39c | ||
|
|
03feb2af49 | ||
|
|
f3f1524f34 | ||
|
|
90c1c89183 | ||
|
|
010131eb85 | ||
|
|
fcf4e0d7fc | ||
|
|
6d3121cdc6 | ||
|
|
6190894f40 | ||
|
|
e554a06ea6 | ||
|
|
599ce7cb77 | ||
|
|
b8c5bdd2f3 | ||
|
|
14cef226d1 | ||
|
|
83a9c39556 | ||
|
|
b0c4a1c24e | ||
|
|
302ab343ca | ||
|
|
073b1fe062 | ||
|
|
47addb4fa4 | ||
|
|
557fb493ca | ||
|
|
ded4f3003d | ||
|
|
cb9fe9f208 |
+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
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ dist_check_SCRIPTS = \
|
|||||||
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/includes-deduplicate/test.bats \
|
||||||
test/functional/no-delta/test.bats \
|
test/functional/no-delta/test.bats \
|
||||||
@@ -127,6 +128,7 @@ dist_check_SCRIPTS = \
|
|||||||
|
|
||||||
if RENAMES
|
if RENAMES
|
||||||
dist_check_SCRIPTS += \
|
dist_check_SCRIPTS += \
|
||||||
|
test/functional/orphaned-renames/test.bats \
|
||||||
test/functional/renames/test.bats
|
test/functional/renames/test.bats
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|||||||
+1
-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.6.2, matthew.johnson@intel.com)
|
AC_INIT(swupd-server, 3.7.4, 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
|
||||||
|
|||||||
+17
-4
@@ -117,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 */
|
||||||
@@ -194,7 +198,8 @@ 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 void clean_renames(struct manifest *manifest);
|
||||||
|
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);
|
||||||
@@ -253,6 +258,7 @@ extern void type_change_detection(struct manifest *manifest);
|
|||||||
|
|
||||||
extern void rename_detection(struct manifest *manifest);
|
extern void rename_detection(struct manifest *manifest);
|
||||||
extern void link_renames(GList *newfiles, int to_version);
|
extern void link_renames(GList *newfiles, int to_version);
|
||||||
|
extern void final_link(GList *files);
|
||||||
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);
|
||||||
@@ -262,10 +268,17 @@ 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);
|
extern bool file_is_debuginfo(const char *path);
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -151,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;
|
||||||
|
|||||||
+48
-8
@@ -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,12 +336,23 @@ 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);
|
if (old_full->format < new_full->format) {
|
||||||
|
old_deleted = remove_deprecated_files(old_full, new_full, both_deleted);
|
||||||
|
} else {
|
||||||
|
old_deleted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
print_elapsed_time("full manifest creation", &previous_time, ¤t_time);
|
print_elapsed_time("full manifest creation", &previous_time, ¤t_time);
|
||||||
@@ -385,16 +407,24 @@ int main(int argc, char **argv)
|
|||||||
/* Detect renamed files specifically for os-core */
|
/* Detect renamed files specifically for os-core */
|
||||||
rename_detection(new_core);
|
rename_detection(new_core);
|
||||||
#endif
|
#endif
|
||||||
old_deleted = remove_old_deleted_files(old_core, new_core);
|
if (old_core->format < new_core->format) {
|
||||||
|
old_deleted = remove_deprecated_files(old_core, new_core, both_deleted);
|
||||||
|
} else {
|
||||||
|
old_deleted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_ghosted = remove_deprecated_files(old_core, new_core, both_ghosted);
|
||||||
sort_manifest_by_version(new_core); /* sorts by filename */
|
sort_manifest_by_version(new_core); /* sorts by filename */
|
||||||
|
/* clean up orphaned renames by marking them as deleted */
|
||||||
|
clean_renames(new_core);
|
||||||
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");
|
||||||
@@ -506,13 +536,21 @@ int main(int argc, char **argv)
|
|||||||
rename_detection(newm);
|
rename_detection(newm);
|
||||||
#endif
|
#endif
|
||||||
/* Step 6b: otherwise, write out the manifest */
|
/* Step 6b: otherwise, write out the manifest */
|
||||||
old_deleted = remove_old_deleted_files(oldm, newm);
|
if (oldm->format < newm->format) {
|
||||||
|
old_deleted = remove_deprecated_files(oldm, newm, both_deleted);
|
||||||
|
} else {
|
||||||
|
old_deleted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
/* clean up orphaned renames by marking them as deleted */
|
||||||
|
clean_renames(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);
|
||||||
@@ -549,6 +587,8 @@ int main(int argc, char **argv)
|
|||||||
maximize_to_full(new_MoM, new_full);
|
maximize_to_full(new_MoM, new_full);
|
||||||
|
|
||||||
sort_manifest_by_version(new_full);
|
sort_manifest_by_version(new_full);
|
||||||
|
/* clean up orphaned renames by marking them as deleted */
|
||||||
|
clean_renames(new_full);
|
||||||
prune_manifest(new_full);
|
prune_manifest(new_full);
|
||||||
if (write_manifest(new_full) != 0) {
|
if (write_manifest(new_full) != 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|||||||
+8
-8
@@ -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);
|
||||||
@@ -303,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");
|
||||||
|
|||||||
+101
-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)
|
||||||
|
|||||||
+161
-135
@@ -240,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') {
|
||||||
@@ -311,6 +323,21 @@ struct manifest *manifest_from_file(int version, char *component)
|
|||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* do not set peer for the case where the old file was deleted and the new file
|
||||||
|
* was added back. This peer is normally set when the names match up and this
|
||||||
|
* triggers an attempt at creating a delta during pack creation. For this case
|
||||||
|
* where the old file is deleted or ghosted and is brought back we do not want
|
||||||
|
* this to be attempted, as the old file will not be present when trying to
|
||||||
|
* create the delta.
|
||||||
|
*
|
||||||
|
* We can go even further and not set peers for files when the old one did not
|
||||||
|
* exist (deleted or ghosted). It is not necessary to check the status of the
|
||||||
|
* new file */
|
||||||
|
bool should_have_peer(struct file *file1, struct file *file2)
|
||||||
|
{
|
||||||
|
return (!(file1->is_deleted || file1->is_ghosted));
|
||||||
|
}
|
||||||
|
|
||||||
void free_manifest(struct manifest *manifest)
|
void free_manifest(struct manifest *manifest)
|
||||||
{
|
{
|
||||||
struct file *file;
|
struct file *file;
|
||||||
@@ -328,6 +355,62 @@ void free_manifest(struct manifest *manifest)
|
|||||||
free(manifest);
|
free(manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool same_file_contents(struct file *file1, struct file *file2)
|
||||||
|
{
|
||||||
|
return (file1->is_dir == file2->is_dir &&
|
||||||
|
file1->is_link == file2->is_link &&
|
||||||
|
file1->is_deleted == file2->is_deleted &&
|
||||||
|
file1->is_file == file2->is_file &&
|
||||||
|
file1->is_config == file2->is_config &&
|
||||||
|
file1->is_state == file2->is_state &&
|
||||||
|
file1->is_boot == file2->is_boot &&
|
||||||
|
hash_compare(file1->hash, file2->hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a deleted file entry for it in the target list. However, since we're
|
||||||
|
* currently walking the list we HAVE to prepend the entry. Calling function
|
||||||
|
* should track to sort at the end.
|
||||||
|
*/
|
||||||
|
static void add_deleted_file(struct file *source, struct manifest *manifest)
|
||||||
|
{
|
||||||
|
struct file *deleted;
|
||||||
|
deleted = calloc(1, sizeof(struct file));
|
||||||
|
if (deleted == NULL) {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleted->filename = strdup(source->filename);
|
||||||
|
hash_set_zeros(deleted->hash);
|
||||||
|
deleted->is_deleted = 1;
|
||||||
|
deleted->is_config = source->is_config;
|
||||||
|
deleted->is_state = source->is_state;
|
||||||
|
deleted->is_boot = source->is_boot;
|
||||||
|
/* ghost deleted boot files */
|
||||||
|
deleted->is_ghosted = source->is_ghosted | source->is_boot & source->is_deleted;
|
||||||
|
|
||||||
|
if (deleted->is_ghosted || source->is_deleted) {
|
||||||
|
/* if the new file is ghosted or the file was deleted, preserve
|
||||||
|
* hash (all zeros if source->is_deleted) and rename status */
|
||||||
|
deleted->is_rename = source->is_rename;
|
||||||
|
hash_assign(source->hash, deleted->hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for deleted files last_change remains the same.
|
||||||
|
* otherwise last change is now */
|
||||||
|
deleted->last_change = source->is_deleted ? source->last_change : manifest->version;
|
||||||
|
|
||||||
|
deleted->peer = source;
|
||||||
|
source->peer = deleted;
|
||||||
|
|
||||||
|
/* if we are adding a deleted file we are walking the old and new
|
||||||
|
* manifest files in-sync. we need to prepend this file in order to
|
||||||
|
* not process it twice. This is why it is important for the calling
|
||||||
|
* function to sort the list again at the end */
|
||||||
|
manifest->files = g_list_prepend(manifest->files, deleted);
|
||||||
|
manifest->count++;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
backfill the "last changed" of each file in a manifest
|
backfill the "last changed" of each file in a manifest
|
||||||
by comparing the hash against the same file in the previous manifest
|
by comparing the hash against the same file in the previous manifest
|
||||||
@@ -342,7 +425,6 @@ int match_manifests(struct manifest *m1, struct manifest *m2)
|
|||||||
struct file *file1, *file2;
|
struct file *file1, *file2;
|
||||||
int must_sort = 0;
|
int must_sort = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int first = 1;
|
|
||||||
|
|
||||||
if (!m1) {
|
if (!m1) {
|
||||||
printf("Matching manifests up failed: No old manifest!\n");
|
printf("Matching manifests up failed: No old manifest!\n");
|
||||||
@@ -370,158 +452,72 @@ int match_manifests(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 && file1->is_rename) {
|
/* file is present in both manifests */
|
||||||
file2->is_rename = file1->is_rename;
|
if (same_file_contents(file1, file2) && file1->last_change >= minversion) {
|
||||||
hash_assign(file1->hash, file2->hash);
|
/* file did not change */
|
||||||
}
|
|
||||||
|
|
||||||
if (hash_compare(file1->hash, file2->hash) &&
|
|
||||||
file1->is_dir == file2->is_dir &&
|
|
||||||
file1->is_link == file2->is_link &&
|
|
||||||
file1->is_deleted == file2->is_deleted &&
|
|
||||||
file1->is_file == file2->is_file &&
|
|
||||||
file1->is_config == file2->is_config &&
|
|
||||||
file1->is_state == file2->is_state &&
|
|
||||||
file1->is_boot == file2->is_boot &&
|
|
||||||
file1->last_change >= minversion) {
|
|
||||||
file2->last_change = file1->last_change;
|
file2->last_change = file1->last_change;
|
||||||
file2->is_rename = file1->is_rename;
|
file2->is_rename = file1->is_rename;
|
||||||
} else {
|
} else {
|
||||||
|
/* file changed */
|
||||||
account_changed_file();
|
account_changed_file();
|
||||||
if (first) {
|
|
||||||
LOG(file1, "file changed", "");
|
|
||||||
first = 0;
|
|
||||||
}
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file1->is_deleted || file2->is_deleted) {
|
/* check if these files should be peers */
|
||||||
|
if (should_have_peer(file1, file2)) {
|
||||||
file1->peer = file2;
|
file1->peer = file2;
|
||||||
file2->peer = file1;
|
file2->peer = file1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* there was a match, advance both lists */
|
||||||
list1 = g_list_next(list1);
|
list1 = g_list_next(list1);
|
||||||
list2 = g_list_next(list2);
|
list2 = g_list_next(list2);
|
||||||
continue;
|
} else if (ret < 0) {
|
||||||
}
|
/* file1 was deleted, create entry for deleted file */
|
||||||
if (first) {
|
add_deleted_file(file1, m2);
|
||||||
LOG(file1, "file added? ", "(file2 is %s)", file2->filename);
|
|
||||||
first = 0;
|
|
||||||
}
|
|
||||||
if (ret < 0) {
|
|
||||||
struct file *file3;
|
|
||||||
/*
|
|
||||||
* if we get here, file1 got deleted... what we must do
|
|
||||||
* is add a file entry for it in the target list.
|
|
||||||
* However, since we're currently walking the list we
|
|
||||||
* HAVE to prepend the entry.. and mark for sort at the
|
|
||||||
* end.
|
|
||||||
*/
|
|
||||||
file3 = calloc(1, sizeof(struct file));
|
|
||||||
if (file3 == NULL) {
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
file3->filename = strdup(file1->filename);
|
|
||||||
hash_set_zeros(file3->hash);
|
|
||||||
file3->is_deleted = 1;
|
|
||||||
file3->is_config = file1->is_config;
|
|
||||||
file3->is_state = file1->is_state;
|
|
||||||
file3->is_boot = file1->is_boot;
|
|
||||||
|
|
||||||
if (!file1->is_deleted) {
|
|
||||||
file3->last_change = m2->version;
|
|
||||||
} else {
|
|
||||||
file3->last_change = file1->last_change;
|
|
||||||
file3->is_rename = file1->is_rename;
|
|
||||||
hash_assign(file1->hash, file3->hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
file3->peer = file1;
|
|
||||||
file1->peer = file3;
|
|
||||||
|
|
||||||
list1 = g_list_next(list1);
|
|
||||||
m2->files = g_list_prepend(m2->files, file3);
|
|
||||||
m2->count++;
|
|
||||||
if (!file1->is_deleted) {
|
if (!file1->is_deleted) {
|
||||||
account_deleted_file();
|
account_deleted_file();
|
||||||
count++;
|
count++;
|
||||||
if (first) {
|
|
||||||
LOG(file1, "file got deleted", "");
|
|
||||||
first = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
must_sort = 1;
|
must_sort = 1;
|
||||||
continue;
|
/* advance list1 for next file */
|
||||||
|
list1 = g_list_next(list1);
|
||||||
|
} else {
|
||||||
|
/* if we get here, ret is > 0, which means this is a new file added */
|
||||||
|
/* all we do is advance the pointer */
|
||||||
|
account_new_file();
|
||||||
|
count++;
|
||||||
|
/* advance list2 to check against same file in list1 */
|
||||||
|
list2 = g_list_next(list2);
|
||||||
}
|
}
|
||||||
/* if we get here, ret is > 0, which means this is a new file added */
|
|
||||||
/* all we do is advance the pointer */
|
|
||||||
account_new_file();
|
|
||||||
list2 = g_list_next(list2);
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now deal with the tail ends */
|
/* now deal with the tail ends */
|
||||||
while (list1) {
|
/* deleted files from list1 */
|
||||||
|
for (; list1; list1 = g_list_next(list1)) {
|
||||||
file1 = list1->data;
|
file1 = list1->data;
|
||||||
|
add_deleted_file(file1, m2);
|
||||||
struct file *file3;
|
|
||||||
|
|
||||||
if (first) {
|
|
||||||
LOG(file1, "file changed tail", "");
|
|
||||||
first = 0;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
/*
|
|
||||||
* if we get here, file1 got deleted... what we must do is add
|
|
||||||
* a file entry for it in the target list. However, since
|
|
||||||
* we're currently walking the list we HAVE to prepend the
|
|
||||||
* entry.. and mark for sort at the end.
|
|
||||||
*/
|
|
||||||
file3 = calloc(1, sizeof(struct file));
|
|
||||||
if (file3 == NULL) {
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
file3->filename = strdup(file1->filename);
|
|
||||||
hash_set_zeros(file3->hash);
|
|
||||||
file3->is_deleted = 1;
|
|
||||||
file3->is_config = file1->is_config;
|
|
||||||
file3->is_state = file1->is_state;
|
|
||||||
file3->is_boot = file1->is_boot;
|
|
||||||
|
|
||||||
if (!file1->is_deleted) {
|
|
||||||
file3->last_change = m2->version;
|
|
||||||
} else {
|
|
||||||
file3->last_change = file1->last_change;
|
|
||||||
file3->is_rename = file1->is_rename;
|
|
||||||
hash_assign(file1->hash, file3->hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
file3->peer = file1;
|
|
||||||
file1->peer = file3;
|
|
||||||
|
|
||||||
list1 = g_list_next(list1);
|
|
||||||
m2->files = g_list_prepend(m2->files, file3);
|
|
||||||
m2->count++;
|
|
||||||
if (!file1->is_deleted) {
|
if (!file1->is_deleted) {
|
||||||
account_deleted_file();
|
account_deleted_file();
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
must_sort = 1;
|
must_sort = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (list2) {
|
/* added files from list2 */
|
||||||
|
for (; list2; list2 = g_list_next(list2)) {
|
||||||
account_new_file();
|
account_new_file();
|
||||||
list2 = g_list_next(list2);
|
|
||||||
if (first) {
|
|
||||||
first = 0;
|
|
||||||
}
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* finally, sort the list if necessary */
|
||||||
if (must_sort) {
|
if (must_sort) {
|
||||||
m2->files = g_list_sort(m2->files, file_sort_filename);
|
m2->files = g_list_sort(m2->files, file_sort_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returned count of changed files */
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,6 +654,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';
|
||||||
}
|
}
|
||||||
@@ -962,13 +962,12 @@ bool changed_includes(struct manifest *old, struct manifest *new)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For a format bump, it's convenient to remove deleted files from manifests
|
/* Remove files deprecated in this version as identified by the compfunc
|
||||||
* that last changed prior to the format bump, since they are no longer
|
* function. This function can be used to remove old deleted files over a format
|
||||||
* considered for deletion as part of an update.
|
* bump and remove deprecated ghosted files.
|
||||||
*
|
|
||||||
* 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;
|
||||||
@@ -984,11 +983,6 @@ int remove_old_deleted_files(struct manifest *m1, struct manifest *m2)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the common case, so bail early, reporting no deletions */
|
|
||||||
if (m1->format == m2->format) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At this point, the manifest formats mismatch, and it's assumed that
|
/* At this point, the manifest formats mismatch, and it's assumed that
|
||||||
* m1 is the old manifest, and m2 is the new.
|
* m1 is the old manifest, and m2 is the new.
|
||||||
*/
|
*/
|
||||||
@@ -1006,7 +1000,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);
|
||||||
@@ -1027,6 +1023,36 @@ int remove_old_deleted_files(struct manifest *m1, struct manifest *m2)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Performs a final link of renames in the manifest to catch any renames from
|
||||||
|
* old versions and then removes any orphaned renames */
|
||||||
|
void clean_renames(struct manifest *manifest)
|
||||||
|
{
|
||||||
|
GList *list;
|
||||||
|
struct file *file;
|
||||||
|
|
||||||
|
/* make sure all renames are linked, this is necessary for renames
|
||||||
|
* from old manifests that carry over to the current one */
|
||||||
|
final_link(manifest->files);
|
||||||
|
|
||||||
|
list = g_list_first(manifest->files);
|
||||||
|
while (list) {
|
||||||
|
file = list->data;
|
||||||
|
/* if a file is marked as a rename but has lost its rename_peer
|
||||||
|
* it needs to be cleaned up */
|
||||||
|
if (file->is_rename && !file->rename_peer) {
|
||||||
|
/* no longer a rename */
|
||||||
|
file->is_rename = 0;
|
||||||
|
/* if the file is marked as deleted and renamed it is a
|
||||||
|
* renamed-from file. Mark these as deleted now */
|
||||||
|
if (file->is_deleted) {
|
||||||
|
hash_set_zeros(file->hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list = g_list_next(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Conditionally remove some things from a manifest.
|
/* Conditionally remove some things from a manifest.
|
||||||
* Returns > 0 when the pruned manifest has new files.
|
* Returns > 0 when the pruned manifest has new files.
|
||||||
* Returns 0 when the pruned manifest no longer has new files.
|
* Returns 0 when the pruned manifest no longer has new files.
|
||||||
@@ -1051,10 +1077,10 @@ int prune_manifest(struct manifest *manifest)
|
|||||||
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 */
|
||||||
manifest->files = g_list_delete_link(manifest->files, list);
|
file->is_deleted = 0;
|
||||||
manifest->count--;
|
file->is_ghosted = 1;
|
||||||
} else if (config_ban_debuginfo() && file_is_debuginfo(file->filename)) {
|
} else if (config_ban_debuginfo() && file_is_debuginfo(file->filename)) {
|
||||||
/* The configuration option to ban debuginfo from the manifests was
|
/* The configuration option to ban debuginfo from the manifests was
|
||||||
* set in server.ini via the [Debuginfo][banned] option. Although
|
* set in server.ini via the [Debuginfo][banned] option. Although
|
||||||
|
|||||||
+14
-4
@@ -138,12 +138,13 @@ static void prepare_pack(struct packdata *pack)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* read in manifest from file */
|
||||||
pack->end_manifest = manifest_from_file(pack->to, pack->module);
|
pack->end_manifest = manifest_from_file(pack->to, pack->module);
|
||||||
|
/* wipe any old packs (failed) and re-create pack directory structure */
|
||||||
empty_pack_stage(0, pack->from, pack->to, pack->module);
|
empty_pack_stage(0, pack->from, pack->to, pack->module);
|
||||||
|
/* match up old and new manifests */
|
||||||
match_manifests(manifest, pack->end_manifest);
|
match_manifests(manifest, pack->end_manifest);
|
||||||
|
/* link renames together */
|
||||||
link_renames(pack->end_manifest->files, pack->to);
|
link_renames(pack->end_manifest->files, pack->to);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +162,11 @@ static void make_pack_full_files(struct packdata *pack)
|
|||||||
while (item) {
|
while (item) {
|
||||||
file = item->data;
|
file = item->data;
|
||||||
item = g_list_next(item);
|
item = g_list_next(item);
|
||||||
if ((!file->peer || file->peer->is_deleted) && !file->is_deleted && !file->rename_peer) {
|
/* only create full files if renames or deltas are not appropriate */
|
||||||
|
if ((!file->peer || file->peer->is_deleted || file->peer->is_ghosted) &&
|
||||||
|
!file->is_deleted && /* no full-files for deletes */
|
||||||
|
!file->is_ghosted && /* no full-files for ghosts */
|
||||||
|
!file->rename_peer) { /* no full-files for renames */
|
||||||
char *from, *to;
|
char *from, *to;
|
||||||
char *fullfrom, *fullto;
|
char *fullfrom, *fullto;
|
||||||
|
|
||||||
@@ -248,6 +253,8 @@ static GList *consolidate_packs_delta_files(GList *files, struct packdata *pack)
|
|||||||
file = item->data;
|
file = item->data;
|
||||||
item = g_list_next(item);
|
item = g_list_next(item);
|
||||||
|
|
||||||
|
/* skip old files, files without a peer, and files that are not
|
||||||
|
* files, directories, or links */
|
||||||
if ((file->last_change <= pack->from) ||
|
if ((file->last_change <= pack->from) ||
|
||||||
(!file->peer) ||
|
(!file->peer) ||
|
||||||
(!file->is_file && !file->is_dir && !file->is_link)) {
|
(!file->is_file && !file->is_dir && !file->is_link)) {
|
||||||
@@ -257,7 +264,10 @@ static GList *consolidate_packs_delta_files(GList *files, struct packdata *pack)
|
|||||||
string_or_die(&from, "%s/%i/delta/%i-%i-%s-%s", staging_dir, file->last_change,
|
string_or_die(&from, "%s/%i/delta/%i-%i-%s-%s", staging_dir, file->last_change,
|
||||||
file->peer->last_change, file->last_change, file->peer->hash, file->hash);
|
file->peer->last_change, file->last_change, file->peer->hash, file->hash);
|
||||||
|
|
||||||
|
/* check for existence */
|
||||||
ret = stat(from, &stat_delta);
|
ret = stat(from, &stat_delta);
|
||||||
|
/* only add if delta does not already exist and the file is not
|
||||||
|
* in files */
|
||||||
if (ret && !find_file_in_list(files, file)) {
|
if (ret && !find_file_in_list(files, file)) {
|
||||||
files = g_list_prepend(files, file);
|
files = g_list_prepend(files, file);
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-4
@@ -184,7 +184,7 @@ double rename_score(struct file *old, struct file *new)
|
|||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void precompute_file_data(int version, const char *component, struct file *file, bool fast)
|
static void precompute_file_data(int version, const char *component, struct file *file)
|
||||||
{
|
{
|
||||||
char *c1, *c2;
|
char *c1, *c2;
|
||||||
char *filename = NULL;
|
char *filename = NULL;
|
||||||
@@ -301,7 +301,7 @@ static GList *new_filtered_list(GList *list, int version, int (*f)(struct file *
|
|||||||
return newlist;
|
return newlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int renamed_file_p(struct file *file, int unused)
|
static int renamed_file_p(struct file *file, int unused __attribute__((unused)))
|
||||||
{
|
{
|
||||||
return file->is_rename;
|
return file->is_rename;
|
||||||
}
|
}
|
||||||
@@ -331,7 +331,7 @@ static GList *list_new_files(struct manifest *manifest)
|
|||||||
GList *ret = list;
|
GList *ret = list;
|
||||||
for (list = g_list_first(list); list; list = g_list_next(list)) {
|
for (list = g_list_first(list); list; list = g_list_next(list)) {
|
||||||
struct file *file = list->data;
|
struct file *file = list->data;
|
||||||
precompute_file_data(manifest->version, manifest->component, file, true);
|
precompute_file_data(manifest->version, manifest->component, file);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -357,7 +357,7 @@ static GList *list_deleted_files(struct manifest *manifest)
|
|||||||
struct file *peer = file->peer;
|
struct file *peer = file->peer;
|
||||||
/* Need to get things from the /full/ as we do not know
|
/* Need to get things from the /full/ as we do not know
|
||||||
* which component may be coming from? */
|
* which component may be coming from? */
|
||||||
precompute_file_data(peer->last_change, "full", peer, false);
|
precompute_file_data(peer->last_change, "full", peer);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -446,6 +446,48 @@ redo:
|
|||||||
g_list_free(deleted_files);
|
g_list_free(deleted_files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void final_link(GList *files)
|
||||||
|
{
|
||||||
|
GList *list1, *list2;
|
||||||
|
struct file *file1, *file2;
|
||||||
|
|
||||||
|
list1 = new_list_renamed_files(files);
|
||||||
|
list1 = list2 = g_list_sort(list1, file_sort_version);
|
||||||
|
|
||||||
|
for (; list1; list1 = g_list_next(list1)) {
|
||||||
|
file1 = list1->data;
|
||||||
|
/* do not re-link already-linked renames */
|
||||||
|
if (file1->rename_peer != NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check the rest of the list */
|
||||||
|
list2 = g_list_next(list1);
|
||||||
|
for (; list2; list2 = g_list_next(list2)) {
|
||||||
|
file2 = list2->data;
|
||||||
|
/* must be the same version to link */
|
||||||
|
if (file1->last_change != file2->last_change) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* need one and only one to be deleted to link as this
|
||||||
|
* indicates a renamed-from/renamed-to relationship
|
||||||
|
* (.d.r -> F..r) */
|
||||||
|
if (file1->is_deleted == file2->is_deleted) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* must have same hash to link */
|
||||||
|
if (!hash_compare(file2->hash, file1->hash)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
file1->rename_peer = file2;
|
||||||
|
file2->rename_peer = file1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* What do we need this for?
|
/* What do we need this for?
|
||||||
*
|
*
|
||||||
* rename_detection has already set up the links in the manifest it
|
* rename_detection has already set up the links in the manifest it
|
||||||
|
|||||||
+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);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ setup() {
|
|||||||
init_test_dir
|
init_test_dir
|
||||||
|
|
||||||
init_server_ini
|
init_server_ini
|
||||||
|
# unban debuginfo
|
||||||
|
sed -i "s|banned=true|banned=false|" $DIR/server.ini
|
||||||
set_latest_ver 0
|
set_latest_ver 0
|
||||||
init_groups_ini os-core
|
init_groups_ini os-core
|
||||||
init_groups_ini test-bundle
|
init_groups_ini test-bundle
|
||||||
@@ -20,6 +22,12 @@ setup() {
|
|||||||
gen_file_plain 10 test-bundle "/usr/lib/debug/foo"
|
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/src/debug/bar"
|
||||||
gen_file_plain 10 test-bundle "/usr/bin/foobar"
|
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" {
|
@test "debuginfo files pruned" {
|
||||||
@@ -29,10 +37,20 @@ setup() {
|
|||||||
[[ 1 -eq $(grep '/usr/bin/foobar$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
[[ 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/lib/debug$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
||||||
[[ 1 -eq $(grep '/usr/src/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 be pruned
|
# These should not be pruned right now, but should be pruned with the next
|
||||||
[[ 0 -eq $(grep '/usr/src/debug/bar$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
# update after being banned in server.ini again
|
||||||
[[ 0 -eq $(grep '/usr/lib/debug/foo$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
|
[[ 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
|
# 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 '/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
|
||||||
Executable
+105
@@ -0,0 +1,105 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
set_os_release 40 os-core
|
||||||
|
set_os_release 40 test-bundle
|
||||||
|
track_bundle 40 os-core
|
||||||
|
track_bundle 40 test-bundle
|
||||||
|
|
||||||
|
# /usr/lib/bar and /one will be renamed to /usr/lib/baz and /two
|
||||||
|
gen_file_plain_with_content 10 test-bundle /usr/lib/bar "$(seq 100)"
|
||||||
|
gen_file_plain_with_content 10 test-bundle /one "$(printf 'a%.0s' {1..200})"
|
||||||
|
gen_file_plain_with_content 10 test-bundle /usr/lib/kernel/a "$(printf 'a%.0s' {1..210})"
|
||||||
|
|
||||||
|
gen_file_plain_with_content 20 test-bundle /usr/lib/baz "$(seq 100)"
|
||||||
|
gen_file_plain_with_content 20 test-bundle /two "$(printf 'a%.0s' {1..200})"
|
||||||
|
gen_file_plain_with_content 20 test-bundle /usr/lib/kernel/ab "$(printf 'a%.0s' {1..210})"
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
gen_file_plain_with_content 30 test-bundle /usr/lib/kernel/abc "$(printf 'a%.0s' {1..210})"
|
||||||
|
|
||||||
|
gen_file_plain_with_content 40 test-bundle /usr/lib/foo "$(seq 100) new"
|
||||||
|
gen_file_plain_with_content 40 test-bundle /usr/lib/kernel/abc "$(printf 'a%.0s' {1..210})"
|
||||||
|
# new file to force manifest generation
|
||||||
|
gen_file_plain_with_content 40 test-bundle /a "testfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
set_latest_ver 30
|
||||||
|
|
||||||
|
sudo $CREATE_UPDATE --osversion 40 --statedir $DIR --format 3
|
||||||
|
sudo $MAKE_FULLFILES --statedir $DIR 40
|
||||||
|
# version 10: add files to 10
|
||||||
|
[ 1 -eq $(grep $'F\.\.\.\t.*\t10\t/usr/lib/bar' $DIR/www/10/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.\.\.\t.*\t10\t/one' $DIR/www/10/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.b\.\t.*\t10\t/usr/lib/kernel/a' $DIR/www/10/Manifest.test-bundle | wc -l) ]
|
||||||
|
# version 20: rename bar to baz and one to two
|
||||||
|
[ 1 -eq $(grep $'\.d\.r\t.*\t20\t/one' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.\.r\t.*\t20\t/two' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.d\.r\t.*\t20\t/usr/lib/bar' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.\.r\t.*\t20\t/usr/lib/baz' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.gbr\t.*\t20\t/usr/lib/kernel/a' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.br\t.*\t20\t/usr/lib/kernel/ab' $DIR/www/20/Manifest.test-bundle | wc -l) ]
|
||||||
|
# version 30: original renamed-from file (bar) is now orphaned and therefore
|
||||||
|
# deleted, baz is now a renamed-from file and foo is a renamed-to file.
|
||||||
|
# /two was deleted in this version, so both /one and /two should be marked as
|
||||||
|
# deleted
|
||||||
|
[ 1 -eq $(grep $'\.d\.\.\t0\{64\}\t20\t/one' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.d\.\.\t0\{64\}\t30\t/two' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.d\.\.\t0\{64\}\t20\t/usr/lib/bar' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.d\.r\t.*\t30\t/usr/lib/baz' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.\.r\t.*\t30\t/usr/lib/foo' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.gbr\t.*\t30\t/usr/lib/kernel/ab' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.br\t.*\t30\t/usr/lib/kernel/abc' $DIR/www/30/Manifest.test-bundle | wc -l) ]
|
||||||
|
|
||||||
|
# version 40: the existing rename from baz -> foo must persist while all
|
||||||
|
# others remain deleted
|
||||||
|
[ 1 -eq $(grep $'\.d\.\.\t0\{64\}\t20\t/one' $DIR/www/40/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.d\.\.\t0\{64\}\t30\t/two' $DIR/www/40/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.d\.\.\t0\{64\}\t20\t/usr/lib/bar' $DIR/www/40/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'\.d\.r\t.*\t30\t/usr/lib/baz' $DIR/www/40/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.\.r\t.*\t30\t/usr/lib/foo' $DIR/www/40/Manifest.test-bundle | wc -l) ]
|
||||||
|
[ 1 -eq $(grep $'F\.b\.\t.*\t30\t/usr/lib/kernel/abc' $DIR/www/40/Manifest.test-bundle | wc -l) ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
|
||||||
Reference in New Issue
Block a user