Merge pull request #159 from Gottox/reproducible-create

Reproducible create
This commit is contained in:
Juan RP 2016-04-07 16:26:38 +02:00
commit 13395d8a50

View File

@ -42,7 +42,6 @@
#include <locale.h> #include <locale.h>
#include <xbps.h> #include <xbps.h>
#include "queue.h"
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic ignored "-Wformat-nonliteral" #pragma clang diagnostic ignored "-Wformat-nonliteral"
@ -55,18 +54,8 @@
# define archive_write_finish(x) archive_write_free(x) # define archive_write_finish(x) archive_write_free(x)
#endif #endif
struct xentry {
TAILQ_ENTRY(xentry) entries;
uint64_t mtime;
char *file, *type, *target, *hash;
ino_t inode;
};
static TAILQ_HEAD(xentry_head, xentry) xentry_list =
TAILQ_HEAD_INITIALIZER(xentry_list);
static uint64_t instsize; static uint64_t instsize;
static xbps_dictionary_t pkg_propsd, pkg_filesd; static xbps_dictionary_t pkg_propsd, pkg_filesd, all_filesd;
static const char *destdir; static const char *destdir;
static void __attribute__((noreturn)) static void __attribute__((noreturn))
@ -302,7 +291,7 @@ entry_is_conf_file(const char *file)
static int static int
ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _unused) ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _unused)
{ {
struct xentry *xe = NULL; xbps_dictionary_t fileinfo = NULL;
const char *filep = NULL; const char *filep = NULL;
char *buf, *p, *p2, *dname; char *buf, *p, *p2, *dname;
ssize_t r; ssize_t r;
@ -321,16 +310,20 @@ ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _u
/* sanitized file path */ /* sanitized file path */
filep = strchr(fpath, '.') + 1; filep = strchr(fpath, '.') + 1;
xe = calloc(1, sizeof(*xe)); fileinfo = xbps_dictionary_create();
assert(xe); /* XXX: fileinfo contains the sanatized path, whereas xe contains the
xe->file = strdup(fpath); * unsanatized path!
assert(xe->file); *
* when handing the files over, do not use the dictionary directly. Instead
* use the keysym, as this value has the unsanatized path.
*/
xbps_dictionary_set_cstring(fileinfo, "file", filep);
xbps_dictionary_set(all_filesd, fpath, fileinfo);
if ((strcmp(fpath, "./INSTALL") == 0) || if ((strcmp(fpath, "./INSTALL") == 0) ||
(strcmp(fpath, "./REMOVE") == 0)) { (strcmp(fpath, "./REMOVE") == 0)) {
/* metadata file */ /* metadata file */
xe->type = strdup("metadata"); xbps_dictionary_set_cstring_nocopy(fileinfo, "type", "metadata");
assert(xe->type);
goto out; goto out;
} }
@ -340,10 +333,10 @@ ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _u
* *
* Find out target file. * Find out target file.
*/ */
xe->type = strdup("links"); xbps_dictionary_set_cstring_nocopy(fileinfo, "type", "links");
assert(xe->type);
/* store modification time for regular files and links */ /* store modification time for regular files and links */
xe->mtime = (uint64_t)sb->st_mtime; xbps_dictionary_set_cstring_nocopy(fileinfo, "type", "links");
xbps_dictionary_set_uint64(fileinfo, "mtime", (uint64_t)sb->st_mtime);
buf = malloc(sb->st_size+1); buf = malloc(sb->st_size+1);
assert(buf); assert(buf);
r = readlink(fpath, buf, sb->st_size+1); r = readlink(fpath, buf, sb->st_size+1);
@ -363,7 +356,7 @@ ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _u
* which might be provided in another package. * which might be provided in another package.
* So let's use the same target. * So let's use the same target.
*/ */
xe->target = strdup(buf); xbps_dictionary_set_cstring(fileinfo, "target", buf);
} else { } else {
/* /*
* Sanitize destdir just in case. * Sanitize destdir just in case.
@ -371,7 +364,7 @@ ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _u
if ((p2 = realpath(destdir, NULL)) == NULL) if ((p2 = realpath(destdir, NULL)) == NULL)
die("failed to sanitize destdir %s: %s", destdir, strerror(errno)); die("failed to sanitize destdir %s: %s", destdir, strerror(errno));
xe->target = strdup(p+strlen(p2)); xbps_dictionary_set_cstring(fileinfo, "target", p+strlen(p2));
free(p2); free(p2);
free(p); free(p);
} }
@ -381,87 +374,101 @@ ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _u
assert(p); assert(p);
dname = dirname(p); dname = dirname(p);
assert(dname); assert(dname);
xe->target = xbps_xasprintf("%s/%s", dname, buf); p2 = xbps_xasprintf("%s/%s", dname, buf);
xbps_dictionary_set_cstring(fileinfo, "target", p2);
free(p2);
free(p); free(p);
} else { } else {
xe->target = strdup(buf); xbps_dictionary_set_cstring(fileinfo, "target", buf);
} }
assert(xe->target); assert(xbps_dictionary_get(fileinfo, "target"));
free(buf); free(buf);
} else if (type == FTW_F) { } else if (type == FTW_F) {
struct xentry *xep; xbps_object_iterator_t iter;
bool hlink = false; xbps_object_t obj;
xbps_dictionary_t linkinfo;
uint64_t inode = 0;
/* /*
* Regular files. First find out if it's a hardlink: * Regular files. First find out if it's a hardlink:
* - st_nlink > 1 * - st_nlink > 1
* and then search for a stored file matching its inode. * and then search for a stored file matching its inode.
*/ */
TAILQ_FOREACH(xep, &xentry_list, entries) { iter = xbps_dictionary_iterator(all_filesd);
if (sb->st_nlink > 1 && xep->inode == sb->st_ino) { assert(iter);
while ((obj = xbps_object_iterator_next(iter))) {
if (sb->st_nlink <= 1)
continue;
linkinfo = xbps_dictionary_get_keysym(all_filesd, obj);
xbps_dictionary_get_uint64(linkinfo, "inode", &inode);
if (inode == sb->st_ino) {
/* matched */ /* matched */
hlink = true; printf("%lu %lu\n", inode, sb->st_ino);
break; break;
} }
} }
if (!hlink) if (inode != sb->st_ino)
instsize += sb->st_size; instsize += sb->st_size;
xbps_object_iterator_release(iter);
/* /*
* Find out if it's a configuration file or not * Find out if it's a configuration file or not
* and calculate sha256 hash. * and calculate sha256 hash.
*/ */
if (entry_is_conf_file(filep)) if (entry_is_conf_file(filep)) {
xe->type = strdup("conf_files"); xbps_dictionary_set_cstring_nocopy(fileinfo, "type", "conf_files");
else } else {
xe->type = strdup("files"); xbps_dictionary_set_cstring_nocopy(fileinfo, "type", "files");
}
assert(xe->type); if ((p = xbps_file_hash(fpath)) == NULL)
if ((xe->hash = xbps_file_hash(fpath)) == NULL)
die("failed to process hash for %s:", fpath); die("failed to process hash for %s:", fpath);
xbps_dictionary_set_cstring(fileinfo, "sha256", p);
free(p);
xe->inode = sb->st_ino; xbps_dictionary_set_uint64(fileinfo, "inode", sb->st_ino);
/* store modification time for regular files and links */ /* store modification time for regular files and links */
xe->mtime = (uint64_t)sb->st_mtime; xbps_dictionary_set_uint64(fileinfo, "mtime", sb->st_mtime);
} else if (type == FTW_D || type == FTW_DP) { } else if (type == FTW_D || type == FTW_DP) {
/* directory */ /* directory */
xe->type = strdup("dirs"); xbps_dictionary_set_cstring_nocopy(fileinfo, "type", "dirs");
assert(xe->type);
} }
out: out:
TAILQ_INSERT_TAIL(&xentry_list, xe, entries); xbps_object_release(fileinfo);
return 0; return 0;
} }
static void static void
process_xentry(const char *key, const char *mutable_files) process_xentry(const char *key, const char *mutable_files)
{ {
xbps_object_iterator_t iter;
xbps_object_t filepathk;
xbps_array_t a; xbps_array_t a;
xbps_dictionary_t d; xbps_dictionary_t fileinfo;
struct xentry *xe; char *saveptr, *args, *tok;
char *p, *saveptr, *args, *tok; const char *p;
bool found = false, mutable_found = false; bool found = false, mutable_found = false;
a = xbps_array_create(); a = xbps_array_create();
assert(a); assert(a);
TAILQ_FOREACH_REVERSE(xe, &xentry_list, xentry_head, entries) { iter = xbps_dictionary_iterator(all_filesd);
if (strcmp(xe->type, key)) assert(iter);
while ((filepathk = xbps_object_iterator_next(iter))) {
fileinfo = xbps_dictionary_get_keysym(all_filesd, filepathk);
if (!xbps_string_equals_cstring(xbps_dictionary_get(fileinfo, "type"), key))
continue; continue;
found = true; found = true;
d = xbps_dictionary_create(); xbps_dictionary_get_cstring_nocopy(fileinfo, "file", &p);
assert(d);
/* sanitize file path */
p = strchr(xe->file, '.') + 1;
/* /*
* Find out if this file is mutable. * Find out if this file is mutable.
*/ */
if (mutable_files) { if (mutable_files) {
if ((strchr(mutable_files, ' ') == NULL) && if ((strchr(mutable_files, ' ') == NULL) &&
(strcmp(mutable_files, p) == 0)) (strcmp(mutable_files, p) == 0))
xbps_dictionary_set_bool(d, "mutable", true); xbps_dictionary_set_bool(fileinfo, "mutable", true);
else { else {
args = strdup(mutable_files); args = strdup(mutable_files);
assert(args); assert(args);
@ -474,23 +481,21 @@ process_xentry(const char *key, const char *mutable_files)
} }
free(args); free(args);
if (mutable_found) { if (mutable_found) {
xbps_dictionary_set_bool(d, "mutable", xbps_dictionary_set_bool(fileinfo, "mutable",
true); true);
mutable_found = false; mutable_found = false;
} }
} }
} }
xbps_dictionary_set_cstring(d, "file", p); /*
if (xe->target) * Clean up dictionary
xbps_dictionary_set_cstring(d, "target", xe->target); */
if (xe->hash) xbps_dictionary_remove(fileinfo, "inode");
xbps_dictionary_set_cstring(d, "sha256", xe->hash);
if (xe->mtime)
xbps_dictionary_set_uint64(d, "mtime", xe->mtime);
xbps_array_add(a, d); xbps_array_add(a, fileinfo);
xbps_object_release(d); xbps_object_release(fileinfo);
} }
xbps_object_iterator_release(iter);
if (found) if (found)
xbps_dictionary_set(pkg_filesd, key, a); xbps_dictionary_set(pkg_filesd, key, a);
@ -552,7 +557,7 @@ write_entry(struct archive *ar, struct archive_entry *entry)
static void static void
process_entry_file(struct archive *ar, process_entry_file(struct archive *ar,
struct archive_entry_linkresolver *resolver, struct archive_entry_linkresolver *resolver,
struct xentry *xe, const char *filematch) const char *filename)
{ {
struct archive_entry *entry, *sparse_entry; struct archive_entry *entry, *sparse_entry;
struct stat st; struct stat st;
@ -560,18 +565,14 @@ process_entry_file(struct archive *ar,
ssize_t len; ssize_t len;
assert(ar); assert(ar);
assert(xe);
if (filematch && strcmp(xe->file, filematch)) p = xbps_xasprintf("%s/%s", destdir, filename);
return;
p = xbps_xasprintf("%s/%s", destdir, xe->file);
if (lstat(p, &st) == -1) if (lstat(p, &st) == -1)
die("failed to add entry (fstat) %s to archive:", xe->file); die("failed to add entry (fstat) %s to archive:", filename);
entry = archive_entry_new(); entry = archive_entry_new();
assert(entry); assert(entry);
archive_entry_set_pathname(entry, xe->file); archive_entry_set_pathname(entry, filename);
if (st.st_uid == geteuid()) if (st.st_uid == geteuid())
st.st_uid = 0; st.st_uid = 0;
if (st.st_gid == getegid()) if (st.st_gid == getegid())
@ -590,7 +591,7 @@ process_entry_file(struct archive *ar,
len = readlink(p, buf, st.st_size+1); len = readlink(p, buf, st.st_size+1);
if (len < 0 || len > st.st_size) if (len < 0 || len > st.st_size)
die("failed to add entry %s (readlink) to archive:", die("failed to add entry %s (readlink) to archive:",
xe->file); filename);
buf[len] = '\0'; buf[len] = '\0';
archive_entry_set_symlink(entry, buf); archive_entry_set_symlink(entry, buf);
} }
@ -611,14 +612,17 @@ process_archive(struct archive *ar,
struct archive_entry_linkresolver *resolver, struct archive_entry_linkresolver *resolver,
const char *pkgver, bool quiet) const char *pkgver, bool quiet)
{ {
struct xentry *xe;
char *xml; char *xml;
const char *filepath, *p;
xbps_object_iterator_t iter;
xbps_object_t filepathk;
xbps_dictionary_t fileinfo;
/* Add INSTALL/REMOVE metadata scripts first */ /* Add INSTALL/REMOVE metadata scripts first */
TAILQ_FOREACH(xe, &xentry_list, entries) { if (xbps_dictionary_get(all_filesd, "./INSTALL"))
process_entry_file(ar, resolver, xe, "./INSTALL"); process_entry_file(ar, resolver, "./INSTALL");
process_entry_file(ar, resolver, xe, "./REMOVE"); if (xbps_dictionary_get(all_filesd, "./REMOVE"))
} process_entry_file(ar, resolver, "./REMOVE");
/* /*
* Add the installed-size object. * Add the installed-size object.
*/ */
@ -640,18 +644,23 @@ process_archive(struct archive *ar,
free(xml); free(xml);
/* Add all package data files and release resources */ /* Add all package data files and release resources */
while ((xe = TAILQ_FIRST(&xentry_list)) != NULL) { iter = xbps_dictionary_iterator(all_filesd);
TAILQ_REMOVE(&xentry_list, xe, entries); assert(iter);
if ((strcmp(xe->type, "metadata") == 0) || while ((filepathk = xbps_object_iterator_next(iter))) {
(strcmp(xe->type, "dirs") == 0)) filepath = xbps_dictionary_keysym_cstring_nocopy(filepathk);
fileinfo = xbps_dictionary_get_keysym(all_filesd, filepathk);
if (xbps_string_equals_cstring(xbps_dictionary_get(fileinfo, "type"), "metadata") ||
xbps_string_equals_cstring(xbps_dictionary_get(fileinfo, "type"), "dirs"))
continue; continue;
if (!quiet) { if (!quiet) {
printf("%s: adding `%s' ...\n", pkgver, xe->file); xbps_dictionary_get_cstring_nocopy(fileinfo, "file", &p);
printf("%s: adding `%s' ...\n", pkgver, p);
fflush(stdout); fflush(stdout);
} }
process_entry_file(ar, resolver, xe, NULL); process_entry_file(ar, resolver, filepath);
} }
xbps_object_iterator_release(iter);
} }
int int
@ -889,6 +898,8 @@ main(int argc, char **argv)
*/ */
pkg_filesd = xbps_dictionary_create(); pkg_filesd = xbps_dictionary_create();
assert(pkg_filesd); assert(pkg_filesd);
all_filesd = xbps_dictionary_create();
assert(all_filesd);
process_destdir(mutable_files); process_destdir(mutable_files);
/* Back to original cwd after file tree walk processing */ /* Back to original cwd after file tree walk processing */