Compare commits

...
121 Commits
Author SHA1 Message Date
Matthew Johnson c28d06c580 Release v3.6.1
This release fixes a bug where swupd-server was creating the staged and
delta directories within the packs with 0750 permissions instead of the
expected 0700 permissions. It also adds a configuration option to
server.ini to ban debuginfo from the manifests and configure where
debuginfo libs and src are stored.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-09-15 15:59:12 -07:00
Matthew Johnson 1fa24df6d6 Release v3.6.0
Calculate contentsize for all files in the manifest, not just in the
current update.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-08-30 15:29:39 -07:00
Matthew Johnson e84600c21d Merge pull request #65 from matthewrsj/contentsize-fix
Calculate entire contentsize for manifest
2017-08-18 10:40:27 -07:00
Matthew Johnson 350bb54fdf Add functional test for contentsize fix
Add functional test to check manifest contentsize across versions and
includes.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-08-18 10:13:11 -07:00
Matthew Johnson 15e6b1466f Calculate entire contentsize for manifest
Instead of only calculating the contentsize for files that were updated
in the current version (an inaccurate number for download size since
this is not the compressed size), calculate for all files in the
manifest. Additionally, do not add submanifest contentsizes to the
current manifest contentsize, as this will result in overcount on client
systems when multiple bundles include the same bundle.

With this change the contentsize field of the manifests will only report
the size of the files unique to that bundle. It is then the client's
responsibility to calculate total bundle size including included
bundles. This is reasonably easy to accomplish with the upcoming
swupd-client bundle-list --deps feature.

Although the above use case is a bit more work for the client, it
additionally allows a user to calculate the installation size of
multiple bundles much more easily, since it only has to count bundle
dependencies once to ensure files are not over counted.

If two bundles not in the same include chain have overlapping content,
summing the include chain of each bundle in the client will result in an
over-estimation of the total size on the system. The more content is
shared, the higher the over-estimation. In reality this overlap will not
be large, but it is currently impossible to calculate the exact
installed size using just the contentsize.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-08-18 10:11:58 -07:00
Tudor Marcu f9ec967aa2 Release v3.5.0
This release removes a stale, unused script, adds a check to ensure format
number validity to ensure format numbers do not decrease, and enables adding
an actions: field in the Manifest.MoM to tell swupd-client to perform certain
actions post update.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2017-07-28 23:54:30 -07:00
Matthew Johnson 925a5203f4 Add functional test for format bump actions field
Adds a functional test to check for the

actions:	update

line in the Manifest.MoM for the new format.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-07-28 23:13:21 -07:00
Matthew Johnson 31bb949b9b Add actions field to Manifest.MoM for format bumps
When a format bump occurs and the new format is greater than the old
format, an actions field is written to the Manifest.MoM containing the
string "update". This "update" action tells the client that it is
necessary to re-execute swupd update to bring the client to the latest
version within the new format.

Signed-off-by: Matthew Johnson <matthew.johnson@intel.com>
2017-07-28 23:13:21 -07:00
Patrick McCarty a0c7025a9b Add basic check for format number validity
Because the intention is for swupd format numbers to either remain the
same between LAST_VER and current, or to increase as part of a format
bump, this leaves the remaining undesirable case.

Add a basic check to make sure the format never decreases, and add a
functional test.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-07-24 15:00:39 -07:00
Patrick McCarty 06320f2862 Remove unused script
The packfsck.pl is unused, so we can safely remove it from the repo.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-07-05 11:23:07 -07:00
Tudor Marcu 4f84b66321 Release v3.4.0
This release adds rename detection support which can be enabled via the
--enable-rename-detection option during configure time.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2017-05-10 11:33:07 -07:00
Tudor Marcu 0d352881e7 Fix code style issues
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2017-05-10 11:05:28 -07:00
Icarus Sparry 7eff79397c Rename support
Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
2017-05-09 16:43:19 -07:00
Icarus Sparry 37f84501eb Additional tests for renames
Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
2017-05-09 16:43:19 -07:00
Icarus Sparry 850e6b9a92 Allow running only a single test
If the environment variable RUN_JUST_ONE is set then skip any test
which is not numerically equal to it.

Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
2017-05-09 16:43:19 -07:00
Icarus Sparry ce594a6529 Helper routine to generate files with content
Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
2017-05-09 16:43:19 -07:00
Icarus Sparry cfb0406b2e Fix creating test files in subdirectories
Allow the filenames to contain directory names

Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
2017-05-09 16:43:19 -07:00
Tudor Marcu 3a46cb6a9d Release v3.3.5
This release fixes duplicate includes lines being printed in manifests.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2017-04-17 12:58:10 -07:00
Patrick McCarty 15c045de8d Avoid duplicate includes lines in manifests
There is no need for duplicate includes to exist in manifest headers, so
search the includes lists first before adding a new entry.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-11 11:53:21 -07:00
Tudor Marcu 22823ec8c8 Release v3.3.4
This release fixes subtracting files from manifests when both versions are marked deleted. Server subtracting of files when both are deleted
proves problematic for client updates, because the version at which the
files were deleted will differ, and the client uses the versions to
determine when it should delete files for an update.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2017-04-10 10:07:47 -07:00
Patrick McCarty c69a5d87bf Add functional test for subtraction of deleted files
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-07 10:47:29 -07:00
Patrick McCarty b103c214a0 Skip subtraction of files when both are deleted
Subtracting files from manifests when both versions are marked deleted
proves problematic for client updates, because the version at which the
files were deleted will differ, and the client uses the versions to
determine when it should delete files for an update.

As an example, consider a distro with 2 bundles, "A" and "B". Bundle B
includes bundle A, which results in files in A being subtracted from B.
In this situation the following sequence of four changes result in a
subtraction that prevents a client update from deleting a file.

1) In version 10, file /usr/foo is added to bundles A and B.
2) In version 20, /usr/foo is deleted from bundle A.
3) In version 30, /usr/foo is deleted from bundle B.
4) In version 40, bundle B is modified.

Due to arbitrary modifications to bundle B in step 4, /usr/foo is
subtracted from bundle B, because it's also deleted in bundle A.
However, the file versions mismatch. So, an update from version 20 to 40
will result in /usr/foo not being deleted because the deleted entry from
bundle A was not deleted in a version newer than 20.

This patch fixes the issue by ensuring that the deleted entry in bundle
B remains intact for version 30. And the client update then works
correctly: an update from 20 to 40 will properly delete /usr/foo,
because 20 < 30 <= 40.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-07 10:47:29 -07:00
Tudor Marcu beeb3ff9b2 Release v3.3.3
This release fixes server to only create deltas between two regular files,
removing errors from attempting to make deltas to/from/between symlinks.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2017-04-04 12:14:55 -07:00
Tudor Marcu 1aca06882a Fix code style
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2017-04-04 12:13:49 -07:00
Patrick McCarty adb8241a83 test: add one more test for symlink dereferences + deltas
In the event that a symlink target changes between two versions, and the
files the symlink points to also change and would create a delta, do not
create a delta between the symlinks. The real delta is between the
underlying files the symlinks point to, which is created separately.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-04 11:45:43 -07:00
Patrick McCarty c963ce9aa6 test: add test for delta file presence/absence
For the F->L and L->F type changes, make sure that there are no delta
files created between the dereferenced symlink on one end and the
regular file on the other.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-04 11:45:43 -07:00
Patrick McCarty 04a1bdfd16 test: support custom file names for delta tests
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-04 11:45:43 -07:00
Patrick McCarty 1518aa4335 build: sort test filenames in Makefile.am
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-04 11:45:43 -07:00
Patrick McCarty 9c1a4a3542 Only create deltas between two regular files
For three different Clear Linux OS builds in the last few months, deltas
were created between files with type change L->F (symlink to file).
This was allowed to occur because there is no check if
file->peer->is_link in __create_delta().

Instead, remove the file->is_link check and simply ensure that the
from/to file types are both F (i.e. "regular file").

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-04-04 11:45:43 -07:00
Patrick McCarty a9f13e939c build: add 'compliant' target for fixing code style issues
To simplify the discovery of C code style issues and enforce the rules
specified in .clang-format, I've added a new 'compliant' target for
running the appropriate clang-format command.

In case code style issues are found, source files are modified in place,
and the resulting diff can be viewed. The exit code in this case will be
1, so make will exit with an error. This helps to automate testing for
code style issues.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-01-03 11:20:27 -08:00
Tudor Marcu 75039ad6b9 Release v3.3.2
This release includes changes across various areas of server, specifically:
- Fixing fallthrough detection logic to detect files and directories correctly
  when checking if they are "state" files.
- Removing stale and unused signature creation code
- Adding functional tests and enabling travis-ci integration
- Create alternative input layout to save IO for some cases(backward compatible)
- Update parallelism code to make it more versatile and editable
- Fix log call and add logging to stdout instead of just logfiles
- Honor proxy and cert checking settings
- Fix extracting files with bsdtar
- Enable locales in all programs

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-12-08 11:04:15 -08:00
Patrick Ohly 9b316bf95c swupd-create-update: alternative input layout
In Ostro OS, we already have a "full" directory with all files.
Splitting it up into bundles just so that swupd-create-update can
reconstruct the "full" directory is a waste of IO, and noticably slow
when run under pseudo.

To streamline the required work, a new layout for the "image" input
directory gets introduced:
- The "full" directory gets created by the caller before invoking
  swupd-create-update.
- For each bundle, instead of a <bundle> directory, there is a
  <bundle>.content.txt file, listing all entries (including directories)
  of the bundle.

The traditional mode of operation still works as before because each operation
which normally works with a bundle directory checks whether there is such a
directory and if not, switches to the new mode.

That way it is even possible to mix the two modes, i.e. replacing only
some bundles with a content list, although that's probably not all
that useful.

This revised commit fixes the use of an uninitialized newversiondircontent
pointer in populate_dirs().

Fixes: swupd-server/#54

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-08 10:53:45 -08:00
Patrick Ohly b618516167 enable locales in all programs
This is a pre-condition for using libarchive directly: libarchive
needs to know what the encoding of filenames is, and it uses the
current locale for that. Without setlocale(), the locale is "C", which
only supports ASCII filenames, leading to warnings about "Can't
encode..." from libarchive when it is forced to fall back to copying
strings verbatim when writing archives that require UTF-8 encoding.

As a side effect, error messages from libc will get translated
according to the user's environment.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-08 10:57:36 -08:00
Patrick Ohly 7542d2152a swupd_make_pack: fix extracting files with bsdtar
TAR_XATTR_ARGS is no longer used as part of a plain string. Embedding
the empty "" value for bsdtar inside an argv argument list passes an
empty parameter to bsdtar, leading to:
  bsdtar: Must specify one of -c, -r, -t, -u, -x

To allow the the "no parameter" case, it has to be argument list: that
can be empty. If not empty, it has to end with a comma.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-08 10:49:02 -08:00
Patrick McCarty 490326f2b9 packfsck: honor proxy settings and cert checking
In case a proxy is used for downloading the manifest/pack, make sure to
honor those settings. And also, make sure certificate verification
occurs for the downloads.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:59:11 -08:00
Tudor Marcu 307d2427a1 Revert "swupd-create-update: alternative input layout"
This reverts commit f01d9ca6c8.

Upon further testing, this patch causes a double free/corruption with the
current master branch and crashes two of the tests. We need to investigate
more before fully enabling it to ensure we don't regress.
2016-12-01 11:43:57 -08:00
Patrick Ohly 72dd27a886 add logging to stdout
When a CI system (like the one from Ostro) captures the output of
commands, but not necessarily intermediate log files, then it is
useful to also log to stdout. Another use case is calling the tools
interactively during development.

The new --log-stdout option in all three commands enables logging to
stdout in addition to the traditional log files.

The implementation recycles the existing init_log_stdout() (not used
before) and gives it the slightly different meaning of "also log to
stdout".

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:20:40 -08:00
Patrick Ohly 7e38f013ef fullfiles.c: fix invalid LOG() call
LOG() takes an additional fixed string before the format string.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:20:05 -08:00
Patrick Ohly 4e0fdd4193 update control over parallelism
The SWUPD_NUM_THREADS env variable is now understood by all three
commands and overrides the default number of threads. Setting it to 1
is useful while debugging the code that runs inside threads (only one
thread hits breakpoints there). If SWUPD_NUM_THREADS is invalid, a
warning is printed and the variable gets ignored, i.e. the default
parallelism is used.

The hard-coded parallelism of 12 threads when analysing the file system
gets replaced with n, where n is the number of available CPUs. The default
is the same as before elsewhere (n for packing, 3 * n for fullfiles).

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:19:35 -08:00
Patrick Ohly f01d9ca6c8 swupd-create-update: alternative input layout
In Ostro OS, we already have a "full" directory with all files.
Splitting it up into bundles just so that swupd-create-update can
reconstruct the "full" directory is a waste of IO, and noticably slow
when run under pseudo.

To streamline the required work, a new layout for the "image" input
directory gets introduced:
- The "full" directory gets created by the caller before invoking
  swupd-create-update.
- For each bundle, instead of a <bundle> directory, there is a
  <bundle>.content.txt file, listing all entries (including directories)
  of the bundle.

The traditional mode of operation still works as before because each operation
which normally works with a bundle directory checks whether there is such a
directory and if not, switches to the new mode.

That way it is even possible to mix the two modes, i.e. replacing only
some bundles with a content list, although that's probably not all
that useful.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-12-01 11:16:21 -08:00
Patrick McCarty 944dfa1d93 Add travis-ci integration for functional testing
Right now, the travis-ci config simply installs required build
dependencies for swupd-server and works around the umask discrepancy to
proper run the functional test suite.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:15:46 -08:00
Patrick McCarty 963e8117b4 Skip some tests if run as root
Three functional tests depend on the effective UID being non-zero
(non-root), so skip the tests if running as root.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:15:46 -08:00
Patrick McCarty 57292a5a03 Add functional test for state file tagging
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:05:36 -08:00
Patrick McCarty fe6f47e4c3 Fix fallthrough state detection logic
This conditional checks for state *directories* that are generally
installed by default, and the conditional immediately below this one
checks for state files within these directories. So, if we do strncmp()
instead of strcmp(), the fallthrough logic doesn't occur, and state
files are not marked as such.

This reverts commit 63fb5fb61b.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 11:05:36 -08:00
Patrick McCarty 13189dd4d4 Remove all signature creation code
Since the enablement of signature verification in swupd-client, the
signature creation step has been decoupled from swupd-server, and is
instead performed as a separate step in a DevOps flow.  As a result of
this decoupling, the signature code in swupd-server has remained unused.

This commit removes all the signature creation code with the assumption
that the separate DevOps step is going to work better long-term. Also,
the existing signature creation support does not accord with
swupd-client's verification support.

An example of how Manifest.MoM files can be signed is found in the
https://github.com/clearlinux/mixer-tools repo.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-01 10:15:42 -08:00
Tudor Marcu b417fc4391 Release v3.3.1
This release fixes some git tagging errors that occured in the previous release.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-11-17 11:09:56 -08:00
Tudor Marcu 478cdc5272 Merge tag 'v3.3.0'
swupd-server release 3.3.0
2016-11-17 11:07:58 -08:00
Tudor Marcu 2992dc1978 Release v3.3.0
This release contains various changes to fix segfaults and memory misuse,
simplifying logic when iterating directories and populating file structs
from manifests, and changing the secondary sort to be lexographically sorted
filenames for version-sorted manifests.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-11-17 01:21:31 -08:00
Tudor Marcu ec31238e1a Release v3.3.0
This release contains various changes to fix segfaults and memory misuse,
simplifying logic when iterating directories and populating file structs
from manifests, and changing the secondary sort to be lexographically sorted
filenames for version-sorted manifests.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-11-17 01:14:42 -08:00
Patrick McCarty 9148ea8a8e Switch secondary sort order for version-sorted manifests
To ease human readability of manifests, but without impacting manifest
delta efficiency, use a lexicographic filename secondary sort order when
sorting manifests by version.

Below is an example of how this commit changes the sorted order (the
first column is the version, and the second column is the filename).

 # Before
 10	zyxw
 10	abcd
 20	test2
 20	test1
 20	abba
 20	aaaa
 30	zzyy

 # After
 10	abcd
 10	zyxw
 20	aaaa
 20	abba
 20	test1
 20	test2
 30	zzyy

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-16 22:28:26 -08:00
Tudor Marcu 74c8f86a94 Simplify logic for iterating directories
The file struct must be populated to contain the proper stats, so just
check if it is a directory and iterate, ignoring the case where d_type may
not be defined on the system.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-11-15 17:54:19 -08:00
Tudor Marcu a43964b110 Revert "Resort to lstat for FS not supporting dirent.d_type"
This reverts commit 983b17d68d.
2016-11-15 17:42:09 -08:00
Dmitry Rozhkov 983b17d68d Resort to lstat for FS not supporting dirent.d_type
According to POSIX.1 only d_name and d_ino fields of struct
dirent are standardized. d_type isn't always correctly set on file
systems like XFS. In such cases it makes sense to resort to
lstat(). Otherwise a user has hard time figuring out what's
wrong with her setup.

Also remove redundant populate_file_struct() as it's called
again in parallel threads.

Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@linux.intel.com>
2016-11-15 17:28:16 -08:00
Joshua Lock 5420a1ea1b Ensure os-core bundle is listed in groups.ini
If the os-core bundle is not listed in groups.ini we will run into
problems later on, including segfaults when processing bundle includes.

Check the os-core bundle is listed in the groups.ini during initialisation
and error gracefully when it is not.

Signed-off-by: Joshua Lock <joshua.g.lock@intel.com>
2016-11-15 17:26:45 -08:00
Patrick Ohly 1c75f53604 swupd_create_fullfiles: avoid segfault when nothing changes
In the (unlikely) case that nothing changed between two builds,
get_deduplicated_fullfile_list() segfaults because it uses
manifest->files without checking for NULL, aka the empty list.

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-11-15 11:32:23 -08:00
Patrick Ohly 2890a11c5a delta.c: fix xattr test after patching
At the moment, swupd_create_pack fails when some files have xattrs and
get patched because the xattrs of the test file do not match the
original, unpatched file.

That's because xattrs_copy() was applied to the wrong target file.

Fixes: swupd-server/#35

Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
2016-11-15 11:28:26 -08:00
Tudor Marcu 2401aae76e Release v3.2.9
This release fixes the path where the server reads the bundle metadata from.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-09-29 17:17:30 -07:00
Patrick McCarty 30b46b9027 Update for new noship dir location
The bundle-chroot-builder changed the location where it stores bundle
includes metadata, so swupd-server needs to read from the new location.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-09-29 13:40:12 -07:00
Tudor Marcu c8ae097ed0 Release v3.2.8
This release updates the latest.version file, and provides minor cleanups
for useability.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-09-29 11:21:43 -07:00
Tudor Marcu 076a76fc4e Update for latest.version name change
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-09-29 11:21:43 -07:00
Tudor Marcu 63fb5fb61b Change strcmp to strncmp for consistency
Signed-off-by: John Andersen <john.s.andersen@intel.com>
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-09-29 10:54:07 -07:00
Auke Kok 7758a6bb77 Document and ship bundle status string.
This patch extracts the [STATUS] field from our bundle information
metadata, and stores it in groups.ini. From there we put the contents
of this string verbatim into the manifest.

We don't interpret, encode or convert the contents of the [STATUS]
field in the manifest. Instead, we just strip non-alphanumeric
characters and pass the contents on. This leaves it entirely to the
client to parse and interpret the value of this field in the manifest.

If the bundle file, or the groups.ini file omits any status, nothing
is output to the Manifest file.
2016-09-29 10:45:03 -07:00
Tudor Marcu 36c4e6324f Change version file to more appropriate name
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-09-07 17:21:09 -07:00
Tudor Marcu c4f61cb023 Release v3.2.7
Bump release version to provide a new server for a format bump.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-09-06 14:15:52 -07:00
Tudor Marcu 379d5189fc Release v3.2.6
This release introduces a change to the hashing scheme regarding delta names,
and directory hashes, as well as minor fixes to support the new hashing.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-08-22 03:23:22 -07:00
Tudor Marcu 6eca4601b0 Update argument passed to system_argv
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-08-22 03:22:14 -07:00
Tudor Marcu 4940d0cf83 Fix functional test for server changes
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-08-22 02:47:16 -07:00
Tudor Marcu 04d2af2f64 Change delta's name from FROM-TO-HASH2 to FROM-TO-HASH1-HASH2 format.
There is an issue when two different files in a newer release have same hash:
This is, when swupd updates a file by applying a delta, it takes the HASH2 to
know the file where delta must be applyed. If files are different in current
release (before updating), there must be 2 deltas, one for each file but as
the two files have same hash in new release delta's name are the same:
FROM-TO-HASH2 and it is when issue arises due to just the last delta file is
kept when swupd server creates files and packs. So when swupd client tries to
apply the delta to one of the file that does not corresponds it will fail and
generates an error. At the first look it will seem like delta file is corrupted
however the issue is that delta file was created for another file.
To solve this issue we include the hash for the original file in the delta's
name so that swupd client can take the correct delta and apply it:
FROM-TO-HASH1-HASH2.

Signed-off-by: Jose R Guzman <jose.r.guzman.mosqueda@intel.com>
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-08-19 17:17:20 -07:00
Tudor Marcu 1f98322737 Make directory hashes independent on filename
Calculate the hash for directories is desiderable to be independent
on the dirname due to the subsequent calculation on the staged/HASH
file. Here is used const "DIRECTORY" string for input name for
all folders.

Signed-off-by: Jose R Guzman <jose.r.guzman.mosqueda@intel.com>
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-08-19 17:15:56 -07:00
Tudor Marcu ddca171dad Release v3.2.5
This release includes a fix to replace system calls by local system_argv
functions, cleanup patches for error output and code style, adding delta
manifests to delta packs, consistency changes for tar and string allocation,
and removes creation of delta manifests out of create_update.
This is a re-roll of release 3.2.4 to fix a time skew issue that caused some
build failures.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-07-19 16:32:20 -07:00
Tudor Marcu 62c31f0088 Release v3.2.4
This release includes a fix to replace system calls by local system_argv
functions, cleanup patches for error output and code style, adding delta
manifests to delta packs, consistency changes for tar and string allocation,
and removes creation of delta manifests out of create_update.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-07-19 23:21:18 -07:00
Tudor Marcu 54874bd4e1 Add null check before calling rmdir in manifest.c
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-07-19 19:15:33 -07:00
Tudor Marcu 9fe3ad2241 Remove delta manifest creation from create_update
Delta manifests are handled by the pack creation step, and should not be
part of the plain manifest/update content creation step. This reduces the time
swupd_create_update takes to run significantly.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-07-19 02:05:07 -07:00
Tudor Marcu be83996e48 Revert "Parallelize manifest creation code"
This reverts commit 7f3b760430 and commit
74f4afa655. The code introduced parallel manifest
creation, and removed delta manifests from being created, but needs some more
rigorous testing and refactoring to ensure all test suites pass.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-07-14 20:32:51 -07:00
Patrick McCarty 7f3b760430 Import latest version of previous patch
I had neglected to rebase to the latest revision of #21, so this commit
incorporates the additional changes.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-06-03 16:35:56 -07:00
Tudor Marcu 74f4afa655 Parallelize manifest creation and remove deltas
The code previously created all the bundle manifests and delta manifests
synchronously in 1 thread. While this worked, it was not optimal considering
the manifests can be created at the same time since they are unique. This
patch creates a threadpool that processes each bundle manifest in its own
thread. Delta manifest creation was removed from create_update because it
can already be handled by our delta pack creation process, and it is a huge
time-sink for create_update as it also runs single threaded.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-05-20 14:49:08 -07:00
Patrick McCarty ea02b67a93 Provide more information for pack hardlink errors
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 16:21:34 -07:00
Patrick McCarty 8af939c817 Update functional test to check for delta manifests in packs
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 15:59:14 -07:00
Patrick McCarty 4128fbbb9b Add delta manifests to delta packs
The delta manifests were being staged correctly but were not being added
to the delta packs.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 15:49:33 -07:00
Patrick McCarty 0d08824801 Permit allocating empty strings in string_or_die()
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 14:21:50 -07:00
Patrick McCarty 8f98e0f570 Update code comment to mention the os-core pack, instead of "base"
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 14:19:17 -07:00
Patrick McCarty 76e5af734b Use tar's -C option everywhere instead of --directory
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 14:14:51 -07:00
Patrick McCarty b7192ee686 Convert the recently added system() call
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 14:14:17 -07:00
Patrick McCarty b0e0b7f77d Fix some clang-format nitpicks
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-17 13:57:48 -07:00
Jose R Guzman 746b9a56a8 Replace system calls by local system_argv functions.
There are new functions that replace system() calls: system_argv(), system_argv_fd and system_argv_pipe().
They are based on execvp() function and they can manage any argument including filenames with characters
that could be interpreted as meta-characters in a regular system() function.
So no escape for the arguments needed and is less prone to errors.

