diff --git a/src/analyze_fs.c b/src/analyze_fs.c
index ce30393..ac3731c 100644
--- a/src/analyze_fs.c
+++ b/src/analyze_fs.c
@@ -275,7 +275,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;
@@ -301,27 +301,151 @@ static bool illegal_characters(char *filename)
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
.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) {
@@ -334,50 +458,14 @@ static void iterate_directory(struct manifest *manifest, char *pathprefix,
}
string_or_die(&sub_filename, "%s/%s", subpath, entry->d_name);
-
- if (illegal_characters(entry->d_name)) {
- printf("WARNING: Filename %s includes illegal character(s) ...skipping.\n", sub_filename);
- free(sub_filename);
- continue;
- }
-
- file = calloc(1, sizeof(struct file));
- if (!file) {
- break;
- }
-
- file->last_change = manifest->version;
- file->filename = sub_filename;
-
string_or_die(&fullname, "%s/%s", fullpath, entry->d_name);
- populate_file_struct(file, fullname);
- free(fullname);
- if (file->is_dir) {
- iterate_directory(manifest, pathprefix, file->filename, do_hash);
- }
+ /* 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 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;
- }
+ if (file && file->is_dir) {
+ iterate_directory(manifest, pathprefix, file->filename, do_hash);
}
- manifest->files = g_list_prepend(manifest->files, file);
- manifest->count++;
}
closedir(dir);
free(fullpath);
diff --git a/src/chroot.c b/src/chroot.c
index 32ed997..f3832e1 100644
--- a/src/chroot.c
+++ b/src/chroot.c
@@ -39,15 +39,21 @@ void chroot_create_full(int newversion)
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(¶m, "%s/%i/os-core/", image_dir, newversion);
- char *const rsynccmd[] = { "rsync", "-aAX", param, full_dir, NULL };
- if (system_argv(rsynccmd) != 0) {
- assert(0);
+ 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);
@@ -58,11 +64,13 @@ void chroot_create_full(int newversion)
break;
}
- LOG(NULL, "Overlaying bundle chroot onto full", "%s", group);
string_or_die(¶m, "%s/%i/%s/", image_dir, newversion, group);
- char *const rsynccmd[] = { "rsync", "-aAX", "--ignore-existing", param, full_dir, NULL };
- if (system_argv(rsynccmd) != 0) {
- assert(0);
+ 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);
+ }
}
free(param);
}
diff --git a/src/create_update.c b/src/create_update.c
index 4dcdd96..f2806a7 100644
--- a/src/create_update.c
+++ b/src/create_update.c
@@ -141,6 +141,7 @@ 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);
@@ -182,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);
@@ -193,6 +196,7 @@ static void populate_dirs(int version)
}
}
free(newversiondir);
+ free(newversiondircontent);
}
static int check_build_env(void)