mirror of
https://github.com/clearlinux/swupd-client.git
synced 2026-09-07 14:11:52 +00:00
305 lines
7.0 KiB
C
305 lines
7.0 KiB
C
/*
|
|
* Software Updater - client side
|
|
*
|
|
* Copyright © 2012-2019 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/>.
|
|
*
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "manifest.h"
|
|
#include "swupd.h"
|
|
|
|
#define MANIFEST_LINE_MAXLEN (PATH_MAX * 2)
|
|
|
|
// strncmp helper to be used with consts
|
|
#define strlen_const(_const) sizeof(_const) - 1
|
|
#define strncmp_const(_str, _const) strncmp(_str, _const, strlen_const(_const))
|
|
|
|
#define MANIFEST_HEADER "MANIFEST\t"
|
|
|
|
struct manifest *manifest_parse(const char *component, const char *filename, bool header_only)
|
|
{
|
|
FILE *infile;
|
|
char line[MANIFEST_LINE_MAXLEN], *c, *c2;
|
|
int count = 0;
|
|
int deleted = 0;
|
|
int err;
|
|
struct manifest *manifest;
|
|
struct list *includes = NULL;
|
|
struct list *optional = NULL;
|
|
unsigned long long filecount = 0;
|
|
unsigned long long contentsize = 0;
|
|
|
|
infile = fopen(filename, "rbm");
|
|
if (infile == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
manifest = calloc(1, sizeof(struct manifest));
|
|
ON_NULL_ABORT(manifest);
|
|
|
|
/* line 1: MANIFEST\t<version> */
|
|
line[0] = 0;
|
|
if (fgets(line, MANIFEST_LINE_MAXLEN, infile) == NULL) {
|
|
goto err_close;
|
|
}
|
|
|
|
if (strncmp_const(line, MANIFEST_HEADER) != 0) {
|
|
goto err_close;
|
|
}
|
|
|
|
c = line + strlen_const(MANIFEST_HEADER);
|
|
err = strtoi_err(c, &manifest->manifest_version);
|
|
|
|
if (manifest->manifest_version <= 0 || err != 0) {
|
|
error("Loaded incompatible manifest version\n");
|
|
goto err_close;
|
|
}
|
|
|
|
line[0] = 0;
|
|
while (strncmp_const(line, "\n") != 0) {
|
|
/* read the header */
|
|
line[0] = 0;
|
|
if (fgets(line, MANIFEST_LINE_MAXLEN, infile) == NULL) {
|
|
break;
|
|
}
|
|
|
|
// Remove line break
|
|
c = strchr(line, '\n');
|
|
if (!c) {
|
|
goto err_close;
|
|
}
|
|
*c = 0;
|
|
|
|
if (c == line) {
|
|
break;
|
|
}
|
|
|
|
// Look for separator
|
|
c = strchr(line, '\t');
|
|
if (c) {
|
|
c++;
|
|
} else {
|
|
goto err_close;
|
|
}
|
|
|
|
if (strncmp_const(line, "version:\t") == 0) {
|
|
err = strtoi_err(c, &manifest->version);
|
|
if (err != 0) {
|
|
error("Invalid manifest version on %s\n", filename);
|
|
goto err_close;
|
|
}
|
|
} else if (strncmp_const(line, "filecount:\t") == 0) {
|
|
errno = 0;
|
|
filecount = strtoull(c, NULL, 10);
|
|
if (filecount > 4000000) {
|
|
/* Note on the number 4,000,000. We want this
|
|
* to be big enough to allow Manifest.Full to
|
|
* pass (currently about 450,000 March 18) but
|
|
* small enough that when multiplied by
|
|
* sizeof(struct file) it fits into
|
|
* size_t. For a system with size_t being a 32
|
|
* bit value, this constrains it to be less
|
|
* than about 6,000,000, but close to infinity
|
|
* for systems with 64 bit size_t.
|
|
*/
|
|
error("Preposterous (%llu) number of files in %s Manifest, more than 4 million skipping\n",
|
|
filecount, component);
|
|
goto err_close;
|
|
} else if (errno != 0) {
|
|
error("Loaded incompatible manifest filecount\n");
|
|
goto err_close;
|
|
}
|
|
} else if (strncmp_const(line, "contentsize:\t") == 0) {
|
|
errno = 0;
|
|
contentsize = strtoull(c, NULL, 10);
|
|
if (contentsize > 2000000000000UL) {
|
|
error("Preposterous (%llu) size of files in %s Manifest, more than 2TB skipping\n",
|
|
contentsize, component);
|
|
goto err_close;
|
|
} else if (errno != 0) {
|
|
error("Loaded incompatible manifest contentsize\n");
|
|
goto err_close;
|
|
}
|
|
} else if (strncmp_const(line, "includes:\t") == 0) {
|
|
includes = list_prepend_data(includes, strdup_or_die(c));
|
|
} else if (strncmp_const(line, "also-add:\t") == 0) {
|
|
optional = list_prepend_data(optional, strdup_or_die(c));
|
|
}
|
|
}
|
|
|
|
manifest->component = strdup_or_die(component);
|
|
manifest->filecount = filecount;
|
|
manifest->contentsize = contentsize;
|
|
manifest->includes = includes;
|
|
manifest->optional = optional;
|
|
|
|
if (header_only) {
|
|
fclose(infile);
|
|
return manifest;
|
|
}
|
|
|
|
/* empty line */
|
|
while (!feof(infile)) {
|
|
struct file *file;
|
|
|
|
if (fgets(line, MANIFEST_LINE_MAXLEN, infile) == NULL) {
|
|
break;
|
|
}
|
|
|
|
// Remove line break
|
|
c = strchr(line, '\n');
|
|
if (!c || c == line) {
|
|
goto err_close;
|
|
}
|
|
*c = 0;
|
|
|
|
// Look for separator
|
|
c = strchr(line, '\t');
|
|
if (c) {
|
|
c++;
|
|
} else {
|
|
goto err_close;
|
|
}
|
|
|
|
file = calloc(1, sizeof(struct file));
|
|
ON_NULL_ABORT(file);
|
|
|
|
if (line[0] == 'F') {
|
|
file->is_file = 1;
|
|
} else if (line[0] == 'D') {
|
|
file->is_dir = 1;
|
|
} else if (line[0] == 'L') {
|
|
file->is_link = 1;
|
|
} else if (line[0] == 'M') {
|
|
file->is_manifest = 1;
|
|
} else if (line[0] == 'I') {
|
|
/* ignore this file for future iterative manifest feature */
|
|
free(file);
|
|
continue;
|
|
}
|
|
|
|
if (line[1] == 'd') {
|
|
file->is_deleted = 1;
|
|
deleted++;
|
|
} else if (line[1] == 'g') {
|
|
file->is_deleted = 1;
|
|
file->is_ghosted = 1;
|
|
deleted++;
|
|
} else if (line[1] == 'e') {
|
|
file->is_experimental = 1;
|
|
}
|
|
|
|
if (line[2] == 'C') {
|
|
file->is_config = 1;
|
|
} else if (line[2] == 's') {
|
|
file->is_state = 1;
|
|
} else if (line[2] == 'b') {
|
|
file->is_boot = 1;
|
|
}
|
|
|
|
if (line[3] == 'r') {
|
|
/* rename flag is ignored */
|
|
} else if (line[3] == 'm') {
|
|
file->is_mix = 1;
|
|
manifest->is_mix = 1;
|
|
}
|
|
|
|
c2 = strchr(c, '\t');
|
|
if (c2) {
|
|
*c2 = 0;
|
|
c2++;
|
|
} else {
|
|
free(file);
|
|
goto err_close;
|
|
}
|
|
|
|
hash_assign(c, file->hash);
|
|
|
|
c = c2;
|
|
c2 = strchr(c, '\t');
|
|
if (c2) {
|
|
*c2 = 0;
|
|
c2++;
|
|
} else {
|
|
free(file);
|
|
goto err_close;
|
|
}
|
|
|
|
err = strtoi_err(c, &file->last_change);
|
|
if (file->last_change <= 0 || err != 0) {
|
|
error("Loaded incompatible manifest last change\n");
|
|
free(file);
|
|
goto err_close;
|
|
}
|
|
|
|
c = c2;
|
|
|
|
file->filename = strdup_or_die(c);
|
|
|
|
if (file->is_manifest) {
|
|
manifest->manifests = list_prepend_data(manifest->manifests, file);
|
|
} else {
|
|
file->is_tracked = 1;
|
|
manifest->files = list_prepend_data(manifest->files, file);
|
|
}
|
|
count++;
|
|
}
|
|
|
|
fclose(infile);
|
|
return manifest;
|
|
|
|
err_close:
|
|
free_manifest(manifest);
|
|
fclose(infile);
|
|
return NULL;
|
|
}
|
|
|
|
void free_manifest_data(void *data)
|
|
{
|
|
struct manifest *manifest = (struct manifest *)data;
|
|
|
|
free_manifest(manifest);
|
|
}
|
|
|
|
void free_manifest(struct manifest *manifest)
|
|
{
|
|
if (!manifest) {
|
|
return;
|
|
}
|
|
|
|
if (manifest->manifests) {
|
|
list_free_list_and_data(manifest->manifests, free_file_data);
|
|
}
|
|
if (manifest->submanifests) {
|
|
list_free_list(manifest->files);
|
|
list_free_list_and_data(manifest->submanifests, free_manifest_data);
|
|
} else {
|
|
list_free_list_and_data(manifest->files, free_file_data);
|
|
}
|
|
if (manifest->includes) {
|
|
list_free_list_and_data(manifest->includes, free);
|
|
}
|
|
if (manifest->optional) {
|
|
list_free_list_and_data(manifest->optional, free);
|
|
}
|
|
free_string(&manifest->component);
|
|
free(manifest);
|
|
}
|