Signed-off-by: Jose R Guzman <jose.r.guzman.mosqueda@intel.com>
2016-05-17 13:56:10 -07:00
Patrick McCarty adfc1067ff Remove pack_maker.sh
Since delta pack creation is very much tied to a DevOps workflow, it
makes more sense for the pack_maker.sh to live outside of swupd-server.

This script will move to the clearlinux/mixer-tools repo, since the
Clear mixer is a DevOps-oriented tool.

Also, update the code comment in versions.c to describe another action
to be taken that is DevOps-specific, namely that the value of
WEBDIR/version/formatN/latest file should be set at release time.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-16 14:20:41 -07:00
Patrick McCarty 122f674e74 Make elapsed time progress messages more flexible
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-11 15:40:50 -07:00
Patrick McCarty 9d4df7e035 Release v3.2.3
This release fixes a bug with adding invalid bundle manifest hashes to
the Manifest.MoM and optimizes swupd_create_update to remove the
unnecessary rsync from the full chroot back to bundle chroots.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-11 11:20:13 -07:00
Tudor Marcu 4df4bf14c3 Remove rsync from full->bundle chroots
This is no longer needed with the current setup, and is just wasting time during
update creation.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-05-10 14:43:06 -07:00
Tudor Marcu 6affbd1ecb Free allocated variable
Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-05-05 12:23:59 -07:00
Patrick McCarty 77a0bcd9f9 Account for deleted manifests in the MoM population
Just in case we encounter a bundle manifest that does not exist anymore,
or the last_change is 0, which is an invalid version to create, make
sure we add the deleted entry in the MoM and do not attempt to untar
that manifest.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-05-05 12:17:49 -07:00
Tudor Marcu bf19e48917 Fix bundle manifest hashes from MoM
The bundle manifest hashes were calculated from the STATE_DIR Manifest.bundle
files, which caused a problem when they the permissions/owner were changed. The
hashes should be calculated by untarring the Manifest.BUNDLE.tar files
referenced in the Manifest.MoM.

Signed-off-by: Tudor Marcu <tudor.marcu@intel.com>
2016-05-05 17:57:59 +00:00
Patrick McCarty d45304c8b7 Add functional test for blacklisted file names
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-29 16:39:11 -07:00
Patrick McCarty 2b4e9eb3c9 test: quote echo strings to protect against shell interpretation
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-29 16:39:08 -07:00
Patrick McCarty b6bd8bf73b Release v3.2.2
This release includes a trivial improvement for reporting paths that
contain blacklisted characters.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-29 15:23:44 -07:00
Patrick McCarty 94240130be Report full paths for files containing blacklisted characters
To better track which files are excluded from manifests because they
contain blacklisted characters, the full path is more useful to log
rather than the file basename.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-29 15:04:16 -07:00
Patrick McCarty ae299e2126 Rename main.c to create_update.c
Since the source files for entry points to the other two binaries are
named after the binaries, do the same for swupd_create_update.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-14 11:14:36 -07:00
Patrick McCarty 64e65bca92 Run functional tests in separate directories
To more easily implement tests, analyze test results, etc, store each
test in a separate directory and dump all logs and the web-dir content
there as well.

The test suite can be run with 'make check', as before, but now tests
can be run individually by directly invoking the appropriate test.bats
file (at toplevel, or within the test directory).

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-10 21:03:06 -07:00
Patrick McCarty e78ef77e06 Release v3.2.1
This release fixes a sorting issue with bundle manifests: they should be
version sorted, not filename sorted.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-05 23:51:22 -07:00
Patrick McCarty a329cc93a9 Fix sorting issue with bundle manifests
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-05 23:50:42 -07:00
Patrick McCarty bd002877c1 Release v3.2.0
This release adds support for a bundle includes feature, which allows
bundles that "include" other bundles to depend on each other. This
change reduces duplication of files in manifests and improves build
times for larger bundle sets that contain many includes.

The two other functional changes are:

- The --format option to swupd_create_update is now mandatory.
- The value passed to --format must now be a positive integer.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-05 21:44:00 -07:00
Patrick McCarty cb2706fb72 Move removal of old deleted files to a new function
Since removing of all of the matching deleted files between a build with
an older manifest format and current needs to happen *after*
match_manifests() finishes accounting for new and deleted entries in the
manifests, simply move the logic into a separate function to be called
after match_manifests().

Because one or more file deletions should trigger a fresh bundle manifest
creation, account for the return value of this new function in addition
to the result of manifest pruning and whether any bundle includes were
changed. Since "os-core" is treated specially, and it's expected to
contain changed files for every build, only the non-"os-core" bundle
manifests need the trigger.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-05 21:03:23 -07:00
Patrick McCarty 383ab112db Properly initialize format for new manifests
For the new full manifest and new bundle manifests, the format member
was not being initialized.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-05 16:29:29 -07:00
Patrick McCarty af7767a768 Add required -F option to basic_creator.sh
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-04 19:01:54 -07:00
Patrick McCarty b954d924f4 Update functional test suite for recent changes
For swupd_create_update, the required -F option needed to be added for
several tests, and the option parsing order changed.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-04 18:57:22 -07:00
Patrick McCarty e0049de3dc Fix remaining issues with handling the format number
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-04 18:48:42 -07:00
Patrick McCarty 2a9af990d6 Fix error in the previous rebase
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-04 14:23:05 -07:00
Patrick McCarty 90c976fdaf Require the -F option for swupd_create_update
To decrease chances of specifying an incorrect format number, since the
default is a hardcoded value "3", always require the user to pass the -F
option to swupd_create_update.

In the future, it would be nice to read the default value from a config
file, with the -F option overriding the value.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-04-04 14:18:15 -07:00
Tim Pepper 3b43313c9e Compare current format versus prior format in manifest match
To better support a workflow for incrementing the format on the command
line, it's useful to have the format be an integer instead of a string.
When reading a manifest (ie: old manifest) we can read that into struct
manifest.  Then when building a new manifest we can compare against the
prior manifest's format.  If the format incremented, we can prune files
previously marked as deleted in the old manifest so they no longer
appear in the new manifest.

This is beneficial because a file that has a version number in its name
will repeatedly be deleted and replaced by a new similar file.  Over
time this leads to manifests getting bigger and bigger because they
retain the entire history of all file names which ever existed, even if
a huge number of them are and have been for a long time marked as
deleted.

Signed-off-by: Tim Pepper <timothy.c.pepper@linux.intel.com>
2016-04-04 14:18:10 -07:00
William Douglas 3d8b81fbcd Fix missing closing comment 2016-03-31 18:50:42 +00:00
William Douglas 9b05b06a9b Fix manifests not detecting include changes
When deciding if a new manifest should be generated for a new version,
the included manifests were not being used to detect a manifest
change. This caused manifests that had new includes: lines in the new
version to not be generated resulting in files from the included
manifest to be missed when adding/updating that bundle.

The fix was to compare includes: lines from the current and previous
manifest versions for differences and generate new manifests when that
was the case. In order to compare manifests duplicate includes: lines
are not allowed and previously os-core could be added multiple times to
a manifest because it was always added. This change also added detection
for os-core before automatically adding it to a bundle.
2016-03-31 18:45:33 +00:00
Patrick McCarty 1cd3cdb96f Update gitignore 2016-03-30 13:08:48 -07:00
William Douglas 9c920d65e8 Don't bump versions for deleted files
In Manifest.full, previously a file being deleted from a bundle caused
the version of the file in Manifest.full to increase incorrectly.
2016-03-29 20:06:57 +00:00
William Douglas 825dc6c75f Add functional tests to validate includes work
Check that os-core is subtracked correctly and that $bundle-includes
files are correctly subtracked as well. Also include testing for new
bundles being subtracked.
2016-03-24 21:17:56 +00:00
William Douglas 4fb32c570f Replace explicit os-core subtraction
Now that much of the includes plumbing present on the output/subtract
side, make sure all non os-core manifests explicitly include os-core,
and then use the new extra semantics of subtract_manifests() to subtract
all included bundles, not just os-core.
2016-03-24 21:17:56 +00:00
William Douglas 79e8a12b75 Populate the manifest->includes list
Using the $bundle-includes files for building submanifests from
directories and previous manifest files includes: sections, create the
manifest->includes list as the names of submanifests included in the
submanifest being instantiated.

This needs to then be processed into a list of pointers to the other
submanifests themselves once all other manifests have been
instantiated before use with functions that operate on the includes list
(subtract_manifest and write_manifest for now)..
2016-03-24 21:17:56 +00:00
William Douglas 9b8b1d5359 Make the subtract_manifests function include-aware
When subtracting a manifest from another, all the included manifests
also will need to be subtracted from the original. To do this build up a
unique list of all manifests included (directly or indirectly) from the
manifest being subtracted from.

With that in place, this gives an opportunity to provide the
functionality of subtracting all included manifests from the main
manifest by calling subtract_manifests(A, A);
2016-03-24 21:17:56 +00:00
William Douglas df7b2adc9d Print included bundles in the manifest header
If a bundle includes one or more other bundles, print out one or more
includes: lines as part of the header of the manifest file.

