Compare commits

..
16 Commits
Author SHA1 Message Date
Patrick McCarty 8ac1869adf Call checkrenamed() with correct arguments
Some of the renames tests omitted the leading forward slash for the two
function arguments, so add them here.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-04-19 11:15:26 -07:00
Patrick McCarty fde00265e2 test: fix some typos in checkrenamed()
Due to these typos being present, several tests were passing that should
not have been.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-04-19 11:15:26 -07:00
Matthew Johnson 25d3ef658d Add deprecation message to README
Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2018-04-19 11:04:08 -07:00
Matthew Johnson 3cf2ade205 Release v3.7.5
This release specifies that /usr/src/kernel/* files are not state files.
These files are needed to ship kernel source files for kernel
development.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-12-13 16:47:30 -08:00
Miguel Bernal Marin e814a73480 heuristics: set /usr/src/kernel as not state dir
The kernel sources will be shipped in /usr/src/kernel but /usr/src/* is
state.

This commit enables /usr/src/kernel and its contents as a no state
directory.

Fixes #108

Signed-off-by: Miguel Bernal Marin <miguel.bernal.marin@linux.intel.com>
2017-12-13 16:40:27 -08:00
Matthew Johnson 9d3a813859 Release v3.7.4
This release cleans up renamed-to files by unsetting their rename flag
when orphaned. This was previously only being done for renamed-from
files.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-12-05 12:21:44 -08:00
Matthew Johnson 5aa1bf9188 Also clean renamed-to files by unsetting the rename bit
Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-12-05 12:21:35 -08:00
Matthew Johnson de04b8400c Release v3.7.3
This release fixes a bug causing fullfile creation to be attempted for
ghosted files.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-12-04 14:15:15 -08:00
Matthew Johnson f5b614e39c Add comments to pack.c
As I was trying to understand the flow of pack.c I added comments as
notes to myself. These are helpful enough to be in the source.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-12-02 11:56:42 -08:00
Matthew Johnson 03feb2af49 Do not attempt full file creation for ghosted files
Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-12-02 11:56:42 -08:00
Matthew Johnson f3f1524f34 Refactor match_manifests function
This function had multiple sections of copy-pasted code. This was
causing an issue of bug-fixes being added to one part of code but not
the other. This patch refactors it to use internal functions where
appropriate and removes some dead code.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-12-02 11:56:01 -08:00
Matthew Johnson 90c1c89183 Release v3.7.2
This release fixes a bug that caused ghosted files to only be deprecated
over format bumps. Ghosted files are now removed from the manifest every
build.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-11-28 13:39:54 -08:00
Matthew Johnson 010131eb85 Fix ghosted-file deprecation
Ghosted files were only being deprecated over format bumps. Move the
format-bump-check logic out to the calling function to allow ghosted
files to be removed on every build. Also assign the is_ghosted flag when
adding "deleted" files from the old manifest so that the file will
actually be removed.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-11-28 13:39:38 -08:00
Matthew Johnson fcf4e0d7fc Release v3.7.1
This release fixes the bug introduced in v3.7.0 that caused valid
renames from old versions to be dropped. Instead of pruning orphaned
renames, they are now marked as deleted as well.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-11-28 09:19:14 -08:00
Matthew Johnson 6d3121cdc6 Add test cases for orphaned renames
* test that renames from old manifests persist
* test that renamed-to deleted is handled properly with the renamed-from
  becoming deleted.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-11-27 17:22:50 -08:00
Matthew Johnson 6190894f40 Do not delete renames from old versions
Perform a final link on all renames in the manifest in order to track
renames from older versions. Do not remove orphaned renames but instead
keep them around as deleted files as well.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-11-27 17:22:50 -08:00
11 changed files with 302 additions and 172 deletions
+9
View File
@@ -1,3 +1,12 @@
With the release of mixer-tools v4.3.0 [1] the swupd-server project is
deprecated. All functionality has been re-written into the mixer tool under
the `mixer build update` command. All development done around creating update
content consumed by swupd-client is now done in that project.
[1] https://github.com/clearlinux/mixer-tools
---
The swupd-server package provides a reference implementation of a software The swupd-server package provides a reference implementation of a software
update server-side component that generates update content consumable by a update server-side component that generates update content consumable by a
software update client (swupd-client). Such content includes manifests that software update client (swupd-client). Such content includes manifests that
+1 -1
View File
@@ -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.7.0, matthew.johnson@intel.com) AC_INIT(swupd-server, 3.7.5, 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
+2
View File
@@ -198,6 +198,7 @@ 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 void clean_renames(struct manifest *manifest);
extern int remove_deprecated_files(struct manifest *m1, struct manifest *m2, bool (*compfunc)(struct file *, struct file *)); 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);
@@ -257,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);
+24 -3
View File
@@ -336,7 +336,12 @@ 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_deprecated_files(old_full, new_full, both_deleted); 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);
@@ -402,9 +407,16 @@ 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_deprecated_files(old_core, new_core, both_deleted); 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); 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");
@@ -524,10 +536,17 @@ 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_deprecated_files(oldm, newm, both_deleted); 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); 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 || old_ghosted > 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, %d ghosted), writing out new manifest", group, newfiles, old_deleted, old_ghosted); LOG(NULL, "", "%s component has changes (%d new, %d deleted, %d ghosted), writing out new manifest", group, newfiles, old_deleted, old_ghosted);
@@ -568,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 -1
View File
@@ -45,6 +45,7 @@ static void runtime_state_heuristics(struct file *file)
/* these are shipped directories that are not themselves state, /* these are shipped directories that are not themselves state,
* rather only their contents are state */ * rather only their contents are state */
if ((strcmp(file->filename, "/usr/src/debug") == 0) || if ((strcmp(file->filename, "/usr/src/debug") == 0) ||
(strcmp(file->filename, "/usr/src/kernel") == 0) ||
(strcmp(file->filename, "/dev") == 0) || (strcmp(file->filename, "/dev") == 0) ||
(strcmp(file->filename, "/home") == 0) || (strcmp(file->filename, "/home") == 0) ||
(strcmp(file->filename, "/proc") == 0) || (strcmp(file->filename, "/proc") == 0) ||
@@ -56,6 +57,12 @@ static void runtime_state_heuristics(struct file *file)
return; return;
} }
/* the contents of these directory are not state,
* but it belongs to a state directory */
if ((strncmp(file->filename, "/usr/src/kernel/", 16) == 0)) {
return;
}
/* the contents of these directory are state, ideally this never /* the contents of these directory are state, ideally this never
* triggers if our package builds are clean */ * triggers if our package builds are clean */
if ((strncmp(file->filename, "/dev/", 5) == 0) || if ((strncmp(file->filename, "/dev/", 5) == 0) ||
@@ -72,7 +79,7 @@ static void runtime_state_heuristics(struct file *file)
return; return;
} }
/* these are commonly added directories for user customizatio, /* these are commonly added directories for user customization,
* ideally this never triggers if our package builds are clean */ * ideally this never triggers if our package builds are clean */
if ((strncmp(file->filename, "/acct", 5) == 0) || if ((strncmp(file->filename, "/acct", 5) == 0) ||
(strncmp(file->filename, "/cache", 6) == 0) || (strncmp(file->filename, "/cache", 6) == 0) ||
+134 -139
View File
@@ -323,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;
@@ -340,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
@@ -354,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");
@@ -382,163 +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;
/* ghost deleted boot files */
file3->is_ghosted = file1->is_boot && file1->is_deleted;
if (file3->is_ghosted || file1->is_deleted) {
/* if the new file is ghosted or the file was deleted, preserve
* hash (all zeros if file1->is_deleted) and rename status */
file3->is_rename = file1->is_rename;
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;
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;
} }
@@ -983,10 +962,9 @@ 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_deprecated_files(struct manifest *m1, struct manifest *m2, bool (*compfunc)(struct file *file1, struct file *file2)) int remove_deprecated_files(struct manifest *m1, struct manifest *m2, bool (*compfunc)(struct file *file1, struct file *file2))
@@ -1005,11 +983,6 @@ int remove_deprecated_files(struct manifest *m1, struct manifest *m2, bool (*com
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.
*/ */
@@ -1050,6 +1023,36 @@ int remove_deprecated_files(struct manifest *m1, struct manifest *m2, bool (*com
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.
@@ -1068,14 +1071,6 @@ 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);
+14 -4
View File
@@ -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);
} }
+42
View File
@@ -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
+1 -1
View File
@@ -52,7 +52,7 @@ setup() {
# version 20: add a new boot file # version 20: add a new boot file
[ 1 -eq $(grep 'F\.b\. .* 20 /usr/lib/kernel/baz' $DIR/www/20/Manifest.full | wc -l) ] [ 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 # 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) ] [ 0 -eq $(grep '/usr/lib/kernel/bar' $DIR/www/30/Manifest.full | wc -l) ]
# version 30: boot file added in version 20 ghosted # 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) ] [ 1 -eq $(grep '\.gb\. .* 30 /usr/lib/kernel/baz' $DIR/www/30/Manifest.full | wc -l) ]
} }
+54 -10
View File
@@ -26,11 +26,29 @@ setup() {
track_bundle 30 os-core track_bundle 30 os-core
track_bundle 30 test-bundle 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 /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 /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 # different content just to make sure this works with delta renames as well as
# direct renames # 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/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" { @test "create updates with renamed-to file getting deleted" {
@@ -46,16 +64,42 @@ setup() {
sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 3 sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 30 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) ] set_latest_ver 30
# version 20: rename bar to baz
[ 1 -eq $(grep '\.d\.r .* 20 /usr/lib/bar' $DIR/www/20/Manifest.test-bundle | wc -l) ] sudo $CREATE_UPDATE --osversion 40 --statedir $DIR --format 3
[ 1 -eq $(grep 'F\.\.r .* 20 /usr/lib/baz' $DIR/www/20/Manifest.test-bundle | wc -l) ] sudo $MAKE_FULLFILES --statedir $DIR 40
# version 30: prune original renamed-from file (bar), baz is now a renamed-from file # version 10: add files to 10
# Check for the new renamed-to file (foo) [ 1 -eq $(grep $'F\.\.\.\t.*\t10\t/usr/lib/bar' $DIR/www/10/Manifest.test-bundle | wc -l) ]
[ 0 -eq $(grep '/usr/lib/bar' $DIR/www/30/Manifest.test-bundle | wc -l) ] [ 1 -eq $(grep $'F\.\.\.\t.*\t10\t/one' $DIR/www/10/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\.b\.\t.*\t10\t/usr/lib/kernel/a' $DIR/www/10/Manifest.test-bundle | wc -l) ]
[ 1 -eq $(grep 'F\.\.r .* 30 /usr/lib/foo' $DIR/www/30/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 # vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+13 -13
View File
@@ -47,7 +47,7 @@ gendataCs() {
} }
checkrenamed(){ checkrenamed(){
local flags sh1 ver name fromsha1="bad" tosha1 local flags sha1 ver name fromsha1="bad" tosha1
# Check that $1 is renamed to $2 # Check that $1 is renamed to $2
exec 9< $DIR/www/20/Manifest.test-bundle exec 9< $DIR/www/20/Manifest.test-bundle
# skip the header # skip the header
@@ -58,11 +58,11 @@ checkrenamed(){
while read -r -u9 flags sha1 ver name while read -r -u9 flags sha1 ver name
do do
case "$flags" in case "$flags" in
(?"dr"?) [ "$name" = "$1" ] && fromsha1=$sha1 ;; (?"d.r") [ "$name" = "$1" ] && fromsha1=$sha1 ;;
(?".r"?) [ "$name" = "$2" ] && tosha1=$sha1 ;; (?"..r") [ "$name" = "$2" ] && tosha1=$sha1 ;;
esac esac
done done
if [ "$fromsh1" = "$tosha1" ] ; then return 0 ; else return 1 ; fi if [ "$fromsha1" = "$tosha1" ] ; then return 0 ; else return 1 ; fi
} }
# Guts of doing an update # Guts of doing an update
@@ -87,7 +87,7 @@ do_an_update() {
gendataA 10 foo gendataA 10 foo
gendataA 20 bar gendataA 20 bar
do_an_update do_an_update
checkrenamed foo bar checkrenamed /foo /bar
} }
@test "ignore rename detection for small files" { @test "ignore rename detection for small files" {
@@ -142,8 +142,8 @@ do_an_update() {
gendataA 20 bar gendataA 20 bar
gendataA 20 baz gendataA 20 baz
do_an_update do_an_update
checkrenamed foo bar checkrenamed /foo /bar
checkrenamed foz baz checkrenamed /foz /baz
} }
@test "rename two files to two, one slightly different" { @test "rename two files to two, one slightly different" {
@@ -153,8 +153,8 @@ do_an_update() {
gendataB 20 baz gendataB 20 baz
do_an_update do_an_update
# we don't actually know how the client we do this rename, but don't care # we don't actually know how the client we do this rename, but don't care
checkrenamed foo bar checkrenamed /foo /bar
checkrenamed foz baz checkrenamed /foz /baz
} }
@test "rename two files to two, each pair slightly different" { @test "rename two files to two, each pair slightly different" {
@@ -163,8 +163,8 @@ do_an_update() {
gendataA 20 bar gendataA 20 bar
gendataB 20 baz gendataB 20 baz
do_an_update do_an_update
checkrenamed foo bar checkrenamed /foo /bar
checkrenamed foz baz checkrenamed /foz /baz
} }
@test "rename two files to two, one very different" { @test "rename two files to two, one very different" {
@@ -183,9 +183,9 @@ do_an_update() {
gendataA 20 bar gendataA 20 bar
gendataCs 20 baz gendataCs 20 baz
do_an_update do_an_update
run checkrenamed foo bar run checkrenamed /foo /bar
if [ $status -eq 1 ] ; then if [ $status -eq 1 ] ; then
checkrenamed foz bar checkrenamed /foz /bar
fi fi
} }