There will be as many includes: lines as included bundles (including
being omitted if a bundle does not include other bundles).
2016-03-24 21:17:56 +00:00
William Douglas 0dcad76694 Add field for expressing included bundles 2016-03-24 21:17:56 +00:00
William Douglas 24ef47f40c Fixups for clang-format v3.8.0 2016-03-24 21:17:56 +00:00
Patrick McCarty 0a41a7e2d5 basic creator: remove unneeded pushd/popd
Since full paths are used everywhere in this script, the pushd/popd
sequence is not needed.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-03-24 09:38:40 -07:00
55 changed files with 2751 additions and 1199 deletions
+5 -4
View File
@@ -19,16 +19,17 @@ stamp-h1
DEADJOE
swupd_create_update
swupd_make_pack
swupd-update-server.log*
swupd_make_fullfiles
swupd-server-*.tar.gz
swupd-server.log.*
cscope.*
*.kdev4
coverage/
src/*.gcno
src/*.gcda
tap-driver.sh
test/functional/*.log
test/functional/*.trs
test/functional/*/*.log
test/functional/*/*.trs
test/functional/*/logs/
test/functional/*/web-dir/
swupd-*.log
test-suite.log
+25
View File
@@ -0,0 +1,25 @@
sudo: required
dist: trusty
language: c
# Pre-install missing build dependencies:
# - libmagic (pull from repo)
# - libcheck 0.9.10 is slightly too old, since 0.9.12 adds TAP support
# - bsdiff 1.* is the Clear Linux OS fork
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y libmagic-dev
install:
- wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz
- tar -xvf check-0.10.0.tar.gz
- pushd check-0.10.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
- tar -xvf bsdiff-1.0.2.tar.xz
- pushd bsdiff-1.0.2 && ./configure --prefix=/usr --disable-tests && make -j48 && sudo make install && popd
# Ubuntu's default umask is 0002, but this break's swupd hash calculations.
script:
- 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'
after_failure: cat test-suite.log
+47 -12
View File
@@ -15,6 +15,7 @@ swupd_create_update_SOURCES = \
src/analyze_fs.c \
src/chroot.c \
src/config.c \
src/create_update.c \
src/delta.c \
src/fullfiles.c \
src/globals.c \
@@ -22,11 +23,9 @@ swupd_create_update_SOURCES = \
src/helpers.c \
src/heuristics.c \
src/log.c \
src/main.c \
src/manifest.c \
src/pack.c \
src/rename.c \
src/signature.c \
src/stats.c \
src/type_change.c \
src/versions.c \
@@ -37,13 +36,13 @@ swupd_make_pack_SOURCES = \
src/config.c \
src/delta.c \
src/globals.c \
src/groups.c \
src/helpers.c \
src/log.c \
src/make_packs.c \
src/manifest.c \
src/pack.c \
src/rename.c \
src/signature.c \
src/stats.c \
src/xattrs.c
@@ -53,13 +52,13 @@ swupd_make_fullfiles_SOURCES = \
src/delta.c \
src/fullfiles.c \
src/globals.c \
src/groups.c \
src/helpers.c \
src/log.c \
src/make_fullfiles.c \
src/manifest.c \
src/pack.c \
src/rename.c \
src/signature.c \
src/stats.c \
src/xattrs.c
@@ -96,24 +95,39 @@ noinst_HEADERS = \
include/swupd.h \
include/xattrs.h
TEST_EXTENSIONS = .sh
TEST_EXTENSIONS = .bats
if ENABLE_TESTS
tap_driver = env AM_TAP_AWK='$(AWK)' $(SHELL) \
$(top_srcdir)/tap-driver.sh
LOG_DRIVER = $(tap_driver)
SH_LOG_DRIVER = $(tap_driver)
BATS_LOG_DRIVER = $(tap_driver)
TESTS = $(dist_check_SCRIPTS)
dist_check_SCRIPTS = \
test/functional/basic.bats \
test/functional/update.bats \
test/functional/fullfiles.bats \
test/functional/pack.bats \
test/functional/full-run.bats \
test/functional/full-run-delta.bats
test/functional/basic/test.bats \
test/functional/contentsize-across-versions-includes/test.bats \
test/functional/delete-no-version-bump/test.bats \
test/functional/file-name-blacklisted/test.bats \
test/functional/format-no-decrement/test.bats \
test/functional/full-run-delta/test.bats \
test/functional/full-run/test.bats \
test/functional/fullfiles/test.bats \
test/functional/include-version-bump/test.bats \
test/functional/includes-deduplicate/test.bats \
test/functional/no-delta/test.bats \
test/functional/pack/test.bats \
test/functional/state-file/test.bats \
test/functional/subtract-delete/test.bats \
test/functional/update/test.bats \
test/functional/format-bump/test.bats
if RENAMES
dist_check_SCRIPTS += \
test/functional/renames/test.bats
endif
endif
if COVERAGE
@@ -131,6 +145,27 @@ endif
distclean-local:
rm -rf aclocal.m4 autogen.sh autom4te.cache config.h.in config.h.in~ configure depcomp install-sh Makefile.in missing compile
compliant:
@git diff --quiet --exit-code include src; ret=$$?; \
if [ $$ret -eq 1 ]; then \
echo "Error: can only check code style when include/ and src/ are clean."; \
echo "Stash or commit your changes and try again."; \
exit $$ret; \
elif [ $$ret -gt 1 ]; then \
exit $$ret; \
fi; \
clang-format -i -style=file include/*.h src/*.c; ret=$$?; \
if [ $$ret -ne 0 ]; then \
exit $$ret; \
fi; \
git diff --quiet --exit-code include src; ret=$$?; \
if [ $$ret -eq 1 ]; then \
echo "Code style issues found. Run 'git diff' to view issues."; \
elif [ $$ret -eq 0 ]; then \
echo "No code style issues found."; \
fi; \
exit $$ret
release:
@git rev-parse v$(PACKAGE_VERSION) &> /dev/null; \
if [ "$$?" -eq 0 ]; then \
+3 -11
View File
@@ -15,7 +15,7 @@ error() {
# eg: all of openstack, pnp, bat, cloud stuff, non-basic scripting language bundles
${SWUPDREPO}/mk_groups_ini.sh
PREVREL=`cat ${UPDATEDIR}/image/latest.version`
PREVREL=`cat ${UPDATEDIR}/image/LAST_VER`
export SWUPD_CERTS_DIR=${SWUPD_CERTS_DIR:-"/root/swupd-certs"}
export LEAF_KEY="leaf.key.pem"
@@ -23,11 +23,9 @@ export LEAF_CERT="leaf.cert.pem"
export CA_CHAIN_CERT="ca-chain.cert.pem"
export PASSPHRASE="${SWUPD_CERTS_DIR}/passphrase"
${SWUPDREPO}/swupd_create_update --osversion ${VER} --statedir ${UPDATEDIR}
${SWUPDREPO}/swupd_create_update --osversion ${VER} --statedir ${UPDATEDIR} --format 3
${SWUPDREPO}/swupd_make_fullfiles --statedir ${UPDATEDIR} ${VER}
pushd ${SWUPDREPO}
# create zero packs
MOM=${UPDATEDIR}/www/${VER}/Manifest.MoM
if [ ! -e ${MOM} ]; then
@@ -47,14 +45,8 @@ for job in $(jobs -p); do
fi
done
# create delta packs for 2 versions back
NUM_PACKS=2
${SWUPDREPO}/pack_maker.sh ${VER} ${NUM_PACKS}
popd
# expose the new build to staging / testing
echo ${VER} > ${UPDATEDIR}/image/latest.version
echo ${VER} > ${UPDATEDIR}/image/LAST_VER
STAGING_FILE="${UPDATEDIR}/www/version/formatstaging/latest"
if [ ! -f "${STAGING_FILE}" ]; then
+6 -1
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.66])
AC_INIT(swupd-server, 3.1.2, timothy.c.pepper@linux.intel.com)
AC_INIT(swupd-server, 3.6.1, matthew.johnson@intel.com)
AM_INIT_AUTOMAKE([foreign -Wall -W subdir-objects])
AM_SILENT_RULES([yes])
AC_PROG_CC
@@ -82,6 +82,11 @@ AS_IF([test "$enable_lzma" != "no"], [
])
AM_CONDITIONAL([ENABLE_LZMA], [test "$enable_lzma" != "no"])
AC_ARG_ENABLE([rename-detection], [AS_HELP_STRING([--enable-rename-detection], [enable rename detection feature])])
AS_IF([test "$enable_rename_detection" = "yes"], [AC_DEFINE(RENAMES,1,[Use rename detection])])
AM_CONDITIONAL([RENAMES], [test "$enable_rename_detection" = "yes"])
AC_CONFIG_FILES([Makefile])
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_OUTPUT
+37 -19
View File
@@ -13,26 +13,31 @@
// SWUPD_NUM_PACKS is also "PREV_CHECK" in releas tool swupd_bb.py (change both)
#define SWUPD_NUM_PACKS 4
#define SWUPD_NUM_MANIFEST_DELTAS 25
#define SWUPD_DEFAULT_FORMAT 3
#define SWUPD_SERVER_STATE_DIR "/var/lib/update"
#if SWUPD_WITH_BSDTAR
#define TAR_COMMAND "bsdtar"
#define TAR_XATTR_ARGS ""
#define TAR_WARN_ARGS ""
#define TAR_XATTR_ARGS_STRLIST
#define TAR_WARN_ARGS_STRLIST
#else
#define TAR_COMMAND "tar"
#define TAR_XATTR_ARGS "--xattrs --xattrs-include='*'"
#define TAR_WARN_ARGS "--warning=no-timestamp"
#define TAR_XATTR_ARGS_STRLIST "--xattrs", "--xattrs-include='*'",
#define TAR_WARN_ARGS_STRLIST "--warning=no-timestamp",
#endif
#if SWUPD_WITH_SELINUX
#define TAR_PERM_ATTR_ARGS "--preserve-permissions --selinux " TAR_XATTR_ARGS
#define TAR_PERM_ATTR_ARGS_STRLIST TAR_XATTR_ARGS_STRLIST "--preserve-permissions", "--selinux"
#else
#define TAR_PERM_ATTR_ARGS "--preserve-permissions " TAR_XATTR_ARGS
#define TAR_PERM_ATTR_ARGS_STRLIST TAR_XATTR_ARGS_STRLIST "--preserve-permissions"
#endif
#define SWUPD_HASH_DIRNAME "DIRECTORY"
#if SWUPD_WITH_STATELESS
#define OS_IS_STATELESS 1
#else
@@ -60,7 +65,15 @@
#include <lzma.h>
#endif
/* Approximatly the smallest size of a pair of input files which
* differ by a single bit that bsdiff can produce a more compact
* deltafile. Files smaller than this are always marked as different.
* See the magic 200 value in the bsdiff/src/diff.c code.
*/
#define BSDIFFSIZE 200
struct manifest {
unsigned long long int format;
int version;
int prevversion;
char *component;
@@ -70,6 +83,10 @@ struct manifest {
GList *manifests; /* as struct file */
GList *submanifests; /* as struct manifest */
GList *includes; /* struct manifests for all bundles included into this one */
GList *actions; /* post-update actions */
};
struct file;
@@ -114,7 +131,7 @@ struct file {
double rename_score;
struct file *rename_peer;
char *alpha_only_filename; /* filename minus all numerics/etc */
char *filetype;
char *filetype; /* The output of 'file', truncated */
char *basename;
char *dirname;
/* end of rename detection fields */
@@ -134,8 +151,7 @@ struct packdata {
extern int current_version;
extern int newversion;
extern int minversion;
extern char *format_string;
extern bool enable_signing;
extern unsigned long long int format;
extern char *state_dir;
extern char *packstage_dir;
@@ -144,7 +160,7 @@ extern char *staging_dir;
extern bool init_globals(void);
extern void free_globals(void);
extern bool set_format_string(char *);
extern bool set_format(char *);
extern void check_root(void);
extern bool set_state_dir(char *);
extern bool init_state_globals(void);
@@ -170,13 +186,16 @@ extern GList *get_last_versions_list(int next_version, int max_versions);
extern char *file_type_to_string(struct file *file);
extern struct manifest *manifest_from_file(int version, char *module);
extern void free_manifest(struct manifest *manifest);
extern struct manifest *alloc_manifest(int version, char *module);
extern struct manifest *alloc_manifest(int version, char *module, GList *actions);
extern int match_manifests(struct manifest *m1, struct manifest *m2);
extern void sort_manifest_by_version(struct manifest *manifest);
extern bool manifest_includes(struct manifest *manifest, char *component);
extern bool changed_includes(struct manifest *old, struct manifest *new);
extern int prune_manifest(struct manifest *manifest);
extern int remove_old_deleted_files(struct manifest *m1, struct manifest *m2);
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 subtract_manifests(struct manifest *m1, struct manifest *m2);
extern void subtract_manifests_frontend(struct manifest *m1, struct manifest *m2);
extern void nest_manifest(struct manifest *parent, struct manifest *sub);
extern void nest_manifest_file(struct manifest *parent, struct file *file);
extern int manifest_subversion(struct manifest *parent, char *group);
@@ -208,8 +227,7 @@ extern void chroot_create_full(int newversion);
extern void read_group_file(char *filename);
extern void release_group_file(void);
extern char *group_groups(char *group);
extern char *group_packages(char *group);
extern char *group_status(char *group);
extern char *next_group(void);
extern void apply_heuristics(struct manifest *manifest);
@@ -231,9 +249,9 @@ extern int previous_version_manifest(struct manifest *mom, char *name);
extern void type_change_detection(struct manifest *manifest);
extern void rename_detection(struct manifest *manifest, int last_change, GList *last_versions_list);
extern void link_renames(GList *newfiles, struct manifest *from_manifest);
extern void __create_delta(struct file *file, int from_version);
extern void rename_detection(struct manifest *manifest);
extern void link_renames(GList *newfiles, int to_version);
extern void __create_delta(struct file *file, int from_version, char *from_hash);
extern void account_delta_hit(void);
extern void account_delta_miss(void);
@@ -241,11 +259,11 @@ extern void account_delta_miss(void);
extern FILE *fopen_exclusive(const char *filename); /* no mode, opens for write only */
extern void dump_file_info(struct file *file);
extern void string_or_die(char **strp, const char *fmt, ...);
extern void print_elapsed_time(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(char *const argv[]);
extern bool signature_initialize(void);
extern void signature_terminate(void);
extern bool signature_sign(const char *filename);
extern int system_argv_fd(char *const argv[], int newstdin, int newstdout, int newstderr);
extern int system_argv_pipe(char *const argvp1[], int stdinp1, int stderrp1,
char *const argvp2[], int stdoutp2, int stderrp2);
extern int num_threads(float scaling);
#endif
+6 -2
View File
@@ -18,8 +18,8 @@ if [ ! -f "$SWUPD_SERVER_INI" ]; then
sed -i "s|/var/lib/update|$UPDATEDIR|" $SWUPD_SERVER_INI
fi
if [ ! -f "$UPDATEDIR/image/latest.version" ]; then
echo "0" > $UPDATEDIR/image/latest.version
if [ ! -f "$UPDATEDIR/image/LAST_VER" ]; then
echo "0" > $UPDATEDIR/image/LAST_VER
fi
echo "rebuilding $SWUPD_GROUPS_INI based on $BUNDLEREPO"
@@ -27,7 +27,11 @@ rm -f $SWUPD_GROUPS_INI
for bundle in $(ls $BUNDLEREPO/bundles)
do
status=$(awk -F: '/^# .STATUS/ {print $2}' $BUNDLEREPO/$bundle | tr -cd '[[:alnum:]]')
echo "[$bundle]" >> $SWUPD_GROUPS_INI
echo "group=$bundle" >> $SWUPD_GROUPS_INI
if [ -n "$status" ]; then
echo "status=$status" >> $SWUPD_GROUPS_INI
fi
echo "" >> $SWUPD_GROUPS_INI
done
-112
View File
@@ -1,112 +0,0 @@
#!/bin/bash -e
# usage:
# pack_maker.sh <to version> <back_count>
# pack_maker.sh 2820 2
# pack_maker.sh 2730 3
#
# For each bundle changed as of the Manifest.MoM at the <to version>,
# create version-pair packs going back <back_count>
# versions of that bundle. For example if bundle "editors" changed in 2650
# and the script is run as "pack_maker.sh 2650 2", it will find that
# "editors" last two change points were 2550 and 2260 and thus create a
# 2550-to-2650 and a 2260-to-2650 version pair pack for the "editors"
# bundle.
#NOTE: fullfiles must be created before calling this script
#NOTE: zero packs need similarly created
#NOTE: build numbers are sparse, search back for the next oldest existing build
#NOTE: in the future isn't simply -10, and when that change happens we must also
# consider which version-pair packs are meaningful for intermediate builds
# and revert builds.
SWUPDREPO=${SWUPDREPO:-"/usr/src/clear-projects/swupd-server"}
BUNDLEREPO=${BUNDLEREPO:-"/usr/src/clear-projects/clr-bundles"}
UPDATEDIR=${UPDATEDIR:-"/var/lib/update"}
SWUPDWEBDIR="${UPDATEDIR}/www"
SWUPD_CERTS_DIR=${SWUPD_CERTS_DIR=:-"/root/swupd-certs"}
export XZ_DEFAULTS="--threads 0"
export SWUPD_CERTS_DIR
export LEAF_KEY="leaf.key.pem"
export LEAF_CERT="leaf.cert.pem"
export CA_CHAIN_CERT="ca-chain.cert.pem"
export PASSPHRASE="${SWUPD_CERTS_DIR}/passphrase"
VER=$1
BACK_COUNT=$2
if [ "${BACK_COUNT}" == "" ]; then
echo "missing to pack count"
exit 1
fi
if [ "${VER}" == "" ]; then
echo "missing to version"
exit 1
fi
MOM=${SWUPDWEBDIR}/${VER}/Manifest.MoM
if [ ! -e ${MOM} ]; then
echo "invalid version (no ${MOM})"
exit 1
fi
#time ${SWUPDREPO}/swupd_make_fullfiles --statedir ${UPDATEDIR} ${VER}
BUNDLE_LIST=$(cat ${MOM} | awk -v V=${VER} '$1 ~ /^M\./ && $3 == V { print $4 }')
# build packs for all bundles changed in $VER
for BUNDLE in $BUNDLE_LIST; do
#${SWUPDREPO}/swupd_make_pack --statedir ${UPDATEDIR} 0 ${VER} ${BUNDLE} &
BUNDLE_VER_LIST=""
MANIFEST_VER=$(cat ${SWUPDWEBDIR}/$VER/Manifest.MoM | grep "^previous:" | cut -f 2)
BUNDLE_VER=""
COUNT=0
while [ $COUNT -lt $BACK_COUNT ] && [ $MANIFEST_VER -gt 0 ]; do
MOM=${SWUPDWEBDIR}/$MANIFEST_VER/Manifest.MoM
if [ -e ${MOM} ]; then
BUNDLE_VER=$(cat ${MOM} | grep " ${BUNDLE}$" | cut -f 3)
if [ "$BUNDLE_VER" != "" ]; then
BUNDLE_VER_LIST="$BUNDLE_VER $BUNDLE_VER_LIST"
MANIFEST_VER=$(cat ${SWUPDWEBDIR}/$BUNDLE_VER/Manifest.MoM | grep "^previous:" | cut -f 2)
let COUNT=$((COUNT+1))
else
# back in history to where bundle didn't exist yet
MANIFEST_VER=0
fi
else
# seek backwards until a manifest if found
MANIFEST_VER=$((MANIFEST_VER-10))
fi
done
for v in $BUNDLE_VER_LIST; do
if [ -e ${SWUPDWEBDIR}/${VER}/pack-${BUNDLE}-from-$v.tar ]; then
echo "${VER}/pack-${BUNDLE}-from-$v.tar already exists, skipping."
else
${SWUPDREPO}/swupd_make_pack --statedir ${UPDATEDIR} $v ${VER} ${BUNDLE} &
fi
done
done
for job in $(jobs -p)
do
wait ${job}
RET=$?
if [ "$RET" != "0" ]; then
echo "pack creation failure!"
exit $RET
fi
done
# NOTE: your devops will want to expose the completed swupd server build to
# trial usage at some point. This would be done via code like:
#
# echo ${VER} > ${SWUPDWEBDIR}/../image/latest.version
# STAGING=$(cat ${SWUPDWEBDIR}/version/formatstaging/latest)
# if [ "${STAGING}" -lt "${VER}" ]; then
# echo ${VER} > ${SWUPDWEBDIR}/version/formatstaging/latest
# fi
-82
View File
@@ -1,82 +0,0 @@
#!/usr/bin/perl -w
#
#
# arguments:
# perl packfsck.pl <target version>
my $target = $ARGV[0];
system("rm /tmp/Manifest");
system("wget --quiet --no-proxy --no-check-certificate --output-document=/tmp/Manifest https://download.clearlinux.org/update/$target/Manifest.os-core");
my $from = $target;
while ($from > $target - 100) {
$from = $from - 10;
print "Testing the $from-$target pack\n";
system("rm /tmp/pack.tar");
system("wget --quiet --no-proxy --no-check-certificate --output-document=/tmp/pack.tar https://download.clearlinux.org/update/$target/pack-os-core-from-$from.tar");
open FILE, "</tmp/Manifest";
my %expected_hashes;
my %files;
while (<FILE>) {
my $line = $_;
if ($line =~ /(^[FDLr\.]+)\s*([0-9a-f]+)\s*([0-9]+)\s*+(.*)\n/) {
my $type = $1;
my $hash = $2;
my $version = $3;
my $filename = $4;
if ($version > $from) {
$expected_hashes{"$hash"} = 1;
$files{"$hash"} = $filename;
# print "$1 - $2 - $3 - $4\n";
}
}
}
close FILE;
open FILE2, "-|", "tar -tf /tmp/pack.tar 2> /dev/null";
while (<FILE2>) {
my $line = $_;
if ($line =~ /^\.\/delta\/[0-9]+.[0-9]+.([0-9a-f]+)/) {
my $hash = $1;
if (!defined($expected_hashes{"$hash"})) {
print "\tUnexpected delta hash found $hash\n";
}
$expected_hashes{"$hash"} = 2;
}
if ($line =~ /\.\/staged\/([0-9a-f]+)/) {
my $hash = $1;
if (!defined($expected_hashes{"$hash"})) {
print "\tUnexpected staged hash found $hash\n";
}
$expected_hashes{"$hash"} = 2;
}
}
close FILE2;
my $count = 0;
foreach my $key ( keys %expected_hashes )
{
my $value = $expected_hashes{$key};
if ($value eq 1) {
my $fn = $files{$key};
print "\tFile $fn ($key) is not in the pack\n";
$count = $count = 1;
}
}
if ($count < 1) {
print "\tThe $from-$target pack has no files missing\n";
}
}
+206 -56
View File
@@ -23,25 +23,29 @@
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <glib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <openssl/hmac.h>
#include <linux/limits.h>
#include <openssl/hmac.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
#include "xattrs.h"
static GThreadPool *threadpool;
/* Why not strcpy? Looks like the hash was going to be stored in
* binary at one stage. Should use g_string_chunk_insert_const to
* change hash_compare to a pointer compare
*/
void hash_assign(char *src, char *dst)
{
memcpy(dst, src, SWUPD_HASH_LEN - 1);
@@ -196,7 +200,7 @@ int compute_hash(struct file *file, char *filename)
return 0;
}
hash_set_zeros(key);
hash_set_zeros(key); /* Set to 64 '0' (not '\0') characters */
if (file->is_link) {
char link[PATH_MAX];
@@ -222,7 +226,7 @@ int compute_hash(struct file *file, char *filename)
hmac_sha256_for_string(file->hash,
(const unsigned char *)key,
key_len,
file->filename); //file->filename not filename
SWUPD_HASH_DIRNAME); // Make independent of dirname
return 0;
}
@@ -275,7 +279,7 @@ static void get_hash(gpointer data, gpointer user_data)
/* disallow characters which can do unexpected things when the filename is
* used on a tar command line via system("tar [args] filename [more args]");
*/
static bool illegal_characters(char *filename)
static bool illegal_characters(const char *filename)
{
char c;
int i;
@@ -286,44 +290,164 @@ static bool illegal_characters(char *filename)
// these breaks the tar transform sed-like expression,
// hopefully can remove this check after moving to libtar
if (strncmp(filename, "+", 1) == 0) {
printf("WARNING: Filename %s begins with '+'! ...skipping.\n", filename);
return true;
}
if (strstr(filename, "+package+") != NULL) {
printf("WARNING: Filename %s contains \"+package+\"! ...skipping.\n", filename);
return true;
}
for (i = 0; i < BAD_CHAR_COUNT; i++) {
c = bad_chars[i];
if (strchr(filename, c) != NULL) {
printf("WARNING: Filename %s includes illegal character '%c'! ...skipping.\n", filename, c);
return true;
}
}
return false;
}
static struct file *add_file(struct manifest *manifest,
const char *entry_name,
char *sub_filename,
char *fullname,
bool do_hash)
{
GError *err = NULL;
struct file *file;
if (illegal_characters(entry_name)) {
printf("WARNING: Filename %s includes illegal character(s) ...skipping.\n", sub_filename);
free(sub_filename);
free(fullname);
return NULL;
}
file = calloc(1, sizeof(struct file));
assert(file);
file->last_change = manifest->version;
file->filename = sub_filename;
populate_file_struct(file, fullname);
if (file->is_deleted) {
/*
* populate_file_struct() logs a stat() failure, but
* does not abort. When adding files that should
* exist, this case is an error.
*/
LOG(NULL, "file not found", "%s", fullname);
assert(0);
}
/* if for some reason there is a file in the official build
* which should not be included in the Manifest, then open a bug
* to get it removed, and work around its presence by
* excluding it here, eg:
if (strncmp(file->filename, "/dev/", 5) == 0) {
continue;
}
*/
if (do_hash) {
/* compute the hash from a thread */
int ret;
ret = g_thread_pool_push(threadpool, file, &err);
if (ret == FALSE) {
printf("GThread hash computation push error\n");
printf("%s\n", err->message);
assert(0);
}
}
manifest->files = g_list_prepend(manifest->files, file);
manifest->count++;
return file;
}
static void iterate_directory(struct manifest *manifest, char *pathprefix,
char *subpath, bool do_hash)
{
DIR *dir;
struct dirent *entry;
char *fullpath;
int ret;
GError *err = NULL;
string_or_die(&fullpath, "%s/%s", pathprefix, subpath);
dir = opendir(fullpath);
if (!dir) {
bool fatal_error = errno != ENOENT;
FILE *content;
free(fullpath);
if (fatal_error) {
return;
}
/*
* If there is a <dir>.content.txt instead of
* the actual directory, then read that
* file. It has a list of path names,
* including all directories. The
* corresponding file system entry is then
* expected to be in a pre-populated "full"
* directory.
*
* Only supported at top level (i.e. empty
* subpath) to keep the code and testing
* simpler.
*/
assert(!subpath[0]);
string_or_die(&fullpath, "%s.content.txt", pathprefix);
content = fopen(fullpath, "r");
free(fullpath);
fullpath = NULL;
if (content) {
char *line = NULL;
size_t len = 0;
ssize_t read;
const char *full;
int full_len;
/*
* determine path to "full" directory: it is assumed to be alongside
* "pathprefix", i.e. pathprefix/../full. But pathprefix does not exit,
* so we have to strip the last path component.
*/
full = strrchr(pathprefix, '/');
if (full) {
full_len = full - pathprefix + 1;
full = pathprefix;
} else {
full = "";
full_len = 0;
}
while ((read = getline(&line, &len, content)) != -1) {
if (read) {
const char *entry_name = strrchr(line, '/');
if (entry_name) {
entry_name++;
} else {
entry_name = line;
}
if (line[read - 1] == '\n') {
line[read - 1] = 0;
}
string_or_die(&fullpath, "%.*sfull/%s", full_len, full, line);
add_file(manifest,
entry_name,
strdup(line),
fullpath,
do_hash);
}
}
free(line);
}
// If both directory and content file are missing, silently (?)
// don't add anything to the manifest.
return;
}
while (dir) {
struct file *file;
char *sub_filename;
char *fullname;
struct file *file;
entry = readdir(dir);
if (!entry) {
@@ -331,48 +455,19 @@ static void iterate_directory(struct manifest *manifest, char *pathprefix,
}
if ((strcmp(entry->d_name, ".") == 0) ||
(strcmp(entry->d_name, "..") == 0) ||
(illegal_characters(entry->d_name))) {
(strcmp(entry->d_name, "..") == 0)) {
continue;
}
file = calloc(1, sizeof(struct file));
if (!file) {
break;
}
file->last_change = manifest->version;
string_or_die(&file->filename, "%s/%s", subpath, entry->d_name);
string_or_die(&sub_filename, "%s/%s", subpath, entry->d_name);
string_or_die(&fullname, "%s/%s", fullpath, entry->d_name);
populate_file_struct(file, fullname);
free(fullname);
if (entry->d_type == DT_DIR) {
/* takes ownership of the strings, so we don't need to free it */
file = add_file(manifest, entry->d_name, sub_filename, fullname, do_hash);
if (file && file->is_dir) {
iterate_directory(manifest, pathprefix, file->filename, do_hash);
}
/* if for some reason there is a file in the official build
* which should not be included in the Manifest, then open a bug
* to get it removed, and work around its presence by
* excluding it here, eg:
if (strncmp(file->filename, "/dev/", 5) == 0) {
continue;
}
*/
if (do_hash) {
/* compute the hash from a thread */
ret = g_thread_pool_push(threadpool, file, &err);
if (ret == FALSE) {
printf("GThread hash computation push error\n");
printf("%s\n", err->message);
closedir(dir);
return;
}
}
manifest->files = g_list_prepend(manifest->files, file);
manifest->count++;
}
closedir(dir);
free(fullpath);
@@ -382,14 +477,15 @@ struct manifest *full_manifest_from_directory(int version)
{
struct manifest *manifest;
char *dir;
int numthreads = num_threads(1.0);
LOG(NULL, "Computing hashes", "for %i/full", version);
manifest = alloc_manifest(version, "full");
manifest = alloc_manifest(version, "full", NULL);
string_or_die(&dir, "%s/%i/full", image_dir, version);
threadpool = g_thread_pool_new(get_hash, dir, 12, FALSE, NULL);
threadpool = g_thread_pool_new(get_hash, dir, numthreads, FALSE, NULL);
iterate_directory(manifest, dir, "", true);
@@ -402,6 +498,58 @@ struct manifest *full_manifest_from_directory(int version)
return manifest;
}
/* Read includes from $manifest-includes file */
GList *get_sub_manifest_includes(char *component, int version)
{
FILE *infile;
GList *includes = NULL;
char *c;
char *conf;
char *filename;
char *included;
char line[8192];
conf = config_image_base();
if (conf == NULL) {
assert(0);
}
string_or_die(&filename, "%s/%i/noship/%s-includes", conf, version, component);
free(conf);
LOG(NULL, "Reading includes", "%s", filename);
infile = fopen(filename, "rb");
if (infile == NULL) {
if (errno != ENOENT) {
LOG(NULL, "Cannot read includes", "%s (%s)\n", filename, strerror(errno));
}
free(filename);
return NULL;
}
line[0] = 0;
while (strcmp(line, "\n") != 0) {
line[0] = 0;
if (fgets(line, 8191, infile) == NULL) {
break;
}
c = strchr(line, '\n');
if (c) {
*c = 0;
}
if (strlen(line) == 0) {
break;
}
included = strdup(line);
includes = g_list_prepend(includes, included);
}
fclose(infile);
includes = g_list_sort(includes, (GCompareFunc)strcmp);
return includes;
}
struct manifest *sub_manifest_from_directory(char *component, int version)
{
struct manifest *manifest;
@@ -409,7 +557,7 @@ struct manifest *sub_manifest_from_directory(char *component, int version)
LOG(NULL, "Creating component manifest", "for %i/%s", version, component);
manifest = alloc_manifest(version, component);
manifest = alloc_manifest(version, component, NULL);
string_or_die(&dir, "%s/%i/%s", image_dir, version, component);
@@ -419,6 +567,8 @@ struct manifest *sub_manifest_from_directory(char *component, int version)
manifest->files = g_list_sort(manifest->files, file_sort_filename);
manifest->includes = get_sub_manifest_includes(component, version);
return manifest;
}
+26 -38
View File
@@ -21,36 +21,41 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <glib.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
void chroot_create_full(int newversion)
{
int ret;
char *group;
char *command;
char *param;
char *full_dir;
string_or_die(&full_dir, "%s/%i/full/", image_dir, newversion);
if (!access(full_dir, R_OK | X_OK)) {
free(full_dir);
return;
}
g_mkdir_with_parents(full_dir, S_IRWXU);
/* start with base */
LOG(NULL, "Copying chroot os-core to full", "");
string_or_die(&command, "rsync -aAX %s/%i/os-core/ %s",
image_dir, newversion, full_dir);
ret = system(command);
assert(ret == 0);
free(command);
string_or_die(&param, "%s/%i/os-core/", image_dir, newversion);
if (!access(param, F_OK)) {
LOG(NULL, "Copying chroot os-core to full", "");
char *const rsynccmd[] = { "rsync", "-aAX", param, full_dir, NULL };
if (system_argv(rsynccmd) != 0) {
assert(0);
}
}
free(param);
/* overlay any new files from each group */
while (1) {
@@ -59,32 +64,15 @@ void chroot_create_full(int newversion)
break;
}
LOG(NULL, "Overlaying bundle chroot onto full", "%s", group);
string_or_die(&command, "rsync -aAX --ignore-existing %s/%i/%s/ %s",
image_dir, newversion, group, full_dir);
ret = system(command);
assert(ret == 0);
free(command);
}
/* and since some dumb files have things like install timestamps in
* them, we pull files from full back to bundles so each instance
* of the "same" file is truly identical across bundles. It may
* make sense at some point to add more logic here for conflict
* detection and resolution, including work to insure renames are
* consistent across groups. */
while (1) {
group = next_group();
if (!group) {
break;
string_or_die(&param, "%s/%i/%s/", image_dir, newversion, group);
if (!access(param, F_OK)) {
LOG(NULL, "Overlaying bundle chroot onto full", "%s", group);
char *const rsynccmd[] = { "rsync", "-aAX", "--ignore-existing", param, full_dir, NULL };
if (system_argv(rsynccmd) != 0) {
assert(0);
}
}
LOG(NULL, "Recopy bundle chroot out of full", "%s", group);
string_or_die(&command, "rsync -aAX --existing %s %s/%i/%s",
full_dir, image_dir, newversion, group);
ret = system(command);
assert(ret == 0);
free(command);
free(param);
}
free(full_dir);
}
+2 -2
View File
@@ -20,11 +20,11 @@
*
*/
#include <assert.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <glib.h>
#include "swupd.h"
+171 -57
View File
@@ -25,17 +25,18 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <glib.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <glib.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <getopt.h>
#include <sys/wait.h>
#include <unistd.h>
#include "swupd.h"
@@ -49,12 +50,12 @@ static void banner(void)
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'v' },
{ "log-stdout", no_argument, 0, 'l' },
{ "osversion", required_argument, 0, 'o' },
{ "minversion", required_argument, 0, 'm' },
{ "format", required_argument, 0, 'F' },
{ "getformat", no_argument, 0, 'g' },
{ "statedir", required_argument, 0, 'S' },
{ "signcontent", no_argument, 0, 's' },
{ 0, 0, 0, 0 }
};
@@ -67,12 +68,12 @@ static void print_help(const char *name)
printf(" -v, --version Show software version\n");
printf("\n");
printf("Application Options:\n");
printf(" -l, --log-stdout Write log messages also to stdout\n");
printf(" -o, --osversion The OS version for which to create an update\n");
printf(" -m, --minversion Optional minimum file version to write into manifests per file\n");
printf(" -F, --format Optional format string [ default:=%d ]\n", SWUPD_DEFAULT_FORMAT);
printf(" -F, --format Format number for the update\n");
printf(" -g, --getformat Print current format string and exit\n");
printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
printf(" -s, --signcontent Enables cryptographic signing of update content\n");
printf("\n");
}
@@ -80,12 +81,15 @@ static bool parse_options(int argc, char **argv)
{
int opt;
while ((opt = getopt_long(argc, argv, "hvo:m:F:g:S:s", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hvo:m:F:g:S:", prog_opts, NULL)) != -1) {
switch (opt) {
case '?':
case 'h':
print_help(argv[0]);
return false;
case 'l':
init_log_stdout();
break;
case 'v':
banner();
return false;
@@ -105,7 +109,7 @@ static bool parse_options(int argc, char **argv)
}
break;
case 'F':
if (!optarg || !set_format_string(optarg)) {
if (!optarg || !set_format(optarg)) {
printf("Invalid --format argument\n\n");
return false;
}
@@ -117,16 +121,13 @@ static bool parse_options(int argc, char **argv)
}
break;
case 'g':
if (format_string == NULL) {
printf("%d\n", SWUPD_DEFAULT_FORMAT);
if (format == 0) {
printf("No format specified\n");
} else {
printf("%s\n", format_string);
printf("%llu\n", format);
free_globals();
}
exit(0);
case 's':
enable_signing = true;
break;
}
}
@@ -140,13 +141,14 @@ static bool parse_options(int argc, char **argv)
static void populate_dirs(int version)
{
char *newversiondir;
char *newversiondircontent = NULL;
string_or_die(&newversiondir, "%s/%d", image_dir, version);
if ((access(newversiondir, F_OK | R_OK) != 0) && (version == 0)) {
char *latestpath = NULL;
string_or_die(&latestpath, "%s/latest.version", image_dir);
string_or_die(&latestpath, "%s/LAST_VER", image_dir);
printf("** %s does not exist... creating and populating\n", newversiondir);
if (mkdir(newversiondir, 0755) != 0) {
@@ -181,9 +183,11 @@ static void populate_dirs(int version)
}
string_or_die(&newversiondir, "%s/%d/%s", image_dir, version, group);
string_or_die(&newversiondircontent, "%s/%d/%s.content.txt", image_dir, version, group);
/* Create the bundle directory(s) as needed */
if (access(newversiondir, F_OK | R_OK) != 0) {
if (access(newversiondir, F_OK | R_OK) != 0 &&
access(newversiondircontent, F_OK | R_OK) != 0) {
printf("%s does not exist...creating\n", group);
if (mkdir(newversiondir, 0755) != 0) {
printf("Failed to create %s subdirectory\n", group);
@@ -192,6 +196,7 @@ static void populate_dirs(int version)
}
}
free(newversiondir);
free(newversiondircontent);
}
static int check_build_env(void)
@@ -212,6 +217,25 @@ static int check_build_env(void)
return 0;
}
static int check_group_file(void)
{
int ret = -1;
// Ensure the os-core group is defined
while (1) {
char *group = next_group();
if (!group) {
break;
}
if (strcmp(group, "os-core") == 0) {
ret = 0;
break;
}
}
return ret;
}
int main(int argc, char **argv)
{
struct manifest *new_core = NULL;
@@ -223,8 +247,11 @@ int main(int argc, char **argv)
struct manifest *old_full = NULL;
struct manifest *new_full = NULL;
GHashTable *new_manifests = g_hash_table_new(g_str_hash, g_str_equal);
GHashTable *old_manifests = g_hash_table_new(g_str_hash, g_str_equal);
GList *manifests_last_versions_list = NULL;
int newfiles = 0;
int old_deleted = 0;
struct timeval current_time;
struct timeval previous_time;
@@ -237,6 +264,11 @@ int main(int argc, char **argv)
/* keep valgrind working well */
setenv("G_SLICE", "always-malloc", 0);
if (!setlocale(LC_ALL, "")) {
fprintf(stderr, "%s: setlocale() failed\n", argv[0]);
return EXIT_FAILURE;
}
if (!parse_options(argc, argv)) {
free_globals();
return EXIT_FAILURE;
@@ -250,12 +282,6 @@ int main(int argc, char **argv)
goto exit;
}
/* Initilize the crypto signature module */
if (!signature_initialize()) {
printf("Can't initialize the crypto signature module!\n");
goto exit;
}
string_or_die(&file_path, "%s/server.ini", state_dir);
if (!read_configuration_file(file_path)) {
printf("Failed to read %s configuration file!\n", state_dir);
@@ -267,8 +293,13 @@ int main(int argc, char **argv)
string_or_die(&file_path, "%s/groups.ini", state_dir);
read_group_file(file_path);
free(file_path);
ret = check_group_file();
if (ret != 0) {
printf("os-core bundle is not listed in groups.ini, this is required.\n");
goto exit;
}
read_current_version("latest.version");
read_current_version("LAST_VER");
printf("Last processed version is %i\n", current_version);
populate_dirs(newversion);
@@ -285,7 +316,7 @@ int main(int argc, char **argv)
printf("Syncing bundle chroots with full chroot\n");
chroot_create_full(newversion);
print_elapsed_time(&previous_time, &current_time);
print_elapsed_time("full chroot creation", &previous_time, &current_time);
printf("Calculating full manifest (this is expensive/slow)\n");
old_full = manifest_from_file(current_version, "full");
@@ -293,18 +324,41 @@ int main(int argc, char **argv)
apply_heuristics(old_full);
apply_heuristics(new_full);
match_manifests(old_full, new_full);
apply_heuristics(new_full);
#warning disabled rename detection for some simplicity
// rename_detection(new_full);
print_elapsed_time(&previous_time, &current_time);
old_deleted = remove_old_deleted_files(old_full, new_full);
if (old_deleted > 0) {
LOG(NULL, "", "Old deleted files (%d) removed from full manifest", old_deleted);
printf("Old deleted files (%d) removed from full manifest\n", old_deleted);
}
apply_heuristics(new_full);
print_elapsed_time("full manifest creation", &previous_time, &current_time);
printf("Entering phase 2: Doing the os-core bundle\n");
/* Phase 2 : the os-core set */
/* Step 2: Make a manifest for the os-core set */
old_MoM = manifest_from_file(current_version, "MoM");
new_MoM = alloc_manifest(newversion, "MoM");
/* Verify that the new format is not older than the previous format. It
* never makes sense to decrease the format number for the next build. */
if (format < old_MoM->format) {
LOG(NULL, "", "Current format (%llu) must be greater than or equal to previous format (%llu). Exiting",
format, old_MoM->format);
printf("Current format (%llu) must be greater than or equal to previous format (%llu). Exiting\n",
format, old_MoM->format);
goto exit;
}
/* Detect a format bump and add the "update" action to the manifest
* "actions:" field */
GList *actions = NULL;
if (format > old_MoM->format) {
actions = g_list_prepend(actions, "update");
}
new_MoM = alloc_manifest(newversion, "MoM", actions);
old_core = manifest_from_file(manifest_subversion(old_MoM, "os-core"), "os-core");
new_core = sub_manifest_from_directory("os-core", newversion);
add_component_hashes_to_manifest(new_core, new_full);
@@ -327,32 +381,83 @@ int main(int argc, char **argv)
apply_heuristics(new_core);
/* Step 3c: ... else save the manifest */
type_change_detection(new_core);
#warning disabled rename detection for some simplicity
/* Detect renamed files specifically for each pack */
// rename_detection(...);
sort_manifest_by_version(new_core);
#ifdef RENAMES
/* Detect renamed files specifically for os-core */
rename_detection(new_core);
#endif
old_deleted = remove_old_deleted_files(old_core, new_core);
sort_manifest_by_version(new_core); /* sorts by filename */
newfiles = prune_manifest(new_core);
if (newfiles <= 0) {
LOG(NULL, "", "Core component has not changed (after pruning), exiting");
printf("Core component has not changed (after pruning), exiting\n");
goto exit;
}
LOG(NULL, "", "Core component has changes (%d), writing out new manifest", newfiles);
printf("Core component has changes (%d), writing out new manifest\n", newfiles);
LOG(NULL, "", "Core component has changes (%d new, %d deleted), writing out new manifest", newfiles, old_deleted);
printf("Core component has changes (%d new, %d deleted), writing out new manifest\n", newfiles, old_deleted);
if (write_manifest(new_core) != 0) {
LOG(NULL, "", "Core component manifest write failed");
printf("Core component manifest write failed\n");
goto exit;
}
create_manifest_deltas(new_core, manifests_last_versions_list);
}
nest_manifest(new_MoM, new_core);
/* Phase 3: the functional bundles */
printf("Entering phase 3: The bundles\n");
while (1) {
char *group = next_group();
if (!group) {
break;
}
(void)g_hash_table_insert(new_manifests, group, sub_manifest_from_directory(group, newversion));
(void)g_hash_table_insert(old_manifests, group, manifest_from_file(manifest_subversion(old_MoM, group), group));
}
while (1) {
GList *manifest_includes = NULL;
GList *name_includes;
char *group = next_group();
struct manifest *manifest;
struct manifest *current = NULL;
if (!group) {
break;
}
manifest = g_hash_table_lookup(new_manifests, group);
name_includes = manifest->includes;
while (name_includes) {
char *name = name_includes->data;
name_includes = g_list_next(name_includes);
current = g_hash_table_lookup(new_manifests, name);
// Avoid adding duplicate includes to the list
if (g_list_find(manifest_includes, current) == NULL) {
manifest_includes = g_list_prepend(manifest_includes, current);
}
}
manifest->includes = manifest_includes;
manifest_includes = NULL;
manifest = g_hash_table_lookup(old_manifests, group);
name_includes = manifest->includes;
while (name_includes) {
char *name = name_includes->data;
name_includes = g_list_next(name_includes);
current = g_hash_table_lookup(old_manifests, name);
// Avoid adding duplicate includes to the list
if (g_list_find(manifest_includes, current) == NULL) {
manifest_includes = g_list_prepend(manifest_includes, current);
}
}
manifest->includes = manifest_includes;
}
while (1) {
char *group = next_group();
struct manifest *oldm;
@@ -369,39 +474,50 @@ int main(int argc, char **argv)
printf("Processing bundle %s\n", group);
/* Step 4: Make a manifest for this functonal group */
oldm = manifest_from_file(manifest_subversion(old_MoM, group), group);
newm = sub_manifest_from_directory(group, newversion);
oldm = g_hash_table_lookup(old_manifests, group);
newm = g_hash_table_lookup(new_manifests, group);
add_component_hashes_to_manifest(newm, new_full);
apply_heuristics(oldm);
apply_heuristics(newm);
newm->prevversion = oldm->version;
/* add os-core as an included manifest */
if (!manifest_includes(oldm, "os-core")) {
oldm->includes = g_list_prepend(oldm->includes, old_core);
}
if (!manifest_includes(newm, "os-core")) {
newm->includes = g_list_prepend(newm->includes, new_core);
}
/* Step 5: Subtract the core files from the manifest */
subtract_manifests(oldm, old_core);
subtract_manifests(newm, new_core);
subtract_manifests_frontend(oldm, oldm);
subtract_manifests_frontend(newm, newm);
/* Step 6: Compare manifest to the previous version... */
if (match_manifests(oldm, newm) == 0) {
if (match_manifests(oldm, newm) == 0 && !changed_includes(oldm, newm)) {
LOG(NULL, "", "%s components have not changed, no new manifest", group);
printf("%s components have not changed, no new manifest\n", group);
/* Step 6a: if nothing changed, stay at the old version */
newm->version = oldm->version;
} else {
apply_heuristics(newm);
#warning missing rename_detection here
#ifdef RENAMES
/* Detect renamed files specifically for this bundle */
rename_detection(newm);
#endif
/* Step 6b: otherwise, write out the manifest */
old_deleted = remove_old_deleted_files(oldm, newm);
sort_manifest_by_version(newm);
type_change_detection(newm);
newfiles = prune_manifest(newm);
if (newfiles > 0) {
LOG(NULL, "", "%s component has changes (%d), writing out new manifest", group, newfiles);
printf("%s component has changes (%d), writing out new manifest\n", group, newfiles);
if (newfiles > 0 || old_deleted > 0 || changed_includes(oldm, newm)) {
LOG(NULL, "", "%s component has changes (%d new, %d deleted), writing out new manifest", group, newfiles, old_deleted);
printf("%s component has changes (%d new, %d deleted), writing out new manifest\n", group, newfiles, old_deleted);
if (write_manifest(newm) != 0) {
LOG(NULL, "", "%s component manifest write failed", group);
printf("%s component manifest write failed\n", group);
goto exit;
}
create_manifest_deltas(newm, manifests_last_versions_list);
} else {
LOG(NULL, "", "%s component has not changed (after pruning), no new manifest", group);
printf("%s component has not changed (after pruning), no new manifest\n", group);
@@ -412,7 +528,7 @@ int main(int argc, char **argv)
nest_manifest(new_MoM, newm);
}
print_elapsed_time(&previous_time, &current_time);
print_elapsed_time("bundle manifest creation", &previous_time, &current_time);
printf("Entering phase 4: completion manifests \n");
/* Phase 4 : manifest completion */
@@ -423,9 +539,8 @@ int main(int argc, char **argv)
printf("Failed to write new MoM\n");
goto exit;
}
create_manifest_deltas(new_MoM, manifests_last_versions_list);
print_elapsed_time(&previous_time, &current_time);
print_elapsed_time("MoM creation", &previous_time, &current_time);
printf("Entering phase 5: creating download content\n");
/* Phase 5: wrapping up */
@@ -442,17 +557,16 @@ int main(int argc, char **argv)
/* Step 8: Prepare delta directory */
prepare_delta_dir(new_full);
print_elapsed_time(&previous_time, &current_time);
print_elapsed_time("finalize update", &previous_time, &current_time);
exit_status = EXIT_SUCCESS;
exit:
if (exit_status == EXIT_SUCCESS) {
write_cookiecrumbs_to_download_area(newversion);
//write_new_version("latest.version", newversion);
//write_new_version("LAST_VER", newversion);
}
release_configuration_data();
release_group_file();
signature_terminate();
g_list_free(manifests_last_versions_list);
close_log(newversion, exit_status);
+18 -22
View File
@@ -21,30 +21,27 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <bsdiff.h>
#include <errno.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <bsdiff.h>
#include <glib.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
#include "xattrs.h"
void __create_delta(struct file *file, int from_version)
void __create_delta(struct file *file, int from_version, char *from_hash)
{
char *original, *newfile, *outfile, *dotfile, *testnewfile, *sanitycheck;
char *conf;
char *original, *newfile, *outfile, *dotfile, *testnewfile, *conf;
int ret;
if (file->is_link) {
return;
if (!file->is_file || !file->peer->is_file) {
return; /* only support deltas between two regular files right now */
}
if (file->is_deleted) {
@@ -64,10 +61,9 @@ void __create_delta(struct file *file, int from_version)
conf = config_output_dir();
string_or_die(&outfile, "%s/%i/delta/%i-%i-%s", conf, file->last_change, from_version, file->last_change, file->hash);
string_or_die(&dotfile, "%s/%i/delta/.%i-%i-%s", conf, file->last_change, from_version, file->last_change, file->hash);
string_or_die(&testnewfile, "%s/%i/delta/.%i-%i-%s.testnewfile", conf, file->last_change, from_version, file->last_change, file->hash);
string_or_die(&sanitycheck, "cmp -s \"%s\" \"%s\"", newfile, testnewfile);
string_or_die(&outfile, "%s/%i/delta/%i-%i-%s-%s", conf, file->last_change, from_version, file->last_change, from_hash, file->hash);
string_or_die(&dotfile, "%s/%i/delta/.%i-%i-%s-%s", conf, file->last_change, from_version, file->last_change, from_hash, file->hash);
string_or_die(&testnewfile, "%s/%i/delta/.%i-%i-%s-%s.testnewfile", conf, file->last_change, from_version, file->last_change, from_hash, file->hash);
LOG(file, "Making delta", "%s->%s", original, newfile);
@@ -101,7 +97,7 @@ void __create_delta(struct file *file, int from_version)
ret = 0;
goto out;
}
xattrs_copy(original, newfile);
xattrs_copy(original, testnewfile);
/* does xattrs have been correctly copied?*/
if (xattrs_compare(original, testnewfile) != 0) {
@@ -112,13 +108,14 @@ void __create_delta(struct file *file, int from_version)
goto out;
}
ret = system(sanitycheck);
if (ret == -1 || !WIFEXITED(ret) || WEXITSTATUS(ret) == 2) {
char *const sanitycheck[] = { "cmp", "-s", newfile, testnewfile, NULL };
ret = system_argv(sanitycheck);
if (ret == -1 || ret == 2) {
printf("Sanity check system command failed %i. \n", ret);
printf("%s->%s via diff %s yielded %s\n", original, newfile, dotfile, testnewfile);
assert(0);
goto out;
} else if (WEXITSTATUS(ret) == 1) {
} else if (ret == 1) {
printf("Delta application resulted in file mismatch %i. \n", ret);
printf("%s->%s via diff %s yielded %s\n", original, newfile, dotfile, testnewfile);
LOG(file, "Delta mismatch:", "%s->%s via diff %s yielded %s", original, newfile, dotfile, testnewfile);
@@ -139,7 +136,6 @@ void __create_delta(struct file *file, int from_version)
LOG(NULL, "Failed to rename", "");
}
out:
free(sanitycheck);
free(testnewfile);
free(conf);
free(newfile);
+64 -49
View File
@@ -22,18 +22,18 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <glib.h>
#include <libgen.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <errno.h>
#include <libgen.h>
#include <glib.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
@@ -42,7 +42,6 @@
static void create_fullfile(struct file *file)
{
char *origin;
char *tarcommand = NULL;
char *tarname = NULL;
char *rename_source = NULL;
char *rename_target = NULL;
@@ -50,6 +49,8 @@ static void create_fullfile(struct file *file)
int ret;
struct stat sbuf;
char *empty, *indir, *outdir;
char *param1, *param2;
int stderrfd;
if (file->is_deleted) {
return; /* file got deleted -> by definition we cannot tar it up */
@@ -71,7 +72,7 @@ static void create_fullfile(struct file *file)
string_or_die(&origin, "%s/%i/full/%s", indir, file->last_change, file->filename);
if (lstat(origin, &sbuf) < 0) {
/* no input file: means earlier phase of update creation failed */
LOG(NULL, "Failed to stat %s\n", origin);
LOG(NULL, "Failed to stat", "%s: %s", origin, strerror(errno));
assert(0);
}
@@ -92,14 +93,22 @@ static void create_fullfile(struct file *file)
assert(0);
}
string_or_die(&tarcommand, TAR_COMMAND " -C %s " TAR_PERM_ATTR_ARGS " -cf - --exclude='%s/?*' './%s' 2> /dev/null | "
TAR_COMMAND " -C %s " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null",
dir, base, base, rename_tmpdir);
if (system(tarcommand) != 0) {
LOG(NULL, "Failed to run command:", "%s", tarcommand);
string_or_die(&param1, "--exclude=%s/?*", base);
string_or_die(&param2, "./%s", base);
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 };
stderrfd = open("/dev/null", O_WRONLY);
if (stderrfd == -1) {
LOG(NULL, "Failed to open /dev/null", "");
assert(0);
}
free(tarcommand);
if (system_argv_pipe(tarcfcmd, -1, stderrfd, tarxfcmd, -1, stderrfd) != 0) {
assert(0);
}
free(param1);
free(param2);
close(stderrfd);
string_or_die(&rename_source, "%s/%s", rename_tmpdir, base);
string_or_die(&rename_target, "%s/%s", rename_tmpdir, file->hash);
@@ -110,13 +119,13 @@ static void create_fullfile(struct file *file)
free(rename_source);
/* for a directory file, tar up simply with gzip */
string_or_die(&tarcommand, TAR_COMMAND " -C %s " TAR_PERM_ATTR_ARGS " -zcf %s/%i/files/%s.tar %s",
rename_tmpdir, outdir, file->last_change, file->hash, file->hash);
if (system(tarcommand) != 0) {
LOG(NULL, "Failed to run command:", "%s", tarcommand);
string_or_die(&param1, "%s/%i/files/%s.tar", outdir, file->last_change, file->hash);
char *const tarcmd[] = { TAR_COMMAND, "-C", rename_tmpdir, TAR_PERM_ATTR_ARGS_STRLIST, "-zcf", param1, file->hash, NULL };
if (system_argv(tarcmd) != 0) {
assert(0);
}
free(tarcommand);
free(param1);
if (rmdir(rename_target)) {
LOG(NULL, "rmdir failed for %s", rename_target);
@@ -125,6 +134,7 @@ static void create_fullfile(struct file *file)
if (rmdir(rename_tmpdir)) {
LOG(NULL, "rmdir failed for %s", rename_tmpdir);
}
free(rename_tmpdir);
free(tmp1);
free(tmp2);
@@ -145,29 +155,38 @@ static void create_fullfile(struct file *file)
/* step 2a: tar it with each compression type */
// lzma
string_or_die(&tarcommand, TAR_COMMAND " --directory=%s " TAR_PERM_ATTR_ARGS " -Jcf %s/%i/files/%s.tar.xz %s",
empty, outdir, file->last_change, file->hash, file->hash);
if (system(tarcommand) != 0) {
LOG(NULL, "Failed to run command:", "%s", tarcommand);
string_or_die(&param1, "--directory=%s", empty);
string_or_die(&param2, "%s/%i/files/%s.tar.xz", outdir, file->last_change, file->hash);
char *const tarlzmacmd[] = { TAR_COMMAND, param1, TAR_PERM_ATTR_ARGS_STRLIST, "-Jcf", param2, file->hash, NULL };
if (system_argv(tarlzmacmd) != 0) {
assert(0);
}
free(tarcommand);
free(param1);
free(param2);
// gzip
string_or_die(&tarcommand, TAR_COMMAND " --directory=%s " TAR_PERM_ATTR_ARGS " -zcf %s/%i/files/%s.tar.gz %s",
empty, outdir, file->last_change, file->hash, file->hash);
if (system(tarcommand) != 0) {
LOG(NULL, "Failed to run command:", "%s", tarcommand);
string_or_die(&param1, "--directory=%s", empty);
string_or_die(&param2, "%s/%i/files/%s.tar.gz", outdir, file->last_change, file->hash);
char *const targzipcmd[] = { TAR_COMMAND, param1, TAR_PERM_ATTR_ARGS_STRLIST, "-zcf", param2, file->hash, NULL };
if (system_argv(targzipcmd) != 0) {
assert(0);
}
free(tarcommand);
free(param1);
free(param2);
#ifdef SWUPD_WITH_BZIP2
string_or_die(&tarcommand, TAR_COMMAND " --directory=%s " TAR_PERM_ATTR_ARGS " -jcf %s/%i/files/%s.tar.bz2 %s",
empty, outdir, file->last_change, file->hash, file->hash);
if (system(tarcommand) != 0) {
LOG(NULL, "Failed to run command:", "%s", tarcommand);
string_or_die(&param1, "--directory=%s", empty);
string_or_die(&param2, "%s/%i/files/%s.tar.bz2", outdir, file->last_change, file->hash);
char *const tarbzip2cmd[] = { TAR_COMMAND, param1, TAR_PERM_ATTR_ARGS_STRLIST, "-jcf", param2, file->hash, NULL };
if (system_argv(tarbzip2cmd) != 0) {
assert(0);
}
free(tarcommand);
free(param1);
free(param2);
#endif
/* step 2b: pick the smallest of the three compression formats */
@@ -230,22 +249,20 @@ static GList *get_deduplicated_fullfile_list(struct manifest *manifest)
struct file *tmp;
// presort by hash for easy deduplication
manifest->files = g_list_sort(manifest->files, file_sort_hash);
list = manifest->files = g_list_sort(manifest->files, file_sort_hash);
list = g_list_first(manifest->files);
while (prev == NULL) {
for (; list; list = g_list_next(list)) {
tmp = list->data;
list = g_list_next(list);
// find first new file
if (tmp->last_change == manifest->version) {
prev = tmp;
outfiles = g_list_prepend(outfiles, tmp);
break;
}
}
while (list) {
for (; list; list = g_list_next(list)) {
file = list->data;
list = g_list_next(list);
// add any new file having a unique hash
//FIXME: rename logic will be needed here
@@ -272,10 +289,11 @@ static void submit_fullfile_tasks(GList *files)
int ret;
int count = 0;
GError *err = NULL;
int numthreads = num_threads(3.0);
LOG(NULL, "fullfile threadpool", "%d threads", sysconf(_SC_NPROCESSORS_ONLN) * 3);
LOG(NULL, "fullfile threadpool", "%d threads", numthreads);
threadpool = g_thread_pool_new(create_fullfile_task, NULL,
sysconf(_SC_NPROCESSORS_ONLN) * 3,
numthreads,
TRUE, NULL);
printf("Starting downloadable fullfiles data creation\n");
@@ -303,17 +321,14 @@ void create_fullfiles(struct manifest *manifest)
{
GList *deduped_file_list;
char *path;
char *cmd;
char *conf, *empty;
empty = config_empty_dir();
string_or_die(&cmd, "rm -rf %s", empty);
if (system(cmd) != 0) {
LOG(NULL, "Failed to run command:", "%s", cmd);
char *const rmcmd[] = { "rm", "-rf", empty, NULL };
if (system_argv(rmcmd) != 0) {
assert(0);
}
free(cmd);
g_mkdir_with_parents(empty, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
free(empty);
+13 -17
View File
@@ -21,40 +21,36 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include "swupd.h"
int newversion = -1;
int minversion = 0;
char *format_string = NULL;
bool enable_signing = false;
unsigned long long int format = 0;
char *state_dir = NULL;
char *packstage_dir = NULL;
char *image_dir = NULL;
char *staging_dir = NULL;
bool set_format_string(char *userinput)
bool set_format(char *userinput)
{
int version;
unsigned long long int user_format;
// expect a positive integer
// format string shall be a positive integer
errno = 0;
version = strtoull(userinput, NULL, 10);
if ((errno < 0) || (version <= 0)) {
user_format = strtoull(userinput, NULL, 10);
if ((errno < 0) || (user_format == 0)) {
return false;
}
if (format_string) {
free(format_string);
}
string_or_die(&format_string, "%d", version);
format = user_format;
return true;
}
@@ -81,8 +77,9 @@ bool set_state_dir(char *dir)
bool init_globals(void)
{
if (format_string == NULL) {
string_or_die(&format_string, "%d", SWUPD_DEFAULT_FORMAT);
if (format == 0) {
printf("Error: Missing format parameter. Please specify a format with -F.\n");
return false;
}
if (!init_state_globals()) {
@@ -101,7 +98,6 @@ bool init_globals(void)
void free_globals(void)
{
free(format_string);
free(state_dir);
free(packstage_dir);
free(image_dir);
+11 -2
View File
@@ -23,11 +23,11 @@
*
*/
#include <assert.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <glib.h>
#include "swupd.h"
@@ -37,6 +37,7 @@ static char **groups = NULL;
static gsize groupcount = 0;
static unsigned int groupcursor = 0;
#if 0
char *group_packages(char *group)
{
assert(groupfile != NULL);
@@ -50,6 +51,14 @@ char *group_groups(char *group)
return g_key_file_get_value(groupfile, group, "groups", NULL);
}
#endif
char *group_status(char *group)
{
assert(groupfile != NULL);
return g_key_file_get_value(groupfile, group, "status", NULL);
}
void read_group_file(char *filename)
{
+127 -14
View File
@@ -21,19 +21,18 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <glib.h>
#include <unistd.h>
#include "swupd.h"
@@ -102,22 +101,27 @@ void string_or_die(char **strp, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
if (vasprintf(strp, fmt, ap) <= 0) {
if (vasprintf(strp, fmt, ap) < 0) {
assert(0);
}
va_end(ap);
}
void print_elapsed_time(struct timeval *previous_time, struct timeval *current_time)
/* "step" should be a descriptive string for the process that completed before
* this function is called */
void print_elapsed_time(const char *step, struct timeval *previous_time,
struct timeval *current_time)
{
char *elapsed;
assert(strlen(step) > 0);
gettimeofday(current_time, NULL);
elapsed = get_elapsed_time(previous_time, current_time);
if (strcmp(elapsed, " ") == 0) {
printf("\t~0.0 elapsed for Manifest.full creation\n");
printf("\t~0.0 elapsed for '%s' step\n", step);
} else {
printf("\t%s elapsed for full chroot creation\n", elapsed);
printf("\t%s elapsed for '%s' step\n", elapsed, step);
}
previous_time->tv_sec = current_time->tv_sec;
previous_time->tv_usec = current_time->tv_usec;
@@ -149,7 +153,7 @@ int system_argv(char *const argv[])
{
int child_exit_status;
pid_t pid;
int status;
int status = -1;
pid = fork();
@@ -182,9 +186,93 @@ int system_argv(char *const argv[])
LOG(NULL, "Failed to run command:", "%s", cmdline);
free(cmdline);
}
return status;
}
return status;
}
int system_argv_fd(char *const argv[], int newstdin, int newstdout, int newstderr)
{
int child_exit_status;
pid_t pid;
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,
char *const argvp2[], int stdoutp2, int stderrp2)
{
int statusp2;
int pipefd[2];
if (pipe(pipefd)) {
LOG(NULL, "Failed to create a pipe", "");
return -1;
}
system_argv_fd(argvp1, stdinp1, pipefd[1], stderrp1);
close(pipefd[1]);
statusp2 = system_argv_fd(argvp2, pipefd[0], stdoutp2, stderrp2);
close(pipefd[0]);
/* Returns the status of the failed process if any
If both processes failed returns the status of first one */
return statusp2;
}
void check_root(void)
@@ -196,3 +284,28 @@ void check_root(void)
exit(EXIT_FAILURE);
}
}
int num_threads(float scaling)
{
const char *var = getenv("SWUPD_NUM_THREADS");
int result = sysconf(_SC_NPROCESSORS_ONLN) * scaling;
if (var && *var) {
char *endptr;
long int value;
errno = 0;
value = strtol(var, &endptr, 0);
if ((errno != 0 && value == 0) || *endptr) {
LOG(NULL, "SWUPD_NUM_THREADS must be an integer", "%s", var);
} else if ((errno == ERANGE && (value == LONG_MAX || value == LONG_MIN)) ||
value < 1 || value > INT_MAX) {
LOG(NULL, "SWUPD_NUM_THREADS out of range", "%s", var);
} else {
result = (int)value;
}
}
return result;
}
+3 -4
View File
@@ -21,13 +21,12 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <glib.h>
#include <unistd.h>
#include "swupd.h"
+21 -15
View File
@@ -22,19 +22,18 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <glib.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/time.h>
#include <glib.h>
#include <unistd.h>
#include "swupd.h"
static FILE *logfile;
static FILE *logfile[2];
static struct timeval start_time;
@@ -42,13 +41,13 @@ void init_log(const char *prefix, const char *bundle, int start, int end)
{
char *filename;
string_or_die(&filename, "%s%s-from-%i-to-%i.log", prefix, bundle, start, end);
logfile = fopen(filename, "w");
logfile[0] = fopen(filename, "w");
free(filename);
gettimeofday(&start_time, NULL);
}
void init_log_stdout(void)
{
logfile = stdout;
logfile[1] = stdout;
gettimeofday(&start_time, NULL);
}
@@ -92,8 +91,9 @@ void __log_message(struct file *file, char *msg, char *filename, int linenr, con
char *logstring = NULL;
char filebuf[4096];
char filebuf2[4096];
int i;
if (!logfile) {
if (!logfile[0] && !logfile[1]) {
return;
}
@@ -120,12 +120,16 @@ void __log_message(struct file *file, char *msg, char *filename, int linenr, con
strcat(filebuf2, " ");
}
fprintf(logfile, "%3i.%03i %5s %s:%03i\t| %s\t| %s\t| %s\n",
(int)current_time.tv_sec, (int)current_time.tv_usec / 1000, logstring, filebuf, linenr, filebuf2, msg, buf);
for (i = 0; i < 2; i++) {
if (logfile[i]) {
fprintf(logfile[i], "%3i.%03i %5s %s:%03i\t| %s\t| %s\t| %s\n",
(int)current_time.tv_sec, (int)current_time.tv_usec / 1000, logstring, filebuf, linenr, filebuf2, msg, buf);
fflush(logfile[i]);
}
}
free(logstring);
free(buf);
fflush(logfile);
}
void close_log(int version, int exit_status)
@@ -134,7 +138,7 @@ void close_log(int version, int exit_status)
int t_sec;
int t_msec;
if (!logfile) {
if (!logfile[0] && !logfile[1]) {
return;
}
@@ -160,6 +164,8 @@ void close_log(int version, int exit_status)
printf("Update build failed for version %i\n", version);
}
fclose(logfile);
logfile = NULL;
if (logfile[0]) {
fclose(logfile[0]);
logfile[0] = NULL;
}
}
+14 -4
View File
@@ -21,18 +21,19 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <getopt.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <unistd.h>
#include "swupd.h"
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "log-stdout", no_argument, 0, 'l' },
{ "statedir", required_argument, 0, 'S' },
{ 0, 0, 0, 0 }
};
@@ -43,6 +44,7 @@ static void usage(const char *name)
printf(" %s <version>\n\n", name);
printf("Help options:\n");
printf(" -h, --help Show help options\n");
printf(" -l, --log-stdout Write log messages also to stdout\n");
printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
printf("\n");
}
@@ -57,6 +59,9 @@ static bool parse_options(int argc, char **argv)
case 'h':
usage(argv[0]);
return false;
case 'l':
init_log_stdout();
break;
case 'S':
if (!optarg || !set_state_dir(optarg)) {
printf("Invalid --statedir argument '%s'\n\n", optarg);
@@ -89,6 +94,11 @@ int main(int argc, char **argv)
/* keep valgrind working well */
setenv("G_SLICE", "always-malloc", 0);
if (!setlocale(LC_ALL, "")) {
fprintf(stderr, "%s: setlocale() failed\n", argv[0]);
return EXIT_FAILURE;
}
if (!parse_options(argc, argv)) {
free_state_globals();
return EXIT_FAILURE;
+18 -23
View File
@@ -21,20 +21,18 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <glib.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <getopt.h>
#include <glib.h>
#include <getopt.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
@@ -47,8 +45,8 @@ static void banner(void)
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
{ "log-stdout", no_argument, 0, 'l' },
{ "statedir", required_argument, 0, 'S' },
{ "signcontent", no_argument, 0, 's' },
{ 0, 0, 0, 0 }
};
@@ -58,8 +56,8 @@ static void usage(const char *name)
printf(" %s <start version> <latest version> <bundle>\n\n", name);
printf("Help options:\n");
printf(" -h, --help Show help options\n");
printf(" -l, --log-stdout Write log messages also to stdout\n");
printf(" -S, --statedir Optional directory to use for state [ default:=%s ]\n", SWUPD_SERVER_STATE_DIR);
printf(" -s, --signcontent Enables cryptographic signing of update content\n");
printf("\n");
}
@@ -73,15 +71,15 @@ static bool parse_options(int argc, char **argv)
case 'h':
usage(argv[0]);
return false;
case 'l':
init_log_stdout();
break;
case 'S':
if (!optarg || !set_state_dir(optarg)) {
printf("Invalid --statedir argument ''%s'\n\n", optarg);
return false;
}
break;
case 's':
enable_signing = true;
break;
}
}
@@ -103,6 +101,11 @@ int main(int argc, char **argv)
int exit_status = EXIT_FAILURE;
char *file_path = NULL;
if (!setlocale(LC_ALL, "")) {
fprintf(stderr, "%s: setlocale() failed\n", argv[0]);
return EXIT_FAILURE;
}
if (!parse_options(argc, argv)) {
free_state_globals();
return EXIT_FAILURE;
@@ -116,12 +119,6 @@ int main(int argc, char **argv)
banner();
check_root();
/* Initilize the crypto signature module */
if (!signature_initialize()) {
printf("Can't initialize the crypto signature module!\n");
return exit_status;
}
string_or_die(&file_path, "%s/server.ini", state_dir);
read_configuration_file(file_path);
free(file_path);
@@ -154,8 +151,6 @@ int main(int argc, char **argv)
exit_status = EXIT_SUCCESS;
}
signature_terminate();
printf("Pack creation %s (pack-%s %i to %li)\n",
exit_status == EXIT_SUCCESS ? "complete" : "failed",
module, start_version, end_version);
+306 -93
View File
@@ -23,19 +23,17 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <bsdiff.h>
#include <errno.h>
#include <glib.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <bsdiff.h>
#include <glib.h>
#include <unistd.h>
#include "swupd.h"
#include "xattrs.h"
@@ -53,6 +51,10 @@ int file_sort_hash(gconstpointer a, gconstpointer b)
return memcmp(A->hash, B->hash, SWUPD_HASH_LEN - 1);
}
/* Standard sort compare function which sorts
* first on the version where the file changed and then
* on the name
*/
int file_sort_version(gconstpointer a, gconstpointer b)
{
struct file *A, *B;
@@ -66,7 +68,7 @@ int file_sort_version(gconstpointer a, gconstpointer b)
return 1;
}
return strcmp(B->filename, A->filename);
return strcmp(A->filename, B->filename);
}
int file_sort_filename(gconstpointer a, gconstpointer b)
@@ -90,7 +92,7 @@ int file_sort_filename(gconstpointer a, gconstpointer b)
return 0;
}
struct manifest *alloc_manifest(int version, char *component)
struct manifest *alloc_manifest(int version, char *component, GList *actions)
{
struct manifest *manifest;
@@ -101,6 +103,8 @@ struct manifest *alloc_manifest(int version, char *component)
manifest->version = version;
manifest->component = strdup(component);
manifest->format = format;
manifest->actions = actions;
return manifest;
}
@@ -108,11 +112,13 @@ struct manifest *alloc_manifest(int version, char *component)
struct manifest *manifest_from_file(int version, char *component)
{
FILE *infile;
GList *includes = NULL;
char line[8192], *c, *c2;
int count = 0;
struct manifest *manifest;
char *filename, *conf;
int previous = 0;
unsigned long long int format_number;
conf = config_output_dir();
if (conf == NULL) {
@@ -128,7 +134,7 @@ struct manifest *manifest_from_file(int version, char *component)
if (infile == NULL) {
LOG(NULL, "Cannot read manifest", "%s (%s)\n", filename, strerror(errno));
free(filename);
return alloc_manifest(version, component);
return alloc_manifest(version, component, NULL);
}
/* line 1: MANIFEST\t<version> */
@@ -144,7 +150,9 @@ struct manifest *manifest_from_file(int version, char *component)
return NULL;
}
c = &line[9];
if (strtoull(c, NULL, 10) == 0) {
format_number = strtoull(c, NULL, 10);
if ((errno < 0) || (format_number == 0)) {
//format string shall be a positive integer
printf("Unknown file format version in MANIFEST line: %s\n", c);
fclose(infile);
return NULL;
@@ -178,10 +186,18 @@ struct manifest *manifest_from_file(int version, char *component)
if (strncmp(line, "previous:", 9) == 0) {
previous = strtoull(c, NULL, 10);
}
if (strncmp(line, "includes:", 9) == 0) {
includes = g_list_prepend(includes, strdup(c));
if (!includes->data) {
abort();
}
}
}
manifest = alloc_manifest(version, component);
manifest = alloc_manifest(version, component, NULL);
manifest->format = format_number;
manifest->prevversion = previous;
manifest->includes = includes;
/* empty line */
while (!feof(infile)) {
@@ -509,29 +525,57 @@ int match_manifests(struct manifest *m1, struct manifest *m2)
return count;
}
/* removes all files from m2 from m1 */
static GList *get_unique_includes(struct manifest *manifest)
{
GHashTable *unique_includes = g_hash_table_new(g_str_hash, g_str_equal);
GHashTableIter iter;
GList *includes = NULL;
GList *l1, *l2;
gpointer k, v;
l1 = g_list_first(manifest->includes);
while (l1) {
struct manifest *m = l1->data;
l1 = g_list_next(l1);
(void)g_hash_table_replace(unique_includes, m->component, m);
l2 = get_unique_includes(m);
while (l2) {
struct manifest *m = l2->data;
l2 = g_list_next(l2);
(void)g_hash_table_replace(unique_includes, m->component, m);
}
g_list_free(l2);
}
g_hash_table_iter_init(&iter, unique_includes);
while (g_hash_table_iter_next(&iter, &k, &v)) {
includes = g_list_prepend(includes, v);
}
return includes;
}
/*
* removes all files from m2 from m1
* This will recurse over all included manifests to also
* subtract those from the m1 manifest.
*
* As a special convenient semantics,
* subtract_manifests(M, M)
* will subtract all included manifests from M,
* but will not subtract M from M itself.
*/
void subtract_manifests(struct manifest *m1, struct manifest *m2)
{
GList *list1, *list2;
struct file *file1, *file2;
if (!m1) {
printf("Subtracting manifests failed: No m1 manifest!\n");
return;
}
if (!m2) {
printf("Subtracting manifests failed: No m2 manifest!\n");
return;
}
m1->files = g_list_sort(m1->files, file_sort_filename);
m2->files = g_list_sort(m2->files, file_sort_filename);
list1 = g_list_first(m1->files);
list2 = g_list_first(m2->files);
while (list1 && list2) {
while (list1 && list2 && m1 != m2) {
int ret;
file1 = list1->data;
file2 = list2->data;
@@ -543,6 +587,16 @@ void subtract_manifests(struct manifest *m1, struct manifest *m2)
list1 = g_list_next(list1);
list2 = g_list_next(list2);
/* When both files are marked deleted, skip
* subtraction. Preserving the deleted entries in both
* manifests is required for 'swupd update' to know
* when to delete the file, because the m2 bundle may
* be installed with or without the m1 bundle.
*/
if (file1->is_deleted && file2->is_deleted) {
continue;
}
if (file1->is_deleted == file2->is_deleted && file1->is_file == file2->is_file) {
m1->files = g_list_delete_link(m1->files, todel);
m1->count--;
@@ -555,6 +609,31 @@ void subtract_manifests(struct manifest *m1, struct manifest *m2)
}
}
void subtract_manifests_frontend(struct manifest *m1, struct manifest *m2)
{
GList *includes;
struct manifest *m;
if (!m1) {
printf("Subtracting manifests failed: No m1 manifest!\n");
return;
}
if (!m2) {
printf("Subtracting manifests failed: No m2 manifest!\n");
return;
}
subtract_manifests(m1, m2);
includes = get_unique_includes(m2);
while (includes) {
m = includes->data;
includes = g_list_next(includes);
subtract_manifests(m1, m);
}
}
char *file_type_to_string(struct file *file)
{
static char type[5];
@@ -596,19 +675,29 @@ char *file_type_to_string(struct file *file)
return type;
}
/* Calculate the contentsize for the manifest based on file sizes.
*
* This should calculate the files uniquely included in this manifest, but none
* of its submanifests, which will allow calculation of sizes of all bundles on
* a system by adding all manifest->contentsizes of installed bundles.
*
* However, if two bundles not in the same include chain have overlapping
* content, summing the include chain of each bundle in the client will result
* in an over-estimation of the total size on the system. The more content is
* shared, the higher the over-estimation. In reality this overlap will not be
* large, but it is currently impossible to calculate the exact installed size
* using just the contentsize.
*/
static void compute_content_size(struct manifest *manifest)
{
/* FIXME: this is a temporary implementation based on worst case */
GList *list;
struct file *file;
struct manifest *submanifest;
list = g_list_first(manifest->files);
while (list) {
file = list->data;
list = g_list_next(list);
if (!file->is_deleted && (file->last_change == manifest->version)) {
if (!file->is_deleted) {
if (file->is_file) {
manifest->contentsize += file->stat.st_size;
} else if (file->is_link) {
@@ -618,52 +707,23 @@ static void compute_content_size(struct manifest *manifest)
}
}
}
list = g_list_first(manifest->submanifests);
while (list) {
submanifest = list->data;
list = g_list_next(list);
/* Do not take into account groups not included in download content */
if (create_download_content_for_group(submanifest->component)) {
manifest->contentsize += submanifest->contentsize;
}
}
}
/* Returns 0 == success, -1 == failure */
static int write_manifest_signature(struct manifest *manifest, const char *suffix)
{
char *conf = config_output_dir();
char *filename = NULL;
int ret = -1;
if (conf == NULL) {
assert(0);
}
string_or_die(&filename, "%s/%i/Manifest.%s%s", conf, manifest->version,
manifest->component, suffix);
if (!signature_sign(filename)) {
fprintf(stderr, "Creating signature for '%s' failed\n", filename);
goto exit;
}
ret = 0;
exit:
free(filename);
free(conf);
return ret;
}
/* Returns 0 == success, -1 == failure */
static int write_manifest_plain(struct manifest *manifest)
{
GList *includes;
GList *list;
GList *actions;
struct file *file;
FILE *out = NULL;
char *base = NULL, *dir;
char *conf = config_output_dir();
char *filename = NULL;
char *status = NULL;
char *submanifest_filename = NULL;
char *manifest_tempdir = NULL;
char *tempmanifest = NULL;
int ret = -1;
if (conf == NULL) {
@@ -687,13 +747,31 @@ static int write_manifest_plain(struct manifest *manifest)
goto exit;
}
fprintf(out, "MANIFEST\t%s\n", format_string);
fprintf(out, "MANIFEST\t%llu\n", format);
fprintf(out, "version:\t%i\n", manifest->version);
fprintf(out, "previous:\t%i\n", manifest->prevversion);
fprintf(out, "filecount:\t%i\n", manifest->count);
fprintf(out, "timestamp:\t%i\n", (int)time(NULL));
compute_content_size(manifest);
fprintf(out, "contentsize:\t%llu\n", (long long unsigned int)manifest->contentsize);
includes = manifest->includes;
status = group_status(manifest->component);
if (status) {
fprintf(out, "status:\t%s\n", status);
}
while (includes) {
struct manifest *sub = includes->data;
includes = g_list_next(includes);
fprintf(out, "includes:\t%s\n", sub->component);
}
actions = manifest->actions;
while (actions) {
char *action = actions->data;
fprintf(out, "actions:\t%s\n", action);
actions = g_list_next(actions);
}
fprintf(out, "\n");
list = g_list_first(manifest->files);
@@ -705,24 +783,61 @@ static int write_manifest_plain(struct manifest *manifest)
}
list = g_list_first(manifest->manifests);
string_or_die(&manifest_tempdir, "%s/XXXXXX", state_dir);
if (!mkdtemp(manifest_tempdir)) {
LOG(NULL, "Failed to create temporary directory %s for untarring Manifests", manifest_tempdir);
assert(0);
}
while (list) {
file = list->data;
list = g_list_next(list);
string_or_die(&submanifest_filename, "%s/%i/Manifest.%s", conf, file->last_change, file->filename);
populate_file_struct(file, submanifest_filename);
ret = compute_hash(file, submanifest_filename);
if (file->is_deleted) {
goto write_entry;
}
char *param1, *param2;
/* Untar the Manifest.BUNDLE.tar and calculate the hash on that,
* otherwise we may get incorrect hashes due to owner permissions on
* the files. */
string_or_die(&param1, "%s", manifest_tempdir);
string_or_die(&param2, "%s.tar", submanifest_filename);
char *const tarcmd[] = { TAR_COMMAND, "-C", param1, TAR_PERM_ATTR_ARGS_STRLIST, "-xf", param2, NULL };
ret = system_argv(tarcmd);
free(param1);
free(param2);
if (ret != 0) {
assert(0);
}
string_or_die(&tempmanifest, "%s/Manifest.%s", manifest_tempdir, file->filename);
populate_file_struct(file, tempmanifest);
ret = compute_hash(file, tempmanifest);
if (ret != 0) {
printf("Hash computation failed\n");
assert(0);
}
unlink(tempmanifest);
free(tempmanifest);
write_entry:
fprintf(out, "%s\t%s\t%i\t%s\n", file_type_to_string(file), file->hash, file->last_change, file->filename);
free(submanifest_filename);
}
ret = 0;
exit:
if (manifest_tempdir) {
if (rmdir(manifest_tempdir) != 0) {
LOG(NULL, "rmdir failed for %s: %s", manifest_tempdir, strerror(errno));
}
free(manifest_tempdir);
}
if (out) {
fclose(out);
}
@@ -736,34 +851,28 @@ exit:
static int write_manifest_tar(struct manifest *manifest)
{
char *conf = config_output_dir();
char *tarcmd = NULL;
int ret = -1;
char *directory, *manifesttar, *manifestcomp;
int ret = 0;
if (conf == NULL) {
assert(0);
}
string_or_die(&directory, "--directory=%s/%i", conf, manifest->version);
string_or_die(&manifesttar, "%s/%i/Manifest.%s.tar", conf, manifest->version, manifest->component);
string_or_die(&manifestcomp, "Manifest.%s", manifest->component);
/* now, tar the thing up for efficient full file download */
/* and put the signature of the plain manifest into the archive, too */
if (enable_signing) {
string_or_die(&tarcmd, TAR_COMMAND " --directory=%s/%i " TAR_PERM_ATTR_ARGS " -Jcf "
"%s/%i/Manifest.%s.tar Manifest.%s Manifest.%s.signed",
conf, manifest->version, conf, manifest->version, manifest->component,
manifest->component, manifest->component);
} else {
string_or_die(&tarcmd, TAR_COMMAND " --directory=%s/%i " TAR_PERM_ATTR_ARGS " -Jcf "
"%s/%i/Manifest.%s.tar Manifest.%s",
conf, manifest->version, conf, manifest->version, manifest->component,
manifest->component);
char *const tarcmd[] = { TAR_COMMAND, directory, TAR_PERM_ATTR_ARGS_STRLIST, "-Jcf",
manifesttar, manifestcomp, NULL };
ret = system_argv(tarcmd);
if (ret) {
fprintf(stderr, "Creation of Manifest.tar failed\n");
}
if (system(tarcmd) != 0) {
fprintf(stderr, "Creation of Manifest.tar failed\n");
goto exit;
}
ret = 0;
exit:
free(tarcmd);
free(directory);
free(manifesttar);
free(manifestcomp);
free(conf);
return ret;
}
@@ -793,9 +902,7 @@ bool compute_hash_with_xattrs(const char *filename)
int write_manifest(struct manifest *manifest)
{
if (write_manifest_plain(manifest) == 0 &&
write_manifest_signature(manifest, "") == 0 &&
write_manifest_tar(manifest) == 0 &&
write_manifest_signature(manifest, ".tar") == 0) {
write_manifest_tar(manifest) == 0) {
return 0;
}
return -1;
@@ -811,6 +918,115 @@ void sort_manifest_by_version(struct manifest *manifest)
}
}
bool manifest_includes(struct manifest *manifest, char *component)
{
GList *includes = g_list_first(manifest->includes);
while (includes) {
if (strcmp(((struct manifest *)includes->data)->component,
component) == 0) {
return true;
}
includes = g_list_next(includes);
}
return false;
}
/* This requires the manifest to have the includes sorted.
* The manifests will be sorted when constructed from
* get_sub_manifest_includes (and then replacing the manifest
* strings with actual manifest structs in the same order as
* is done in the main.c's main()).
*/
bool changed_includes(struct manifest *old, struct manifest *new)
{
GList *includes_old = g_list_first(old->includes);
GList *includes_new = g_list_first(new->includes);
if (g_list_length(includes_old) != g_list_length(includes_new)) {
return true;
}
while (includes_old && includes_new) {
char *bundle_old = ((struct manifest *)includes_old->data)->component;
char *bundle_new = ((struct manifest *)includes_new->data)->component;
if (strcmp(bundle_old, bundle_new) != 0) {
return true;
}
includes_old = g_list_next(includes_old);
includes_new = g_list_next(includes_new);
}
return false;
}
/* For a format bump, it's convenient to remove deleted files from manifests
* that last changed prior to the format bump, since they are no longer
* considered for deletion as part of an update.
*
* Note: this function should be called after match_manifests().
*/
int remove_old_deleted_files(struct manifest *m1, struct manifest *m2)
{
GList *list1, *list2;
struct file *file1, *file2;
int count = 0;
if (!m1) {
printf("No old manifest!\n");
return -1;
}
if (!m2) {
printf("No new manifest!\n");
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
* m1 is the old manifest, and m2 is the new.
*/
m1->files = g_list_sort(m1->files, file_sort_filename);
m2->files = g_list_sort(m2->files, file_sort_filename);
list1 = g_list_first(m1->files);
list2 = g_list_first(m2->files);
while (list1 && list2) {
int ret;
file1 = list1->data;
file2 = list2->data;
ret = strcmp(file1->filename, file2->filename);
if (ret == 0) {
if (file1->is_deleted && file2->is_deleted) {
GList *to_delete = list2;
list1 = g_list_next(list1);
list2 = g_list_next(list2);
m2->files = g_list_delete_link(m2->files, to_delete);
m2->count--;
count++;
continue;
}
list1 = g_list_next(list1);
list2 = g_list_next(list2);
} else if (ret < 0) {
list1 = g_list_next(list1);
} else {
list2 = g_list_next(list2);
}
}
return count;
}
/* Conditionally remove some things from a manifest.
* Returns > 0 when the pruned manifest has new files.
* Returns 0 when the pruned manifest no longer has new files.
@@ -888,9 +1104,6 @@ void create_manifest_delta(int oldversion, int newversion, char *module)
}
LOG(NULL, "Failed to rename", "");
}
if (!signature_sign(outfile)) {
fprintf(stderr, "Creating signature for '%s' failed\n", outfile);
}
} else {
sleep(1); /* we raced. whatever. sleep for a bit to get the other guy to make progress */
}
@@ -1001,7 +1214,7 @@ static void maximize_version_manifests(struct manifest *m1, struct manifest *m2)
ret = strcmp(file1->filename, file2->filename);
if (ret == 0) {
if (file1->last_change > file2->last_change) {
if (!file1->is_deleted && file1->last_change > file2->last_change) {
LOG(file1, "Update", "Moving %s to version %i", file1->filename, file1->last_change);
file2->last_change = file1->last_change;
}
+47 -69
View File
@@ -24,38 +24,35 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <glib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <glib.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
static void empty_pack_stage(int full, int from_version, int to_version, char *module)
{
char *cmd;
char *param;
char *path;
int ret;
// clean any stale data (eg: re-run after a failure)
string_or_die(&cmd, "rm -rf %s/%s/%i_to_%i/", packstage_dir, module,
from_version, to_version);
ret = system(cmd);
if (ret) {
string_or_die(&param, "%s/%s/%i_to_%i/", packstage_dir, module, from_version, to_version);
char *const rmcmd[] = { "rm", "-fr", param, NULL };
if (system_argv(rmcmd) != 0) {
fprintf(stderr, "Failed to clean %s/%s/%i_to_%i\n",
packstage_dir, module, from_version, to_version);
free(cmd);
free(param);
exit(EXIT_FAILURE);
}
free(cmd);
free(param);
if (!full) {
// (re)create module/version/{delta,staged}
@@ -90,7 +87,7 @@ static void explode_pack_stage(int from_version, int to_version, char *module)
}
free(path);
while (1) {
char *path, *tar;
char *path, *param;
int ret;
entry = readdir(dir);
@@ -118,14 +115,12 @@ static void explode_pack_stage(int from_version, int to_version, char *module)
* the resulting pack is slightly smaller, and in addition, we're saving CPU
* time on the client...
*/
string_or_die(&tar, TAR_COMMAND " --directory=%s/%s/%i_to_%i/staged " TAR_WARN_ARGS " "
TAR_PERM_ATTR_ARGS " -xf %s",
packstage_dir, module, from_version, to_version, path);
ret = system(tar);
if (!ret) {
string_or_die(&param, "%s/%s/%i_to_%i/staged", packstage_dir, module, from_version, to_version);
char *const tarcmd[] = { TAR_COMMAND, "-C", param, TAR_WARN_ARGS_STRLIST TAR_PERM_ATTR_ARGS_STRLIST, "-xf", path, NULL };
if (system_argv(tarcmd) == 0) {
unlink(path);
}
free(tar);
free(param);
free(path);
}
closedir(dir);
@@ -149,7 +144,7 @@ static void prepare_pack(struct packdata *pack)
match_manifests(manifest, pack->end_manifest);
link_renames(pack->end_manifest->files, manifest);
link_renames(pack->end_manifest->files, pack->to);
}
static void make_pack_full_files(struct packdata *pack)
@@ -259,8 +254,8 @@ static GList *consolidate_packs_delta_files(GList *files, struct packdata *pack)
continue;
}
string_or_die(&from, "%s/%i/delta/%i-%i-%s", staging_dir, file->last_change,
file->peer->last_change, file->last_change, file->hash);
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);
ret = stat(from, &stat_delta);
if (ret && !find_file_in_list(files, file)) {
@@ -279,7 +274,7 @@ static void create_delta(gpointer data, __unused__ gpointer user_data)
/* if the file was not found in the from version, skip delta creation */
if (file->peer) {
__create_delta(file, file->peer->last_change);
__create_delta(file, file->peer->last_change, file->peer->hash);
}
}
@@ -290,10 +285,11 @@ static void make_pack_deltas(GList *files)
struct file *file;
int ret;
GError *err = NULL;
int numthreads = num_threads(1.0);
LOG(NULL, "pack deltas threadpool", "%d threads", sysconf(_SC_NPROCESSORS_ONLN));
LOG(NULL, "pack deltas threadpool", "%d threads", numthreads);
threadpool = g_thread_pool_new(create_delta, NULL,
sysconf(_SC_NPROCESSORS_ONLN), FALSE, NULL);
numthreads, FALSE, NULL);
item = g_list_first(files);
while (item) {
@@ -312,31 +308,13 @@ static void make_pack_deltas(GList *files)
g_thread_pool_free(threadpool, FALSE, TRUE);
}
/* Returns 0 == success, -1 == failure */
static int write_pack_signature(struct packdata *pack)
{
char *filename = NULL;
int ret = -1;
string_or_die(&filename, "%s/%i/pack-%s-from-%i.tar",
staging_dir, pack->to, pack->module, pack->from);
if (!signature_sign(filename)) {
fprintf(stderr, "Creating signature for '%s' failed\n", filename);
goto exit;
}
ret = 0;
exit:
free(filename);
return ret;
}
/* Returns 0 == success, other == failure */
static int make_final_pack(struct packdata *pack)
{
GList *item;
struct file *file;
int ret;
char *tar;
char *param1, *param2;
double penalty;
LOG(NULL, "make_final_pack", "%s: %i to %i", pack->module, pack->from, pack->to);
@@ -358,11 +336,11 @@ static int make_final_pack(struct packdata *pack)
/* for each file changed since <X> */
/* locate delta, check if the diff it's from is >= <X> */
string_or_die(&from, "%s/%i/delta/%i-%i-%s", staging_dir, file->last_change,
file->peer->last_change, file->last_change, file->hash);
string_or_die(&to, "%s/%s/%i_to_%i/delta/%i-%i-%s", packstage_dir,
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);
string_or_die(&to, "%s/%s/%i_to_%i/delta/%i-%i-%s-%s", packstage_dir,
pack->module, pack->from, pack->to, file->peer->last_change,
file->last_change, file->hash);
file->last_change, file->peer->hash, file->hash);
string_or_die(&tarfrom, "%s/%i/files/%s.tar", staging_dir,
file->last_change, file->hash);
string_or_die(&tarto, "%s/%s/%i_to_%i/staged/%s.tar", packstage_dir,
@@ -440,6 +418,8 @@ static int make_final_pack(struct packdata *pack)
explode_pack_stage(pack->from, pack->to, pack->module);
}
char *bundle_delta = NULL;
/* now... link in the Manifest pack */
if (pack->from != 0) {
char *from, *to;
@@ -454,20 +434,23 @@ static int make_final_pack(struct packdata *pack)
create_manifest_delta(pack->from, pack->to, pack->module);
}
string_or_die(&to, "%s/%s/%i_to_%i/Manifest-%s-delta-from-%i", packstage_dir,
pack->module, pack->from, pack->to, pack->module, pack->from);
ret = link(from, to);
if (ret) {
LOG(NULL, "Failed to link", "Manifest-delta-from-%i", pack->from);
LOG(NULL, "Failed to link", "Manifest-%s-delta-from-%i (%s)", pack->module, pack->from, strerror(errno));
} else {
string_or_die(&bundle_delta, "Manifest-%s-delta-from-%i", pack->module, pack->from);
}
free(from);
free(to);
}
/* now... link in the MoM Manifest into the base pack */
char *mom_delta = NULL;
/* now... link in the MoM Manifest into the os-core pack */
if ((pack->from != 0) && (strcmp(pack->module, "os-core") == 0)) {
char *from, *to;
struct stat st;
@@ -485,7 +468,9 @@ static int make_final_pack(struct packdata *pack)
ret = link(from, to);
if (ret) {
LOG(NULL, "Failed to link", "Manifest-delta-from-%i", pack->from);
LOG(NULL, "Failed to link", "Manifest-MoM-delta-from-%i (%s)", pack->from, strerror(errno));
} else {
string_or_die(&mom_delta, "Manifest-MoM-delta-from-%i", pack->from);
}
free(from);
@@ -494,24 +479,17 @@ static int make_final_pack(struct packdata *pack)
/* tar the staging directory up */
LOG(NULL, "starting tar for pack", "%s: %i to %i", pack->module, pack->from, pack->to);
string_or_die(&tar, TAR_COMMAND " " TAR_PERM_ATTR_ARGS " --directory=%s/%s/%i_to_%i/ "
"--numeric-owner -Jcf %s/%i/pack-%s-from-%i.tar delta staged",
packstage_dir, pack->module, pack->from, pack->to, staging_dir, pack->to,
pack->module, pack->from);
ret = system(tar);
free(tar);
string_or_die(&param1, "%s/%s/%i_to_%i/", packstage_dir, pack->module, pack->from, pack->to);
string_or_die(&param2, "%s/%i/pack-%s-from-%i.tar", staging_dir, pack->to, pack->module, pack->from);
char *const tarcmd[] = { TAR_COMMAND, "-C", param1, TAR_PERM_ATTR_ARGS_STRLIST, "--numeric-owner", "-Jcf", param2, "delta", "staged", bundle_delta, mom_delta, NULL };
ret = system_argv(tarcmd);
free(param1);
free(param2);
LOG(NULL, "finished tar for pack", "%s: %i to %i", pack->module, pack->from, pack->to);
/* FIXME: this is a hack workaround, needs diagnosed and removed */
if ((ret != 0) && (ret != 256)) {
if ((ret != 0) && (ret != 1)) {
fprintf(stderr, "Unexpected return value (%d) creating tar of pack %s from %i to %i\n",
ret, pack->module, pack->from, pack->to);
} else {
/* Write the signature file */
ret = write_pack_signature(pack);
if (ret != 0) {
fprintf(stderr, "Failure creating signature of pack %s from %i to %i\n",
pack->module, pack->from, pack->to);
}
}
/* and clean up */
+223 -159
View File
@@ -21,24 +21,74 @@
*
*/
/* Rename detection and support.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <glib.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "swupd.h"
#include <magic.h>
static magic_t mcookie;
static bool samefiletype(char *t1, char *t2)
{
if (t1 && (t1 == t2) && *t1) {
return true;
}
return false;
}
/* For an elf binary, we get the BuildID in it, which
* is essentially a hash of the original loaded sections
* and hence is unique. This hardly makes for a 'type'
*/
static char *getmagic(char *filename)
{
static magic_t mcookie;
static GStringChunk *typestore;
char *c2;
char *c1;
if (mcookie == NULL) {
mcookie = magic_open(MAGIC_NO_CHECK_COMPRESS);
magic_load(mcookie, NULL);
typestore = g_string_chunk_new(200);
}
c1 = (char *)magic_file(mcookie, filename);
if (!c1) {
LOG(NULL, "Cannot find file type", "%s", filename);
c1 = "";
}
c1 = strdup(c1);
c2 = strstr(c1, ", BuildID[");
if (c2) {
*c2 = 0;
}
c2 = strstr(c1, "not stripped");
if (c2) {
*c2 = 0;
}
c2 = strstr(c1, "stripped");
if (c2) {
*c2 = 0;
}
c2 = g_string_chunk_insert_const(typestore, c1);
free(c1);
return c2;
}
/* Assign a score roughly in the range -100 to 1000 to express how similar
* two files are.
*/
double rename_score(struct file *old, struct file *new)
{
double score = 0.0;
@@ -50,6 +100,16 @@ double rename_score(struct file *old, struct file *new)
score += 400;
}
/* If the files are smaller than about 200 bytes then even a
* single byte change using bsdiff is going to work out as
* bigger than just shipping the new file, so stop if they are
* not the same. No point in running up bsdiff just for the
* sake of it.
*/
if (new->stat.st_size < BSDIFFSIZE) {
return -99.0;
}
/* points for being in the same directory */
if (strcmp(old->dirname, new->dirname) == 0) {
score += 10;
@@ -86,12 +146,14 @@ double rename_score(struct file *old, struct file *new)
in--;
}
#if 0
/* if both start with /boot/vmlinuz give it a boost; this is a local hack due to vmlinuz being very short */
if (strncmp(old->filename, "/boot/vmlinuz", 13) == 0 && strncmp(new->filename, "/boot/vmlinuz", 13) == 0) {
score += 80;
}
/* if ELF, points for sharing the same soname to the first dot */
#endif
/* negative points for not being within 25%+/-1Kb of the same file size */
if (old->stat.st_size > ((new->stat.st_size * 1.25) + 1024)) {
@@ -115,66 +177,40 @@ double rename_score(struct file *old, struct file *new)
}
/* negative points for not having the same 'file' type */
if (old->filetype && new->filetype && strcmp(old->filetype, new->filetype) != 0) {
if (!samefiletype(old->filetype, new->filetype)) {
score -= 60;
}
return score;
}
static void precompute_file_data(struct manifest *manifest, struct file *file, int old_rename, GList *last_versions_list)
static void precompute_file_data(int version, const char *component, struct file *file, bool fast)
{
GList *item;
char *c1, *c2;
char *filename = NULL;
int last_change;
struct stat buf;
assert(file);
/* fill in the filename-minus-the-numbers field */
file->alpha_only_filename = calloc(strlen(file->filename) + 1, sizeof(char));
c1 = file->filename;
c2 = file->alpha_only_filename;
while (*c1) {
while (*c1 && !isalpha(*c1))
c1++;
if (!*c1) {
break;
}
if (c2 == NULL) {
break;
}
*c2 = *c1;
c1++;
c2++;
}
if (manifest) {
string_or_die(&filename, "%s/%i/%s/%s", image_dir, manifest->version, manifest->component, file->filename);
} else if (old_rename) {
item = g_list_first(last_versions_list);
while (item) {
last_change = GPOINTER_TO_INT(item->data);
item = g_list_next(item);
free(filename);
string_or_die(&filename, "%s/%i/full/%s", image_dir, last_change, file->filename);
if (!lstat(filename, &buf)) {
break;
if (c2) {
for (; *c1; c1++) {
if (isalpha(*c1)) { /* Only copy letters */
*c2++ = *c1;
}
}
} else {
string_or_die(&filename, "%s/%i/full/%s", image_dir, file->last_change, file->filename);
/* alpha_only_filename is NUL terminated by calloc */
}
string_or_die(&filename, "%s/%i/%s/%s", image_dir, version, component, file->filename);
/* make sure file->stat.st_size is valid */
if (file->stat.st_size == 0) {
int ret;
struct stat buf;
if (filename == NULL) {
printf("filename is null...impossible to stat\n");
assert(0);
}
assert(filename);
ret = lstat(filename, &buf);
if (!ret) {
file->stat.st_size = buf.st_size;
@@ -182,27 +218,19 @@ static void precompute_file_data(struct manifest *manifest, struct file *file, i
printf("Stat failure on %s\n", filename);
}
}
c1 = (char *)magic_file(mcookie, filename);
if (c1) {
char *c2;
file->filetype = strdup(c1);
c2 = strstr(file->filetype, "not stripped");
if (c2) {
*c2 = 0;
}
c2 = strstr(file->filetype, "stripped");
if (c2) {
*c2 = 0;
}
if (file->stat.st_size < BSDIFFSIZE) {
/* thing is too small, always will be regenerated
* so no point in trying to figure out what kind
* of file it is
*/
} else {
LOG(file, "Cannot find file type", "%s", filename);
file->filetype = getmagic(filename);
}
free(filename);
file->basename = strdup(basename(file->filename));
file->dirname = strdup(dirname(file->filename));
file->basename = g_path_get_basename(file->filename);
file->dirname = g_path_get_dirname(file->filename);
}
int file_sort_score(gconstpointer a, gconstpointer b)
@@ -222,6 +250,10 @@ int file_sort_score(gconstpointer a, gconstpointer b)
return 0;
}
/* compare file to each deleted file.
* set file->rename_peer to best matched deleted file
* set file->rename_score to the score
*/
static void score_file(GList *deleted_files, struct file *file)
{
GList *list2;
@@ -246,61 +278,107 @@ static void score_file(GList *deleted_files, struct file *file)
}
}
void rename_detection(struct manifest *manifest, int last_change, GList *last_versions_list)
/* delete the first element of the list and return the new head */
static GList *del_first(GList *list)
{
GList *new_files = NULL;
GList *deleted_files = NULL;
/* The first list is the pointer to the list, the second is
* the pointer to what to delete */
return g_list_delete_link(list, list);
}
/* Take a list, return a new list where the filter function returns true */
static GList *new_filtered_list(GList *list, int version, int (*f)(struct file *file, int version))
{
/* make a list of new files, no peer */
GList *newlist = NULL;
list = g_list_first(list);
for (; list; list = g_list_next(list)) {
struct file *file = list->data;
if (f(file, version)) {
newlist = g_list_prepend(newlist, file);
}
}
return newlist;
}
static int renamed_file_p(struct file *file, int unused)
{
return file->is_rename;
}
/* Return a new list of renamed files */
static GList *new_list_renamed_files(GList *infiles)
{
return new_filtered_list(infiles, 0, renamed_file_p);
}
/* Predicate that returns true if this is a new file in the stated version */
static int new_file_p(struct file *file, int version)
{
if ((file->last_change != version) ||
(file->is_deleted) ||
(!file->is_file) ||
(file->peer)) {
return 0;
}
return 1;
}
/* return a new list of the new files */
static GList *list_new_files(struct manifest *manifest)
{
GList *list = new_filtered_list(manifest->files, manifest->version, new_file_p);
/* call precompute_file_data for each file on list, return the list */
GList *ret = list;
for (list = g_list_first(list); list; list = g_list_next(list)) {
struct file *file = list->data;
precompute_file_data(manifest->version, manifest->component, file, true);
}
return ret;
}
static int deleted_p(struct file *file, int version)
{
if ((!file->is_deleted) ||
(!file->peer) ||
(file->last_change != version) ||
(file->peer->is_dir || file->peer->is_link)) {
return 0;
}
return 1;
}
static GList *list_deleted_files(struct manifest *manifest)
{
GList *list = new_filtered_list(manifest->files, manifest->version, deleted_p);
GList *ret = list;
/* call precompute_file_data for each peer of file on list */
for (list = g_list_first(list); list; list = g_list_next(list)) {
struct file *file = list->data;
struct file *peer = file->peer;
/* Need to get things from the /full/ as we do not know
* which component may be coming from? */
precompute_file_data(peer->last_change, "full", peer, false);
}
return ret;
}
void rename_detection(struct manifest *manifest)
{
GList *new_files;
GList *deleted_files;
GList *list;
struct file *file;
int old_rename = 0;
if (last_change != manifest->version) {
old_rename = 1;
}
new_files = list_new_files(manifest);
if (mcookie == NULL) {
mcookie = magic_open(MAGIC_NO_CHECK_COMPRESS);
magic_load(mcookie, NULL);
}
/* make a list of new files, no peer */
list = g_list_first(manifest->files);
while (list) {
file = list->data;
list = g_list_next(list);
if ((file->last_change != manifest->version) ||
(file->is_deleted) ||
(!file->is_file) ||
(file->peer)) {
continue;
}
new_files = g_list_prepend(new_files, file);
precompute_file_data(manifest, file, old_rename, last_versions_list);
}
/* if there are no new files, we're not having any renames -- early exit */
/* no new files --> no renames -- early exit */
if (!new_files) {
LOG(NULL, "No new files, no rename detection", "%s", manifest->component);
return;
}
/* make a list of newly deleted files that have a peer */
list = g_list_first(manifest->files);
while (list) {
file = list->data;
list = g_list_next(list);
if ((!file->is_deleted) ||
(!file->peer) ||
(file->last_change != last_change) ||
(file->peer->is_dir || file->peer->is_link)) {
continue;
}
deleted_files = g_list_prepend(deleted_files, file);
precompute_file_data(NULL, file->peer, old_rename, last_versions_list);
}
deleted_files = list_deleted_files(manifest);
/* nothing got deleted --> no renames --> early exit */
if (!deleted_files) {
@@ -309,7 +387,8 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
return;
}
/* for each new file, find the deleted file with the highest score, and store the score */
/* for each new file, find the deleted file with the highest score,
* store it in file->rename_peer and store the score */
list = g_list_first(new_files);
while (list) {
file = list->data;
@@ -317,20 +396,24 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
score_file(deleted_files, file);
}
redo:
/* sort all new files by score */
/* walk the sorted score list.
* pick the top score,
* check if the score is still valid,
* if not, recompute the score and resort
* This is probably an O(n^3).
*/
new_files = g_list_sort(new_files, file_sort_score);
/* pick the top score, check if the score is still valid, if not, recompute the score and resort */
while (new_files) {
for (; new_files; new_files = del_first(new_files)) {
file = new_files->data;
if (file->rename_peer == NULL) {
continue;
}
if (file->rename_score < 15.0 || file->rename_peer == NULL) {
new_files = g_list_delete_link(new_files, new_files);
if (file->rename_peer) {
LOG(NULL, "Rename not done due to insufficient high score", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
}
if (file->rename_score < 15.0) {
LOG(NULL, "Rename not done due to insufficient high score", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
continue;
}
@@ -338,10 +421,8 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
/* the candidate peer got already taken by another file! */
LOG(NULL, "Rename not done due to target already taken", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
file->rename_peer = NULL;
file->rename_score = -100;
score_file(deleted_files, file);
new_files = g_list_sort(new_files, file_sort_score);
continue;
goto redo;
}
/* if valid and score is high enough, make the link by setting the flag and storing the hash */
@@ -349,13 +430,14 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
LOG(NULL, "Rename detected!", "%s -> %s score %4.1f", file->rename_peer->filename, file->filename, file->rename_score);
file->rename_peer->rename_peer = file;
/* must delete the file from the deleted list */
/* must remove the file from the deleted list */
deleted_files = g_list_remove(deleted_files, file->rename_peer);
hash_assign(file->hash, file->rename_peer->hash);
file->is_rename = 1;
file->rename_peer->is_rename = 1;
new_files = g_list_delete_link(new_files, new_files);
if (!deleted_files) {
break; /* No more deleted files to rename */
}
} /* lather, rinse, repeat until all files have a target */
@@ -364,65 +446,47 @@ void rename_detection(struct manifest *manifest, int last_change, GList *last_ve
g_list_free(deleted_files);
}
static int file_found_in_older_manifest(struct manifest *from_manifest, struct file *searched_file)
{
GList *list;
struct file *file;
list = g_list_first(from_manifest->files);
while (list) {
file = list->data;
list = g_list_next(list);
if (file->is_deleted) {
continue;
}
if (!strcmp(file->filename, searched_file->filename)) {
return 1;
}
}
return 0;
}
void link_renames(GList *newfiles, struct manifest *from_manifest)
/* What do we need this for?
*
* rename_detection has already set up the links in the manifest it
* was given.
*
*/
void link_renames(GList *newfiles, int to_version)
{
GList *list1, *list2;
GList *targets;
struct file *file1, *file2;
targets = newfiles = g_list_sort(newfiles, file_sort_version);
targets = new_list_renamed_files(newfiles);
/* TODO: Check that g_list_sort is reasonable speed */
targets = newfiles = g_list_sort(targets, file_sort_version);
list1 = g_list_first(newfiles);
/* todo: sort newfiles and targets by hash */
while (list1) {
for (list1 = newfiles; list1; list1 = g_list_next(list1)) {
file1 = list1->data;
list1 = g_list_next(list1);
if ((file1->peer || !file1->is_rename) ||
(file1->is_deleted)) {
if (file1->peer || file1->is_deleted) {
continue;
}
/* now, file1 is the new file that got renamed. time to search the rename targets */
/* now, file1 is the new file that got renamed.
* time to search the rename targets */
list2 = g_list_first(targets);
while (list2) {
for (; list2; list2 = g_list_next(list2)) {
file2 = list2->data;
list2 = g_list_next(list2);
if ((!file2->peer || !file2->is_rename) ||
/* This is like deleted_p but not quite */
/* deleted_p returns false for directories and links */
if (!file2->peer ||
(!file2->is_deleted) ||
(!file_found_in_older_manifest(from_manifest, file2))) {
(file2->last_change != to_version)) {
continue;
}
if (hash_compare(file2->hash, file1->hash)) {
file1->rename_peer = file2->peer;
file1->peer = file2->peer;
file2->peer->rename_peer = file1;
list2 = NULL;
break;
}
}
}
free(from_manifest);
g_list_free(targets);
}
-171
View File
@@ -1,171 +0,0 @@
/*
* Software Updater - server side
*
* Copyright © 2012-2016 Intel Corporation.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 or later of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Tom Keel <thomas.keel@intel.com>
*
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <err.h>
#include "swupd.h"
static char *make_filename(const char *, const char *, const char *);
static const char *CMD_FMT = "openssl smime -sign -in %s -binary "
"-out %s.signed -outform PEM -md sha256 -inkey %s -signer %s "
"-certfile %s -passin file:%s";
static char *leaf_key = NULL;
static char *leaf_cert = NULL;
static char *ca_chain_cert = NULL;
static char *passphrase = NULL;
static bool initialized = false;
/*
* Initialize this module.
* @return true <=> success
*/
bool signature_initialize(void)
{
if (!enable_signing) {
return true;
}
char *cdir;
char *pphr;
struct stat s;
if (initialized) {
return true;
}
cdir = getenv("SWUPD_CERTS_DIR");
if (cdir == NULL || cdir[0] == '\0') {
printf("No certificates directory specified\n");
goto err;
}
if (stat(cdir, &s)) {
printf("Can't stat certificates directory '%s' (%s)\n", cdir,
strerror(errno));
goto err;
}
leaf_key = make_filename(cdir, "LEAF_KEY", "leaf key");
if (leaf_key == NULL) {
goto err;
}
leaf_cert = make_filename(cdir, "LEAF_CERT", "leaf certificate");
if (leaf_cert == NULL) {
goto err;
}
ca_chain_cert = make_filename(cdir, "CA_CHAIN_CERT", "CA chain certificate");
if (ca_chain_cert == NULL) {
goto err;
}
pphr = getenv("PASSPHRASE");
if (pphr == NULL || (passphrase = strdup(pphr)) == NULL) {
goto err;
}
if (stat(passphrase, &s)) {
printf("Can't stat '%s' (%s)\n", passphrase,
strerror(errno));
goto err;
}
initialized = true;
return true;
err:
signature_terminate();
return false;
}
/* Make filename from dir name and env variable containing basename */
static char *make_filename(const char *dir, const char *env, const char *desc)
{
char *fn = getenv(env);
char *result = NULL;
struct stat s;
if (fn == NULL || fn[0] == '\0') {
printf("No %s file specified\n", desc);
return NULL;
}
string_or_die(&result, "%s/%s", dir, fn);
if (stat(result, &s)) {
printf("Can't stat %s '%s' (%s)\n", desc, result, strerror(errno));
free(result);
return NULL;
}
return result;
}
/*
* Terminate this module, free resources.
*/
void signature_terminate(void)
{
if (!enable_signing) {
return;
}
free(leaf_key);
free(leaf_cert);
free(ca_chain_cert);
free(passphrase);
leaf_key = NULL;
leaf_cert = NULL;
ca_chain_cert = NULL;
passphrase = NULL;
initialized = false;
}
/*
* Write the signature file corresponding to the given data file.
* The name of the signature file is the name of the data file with suffix
* ".signed" appended.
*/
bool signature_sign(const char *filename)
{
char *cmd = NULL;
int status;
if (!enable_signing) {
return true;
}
if (!initialized) {
return false;
}
string_or_die(&cmd, CMD_FMT, filename, filename, leaf_key, leaf_cert,
ca_chain_cert, passphrase);
status = system(cmd);
if (status) {
printf("Bad status %d from signing command:%s\n", status, cmd);
}
free(cmd);
return status == 0;
}
+3 -4
View File
@@ -22,13 +22,12 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <glib.h>
#include <unistd.h>
#include "swupd.h"
+47 -19
View File
@@ -23,17 +23,17 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "swupd.h"
@@ -127,35 +127,63 @@ void ensure_version_image_exists(int version)
void write_cookiecrumbs_to_download_area(int version)
{
char *conf;
char *cmd;
char *filename, *strformat;
FILE *versionfd, *formatfd;
conf = config_output_dir();
if (conf == NULL) {
assert(0);
}
string_or_die(&cmd, "echo %s > %s/%i/swupd-server-src-version", VERSION, conf, version);
if (system(cmd) != 0) {
string_or_die(&filename, "%s/%i/swupd-server-src-version", conf, version);
versionfd = fopen(filename, "w");
if (versionfd == NULL) {
assert(0);
}
free(cmd);
string_or_die(&cmd, "echo %s > %s/%i/format", format_string, conf, version);
if (system(cmd) != 0) {
if (fwrite(VERSION, 1, strlen(VERSION), versionfd) != strlen(VERSION)) {
assert(0);
}
free(cmd);
fclose(versionfd);
free(filename);
/* written from pack_maker.sh */
string_or_die(&filename, "%s/%i/format", conf, version);
string_or_die(&strformat, "%llu", format);
formatfd = fopen(filename, "w");
if (formatfd == NULL) {
assert(0);
}
if (fwrite(strformat, 1, strlen(strformat), formatfd) != strlen(strformat)) {
assert(0);
}
fclose(formatfd);
free(filename);
free(strformat);
/* Updating the WEBDIR/version/formatN/latest file is an operation very closely tied to a DevOps
* workflow and not something swupd should make a decision about.
*
* The supported format values (replacing the "N" in the path above) are:
*
* - "staging": intended for testing only
* - [1-+inf] (positive integer): the number that a corresponding version of swupd-client
* understands to run software updates. For example, if a given swupd-client release understands
* format 1, then WEBDIR/version/format1/latest contains the value of most recent release that
* that swupd-client can update to.
*
* Adapt this step appropriately for your DevOps flow.
*/
#if 0
FILE *file;
char *fullfile = NULL;
char *param;
string_or_die(&cmd, "mkdir -p %s/version/formatstaging/", conf);
if (system(cmd) != 0) {
string_or_die($param, "%s/version/formatstaging/", conf);
char *const mkdircmd[] = { "mkdir", "-p", param, NULL };
if (system_argv(mkdircmd) != 0) {
assert(0);
}
free(cmd);
free(param);
string_or_die(&fullfile, "%s/version/formatstaging/latest", conf);
file = fopen(fullfile, "w");
+6 -7
View File
@@ -20,15 +20,15 @@
*
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/xattr.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include "swupd.h"
#include "xattrs.h"
@@ -60,8 +60,7 @@ static int xattr_get_value(const char *path, const char *name, char **blob,
/* realloc needed len + 1 in case we need to add final zero
* to ensure consistent blob */
value = realloc(*blob, *blob_len + len +
(action == XATTRS_ACTION_GET_BLOB ? 1 : 0));
value = realloc(*blob, *blob_len + len + (action == XATTRS_ACTION_GET_BLOB ? 1 : 0));
assert(value);
*blob = value;
@@ -1,23 +1,25 @@
#!/usr/bin/env bats
load "../swupdlib"
@test "create_update help output" {
run $srcdir/swupd_create_update --help
run $CREATE_UPDATE --help
[ "$status" -eq 1 ]
run $srcdir/swupd_create_update -h
run $CREATE_UPDATE -h
[ "$status" -eq 1 ]
}
@test "make_fullfiles help output" {
run $srcdir/swupd_make_fullfiles --help
run $MAKE_FULLFILES --help
[ "$status" -eq 1 ]
run $srcdir/swupd_make_fullfiles -h
run $MAKE_FULLFILES -h
[ "$status" -eq 1 ]
}
@test "make_pack help output" {
run $srcdir/swupd_make_pack --help
run $MAKE_PACK --help
[ "$status" -eq 1 ]
run $srcdir/swupd_make_pack -h
run $MAKE_PACK -h
[ "$status" -eq 1 ]
}
@@ -0,0 +1,64 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
init_groups_ini os-core test-bundle1 test-bundle2
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle1
track_bundle 10 test-bundle2
gen_file_plain 10 test-bundle1 foo
gen_file_plain 10 test-bundle1 foobar
gen_file_plain 10 test-bundle2 foo2
gen_includes_file test-bundle2 10 test-bundle1
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle1
track_bundle 20 test-bundle2
gen_file_plain 20 test-bundle1 foo
gen_file_plain 20 test-bundle1 foobar
gen_file_plain 20 test-bundle1 foobarbaz
gen_file_plain 20 test-bundle2 foo2
gen_file_plain 20 test-bundle2 foo2bar
gen_includes_file test-bundle2 20 test-bundle1
}
@test "correct contentsize" {
# create a couple updates to both check that contentsize does not add included
# bundles and to verify that files changed in previous updates are counted.
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
set_latest_ver 10
# contentsize for test-bundle2 should not include test-bundle1's contentsize
[[ 1 -eq $(grep '^contentsize: 11$' $DIR/www/10/Manifest.test-bundle1 | wc -l) ]]
[[ 1 -eq $(grep '^contentsize: 5$' $DIR/www/10/Manifest.test-bundle2 | wc -l) ]]
# os-core is large because it includes /usr/*
[[ 1 -eq $(grep '^contentsize: 5134$' $DIR/www/10/Manifest.os-core | wc -l) ]]
# 5134 + 11 + 5 = 5150
[[ 1 -eq $(grep '^contentsize: 5150$' $DIR/www/10/Manifest.full | wc -l) ]]
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
set_latest_ver 20
# one new file: foobarbaz (10 bytes)
[[ 1 -eq $(grep '^contentsize: 21$' $DIR/www/20/Manifest.test-bundle1 | wc -l) ]]
# one new file: foo2bar (8 bytes)
[[ 1 -eq $(grep '^contentsize: 13$' $DIR/www/20/Manifest.test-bundle2 | wc -l) ]]
# os-core should not change size
[[ 1 -eq $(grep '^contentsize: 5134$' $DIR/www/10/Manifest.os-core | wc -l) ]]
# contentsize for full should be all files, including ones not changed in this release
# two new files: foo2bar (8 bytes) and foobarbaz (10 bytes)
# 5150 + 10 + 8 = 5168
# 5134 + 21 + 13 = 5168
[[ 1 -eq $(grep '^contentsize: 5168$' $DIR/www/20/Manifest.full | wc -l) ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core test-bundle1 test-bundle2
set_os_release 10 os-core
set_os_release 10 test-bundle1
set_os_release 10 test-bundle2
track_bundle 10 os-core
track_bundle 10 test-bundle1
track_bundle 10 test-bundle2
set_os_release 20 os-core
set_os_release 20 test-bundle1
set_os_release 20 test-bundle2
track_bundle 20 os-core
track_bundle 20 test-bundle1
track_bundle 20 test-bundle2
gen_file_plain 10 test-bundle1 foo
gen_file_plain 10 test-bundle2 foo
gen_file_plain 20 test-bundle1 foo
}
@test "delete no version bump update creation" {
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
[ 1 -eq $(grep '10 /foo' $DIR/www/10/Manifest.full | wc -l) ]
[ 1 -eq $(grep '10 /foo' $DIR/www/20/Manifest.full | wc -l) ]
[ 0 -eq $(grep '20 /foo' $DIR/www/20/Manifest.full | wc -l) ]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core
set_os_release 10 os-core
track_bundle 10 os-core
gen_file_plain 10 os-core "semicolon;"
}
assert_illegal_char() {
local output="$1"
local str=$(echo "Filename $2 includes illegal character(s)")
[[ "$output" =~ "$str" ]]
}
assert_valid_char() {
local output="$1"
local str=$(echo "Filename $2 includes illegal character(s)")
[[ ! "$output" =~ "$str" ]]
}
@test "file names with blacklisted characters" {
run sudo sh -c "$CREATE_UPDATE --osversion 10 --statedir $DIR --format 3"
echo "$output"
assert_illegal_char "$output" "/semicolon;"
assert_valid_char "$output" "/usr"
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core
set_os_release 10 os-core
track_bundle 10 os-core
set_os_release 20 os-core
track_bundle 20 os-core
set_os_release 30 os-core
track_bundle 30 os-core
}
@test "full run update creation with delta packs over format bump" {
# build the first version
echo $CREATE_UPDATE
echo $DIR
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
set_latest_ver 10
# then the second version...
echo $CREATE_UPDATE
echo $DIR
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 20
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
set_latest_ver 20
# then the third version...
echo $CREATE_UPDATE
echo $DIR
sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 4
sudo $MAKE_FULLFILES --statedir $DIR 30
sudo $MAKE_PACK --statedir $DIR 0 30 os-core
# zero packs should exist (non-zero size) for all versions
[ -s $DIR/www/10/pack-os-core-from-0.tar ]
[ -s $DIR/www/20/pack-os-core-from-0.tar ]
[ -s $DIR/www/30/pack-os-core-from-0.tar ]
[[ 0 -eq $(grep '^actions: update$' $DIR/www/10/Manifest.MoM | wc -l) ]]
[[ 0 -eq $(grep '^actions: update$' $DIR/www/20/Manifest.MoM | wc -l) ]]
[[ 1 -eq $(grep '^actions: update$' $DIR/www/30/Manifest.MoM | wc -l) ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core
set_os_release 10 os-core
track_bundle 10 os-core
set_os_release 20 os-core
track_bundle 20 os-core
gen_file_plain 10 os-core foo
gen_file_plain 10 os-core bar
gen_file_plain 20 os-core foo
gen_file_plain 20 os-core baz
}
@test "ensure format numbers cannot be decremented" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
set_latest_ver 10
# Now, decrement the format number. Should result in an EXIT_FAILURE.
run sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 2
echo "$output"
[ $status -eq 1 ]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
-58
View File
@@ -1,58 +0,0 @@
#!/usr/bin/env bats
# common functions
load swupdlib
setup() {
DIR=$(init_web_dir "$srcdir/web-dir")
export DIR
init_server_ini
set_latest_ver 0
init_groups_ini os-core test-bundle
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
gen_file_to_delta 10 4096 20 4 test-bundle
}
@test "full run update creation with delta packs" {
# build the first version
sudo $srcdir/swupd_create_update --osversion 10 --statedir $DIR
sudo $srcdir/swupd_make_fullfiles --statedir $DIR 10
sudo $srcdir/swupd_make_pack --statedir $DIR 0 10 os-core
sudo $srcdir/swupd_make_pack --statedir $DIR 0 10 test-bundle
set_latest_ver 10
# then the second version...
sudo $srcdir/swupd_create_update --osversion 20 --statedir $DIR
sudo $srcdir/swupd_make_fullfiles --statedir $DIR 20
sudo $srcdir/swupd_make_pack --statedir $DIR 0 20 os-core
sudo $srcdir/swupd_make_pack --statedir $DIR 0 20 test-bundle
# and with delta packs this time
sudo $srcdir/swupd_make_pack --statedir $DIR 10 20 os-core
sudo $srcdir/swupd_make_pack --statedir $DIR 10 20 test-bundle
# zero packs should exist (non-zero size) for both versions
[ -s $DIR/www/10/pack-os-core-from-0.tar ]
[ -s $DIR/www/10/pack-test-bundle-from-0.tar ]
[ -s $DIR/www/20/pack-os-core-from-0.tar ]
[ -s $DIR/www/20/pack-test-bundle-from-0.tar ]
# and delta packs should exist (non-zero size) for the latest version
[ -s $DIR/www/20/pack-os-core-from-10.tar ]
[ -s $DIR/www/20/pack-test-bundle-from-10.tar ]
}
teardown() {
sudo rm -rf $DIR
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+89
View File
@@ -0,0 +1,89 @@
#!/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 included included-two included-nested
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
track_bundle 20 included
track_bundle 20 included-two
track_bundle 20 included-nested
gen_file_to_delta 10 4096 20 4 test-bundle randomfile
gen_file_plain 10 test-bundle foo
gen_file_plain 10 test-bundle foobarbaz
gen_file_plain 20 test-bundle foo
gen_file_plain 20 included foo
gen_file_plain 20 included-two foo
gen_file_plain 20 included-two foobar
gen_file_plain 20 included-nested foobarbaz
gen_includes_file test-bundle 20 included included-two
gen_includes_file included 20 included-nested
}
@test "full run update creation with delta packs" {
# build the first version
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
set_latest_ver 10
# then the second version...
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 20
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
# and with delta packs this time
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
# zero packs should exist (non-zero size) for both versions
[ -s $DIR/www/10/pack-os-core-from-0.tar ]
[ -s $DIR/www/10/pack-test-bundle-from-0.tar ]
[ -s $DIR/www/20/pack-os-core-from-0.tar ]
[ -s $DIR/www/20/pack-test-bundle-from-0.tar ]
# and delta packs should exist (non-zero size) for the latest version
[ -s $DIR/www/20/pack-os-core-from-10.tar ]
[ -s $DIR/www/20/pack-test-bundle-from-10.tar ]
[[ 1 -eq $(grep '^includes: os-core$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/foo$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/foobarbaz$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 0 -eq $(grep '/foobar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^includes: os-core$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^includes: included$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^includes: included-two$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^.d.. 0000000000000000000000000000000000000000000000000000000000000000 20 /foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^.d.. 0000000000000000000000000000000000000000000000000000000000000000 20 /foobarbaz$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/foo$' $DIR/www/20/Manifest.included | wc -l) ]]
[[ 1 -eq $(grep '/foo$' $DIR/www/20/Manifest.included-two | wc -l) ]]
[[ 1 -eq $(grep '/foobar$' $DIR/www/20/Manifest.included-two | wc -l) ]]
[[ 1 -eq $(grep '/foobarbaz$' $DIR/www/20/Manifest.included-nested | wc -l) ]]
[[ 6 -eq $(tar -tf $DIR/www/10/pack-test-bundle-from-0.tar | wc -l) ]]
[[ 4 -eq $(tar -tf $DIR/www/20/pack-test-bundle-from-0.tar | wc -l) ]]
# delta packs should contain appropriate delta manifests
[[ $(tar -tf $DIR/www/20/pack-os-core-from-10.tar | grep '^Manifest-os-core-delta-from-10') ]]
[[ $(tar -tf $DIR/www/20/pack-os-core-from-10.tar | grep '^Manifest-MoM-delta-from-10') ]]
[[ $(tar -tf $DIR/www/20/pack-test-bundle-from-10.tar | grep '^Manifest-test-bundle-delta-from-10') ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
-35
View File
@@ -1,35 +0,0 @@
#!/usr/bin/env bats
# common functions
load swupdlib
setup() {
DIR=$(init_web_dir "$srcdir/web-dir")
export 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
}
@test "full run update creation" {
sudo $srcdir/swupd_create_update --osversion 10 --statedir $DIR
sudo $srcdir/swupd_make_fullfiles --statedir $DIR 10
sudo $srcdir/swupd_make_pack --statedir $DIR 0 10 os-core
sudo $srcdir/swupd_make_pack --statedir $DIR 0 10 test-bundle
# zero packs should exist (non-zero size) for the build
[ -s $DIR/www/10/pack-os-core-from-0.tar ]
[ -s $DIR/www/10/pack-test-bundle-from-0.tar ]
}
teardown() {
sudo rm -rf $DIR
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+45
View File
@@ -0,0 +1,45 @@
#!/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
gen_file_plain 10 test-bundle foo
}
@test "full run update creation" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
# zero packs should exist (non-zero size) for the build
[ -s $DIR/www/10/pack-os-core-from-0.tar ]
[ -s $DIR/www/10/pack-test-bundle-from-0.tar ]
[[ 1 -eq $(grep '^includes: os-core$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/usr/share/clear/bundles/test-bundle$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/foo$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/usr/share/clear/bundles/os-core$' $DIR/www/10/Manifest.os-core | wc -l) ]]
[[ 1 -eq $(grep '/usr$' $DIR/www/10/Manifest.os-core | wc -l) ]]
[[ 1 -eq $(grep '/usr/lib$' $DIR/www/10/Manifest.os-core | wc -l) ]]
[[ 1 -eq $(grep '/usr/share$' $DIR/www/10/Manifest.os-core | wc -l) ]]
[[ 1 -eq $(grep '/usr/share/clear$' $DIR/www/10/Manifest.os-core | wc -l) ]]
[[ 1 -eq $(grep '/usr/share/clear/bundles$' $DIR/www/10/Manifest.os-core | wc -l) ]]
[[ 4 -eq $(tar -tf $DIR/www/10/pack-test-bundle-from-0.tar | wc -l) ]]
[[ 5 -eq $(tar -tf $DIR/www/10/pack-os-core-from-0.tar | wc -l) ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
@@ -1,18 +1,21 @@
#!/usr/bin/env bats
load "../swupdlib"
@test "make_fullfiles required arg" {
run $srcdir/swupd_make_fullfiles
run $MAKE_FULLFILES
[ "$status" -eq 1 ]
}
@test "make_fullfiles too many arguments" {
# exactly one argument must be passed
run $srcdir/swupd_make_fullfiles foo bar
run $MAKE_FULLFILES foo bar
[ "$status" -eq 1 ]
}
@test "make_fullfiles root priv check" {
run $srcdir/swupd_make_fullfiles foo
[ $EUID -eq 0 ] && skip "test can only be run as non-root"
run $MAKE_FULLFILES foo
[ "$status" -eq 1 ]
[[ "$output" =~ "not being run as root.. exiting" ]]
}
+99
View File
@@ -0,0 +1,99 @@
#!/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 included included-two included-nested
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
track_bundle 20 included
track_bundle 20 included-two
set_os_release 30 os-core
track_bundle 30 os-core
track_bundle 30 test-bundle
track_bundle 30 included
track_bundle 30 included-two
track_bundle 30 included-nested
gen_file_plain 10 test-bundle foo
gen_file_plain 20 test-bundle foo
gen_file_plain 20 included bar
gen_file_plain 20 included-two baz
gen_file_plain 30 test-bundle foo
gen_file_plain 30 included bar
gen_file_plain 30 included-two baz
gen_file_plain 30 included-nested foobarbaz
gen_includes_file test-bundle 20 included included-two
gen_includes_file test-bundle 30 included included-two
gen_includes_file included 30 included-nested
}
@test "full run update creation with delta packs" {
# build the first version
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
set_latest_ver 10
# then the second version...
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 20
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
sudo $MAKE_PACK --statedir $DIR 0 20 included
sudo $MAKE_PACK --statedir $DIR 0 20 included-two
set_latest_ver 20
# then the third version...
sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 30
sudo $MAKE_PACK --statedir $DIR 0 30 os-core
sudo $MAKE_PACK --statedir $DIR 0 30 test-bundle
sudo $MAKE_PACK --statedir $DIR 0 30 included
sudo $MAKE_PACK --statedir $DIR 0 30 included-two
sudo $MAKE_PACK --statedir $DIR 0 30 included-nested
# zero packs should exist (non-zero size) for both versions
[ -s $DIR/www/10/pack-os-core-from-0.tar ]
[ -s $DIR/www/10/pack-test-bundle-from-0.tar ]
[ -s $DIR/www/20/pack-os-core-from-0.tar ]
[ -s $DIR/www/20/pack-test-bundle-from-0.tar ]
[ -s $DIR/www/20/pack-included-from-0.tar ]
[ -s $DIR/www/20/pack-included-two-from-0.tar ]
[ -s $DIR/www/30/pack-os-core-from-0.tar ]
[ -s $DIR/www/30/pack-test-bundle-from-0.tar ]
[ -s $DIR/www/30/pack-included-from-0.tar ]
[ -s $DIR/www/30/pack-included-two-from-0.tar ]
[ -s $DIR/www/30/pack-included-nested-from-0.tar ]
[[ 1 -eq $(grep '^includes: os-core$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/foo$' $DIR/www/10/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^includes: os-core$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^includes: included$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^includes: included-two$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '/bar$' $DIR/www/20/Manifest.included | wc -l) ]]
[[ 1 -eq $(grep '/baz$' $DIR/www/20/Manifest.included-two | wc -l) ]]
[[ 1 -eq $(grep '^includes: included-nested$' $DIR/www/30/Manifest.included | wc -l) ]]
[[ 1 -eq $(grep '/foobarbaz$' $DIR/www/30/Manifest.included-nested | wc -l) ]]
[[ 0 -eq $(ls $DIR/www/30/Manifest.test-bundle | wc -l) ]]
[[ 0 -eq $(ls $DIR/www/30/Manifest.included-two | wc -l) ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core test-bundle1 test-bundle2
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle1
track_bundle 10 test-bundle2
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle1
track_bundle 20 test-bundle2
gen_file_plain 10 os-core test0
gen_file_plain 10 test-bundle1 test1
gen_file_plain 10 test-bundle2 test2
gen_includes_file test-bundle2 10 test-bundle1 test-bundle1
gen_includes_file test-bundle2 20 test-bundle1 test-bundle1
}
@test "deduplicate bundle includes" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
set_latest_ver 10
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
# includes list should be deduplicated in both the old and new manifests
[[ 1 -eq $(grep '^includes: test-bundle1$' $DIR/www/10/Manifest.test-bundle2 | wc -l) ]]
[[ 1 -eq $(grep '^includes: test-bundle1$' $DIR/www/20/Manifest.test-bundle2 | wc -l) ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core
set_os_release 10 os-core
track_bundle 10 os-core
set_os_release 20 os-core
track_bundle 20 os-core
# symlink -> regular file type change (L -> F)
gen_file_to_delta 10 4096 20 4 os-core testfile1
gen_symlink_to_file 10 os-core testsym1 testfile1
copy_file 10 os-core testfile1 20 os-core testsym1
# regular file -> symlink type change (F -> L)
gen_file_to_delta 10 4096 20 4 os-core testfile2
copy_file 10 os-core testfile2 10 os-core testsym2
gen_symlink_to_file 20 os-core testsym2 testfile2
# symlink change + symlink target change; delta should be created for
# testfile3, but not for the dereferenced testsym3
gen_file_to_delta 10 4096 20 4 os-core testfile3
copy_file 20 os-core testfile3 20 os-core testfile4
gen_symlink_to_file 10 os-core testsym3 testfile3
gen_symlink_to_file 20 os-core testsym3 testfile4
}
@test "no deltas created for type changes or dereferenced symlinks" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
set_latest_ver 10
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
# attempt to create some deltas
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
# F -> F deltas should exist
hash1=$(hash_for 10 os-core "/testfile1")
hash2=$(hash_for 20 os-core "/testfile1")
[ -f $DIR/www/20/delta/10-20-$hash1-$hash2 ]
hash1=$(hash_for 10 os-core "/testfile2")
hash2=$(hash_for 20 os-core "/testfile2")
[ -f $DIR/www/20/delta/10-20-$hash1-$hash2 ]
hash1=$(hash_for 10 os-core "/testfile3")
hash2=$(hash_for 20 os-core "/testfile3")
[ -f $DIR/www/20/delta/10-20-$hash1-$hash2 ]
# deltas for symlink type changes should not be created
hash1=$(hash_for 10 os-core "/testsym1")
hash2=$(hash_for 20 os-core "/testsym1")
[ ! -f $DIR/www/20/delta/10-20-$hash1-$hash2 ]
hash1=$(hash_for 10 os-core "/testsym2")
hash2=$(hash_for 20 os-core "/testsym2")
[ ! -f $DIR/www/20/delta/10-20-$hash1-$hash2 ]
hash1=$(hash_for 10 os-core "/testsym3")
hash2=$(hash_for 20 os-core "/testsym3")
[ ! -f $DIR/www/20/delta/10-20-$hash1-$hash2 ]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
@@ -1,24 +1,27 @@
#!/usr/bin/env bats
load "../swupdlib"
@test "make_pack required arg" {
run $srcdir/swupd_make_pack
run $MAKE_PACK
[ "$status" -eq 1 ]
}
@test "make_pack too few arguments" {
run $srcdir/swupd_make_pack foo
run $MAKE_PACK foo
[ "$status" -eq 1 ]
run $srcdir/swupd_make_pack foo bar
run $MAKE_PACK foo bar
[ "$status" -eq 1 ]
}
@test "make_pack too many arguments" {
run $srcdir/swupd_make_pack foo bar foo bar
run $MAKE_PACK foo bar foo bar
[ "$status" -eq 1 ]
}
@test "make_pack root priv check" {
run $srcdir/swupd_make_pack foo bar foo
[ $EUID -eq 0 ] && skip "test can only be run as non-root"
run $MAKE_PACK foo bar foo
[ "$status" -eq 1 ]
[[ "$output" =~ "not being run as root.. exiting" ]]
}
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
export BATS_TEST_DIRNAME=$PWD
export TIMEFORMAT
TIMEFORMAT="time %R %P%% (%U+%S)"
source ../swupdlib.bash
exec 3< rundata
while read -r -u 3
do
read -i "$REPLY" -e CMD
eval "set -x ; time ( $CMD ) ; set +x "
done
+22
View File
@@ -0,0 +1,22 @@
sudo rm -r logs/ web-dir/
tar xf ~/data/test.tar
init_test_dir
init_server_ini
set_latest-ver 0
init_groups_ini os-core test-bundle
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
time sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
set_latest-ver 10
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 20
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
+246
View File
@@ -0,0 +1,246 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
maybeskip
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core test-bundle
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
}
# Generate data files of 3 types in test-bundle
# All files are big enough that they get rename detection
gendataA() {
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 100)"
}
gendataB() {
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 50) $(seq 52 101)"
}
gendataC() {
# cache string
[ -z "$dataC" ] || dataC="$(seq 1000 | gzip | uuencode wombat)"
gen_file_plain_with_content "$1" test-bundle "$2" "$dataC"
}
# Generate small data files
gendataAs() {
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 50)"
}
gendataBs() {
gen_file_plain_with_content "$1" test-bundle "$2" "$(seq 24) $(seq 26 49)"
}
gendataCs() {
# cache string
[ -z "$dataCs" ] || dataC="$(seq 50 | gzip | uuencode wombat)"
gen_file_plain_with_content "$1" test-bundle "$2" "$dataCs"
}
checkrenamed(){
local flags sh1 ver name fromsha1="bad" tosha1
# Check that $1 is renamed to $2
exec 9< $DIR/www/20/Manifest.test-bundle
# skip the header
while read -u9
do
[ -z "$REPLY" ] && break
done
while read -r -u9 flags sha1 ver name
do
case "$flags" in
(?"dr"?) [ "$name" = "$1" ] && fromsha1=$sha1 ;;
(?".r"?) [ "$name" = "$2" ] && tosha1=$sha1 ;;
esac
done
if [ "$fromsh1" = "$tosha1" ] ; then return 0 ; else return 1 ; fi
}
# Guts of doing an update
do_an_update() {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
set_latest_ver 10
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 20
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
}
@test "basic rename detection support" {
gendataA 10 foo
gendataA 20 bar
do_an_update
checkrenamed foo bar
}
@test "ignore rename detection for small files" {
gendataAs 10 foo
gendataAs 20 bar
do_an_update
# A renamed file comprises a new file and a deleted file
[[ 0 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 0 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
}
@test "ignore rename detection for large to small files" {
gendataA 10 foo
gendataAs 20 bar
do_an_update
# A renamed file comprises a new file and a deleted file
[[ 0 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 0 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
}
@test "ignore rename detection for small to large files" {
gendataAs 10 foo
gendataA 20 bar
do_an_update
# A renamed file comprises a new file and a deleted file
[[ 0 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 0 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
}
@test "rename one file to two" {
gendataA 10 foo
gendataA 20 bar
gendataA 20 baz
do_an_update
# A renamed file comprises a new file and a deleted file
[[ 1 -eq $(grep '^F\.\.r.*/ba[rz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
}
@test "rename two file to one" {
gendataA 10 foo
gendataA 10 foz
gendataA 20 baz
do_an_update
# A renamed file comprises a new file and a deleted file
[[ 1 -eq $(grep '^F\.\.r.*/baz$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^\.d\.\..*/fo[oz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^\.d\.r.*/fo[oz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
}
@test "rename two files to two" {
gendataA 10 foo
gendataA 10 foz
gendataA 20 bar
gendataA 20 baz
do_an_update
checkrenamed foo bar
checkrenamed foz baz
}
@test "rename two files to two, one slightly different" {
gendataA 10 foo
gendataA 10 foz
gendataA 20 bar
gendataB 20 baz
do_an_update
# we don't actually know how the client we do this rename, but don't care
checkrenamed foo bar
checkrenamed foz baz
}
@test "rename two files to two, each pair slightly different" {
gendataA 10 foo
gendataB 10 foz
gendataA 20 bar
gendataB 20 baz
do_an_update
checkrenamed foo bar
checkrenamed foz baz
}
@test "rename two files to two, one very different" {
gendataA 10 foo
gendataA 10 foz
gendataA 20 bar
gendataC 20 baz
do_an_update
# A renamed file comprises a new file and a deleted file
[[ 1 -eq $(grep '^F\.\.r.*/ba[rz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^\.d\.r.*/fo[oz]$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
}
@test "rename two files to two, one small" {
gendataA 10 foo
gendataA 10 foz
gendataA 20 bar
gendataCs 20 baz
do_an_update
run checkrenamed foo bar
if [ $status -eq 1 ] ; then
checkrenamed foz bar
fi
}
@test "directory name changes" {
gendataA 10 dir1/foo
gendataA 20 dir2/foo
do_an_update
checkrenamed /dir1/foo /dir2/foo
}
@test "directory name changes small files" {
gendataAs 10 dir1/foo
gendataAs 20 dir2/foo
do_an_update
if ! checkrenamed /dir1/foo /dir2/foo ; then false ; fi
}
@test "directory name and small data changes" {
gendataA 10 dir1/foo
gendataB 20 dir2/foz
do_an_update
checkrenamed /dir1/foo /dir2/foz
}
@test "directory name and small data changes, choose same name" {
gendataA 10 dir1/foo
gendataA 10 dir1/foz
gendataB 20 dir2/foz
do_an_update
checkrenamed /dir1/foz /dir2/foz
}
@test "same basename test" {
gendataA 10 dir1/foo.so.1
gendataA 10 dir1/foz.so.1
gendataB 20 dir2/foo.so.2
do_an_update
checkrenamed /dir1/foo.so.1 /dir2/foo.so.2
}
@test "rename file to dir/file" {
gendataA 10 foo
gendataA 20 foo/bar
do_an_update
checkrenamed /foo /foo/bar
}
# @test "rename foo/foo to foo" {
# gendataA 10 foo/foo
# gendataA 20 foo
# do_an_update
# checkrenamed /foo/foo /foo
# }
# Emacs and vi support
# Local variables:
# sh-indentation: 2
# End:
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
export BATS_TEST_DIRNAME=$PWD
export TIMEFORMAT
TIMEFORMAT="time %R %P%% (%U+%S)"
source ../swupdlib.bash
exec 3< rundata
while read -r -u 3
do
read -i "$REPLY" -e CMD
eval "set -x ; time ( $CMD ) ; set +x "
done
+22
View File
@@ -0,0 +1,22 @@
sudo rm -r logs/ web-dir/
tar xf ~/data/test.tar
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core test-bundle
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
time sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
set_latest_ver 10
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 20
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
+144
View File
@@ -0,0 +1,144 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
tar x -C $DIR/.. -f ~/data/test.tar
mv $DIR/image/20/test-bundle/usr/share/bash-completion/completions{,.old}
init_server_ini
set_latest_ver 0
init_groups_ini os-core test-bundle
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
# Files have different names ("foo" vs "bar"), but have the same content
gen_file_plain_with_content 10 test-bundle "foo" "data"
gen_file_plain_with_content 20 test-bundle "bar" "data"
}
@test "rename detection support" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 10
sudo $MAKE_PACK --statedir $DIR 0 10 os-core
sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
set_latest_ver 10
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
sudo $MAKE_FULLFILES --statedir $DIR 20
sudo $MAKE_PACK --statedir $DIR 0 20 os-core
sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
sudo $MAKE_PACK --statedir $DIR 10 20 os-core
sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
# A renamed file comprises a new file and a deleted file
[[ 1 -eq $(grep '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
[[ 1 -eq $(grep '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle | wc -l) ]]
}
# @test "rename detection support 1 to 2" {
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 10
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
# set_latest_ver 10
# gen_file_plain_with_content 20 test-bundle "baz" "data"
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 20
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
# # A renamed file comprises a new file and a deleted file.
# # Should be one file that is not renamed
# # Need to figure out what happens to hard links
# [[ 1 -eq $(grep -c '^F\.\.r.*/ba[rz]$' $DIR/www/20/Manifest.test-bundle) ]]
# [[ 1 -eq $(grep -c '^F\.\.\..*/ba[rz]$' $DIR/www/20/Manifest.test-bundle) ]]
# [[ 1 -eq $(grep -c '^\.d\.r.*/foo$' $DIR/www/20/Manifest.test-bundle) ]]
# }
# @test "rename detection support 2 to 2" {
# gen_file_plain_with_content 10 test-bundle "fee" "data"
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 10
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
# set_latest_ver 10
# gen_file_plain_with_content 20 test-bundle "foo~" "data"
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 20
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
# # A renamed file comprises a new file and a deleted file.
# # Should be one file that is not renamed
# # Need to figure out what happens to hard links
# [[ 1 -eq $(grep -c '^F\.\.r.*/bar$' $DIR/www/20/Manifest.test-bundle) ]]
# [[ 1 -eq $(grep -c '^F\.\.r.*/foo~$' $DIR/www/20/Manifest.test-bundle) ]]
# [[ 2 -eq $(grep -c '^\.d\.r.*/f..$' $DIR/www/20/Manifest.test-bundle) ]]
# }
# @test "rename detection support binary files" {
# sudo mkdir -p $DIR/image/10/test-bundle/bin/
# sudo cp /bin/gcc-ar $DIR/image/10/test-bundle/bin/gcc-ar.1
# sudo cp /bin/gcc-ar $DIR/image/20/test-bundle/gcc-ar.1
# strip $DIR/image/20/test-bundle/gcc-ar.1
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 10
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
# set_latest_ver 10
# gen_file_plain_with_content 20 test-bundle "foo~" "And now not the same"
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 20
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
# # A renamed file comprises a new file and a deleted file.
# # Should be one file that is not renamed
# # Need to figure out what happens to hard links
# }
# @test "rename detection support binary files 2" {
# sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 10
# sudo $MAKE_PACK --statedir $DIR 0 10 os-core
# sudo $MAKE_PACK --statedir $DIR 0 10 test-bundle
# set_latest_ver 10
# gen_file_plain_with_content 20 test-bundle "foo" "And now not the same"
# gen_file_plain_with_content 20 test-bundle "foo.1" "data"
# sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
# sudo $MAKE_FULLFILES --statedir $DIR 20
# sudo $MAKE_PACK --statedir $DIR 0 20 os-core
# sudo $MAKE_PACK --statedir $DIR 0 20 test-bundle
# sudo $MAKE_PACK --statedir $DIR 10 20 os-core
# sudo $MAKE_PACK --statedir $DIR 10 20 test-bundle
# # A renamed file comprises a new file and a deleted file.
# # Should be one file that is not renamed
# # Need to figure out what happens to hard links
# }
# # vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bats
# common functions
load "../swupdlib"
setup() {
clean_test_dir
init_test_dir
init_server_ini
set_latest_ver 0
init_groups_ini os-core
set_os_release 10 os-core
track_bundle 10 os-core
gen_file_plain 10 os-core "/var/lib/test"
}
@test "state file marked in manifest" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
# a file and dir installed in /var should be marked state
grep '^D\.s\..*/var/lib$' $DIR/www/10/Manifest.os-core
grep '^F\.s\..*/var/lib/test$' $DIR/www/10/Manifest.os-core
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+56
View File
@@ -0,0 +1,56 @@
#!/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
# start with both bundles containing a file "foo"
set_os_release 10 os-core
track_bundle 10 os-core
track_bundle 10 test-bundle
gen_file_plain 10 os-core foo
gen_file_plain 10 test-bundle foo
# delete "foo" from os-core (the included bundle)
set_os_release 20 os-core
track_bundle 20 os-core
track_bundle 20 test-bundle
gen_file_plain 20 test-bundle foo
# delete "foo" from test-bundle
set_os_release 30 os-core
track_bundle 30 os-core
track_bundle 30 test-bundle
# make modification (add new file) to test-bundle
set_os_release 40 os-core
track_bundle 40 os-core
track_bundle 40 test-bundle
gen_file_plain 40 test-bundle foobar
}
@test "no subtraction for two deleted files" {
sudo $CREATE_UPDATE --osversion 10 --statedir $DIR --format 3
set_latest_ver 10
sudo $CREATE_UPDATE --osversion 20 --statedir $DIR --format 3
set_latest_ver 20
sudo $CREATE_UPDATE --osversion 30 --statedir $DIR --format 3
set_latest_ver 30
sudo $CREATE_UPDATE --osversion 40 --statedir $DIR --format 3
set_latest_ver 40
# If the file is absent in test-bundle, this means it was subtracted. If
# present, subtraction was not performed.
hash1=$(hash_for 40 test-bundle "/foo")
[ -n "$hash1" ]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+111 -9
View File
@@ -1,19 +1,33 @@
# NOTE: source this file from a *.bats file
init_web_dir() {
# absolute path is a hard requirement right now
local dir=$(realpath $1)
mkdir -p $dir/{image,www}
echo $dir
# The location of the swupd_* binaries
export SRCDIR="$BATS_TEST_DIRNAME/../../../"
export CREATE_UPDATE="$SRCDIR/swupd_create_update"
export MAKE_FULLFILES="$SRCDIR/swupd_make_fullfiles"
export MAKE_PACK="$SRCDIR/swupd_make_pack"
export DIR="$BATS_TEST_DIRNAME/web-dir"
init_test_dir() {
local testdir="$BATS_TEST_DIRNAME"
mkdir -p "$testdir"/logs
mkdir -p $DIR/{image,www}
# run swupd_* inside the directory to dump the logs
cd "$testdir"/logs
}
clean_test_dir() {
sudo rm -rf $DIR "$BATS_TEST_DIRNAME"/logs
}
init_server_ini() {
cp $srcdir/server.ini $DIR
cp $SRCDIR/server.ini $DIR
sed -i "s|/var/lib/update|$DIR|" $DIR/server.ini
}
set_latest_ver() {
echo "$1" > $DIR/image/latest.version
echo "$1" > $DIR/image/LAST_VER
}
init_groups_ini() {
@@ -21,10 +35,16 @@ init_groups_ini() {
cat >> $DIR/groups.ini << EOF
[$bundle]
group=$bundle
status=ACTIVE
EOF
done
}
# If the variable RUN_JUST_ONE is set then only run that test
maybeskip() {
[ -z "$RUN_JUST_ONE" ] || [ "$RUN_JUST_ONE" -eq "$BATS_TEST_NUMBER" ] || skip
}
set_os_release() {
local ver=$1
local bundle=$2
@@ -39,23 +59,105 @@ track_bundle() {
touch $DIR/image/$ver/$bundle/usr/share/clear/bundles/$bundle
}
gen_includes_file() {
local bundle=$1
local ver=$2
local includes="${@:3}"
mkdir -p $DIR/image/$ver/noship
for b in $includes; do
cat >> $DIR/image/$ver/noship/"$bundle"-includes << EOF
$b
EOF
done
}
gen_file_to_delta() {
local origver=$1
local origsize=$2
local newver=$3
local newbytes=$4
local bundle=$5
local name=$6
# create some random data for the original version
mkdir -p $DIR/image/$origver/$bundle
dd if=/dev/urandom of=$DIR/image/$origver/$bundle/randomfile bs=1 count=$origsize
dd if=/dev/urandom of=$DIR/image/$origver/$bundle/$name bs=1 count=$origsize
# append more random data to the end of the file in the new version
TMP=$(mktemp foo.XXXXXX)
mkdir -p $DIR/image/$newver/$bundle
dd if=/dev/urandom of=$TMP bs=1 count=$newbytes
cat $DIR/image/$origver/$bundle/randomfile $TMP > $DIR/image/$newver/$bundle/randomfile
cat $DIR/image/$origver/$bundle/$name $TMP > $DIR/image/$newver/$bundle/$name
rm $TMP
}
gen_file_plain() {
local ver=$1
local bundle=$2
local name="$3"
# Add plain text file into a bundle
case "$name" in
(*"/"*) mkdir -p "$DIR/image/$ver/$bundle/${name%/*}" ;;
(*) mkdir -p $DIR/image/$ver/$bundle ;;
esac
echo "$name" > $DIR/image/$ver/$bundle/"$name"
}
gen_file_plain_change() {
local ver=$1
local bundle=$2
local name="$3"
# Add plain text file into a bundle
case "$name" in
(*"/"*) mkdir -p "$DIR/image/$ver/$bundle/${name%/*}" ;;
(*) mkdir -p $DIR/image/$ver/$bundle ;;
esac
echo "$ver $name" > $DIR/image/$ver/$bundle/"$name"
}
gen_symlink_to_file() {
local ver=$1
local bundle=$2
local symname="$3"
local filename="$4"
mkdir -p $DIR/image/$ver/$bundle/$(dirname "$symname")
ln -s "$filename" $DIR/image/$ver/$bundle/"$symname"
}
copy_file() {
local origver=$1
local origbundle=$2
local origname="$3"
local newver=$4
local newbundle=$5
local newname="$6"
mkdir -p $DIR/image/$newver/$newbundle/$(dirname "$newname")
cp -a $DIR/image/$origver/$origbundle/"$origname" $DIR/image/$newver/$newbundle/"$newname"
}
# Gets the hash for file NAME in BUNDLE manifest for VER
hash_for() {
local ver=$1
local bundle=$2
local name="$3"
awk -F'\t' -v NAME="$name" 'NF == 4 && $4 == NAME { print $2 }' $DIR/www/$ver/Manifest.$bundle
}
gen_file_plain_with_content() {
local ver=$1
local bundle=$2
local name="$3"
local content="$4"
case "$name" in
(*"/"*) mkdir -p "$DIR/image/$ver/$bundle/${name%/*}" ;;
(*) mkdir -p $DIR/image/$ver/$bundle ;;
esac
echo "$content" > $DIR/image/$ver/$bundle/"$name"
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
-15
View File
@@ -1,15 +0,0 @@
#!/usr/bin/env bats
@test "create_update required arg" {
run $srcdir/swupd_create_update
[ "$status" -eq 1 ]
[[ "$output" =~ "Missing version parameter:" ]]
}
@test "create_update root priv check" {
run $srcdir/swupd_create_update -o 10
[ "$status" -eq 1 ]
[[ "$output" =~ "not being run as root.. exiting" ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bats
load "../swupdlib"
@test "create_update required format" {
run $CREATE_UPDATE
[ "$status" -eq 1 ]
[[ "$output" =~ "Missing format parameter" ]]
}
@test "create_update required version" {
run $CREATE_UPDATE -F 3
[ "$status" -eq 1 ]
[[ "$output" =~ "Missing version parameter:" ]]
}
@test "create_update root priv check" {
[ $EUID -eq 0 ] && skip "test can only be run as non-root"
run $CREATE_UPDATE -F 3 -o 10
[ "$status" -eq 1 ]
[[ "$output" =~ "not being run as root.. exiting" ]]
}
# vi: ft=sh ts=8 sw=2 sts=2 et tw=80