[darwinbuild-changes] [294] trunk
source_changes at macosforge.org
source_changes at macosforge.org
Wed Oct 4 02:02:25 PDT 2006
Revision: 294
http://trac.macosforge.org/projects/darwinbuild/changeset/294
Author: kevin
Date: 2006-10-04 02:02:24 -0700 (Wed, 04 Oct 2006)
Log Message:
-----------
- initial revision of Darwin Update
Added Paths:
-----------
trunk/darwinup/
trunk/darwinup/Archive.cpp
trunk/darwinup/Archive.h
trunk/darwinup/Depot.cpp
trunk/darwinup/Depot.h
trunk/darwinup/Digest.cpp
trunk/darwinup/Digest.h
trunk/darwinup/File.cpp
trunk/darwinup/File.h
trunk/darwinup/Makefile
trunk/darwinup/NOTES
trunk/darwinup/SerialSet.cpp
trunk/darwinup/SerialSet.h
trunk/darwinup/Utils.cpp
trunk/darwinup/Utils.h
trunk/darwinup/libredo.o
trunk/darwinup/main.cpp
trunk/darwinup/redo_prebinding.h
Added: trunk/darwinup/Archive.cpp
===================================================================
--- trunk/darwinup/Archive.cpp (rev 0)
+++ trunk/darwinup/Archive.cpp 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Archive.h"
+#include "Depot.h"
+#include "File.h"
+#include "Utils.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern char** environ;
+
+Archive::Archive(const char* path) {
+ m_serial = 0;
+ uuid_generate_random(m_uuid);
+ m_path = strdup(path);
+ m_name = strdup(basename(m_path));
+ m_info = 0;
+ m_date_installed = time(NULL);
+}
+
+Archive::Archive(uint64_t serial, uuid_t uuid, const char* name, const char* path, uint64_t info, time_t date_installed) {
+ m_serial = serial;
+ uuid_copy(m_uuid, uuid);
+ m_name = name ? strdup(name) : NULL;
+ m_path = path ? strdup(path) : NULL;
+ m_info = info;
+ m_date_installed = date_installed;
+}
+
+
+Archive::~Archive() {
+ if (m_path) free(m_path);
+ if (m_name) free(m_name);
+}
+
+uint64_t Archive::serial() { return m_serial; }
+uint8_t* Archive::uuid() { return m_uuid; }
+const char* Archive::name() { return m_name; }
+const char* Archive::path() { return m_path; }
+uint64_t Archive::info() { return m_info; }
+time_t Archive::date_installed() { return m_date_installed; }
+
+char* Archive::directory_name(const char* prefix) {
+ char* path = NULL;
+ char uuidstr[37];
+ uuid_unparse_upper(m_uuid, uuidstr);
+ asprintf(&path, "%s/%s", prefix, uuidstr);
+ if (path == NULL) {
+ fprintf(stderr, "%s:%d: out of memory\n", __FILE__, __LINE__);
+ }
+ return path;
+}
+
+char* Archive::create_directory(const char* prefix) {
+ int res = 0;
+ char* path = this->directory_name(prefix);
+ if (path && res == 0) res = mkdir(path, 0777);
+ if (res != 0) {
+ fprintf(stderr, "%s:%d: could not create directory: %s: %s (%d)\n", __FILE__, __LINE__, path, strerror(errno), errno);
+ free(path);
+ path = NULL;
+ }
+ if (res == 0) res = chown(path, 0, 0);
+ return path;
+}
+
+int Archive::compact_directory(const char* prefix) {
+ int res = 0;
+ char* tarpath = NULL;
+ char uuidstr[37];
+ uuid_unparse_upper(m_uuid, uuidstr);
+ asprintf(&tarpath, "%s/%s.tar.bz2", prefix, uuidstr);
+ if (tarpath) {
+ const char* args[] = {
+ "/usr/bin/tar",
+ "cjf", tarpath,
+ "-C", prefix,
+ uuidstr,
+ NULL
+ };
+ res = exec_with_args(args);
+ free(tarpath);
+ } else {
+ fprintf(stderr, "%s:%d: out of memory\n", __FILE__, __LINE__);
+ res = -1;
+ }
+ return res;
+}
+
+int Archive::expand_directory(const char* prefix) {
+ int res = 0;
+ char* tarpath = NULL;
+ char uuidstr[37];
+ uuid_unparse_upper(m_uuid, uuidstr);
+ asprintf(&tarpath, "%s/%s.tar.bz2", prefix, uuidstr);
+ if (tarpath) {
+ const char* args[] = {
+ "/usr/bin/tar",
+ "xjf", tarpath,
+ "-C", prefix,
+ "-p", // --preserve-permissions
+ NULL
+ };
+ res = exec_with_args(args);
+ free(tarpath);
+ } else {
+ fprintf(stderr, "%s:%d: out of memory\n", __FILE__, __LINE__);
+ res = -1;
+ }
+ return res;
+}
+
+
+int Archive::extract(const char* destdir) {
+ // not implemented
+ return -1;
+}
+
+
+
+RollbackArchive::RollbackArchive() : Archive("<Rollback>") {
+ m_info = ARCHIVE_INFO_ROLLBACK;
+}
+
+
+
+DittoArchive::DittoArchive(const char* path) : Archive(path) {}
+
+int DittoArchive::extract(const char* destdir) {
+ const char* args[] = {
+ "/usr/bin/ditto",
+ m_path, destdir,
+ NULL
+ };
+ return exec_with_args(args);
+}
+
+
+TarArchive::TarArchive(const char* path) : Archive(path) {}
+
+int TarArchive::extract(const char* destdir) {
+ const char* args[] = {
+ "/usr/bin/tar",
+ "xf", m_path,
+ "-C", destdir,
+ NULL
+ };
+ return exec_with_args(args);
+}
+
+
+TarGZArchive::TarGZArchive(const char* path) : Archive(path) {}
+
+int TarGZArchive::extract(const char* destdir) {
+ const char* args[] = {
+ "/usr/bin/tar",
+ "xzf", m_path,
+ "-C", destdir,
+ NULL
+ };
+ return exec_with_args(args);
+}
+
+
+TarBZ2Archive::TarBZ2Archive(const char* path) : Archive(path) {}
+
+int TarBZ2Archive::extract(const char* destdir) {
+ const char* args[] = {
+ "/usr/bin/tar",
+ "xjf", m_path,
+ "-C", destdir,
+ NULL
+ };
+ return exec_with_args(args);
+}
+
+
+Archive* ArchiveFactory(const char* path) {
+ Archive* archive = NULL;
+
+ // make sure the archive exists
+ struct stat sb;
+ int res = stat(path, &sb);
+ if (res == -1 && errno == ENOENT) {
+ return NULL;
+ }
+
+ if (is_directory(path)) {
+ archive = new DittoArchive(path);
+ } else if (has_suffix(path, ".tar")) {
+ archive = new TarArchive(path);
+ } else if (has_suffix(path, ".tar.gz") || has_suffix(path, ".tgz")) {
+ archive = new TarGZArchive(path);
+ } else if (has_suffix(path, ".tar.bz2") || has_suffix(path, ".tbz2")) {
+ archive = new TarBZ2Archive(path);
+ } else {
+ fprintf(stderr, "Error: unknown archive type: %s\n", path);
+ }
+ return archive;
+}
\ No newline at end of file
Property changes on: trunk/darwinup/Archive.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Archive.h
===================================================================
--- trunk/darwinup/Archive.h (rev 0)
+++ trunk/darwinup/Archive.h 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <uuid/uuid.h>
+
+
+//
+// ARCHIVE_INFO flags stored in the database
+//
+const uint64_t ARCHIVE_INFO_ROLLBACK = 0x0001;
+
+struct Archive;
+struct Depot;
+
+////
+// Archive
+//
+// Archive is the root class of all archive formats
+// supported by darwinup.
+//
+// Conceptually it's an abstract class, although that
+// hasn't been formalized.
+//
+// ArchiveFactory exists to return the correct
+// concrete subclass for a given archive to be
+// installed. Currently this is determined
+// by the file's suffix.
+////
+
+Archive* ArchiveFactory(const char* path);
+
+struct Archive {
+ Archive(const char* path);
+ virtual ~Archive();
+
+ ////
+ // Public Accessor functions
+ ////
+
+ // Unique serial number for the archive (used by database).
+ virtual uint64_t serial();
+
+ // Universally unique identifier for the archive.
+ virtual uint8_t* uuid();
+
+ // The name of the archive as it was installed.
+ // Determined by basename(3).
+ // Do not modify or free(3).
+ virtual const char* name();
+
+ // The path to the archive as it was installed.
+ // Do not modify or free(3).
+ virtual const char* path();
+
+ // ARCHIVE_INFO flags.
+ virtual uint64_t info();
+
+ // The epoch seconds when the archive was installed.
+ virtual time_t date_installed();
+
+
+ ////
+ // Member functions
+ ////
+
+ // Extracts the archive into the specified destination.
+ // Not implemented for Archive, expected to be implemented
+ // by concrete subclasses.
+ virtual int extract(const char* destdir);
+
+ // Returns the backing-store directory name for the archive.
+ // This is prefix/uuid.
+ // The result should be released with free(3).
+ char* directory_name(const char* prefix);
+
+ // Creates the backing-store directory for the archive.
+ // Same directory name as returned by directory_name().
+ char* create_directory(const char* prefix);
+
+ // Compacts the backing-store directory into a single file.
+ int compact_directory(const char* prefix);
+
+ // Expands the backing-store directory from its single file.
+ int expand_directory(const char* prefix);
+
+ protected:
+
+ // Constructor for subclasses and Depot to use when unserializing an archive from the database.
+ Archive(uint64_t serial, uuid_t uuid, const char* name, const char* path, uint64_t info, time_t date_installed);
+
+ uint64_t m_serial;
+ uuid_t m_uuid;
+ char* m_name;
+ char* m_path;
+ uint64_t m_info;
+ time_t m_date_installed;
+
+ friend struct Depot;
+};
+
+
+////
+// RollbackArchive
+//
+// Not a file format. RollbackArchive is an internal representation
+// of archives that are created to store the user-data that darwinup
+// archives as part of installation.
+////
+struct RollbackArchive : public Archive {
+ RollbackArchive();
+};
+
+
+////
+// DittoArchive
+//
+// Not a file format, but this allows a directory tree to be installed
+// using the ditto(1) command line tool. This is useful for installing
+// Darwin roots built with DarwinBuild.
+////
+struct DittoArchive : public Archive {
+ DittoArchive(const char* path);
+ virtual int extract(const char* destdir);
+};
+
+
+////
+// PkgArchive
+//
+// Corresponds to the Mac OS X installer's .pkg bundle format.
+// Installs the archive using the pax(1) command line tool.
+// NOTE: this does not make any attempt to perform any of the
+// volume checks or run any preflight or postflight actions.
+////
+struct PkgArchive : public Archive {
+ PkgArchive(const char* path);
+ virtual int extract(const char* destdir);
+};
+
+////
+// TarArchive
+//
+// Corresponds to the tar(1) file format. This handles uncompressed tar
+// archives by using the tar(1) command line tool.
+////
+struct TarArchive : public Archive {
+ TarArchive(const char* path);
+ virtual int extract(const char* destdir);
+};
+
+
+////
+// TarGZArchive
+//
+// Corresponds to the tar(1) file format, compressed with gzip(1).
+// This installs archives using the tar(1) command line tool with
+// the -z option.
+////
+struct TarGZArchive : public Archive {
+ TarGZArchive(const char* path);
+ virtual int extract(const char* destdir);
+};
+
+
+////
+// TarBZ2Archive
+//
+// Corresponds to the tar(1) file format, compressed with bzip2(1).
+// This installs archives using the tar(1) command line tool with
+// the -j option.
+////
+struct TarBZ2Archive : public Archive {
+ TarBZ2Archive(const char* path);
+ virtual int extract(const char* destdir);
+};
Property changes on: trunk/darwinup/Archive.h
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Depot.cpp
===================================================================
--- trunk/darwinup/Depot.cpp (rev 0)
+++ trunk/darwinup/Depot.cpp 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,1019 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Archive.h"
+#include "Depot.h"
+#include "File.h"
+#include "SerialSet.h"
+#include "Utils.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <sqlite3.h>
+
+Depot::Depot() {
+ m_depot_path = NULL;
+ m_database_path = NULL;
+ m_archives_path = NULL;
+ m_db = NULL;
+}
+
+Depot::Depot(const char* prefix) {
+ asprintf(&m_depot_path, "%s/.DarwinDepot", prefix);
+ asprintf(&m_database_path, "%s/Database-V100", m_depot_path);
+ asprintf(&m_archives_path, "%s/Archives", m_depot_path);
+
+ mkdir(m_depot_path, m_depot_mode);
+ mkdir(m_archives_path, m_depot_mode);
+
+ int exists = is_regular_file(m_database_path);
+
+ int res = sqlite3_open(m_database_path, &m_db);
+ if (res != 0) {
+ sqlite3_close(m_db);
+ m_db = NULL;
+ }
+
+ if (m_db && !exists) {
+ this->SQL("CREATE TABLE archives (serial INTEGER PRIMARY KEY AUTOINCREMENT, uuid BLOB UNIQUE, name TEXT, date_added INTEGER, active INTEGER, info INTEGER)");
+ this->SQL("CREATE TABLE files (serial INTEGER PRIMARY KEY AUTOINCREMENT, archive INTEGER, info INTEGER, mode INTEGER, uid INTEGER, gid INTEGER, size INTEGER, digest BLOB, path TEXT)");
+
+ this->SQL("CREATE INDEX archives_uuid ON archives (uuid)");
+ this->SQL("CREATE INDEX files_path ON files (path)");
+ }
+}
+
+Depot::~Depot() {
+ if (m_db) sqlite3_close(m_db);
+ if (m_depot_path) free(m_depot_path);
+ if (m_database_path) free(m_database_path);
+ if (m_archives_path) free(m_archives_path);
+}
+
+const char* Depot::archives_path() { return m_archives_path; }
+
+// Unserialize an archive from the database.
+// Find the archive by UUID.
+// XXX: should be memoized
+Archive* Depot::archive(uuid_t uuid) {
+ int res = 0;
+ Archive* archive = NULL;
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "SELECT serial, name, info, date_added FROM archives WHERE uuid=?";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ res = sqlite3_bind_blob(stmt, 1, uuid, sizeof(uuid_t), SQLITE_STATIC);
+ if (res == 0) res = sqlite3_step(stmt);
+ if (res == SQLITE_ROW) {
+ uint64_t serial = sqlite3_column_int64(stmt, 0);
+ const unsigned char* name = sqlite3_column_text(stmt, 1);
+ uint64_t info = sqlite3_column_int64(stmt, 2);
+ time_t date_added = sqlite3_column_int(stmt, 3);
+ archive = new Archive(serial, uuid, (const char*)name, NULL, info, date_added);
+ }
+ sqlite3_reset(stmt);
+ }
+ return archive;
+}
+
+// Unserialize an archive from the database.
+// Find the archive by serial.
+// XXX: should be memoized
+Archive* Depot::archive(uint64_t serial) {
+ int res = 0;
+ Archive* archive = NULL;
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "SELECT uuid, name, info, date_added FROM archives WHERE serial=?";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ res = sqlite3_bind_int64(stmt, 1, serial);
+ if (res == 0) res = sqlite3_step(stmt);
+ if (res == SQLITE_ROW) {
+ uuid_t uuid;
+ const void* blob = sqlite3_column_blob(stmt, 0);
+ int blobsize = sqlite3_column_bytes(stmt, 0);
+ if (blobsize > 0) {
+ assert(blobsize == sizeof(uuid_t));
+ memcpy(uuid, blob, sizeof(uuid_t));
+ } else {
+ uuid_clear(uuid);
+ }
+ const unsigned char* name = sqlite3_column_text(stmt, 1);
+ uint64_t info = sqlite3_column_int64(stmt, 2);
+ time_t date_added = sqlite3_column_int(stmt, 3);
+ archive = new Archive(serial, uuid, (const char*)name, NULL, info, date_added);
+ }
+ sqlite3_reset(stmt);
+ }
+ return archive;
+}
+
+Archive* Depot::archive(const char* uuid) {
+ uuid_t uu;
+ if (uuid_parse(uuid, uu) == 0) {
+ return Depot::archive(uu);
+ } else {
+ return NULL;
+ }
+}
+
+int Depot::iterate_archives(ArchiveIteratorFunc func, void* context) {
+ int res = 0;
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "SELECT serial, uuid, name, info, date_added FROM archives ORDER BY serial DESC";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ while (res == 0) {
+ res = sqlite3_step(stmt);
+ if (res == SQLITE_ROW) {
+ res = 0;
+ uuid_t uuid;
+ uint64_t serial = sqlite3_column_int64(stmt, 0);
+ const void* blob = sqlite3_column_blob(stmt, 1);
+ int blobsize = sqlite3_column_bytes(stmt, 1);
+ const unsigned char* name = sqlite3_column_text(stmt, 2);
+ uint64_t info = sqlite3_column_int64(stmt, 3);
+ time_t date_added = sqlite3_column_int(stmt, 4);
+ if (blobsize > 0) {
+ assert(blobsize == sizeof(uuid_t));
+ memcpy(uuid, blob, sizeof(uuid_t));
+ } else {
+ uuid_clear(uuid);
+ }
+ Archive* archive = new Archive(serial, uuid, (const char*)name, NULL, info, date_added);
+ if (archive) {
+ res = func(archive, context);
+ delete archive;
+ } else {
+ fprintf(stderr, "%s:%d: new Archive returned NULL\n", __FILE__, __LINE__);
+ res = -1;
+ break;
+ }
+ } else if (res == SQLITE_DONE) {
+ res = 0;
+ break;
+ }
+ }
+ sqlite3_reset(stmt);
+ }
+ return res;
+}
+
+int Depot::iterate_files(Archive* archive, FileIteratorFunc func, void* context) {
+ int res = 0;
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "SELECT serial, info, path, mode, uid, gid, size, digest FROM files WHERE archive=? ORDER BY path";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ res = sqlite3_bind_int64(stmt, 1, archive->serial());
+ while (res == 0) {
+ res = sqlite3_step(stmt);
+ if (res == SQLITE_ROW) {
+ res = 0;
+ int i = 0;
+ uint64_t serial = sqlite3_column_int64(stmt, i++);
+ uint32_t info = sqlite3_column_int(stmt, i++);
+ const unsigned char* path = sqlite3_column_text(stmt, i++);
+ mode_t mode = sqlite3_column_int(stmt, i++);
+ uid_t uid = sqlite3_column_int(stmt, i++);
+ gid_t gid = sqlite3_column_int(stmt, i++);
+ off_t size = sqlite3_column_int64(stmt, i++);
+ const void* blob = sqlite3_column_blob(stmt, i);
+ int blobsize = sqlite3_column_bytes(stmt, i++);
+
+ Digest* digest = NULL;
+ if (blobsize > 0) {
+ digest = new Digest();
+ digest->m_size = blobsize;
+ memcpy(digest->m_data, blob, (blobsize < sizeof(digest->m_data)) ? blobsize : sizeof(digest->m_data));
+ }
+
+ File* file = FileFactory(serial, archive, info, (const char*)path, mode, uid, gid, size, digest);
+ if (file) {
+ res = func(file, context);
+ delete file;
+ } else {
+ fprintf(stderr, "%s:%d: FileFactory returned NULL\n", __FILE__, __LINE__);
+ res = -1;
+ break;
+ }
+ } else if (res == SQLITE_DONE) {
+ res = 0;
+ break;
+ }
+ }
+ sqlite3_reset(stmt);
+ }
+ return res;
+}
+
+
+int Depot::analyze_stage(const char* path, Archive* archive, Archive* rollback, int* rollback_files) {
+ int res = 0;
+ assert(archive != NULL);
+ assert(rollback != NULL);
+ assert(rollback_files != NULL);
+
+ *rollback_files = 0;
+
+ const char* path_argv[] = { path, NULL };
+
+ FTS* fts = fts_open((char**)path_argv, FTS_PHYSICAL | FTS_COMFOLLOW | FTS_XDEV, fts_compare);
+ FTSENT* ent = fts_read(fts); // throw away the entry for path itself
+ while (res != -1 && (ent = fts_read(fts)) != NULL) {
+ File* file = FileFactory(archive, ent);
+ if (file) {
+ char state = '?';
+
+ IF_DEBUG("[analyze] %s\n", file->path());
+
+ // Perform a three-way-diff between the file to be installed (file),
+ // the file we last installed in this location (preceding),
+ // and the file that actually exists in this location (actual).
+
+ File* actual = FileFactory(file->path());
+ File* preceding = this->file_preceded_by(file);
+
+ if (actual == NULL) {
+ // No actual file exists already, so we create a placeholder.
+ actual = new NoEntry(file->path());
+ IF_DEBUG("[analyze] actual == NULL\n");
+ }
+
+ if (preceding == NULL) {
+ // Nothing is known about this file.
+ // We'll insert this file into the rollback archive as a
+ // base system file. Back up its data (if not a directory).
+ actual->info_set(FILE_INFO_BASE_SYSTEM);
+ IF_DEBUG("[analyze] base system\n");
+ if (!S_ISDIR(actual->mode()) && !INFO_TEST(actual->info(), FILE_INFO_NO_ENTRY)) {
+ IF_DEBUG("[analyze] needs base system backup, and installation\n");
+ actual->info_set(FILE_INFO_ROLLBACK_DATA);
+ file->info_set(FILE_INFO_INSTALL_DATA);
+ }
+ preceding = actual;
+ }
+
+ uint32_t actual_flags = File::compare(file, actual);
+ uint32_t preceding_flags = File::compare(actual, preceding);
+
+ // If file == actual && actual == preceding then nothing needs to be done.
+ if (actual_flags == FILE_INFO_IDENTICAL && preceding_flags == FILE_INFO_IDENTICAL) {
+ state = ' ';
+ IF_DEBUG("[analyze] no changes\n");
+ }
+
+ // If file != actual, but actual == preceding, then install file
+ // but we don't need to save actual, since it's already saved by preceding.
+ // i.e. no user changes since last installation
+ // If file != actual, and actual != preceding, then install file
+ // after saving actual in the rollback archive.
+ // i.e. user changes since last installation
+ if (actual_flags != FILE_INFO_IDENTICAL) {
+ if (INFO_TEST(actual->info(), FILE_INFO_NO_ENTRY)) {
+ state = 'A';
+ } else {
+ state = 'U';
+ }
+
+ if (INFO_TEST(actual_flags, FILE_INFO_TYPE_DIFFERS) ||
+ INFO_TEST(actual_flags, FILE_INFO_DATA_DIFFERS)) {
+ IF_DEBUG("[analyze] needs installation\n");
+ file->info_set(FILE_INFO_INSTALL_DATA);
+
+ if ((INFO_TEST(preceding_flags, FILE_INFO_TYPE_DIFFERS) ||
+ INFO_TEST(preceding_flags, FILE_INFO_DATA_DIFFERS)) &&
+ !INFO_TEST(actual->info(), FILE_INFO_NO_ENTRY)) {
+ IF_DEBUG("[analyze] needs user data backup\n");
+ actual->info_set(FILE_INFO_ROLLBACK_DATA);
+ }
+ }
+ }
+
+ // XXX: should this be done in backup_file?
+ // If we're going to need to squirrel away data, create
+ // the directory hierarchy now.
+ if (INFO_TEST(actual->info(), FILE_INFO_ROLLBACK_DATA)) {
+ const char* path = actual->path();
+ char* backup_dirpath;
+
+ const char* dir = dirname(path);
+ assert(dir != NULL);
+
+ char uuidstr[37];
+ uuid_unparse_upper(rollback->uuid(), uuidstr);
+
+ asprintf(&backup_dirpath, "%s/%s/%s", m_archives_path, uuidstr, dir);
+ assert(backup_dirpath != NULL);
+
+ res = mkdir_p(backup_dirpath);
+ if (res != 0 && errno != EEXIST) {
+ fprintf(stderr, "%s:%d: %s: %s (%d)\n", __FILE__, __LINE__, backup_dirpath, strerror(errno), errno);
+ } else {
+ res = 0;
+ }
+ }
+
+
+ if ((state != ' ' && preceding_flags != FILE_INFO_IDENTICAL) ||
+ INFO_TEST(actual->info(), FILE_INFO_BASE_SYSTEM | FILE_INFO_ROLLBACK_DATA)) {
+ *rollback_files += 1;
+ IF_DEBUG("[analyze] insert rollback\n");
+ res = this->insert(rollback, actual);
+ assert(res == 0);
+ }
+
+ fprintf(stderr, "%c %s\n", state, file->path());
+ res = this->insert(archive, file);
+ assert(res == 0);
+ if (preceding && preceding != actual) delete preceding;
+ if (actual) delete actual;
+ delete file;
+ }
+ }
+ if (fts) fts_close(fts);
+ return res;
+}
+
+
+
+struct InstallContext {
+ InstallContext(Depot* d, Archive* a) {
+ depot = d;
+ archive = a;
+ files_modified = 0;
+ files_added = 0;
+ files_removed = 0;
+ files_to_remove = new SerialSet();
+ }
+
+ ~InstallContext() {
+ delete files_to_remove;
+ }
+
+ Depot* depot;
+ Archive* archive;
+ uint64_t files_modified;
+ uint64_t files_added;
+ uint64_t files_removed;
+ SerialSet* files_to_remove; // for uninstall
+};
+
+int Depot::backup_file(File* file, void* ctx) {
+ InstallContext* context = (InstallContext*)ctx;
+ int res = 0;
+
+ if (INFO_TEST(file->info(), FILE_INFO_ROLLBACK_DATA)) {
+ char* dstpath;
+ char uuidstr[37];
+ uuid_unparse_upper(context->archive->uuid(), uuidstr);
+ asprintf(&dstpath, "%s/%s/%s", context->depot->m_archives_path, uuidstr, file->path());
+ assert(dstpath != NULL);
+
+ ++context->files_modified;
+
+ // XXX: res = file->backup()
+ IF_DEBUG("[backup] rename(%s, %s)\n", file->path(), dstpath);
+ res = rename(file->path(), dstpath);
+ if (res != 0) fprintf(stderr, "%s:%d: backup failed: %s: %s (%d)\n", __FILE__, __LINE__, dstpath, strerror(errno), errno);
+ free(dstpath);
+ }
+ return res;
+}
+
+
+int Depot::install_file(File* file, void* ctx) {
+ InstallContext* context = (InstallContext*)ctx;
+ int res = 0;
+
+ if (INFO_TEST(file->info(), FILE_INFO_INSTALL_DATA)) {
+ ++context->files_modified;
+
+ res = file->install(context->depot->m_archives_path);
+ } else {
+ res = file->install_info();
+ }
+ if (res != 0) fprintf(stderr, "%s:%d: install failed: %s: %s (%d)\n", __FILE__, __LINE__, file->path(), strerror(errno), errno);
+ return res;
+}
+
+
+int Depot::install(Archive* archive) {
+ int res = 0;
+ Archive* rollback = new RollbackArchive();
+
+ assert(rollback != NULL);
+ assert(archive != NULL);
+
+ // Check the consistency of the database before proceeding with the installation
+ // If this fails, abort the installation.
+// res = this->check_consistency();
+// if (res != 0) return res;
+
+
+ //
+ // The fun starts here
+ //
+ if (res == 0) res = this->begin_transaction();
+
+ //
+ // Insert the rollback archive before the new archive to install, thus keeping
+ // the chronology of the serial numbers correct. We may later choose to delete
+ // the rollback archive if we determine that it was not necessary.
+ //
+ if (res == 0) res = this->insert(rollback);
+ if (res == 0) res = this->insert(archive);
+
+ //
+ // Create the stage directory and rollback backing store directories
+ //
+ char* archive_path = archive->create_directory(m_archives_path);
+ assert(archive_path != NULL);
+ char* rollback_path = rollback->create_directory(m_archives_path);
+ assert(rollback_path != NULL);
+
+
+ // Extract the archive into its backing store directory
+ if (res == 0) res = archive->extract(archive_path);
+
+ // Analyze the files in the archive backing store directory
+ // Inserts new file records into the database for both the new archive being
+ // installed and the rollback archive.
+ int rollback_files = 0;
+ if (res == 0) res = this->analyze_stage(archive_path, archive, rollback, &rollback_files);
+
+ // If no files were added to the rollback archive, delete the rollback archive.
+ if (res == 0 && rollback_files == 0) {
+ res = this->remove(rollback);
+ }
+
+ // Commit the archive and its list of files to the database.
+ // Note that the archive's "active" flag is still not set.
+ if (res == 0) {
+ res = this->commit_transaction();
+ } else {
+ this->rollback_transaction();
+ }
+
+ // Save a copy of the backing store directory now, we will soon
+ // be moving the files into place.
+ if (res == 0) res = archive->compact_directory(m_archives_path);
+
+ //
+ // Move files from the root file system to the rollback archive's backing store,
+ // then move files from the archive backing directory to the root filesystem
+ //
+ InstallContext rollback_context(this, rollback);
+ if (res == 0) res = this->iterate_files(rollback, &Depot::backup_file, &rollback_context);
+
+ // compact the rollback archive (if we actually added any files)
+ if (rollback_context.files_modified > 0) {
+ if (res == 0) res = rollback->compact_directory(m_archives_path);
+ }
+
+ InstallContext install_context(this, archive);
+ if (res == 0) res = this->iterate_files(archive, &Depot::install_file, &install_context);
+
+ // Installation is complete. Activate the archive in the database.
+ if (res == 0) res = this->begin_transaction();
+ if (res == 0) res = SQL("UPDATE archives SET active=1 WHERE serial=%lld;", rollback->serial());
+ if (res == 0) res = SQL("UPDATE archives SET active=1 WHERE serial=%lld;", archive->serial());
+ if (res == 0) res = this->commit_transaction();
+
+ // Remove the stage and rollback directories (save disk space)
+ remove_directory(archive_path);
+ remove_directory(rollback_path);
+ if (rollback_path) free(rollback_path);
+ if (archive_path) free(archive_path);
+
+ return res;
+}
+
+// deletes expanded backing store directories in m_archives_path
+int Depot::prune_directories() {
+ int res = 0;
+
+ const char* path_argv[] = { m_archives_path, NULL };
+
+ FTS* fts = fts_open((char**)path_argv, FTS_PHYSICAL | FTS_COMFOLLOW | FTS_XDEV, fts_compare);
+ FTSENT* ent = fts_read(fts); // get the entry for m_archives_path itself
+ ent = fts_children(fts, 0);
+ while (res != -1 && ent != NULL) {
+ if (ent->fts_info == FTS_D) {
+ char path[PATH_MAX];
+ snprintf(path, PATH_MAX, "%s/%s", m_archives_path, ent->fts_name);
+ IF_DEBUG("pruning: %s\n", path);
+ res = remove_directory(path);
+ }
+ ent = ent->fts_link;
+ }
+ if (fts) fts_close(fts);
+ return res;
+}
+
+int Depot::prune_archives() {
+ int res = 0;
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "DELETE FROM archives WHERE serial IN (SELECT serial FROM archives WHERE serial NOT IN (SELECT DISTINCT archive FROM files));";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ if (res == 0) res = sqlite3_step(stmt);
+ if (res == SQLITE_DONE) {
+ res = 0;
+ } else {
+ fprintf(stderr, "%s:%d: Could not prune archives in database: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
+ }
+ sqlite3_reset(stmt);
+ }
+ return res;
+}
+
+int Depot::uninstall_file(File* file, void* ctx) {
+ InstallContext* context = (InstallContext*)ctx;
+ int res = 0;
+ char state = ' ';
+
+ IF_DEBUG("[uninstall] %s\n", file->path());
+
+ // We never uninstall a file that was part of the base system
+ if (INFO_TEST(file->info(), FILE_INFO_BASE_SYSTEM)) {
+ IF_DEBUG("[uninstall] base system; skipping\n");
+ return 0;
+ }
+
+ File* actual = FileFactory(file->path());
+ uint32_t flags = File::compare(file, actual);
+
+ if (actual != NULL && flags != FILE_INFO_IDENTICAL) {
+ // XXX: probably not the desired behavior
+ IF_DEBUG("[uninstall] changes since install; skipping\n");
+ } else {
+ File* superseded = context->depot->file_superseded_by(file);
+ if (superseded == NULL) {
+ // no one's using this file anymore
+ File* preceding = context->depot->file_preceded_by(file);
+ assert(preceding != NULL);
+ if (INFO_TEST(preceding->info(), FILE_INFO_NO_ENTRY)) {
+ state = 'R';
+ IF_DEBUG("[uninstall] removing file\n");
+ if (actual && res == 0) res = actual->remove();
+ } else {
+ // copy the preceding file back out to the system
+ // if it's different from what's already there
+ uint32_t flags = File::compare(file, preceding);
+ if (INFO_TEST(flags, FILE_INFO_DATA_DIFFERS)) {
+ state = 'U';
+ IF_DEBUG("[uninstall] restoring\n");
+ if (res == 0) res = preceding->install(context->depot->m_archives_path);
+ } else if (INFO_TEST(flags, FILE_INFO_MODE_DIFFERS) ||
+ INFO_TEST(flags, FILE_INFO_GID_DIFFERS) ||
+ INFO_TEST(flags, FILE_INFO_UID_DIFFERS)) {
+ if (res == 0) res = preceding->install_info();
+ } else {
+ IF_DEBUG("[uninstall] no changes; leaving in place\n");
+ }
+ }
+ uint32_t info = preceding->info();
+ if (INFO_TEST(info, FILE_INFO_NO_ENTRY | FILE_INFO_ROLLBACK_DATA) &&
+ !INFO_TEST(info, FILE_INFO_BASE_SYSTEM)) {
+ if (res == 0) res = context->files_to_remove->add(preceding->serial());
+ }
+ delete preceding;
+ } else {
+ IF_DEBUG("[uninstall] in use by newer installation; leaving in place\n");
+ delete superseded;
+ }
+ }
+
+ fprintf(stderr, "%c %s\n", state, file->path());
+
+ if (res != 0) fprintf(stderr, "%s:%d: uninstall failed: %s\n", __FILE__, __LINE__, file->path());
+ return res;
+}
+
+int Depot::uninstall(Archive* archive) {
+ int res = 0;
+
+ assert(archive != NULL);
+ uint64_t serial = archive->serial();
+
+ if (INFO_TEST(archive->info(), ARCHIVE_INFO_ROLLBACK)) {
+ fprintf(stderr, "%s:%d: cannot uninstall a rollback archive.\n", __FILE__, __LINE__);
+ return -1;
+ }
+
+// res = this->check_consistency();
+// if (res != 0) return res;
+
+ // XXX: this may be superfluous
+ // uninstall_file should be smart enough to do a mtime check...
+ if (res == 0) res = this->prune_directories();
+
+ // We do this here to get an exclusive lock on the database.
+ if (res == 0) res = this->begin_transaction();
+ if (res == 0) res = SQL("UPDATE archives SET active=0 WHERE serial=%lld;", serial);
+ if (res == 0) res = this->commit_transaction();
+
+ InstallContext context(this, archive);
+ if (res == 0) res = this->iterate_files(archive, &Depot::uninstall_file, &context);
+
+ if (res == 0) res = this->begin_transaction();
+ int i;
+ for (i = 0; i < context.files_to_remove->count; ++i) {
+ uint64_t serial = context.files_to_remove->values[i];
+ IF_DEBUG("deleting file %lld\n", serial);
+ if (res == 0) res = SQL("DELETE FROM files WHERE serial=%lld;", serial);
+ }
+ if (res == 0) res = this->commit_transaction();
+
+ if (res == 0) res = this->begin_transaction();
+ if (res == 0) res = this->remove(archive);
+ if (res == 0) res = this->commit_transaction();
+
+ // delete all of the expanded archive backing stores to save disk space
+ if (res == 0) res = this->prune_directories();
+
+ if (res == 0) res = prune_archives();
+
+ return res;
+}
+
+int Depot::verify_file(File* file, void* context) {
+ File* actual = FileFactory(file->path());
+ if (actual) {
+ uint32_t flags = File::compare(file, actual);
+
+ if (flags != FILE_INFO_IDENTICAL) {
+ fprintf(stdout, "M ");
+ } else {
+ fprintf(stdout, " ");
+ }
+ } else {
+ fprintf(stdout, "R ");
+ }
+ file->print(stdout);
+ return 0;
+}
+
+int Depot::verify(Archive* archive) {
+ int res = 0;
+ if (res == 0) res = this->iterate_files(archive, &Depot::verify_file, NULL);
+ return res;
+}
+
+int Depot::list_archive(Archive* archive, void* context) {
+ extern uint32_t verbosity;
+ char uuid[37];
+ uuid_unparse_upper(archive->uuid(), uuid);
+
+ char date[100];
+ struct tm local;
+ time_t seconds = archive->date_installed();
+ localtime_r(&seconds, &local);
+ strftime(date, sizeof(date), "%F %T %Z", &local);
+
+ if (!INFO_TEST(archive->info(), ARCHIVE_INFO_ROLLBACK) ||
+ (verbosity & VERBOSE_DEBUG)) {
+ fprintf((FILE*)context, "%-36s %-23s %s\n", uuid, date, archive->name());
+ }
+
+ return 0;
+}
+
+int Depot::list() {
+ int res = 0;
+ fprintf(stdout, "%-36s %-23s %s\n", "UUID", "Date Installed", "Name");
+ fprintf(stdout, "==================================== ======================= =================\n");
+ if (res == 0) res = this->iterate_archives(&Depot::list_archive, stdout);
+ return res;
+}
+
+int Depot::print_file(File* file, void* context) {
+ extern uint32_t verbosity;
+ if (verbosity & VERBOSE_DEBUG) fprintf((FILE*)context, "%04x ", file->info());
+ file->print((FILE*)context);
+ return 0;
+}
+
+int Depot::files(Archive* archive) {
+ int res = 0;
+ fprintf(stdout, "%-36s %-23s %s\n", "UUID", "Date Installed", "Name");
+ fprintf(stdout, "==================================== ======================= =================\n");
+ list_archive(archive, stdout);
+ fprintf(stdout, "================================================================================\n");
+ if (res == 0) res = this->iterate_files(archive, &Depot::print_file, stdout);
+ return res;
+}
+
+int Depot::dump_archive(Archive* archive, void* context) {
+ Depot* depot = (Depot*)context;
+ int res = 0;
+ list_archive(archive, stdout);
+ fprintf(stdout, "================================================================================\n");
+ if (res == 0) res = depot->iterate_files(archive, &Depot::print_file, stdout);
+ fprintf(stdout, "================================================================================\n\n\n");
+ return res;
+}
+
+int Depot::dump() {
+ extern uint32_t verbosity;
+ verbosity = 0xFFFFFFFF; // dump is intrinsically a debug command
+ int res = 0;
+ fprintf(stdout, "%-36s %-23s %s\n", "UUID", "Date Installed", "Name");
+ fprintf(stdout, "==================================== ======================= =================\n");
+ if (res == 0) res = this->iterate_archives(&Depot::dump_archive, this);
+ return res;
+}
+
+
+File* Depot::file_star_eded_by(File* file, sqlite3_stmt* stmt) {
+ assert(file != NULL);
+ assert(file->archive() != NULL);
+
+ File* result = NULL;
+ uint64_t serial = 0;
+ int res = 0;
+ if (stmt && res == 0) {
+ if (res == 0) res = sqlite3_bind_int64(stmt, 1, file->archive()->serial());
+ if (res == 0) res = sqlite3_bind_text(stmt, 2, file->path(), -1, SQLITE_STATIC);
+ if (res == 0) res = sqlite3_step(stmt);
+ switch (res) {
+ case SQLITE_DONE:
+ serial = 0;
+ break;
+ case SQLITE_ROW:
+ {
+ int i = 0;
+ uint64_t serial = sqlite3_column_int64(stmt, i++);
+ uint64_t archive_serial = sqlite3_column_int64(stmt, i++);
+ uint32_t info = sqlite3_column_int(stmt, i++);
+ const unsigned char* path = sqlite3_column_text(stmt, i++);
+ mode_t mode = sqlite3_column_int(stmt, i++);
+ uid_t uid = sqlite3_column_int(stmt, i++);
+ gid_t gid = sqlite3_column_int(stmt, i++);
+ off_t size = sqlite3_column_int64(stmt, i++);
+ const void* blob = sqlite3_column_blob(stmt, i);
+ int blobsize = sqlite3_column_bytes(stmt, i++);
+
+ Digest* digest = NULL;
+ if (blobsize > 0) {
+ digest = new Digest();
+ digest->m_size = blobsize;
+ memcpy(digest->m_data, blob, (blobsize < sizeof(digest->m_data)) ? blobsize : sizeof(digest->m_data));
+ }
+
+ Archive* archive = this->archive(archive_serial);
+
+ result = FileFactory(serial, archive, info, (const char*)path, mode, uid, gid, size, digest);
+ }
+ break;
+ default:
+ fprintf(stderr, "%s:%d: unexpected SQL error: %d\n", __FILE__, __LINE__, res);
+ break;
+ }
+ sqlite3_reset(stmt);
+ } else {
+ fprintf(stderr, "%s:%d: unexpected SQL error: %d\n", __FILE__, __LINE__, res);
+ }
+
+ return result;
+}
+
+File* Depot::file_superseded_by(File* file) {
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ // archive which installed this file immediately after
+ const char* query = "SELECT serial, archive, info, path, mode, uid, gid, size, digest FROM files WHERE archive>? AND path=? ORDER BY archive ASC LIMIT 1";
+ int res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ return this->file_star_eded_by(file, stmt);
+}
+
+File* Depot::file_preceded_by(File* file) {
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ // archive which installed this file immediately before
+ const char* query = "SELECT serial, archive, info, path, mode, uid, gid, size, digest FROM files WHERE archive<? AND path=? ORDER BY archive DESC LIMIT 1";
+ int res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ return this->file_star_eded_by(file, stmt);
+}
+
+int Depot::check_consistency() {
+ int res = 0;
+
+ SerialSet* inactive = new SerialSet();
+ assert(inactive != NULL);
+
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "SELECT serial FROM archives WHERE active=0 ORDER BY serial DESC";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ while (res == 0) {
+ res = sqlite3_step(stmt);
+ if (res == SQLITE_ROW) {
+ res = 0;
+ uint64_t serial = sqlite3_column_int64(stmt, 0);
+ inactive->add(serial);
+ } else if (res == SQLITE_DONE) {
+ res = 0;
+ break;
+ } else {
+ fprintf(stderr, "%s:%d: unexpected SQL error: %d\n", __FILE__, __LINE__, res);
+ }
+ }
+ sqlite3_reset(stmt);
+ }
+
+ if (res == 0 && inactive && inactive->count > 0) {
+ fprintf(stderr, "The following archive%s in an inconsistent state and must be uninstalled before proceeding:\n\n", inactive->count > 1 ? "s are" : " is");
+ uint32_t i;
+ fprintf(stderr, "%-36s %-23s %s\n", "UUID", "Date Installed", "Name");
+ fprintf(stderr, "==================================== ======================= =================\n");
+ for (i = 0; i < inactive->count; ++i) {
+ Archive* archive = this->archive(inactive->values[i]);
+ if (archive) {
+ list_archive(archive, stderr);
+ delete archive;
+ }
+ }
+ fprintf(stderr, "\nWould you like to uninstall %s now? [y/n] ", inactive->count > 1 ? "them" : "it");
+ int c = getchar();
+ fprintf(stderr, "\n");
+ if (c == 'y' || c == 'Y') {
+ for (i = 0; i < inactive->count; ++i) {
+ Archive* archive = this->archive(inactive->values[i]);
+ if (archive) {
+ res = this->uninstall(archive);
+ delete archive;
+ }
+ if (res != 0) break;
+ }
+ }
+ }
+
+ return res;
+}
+
+
+int Depot::begin_transaction() {
+ return this->SQL("BEGIN TRANSACTION");
+}
+
+int Depot::rollback_transaction() {
+ return this->SQL("ROLLBACK TRANSACTION");
+}
+
+int Depot::commit_transaction() {
+ return this->SQL("COMMIT TRANSACTION");
+}
+
+int Depot::insert(Archive* archive) {
+ // Don't insert an archive that is already in the database
+ assert(archive->serial() == 0);
+
+ int res = 0;
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "INSERT INTO archives (uuid, info, name, date_added) VALUES (?, ?, ?, ?)";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ int i = 1;
+ if (res == 0) res = sqlite3_bind_blob(stmt, i++, archive->uuid(), sizeof(uuid_t), SQLITE_STATIC);
+ if (res == 0) res = sqlite3_bind_int(stmt, i++, archive->info());
+ if (res == 0) res = sqlite3_bind_text(stmt, i++, archive->name(), -1, SQLITE_STATIC);
+ if (res == 0) res = sqlite3_bind_int(stmt, i++, archive->date_installed());
+ if (res == 0) res = sqlite3_step(stmt);
+ if (res == SQLITE_DONE) {
+ archive->m_serial = (uint64_t)sqlite3_last_insert_rowid(m_db);
+ res = 0;
+ } else {
+ fprintf(stderr, "%s:%d: Could not add archive to database: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
+ }
+ sqlite3_reset(stmt);
+ }
+ return res;
+}
+
+int Depot::insert(Archive* archive, File* file) {
+ int res = 0;
+ static sqlite3_stmt* stmt = NULL;
+ if (stmt == NULL && m_db) {
+ const char* query = "INSERT INTO files (archive, info, mode, uid, gid, digest, path) VALUES (?, ?, ?, ?, ?, ?, ?)";
+ res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
+ if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
+ }
+ if (stmt && res == 0) {
+ int i = 1;
+ if (res == 0) res = sqlite3_bind_int64(stmt, i++, archive->serial());
+ if (res == 0) res = sqlite3_bind_int(stmt, i++, file->info());
+ if (res == 0) res = sqlite3_bind_int(stmt, i++, file->mode());
+ if (res == 0) res = sqlite3_bind_int(stmt, i++, file->uid());
+ if (res == 0) res = sqlite3_bind_int(stmt, i++, file->gid());
+ Digest* dig = file->digest();
+ if (res == 0 && dig) res = sqlite3_bind_blob(stmt, i++, dig->data(), dig->size(), SQLITE_STATIC);
+ else if (res == 0) res = sqlite3_bind_blob(stmt, i++, NULL, 0, SQLITE_STATIC);
+ if (res == 0) res = sqlite3_bind_text(stmt, i++, file->path(), -1, SQLITE_STATIC);
+ if (res == 0) res = sqlite3_step(stmt);
+ if (res == SQLITE_DONE) {
+ file->m_serial = (uint64_t)sqlite3_last_insert_rowid(m_db);
+ res = 0;
+ } else {
+ fprintf(stderr, "%s:%d: Could not add file to database: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
+ }
+ sqlite3_reset(stmt);
+ }
+ return res;
+}
+
+int Depot::remove(Archive* archive) {
+ int res = 0;
+ uint64_t serial = archive->serial();
+ if (res == 0) res = SQL("DELETE FROM files WHERE archive=%lld", serial);
+ if (res == 0) res = SQL("DELETE FROM archives WHERE serial=%lld", serial);
+ return res;
+}
+
+int Depot::remove(File* file) {
+ int res = 0;
+ uint64_t serial = file->serial();
+ if (res == 0) res = SQL("DELETE FROM files WHERE serial=%lld", serial);
+ return res;
+}
+
+
+#define __SQL(callback, context, fmt) \
+ va_list args; \
+ char* errmsg; \
+ va_start(args, fmt); \
+ if (this->m_db) { \
+ char *query = sqlite3_vmprintf(fmt, args); \
+ res = sqlite3_exec(this->m_db, query, callback, context, &errmsg); \
+ if (res != SQLITE_OK) { \
+ fprintf(stderr, "Error: %s (%d)\n SQL: %s\n", errmsg, res, query); \
+ } \
+ sqlite3_free(query); \
+ } else { \
+ fprintf(stderr, "Error: database not open.\n"); \
+ res = SQLITE_ERROR; \
+ } \
+ va_end(args);
+
+int Depot::SQL(const char* fmt, ...) {
+ int res;
+ __SQL(NULL, NULL, fmt);
+ return res;
+}
+
+#undef __SQL
Property changes on: trunk/darwinup/Depot.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Depot.h
===================================================================
--- trunk/darwinup/Depot.h (rev 0)
+++ trunk/darwinup/Depot.h 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <uuid/uuid.h>
+#include <sqlite3.h>
+
+struct Archive;
+struct File;
+
+typedef int (*ArchiveIteratorFunc)(Archive* archive, void* context);
+typedef int (*FileIteratorFunc)(File* file, void* context);
+
+struct Depot {
+ Depot();
+ Depot(const char* prefix);
+
+ virtual ~Depot();
+
+ const char* database_path();
+ const char* archives_path();
+
+ virtual int begin_transaction();
+ virtual int commit_transaction();
+ virtual int rollback_transaction();
+
+ Archive* archive(uint64_t serial);
+ Archive* archive(uuid_t uuid);
+ Archive* archive(const char* uuid);
+
+ int dump();
+ static int dump_archive(Archive* archive, void* context);
+
+ int list();
+ static int list_archive(Archive* archive, void* context);
+
+ int install(Archive* archive);
+ static int install_file(File* file, void* context);
+ static int backup_file(File* file, void* context);
+
+ int uninstall(Archive* archive);
+ static int uninstall_file(File* file, void* context);
+
+ int verify(Archive* archive);
+ static int verify_file(File* file, void* context);
+
+ int files(Archive* archive);
+ static int print_file(File* file, void* context);
+
+ int iterate_files(Archive* archive, FileIteratorFunc func, void* context);
+ int iterate_archives(ArchiveIteratorFunc func, void* context);
+
+ protected:
+
+ // Inserts an Archive into the database.
+ // This modifies the Archive's serial number.
+ // If the Archive already has a serial number, it cannot be inserted.
+ int insert(Archive* archive);
+
+ // Inserts a File into the database, as part of the specified Archive.
+ // This modifies the File's serial number.
+ // This modifies the File's Archive pointer.
+ // If the File already has a serial number, it cannot be inserted.
+ int insert(Archive* archive, File* file);
+
+ // Removes an Archive from the database.
+ int remove(Archive* archive);
+
+ // Removes a File from the database.
+ int remove(File* file);
+
+ int analyze_stage(const char* path, Archive* archive, Archive* rollback, int* rollback_files);
+ int prune_directories();
+
+ // Removes all archive entries which have no corresponding files entries.
+ int prune_archives();
+
+ File* file_superseded_by(File* file);
+ File* file_preceded_by(File* file);
+ File* file_star_eded_by(File* file, sqlite3_stmt* stmt);
+
+ int check_consistency();
+
+
+ virtual int SQL(const char* fmt, ...);
+
+ sqlite3* m_db;
+ mode_t m_depot_mode;
+ char* m_depot_path;
+ char* m_database_path;
+ char* m_archives_path;
+};
Property changes on: trunk/darwinup/Depot.h
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Digest.cpp
===================================================================
--- trunk/darwinup/Digest.cpp (rev 0)
+++ trunk/darwinup/Digest.cpp 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Digest.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+// For SHA1DigestMachO
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+extern "C" {
+// <rdar://problem/4319807> redo_prebinding.h should use extern "C"
+//#include <mach-o/redo_prebinding.h> // from cctools_ofiles
+#include "redo_prebinding.h"
+}
+
+Digest::Digest() {
+ memset(m_data, 0, sizeof(m_data));
+ m_size = 0;
+}
+
+Digest::Digest(const EVP_MD* md, int fd) {
+ digest(md, fd);
+}
+
+void Digest::digest(const EVP_MD* md, int fd) {
+ EVP_MD_CTX ctx;
+ EVP_MD_CTX_init(&ctx);
+ EVP_DigestInit(&ctx, md);
+
+ int len;
+ const unsigned int blocklen = 8192;
+ static uint8_t* block = NULL;
+ if (block == NULL) {
+ block = (uint8_t*)malloc(blocklen);
+ }
+ while(1) {
+ len = read(fd, block, blocklen);
+ if (len == 0) { close(fd); break; }
+ if ((len < 0) && (errno == EINTR)) continue;
+ if (len < 0) { close(fd); return; }
+ EVP_DigestUpdate(&ctx, block, len);
+ }
+ if (len >= 0) {
+ EVP_DigestFinal(&ctx, m_data, &m_size);
+ }
+}
+
+Digest::Digest(const EVP_MD* md, uint8_t* data, uint32_t size) {
+ digest(md, data, size);
+}
+
+void Digest::digest(const EVP_MD* md, uint8_t* data, uint32_t size) {
+ EVP_MD_CTX ctx;
+ EVP_MD_CTX_init(&ctx);
+ EVP_DigestInit(&ctx, md);
+ EVP_DigestUpdate(&ctx, data, size);
+ EVP_DigestFinal(&ctx, m_data, &m_size);
+}
+
+uint8_t* Digest::data() { return m_data; }
+uint32_t Digest::size() { return m_size; }
+
+char* Digest::string() {
+ static const char* hexabet = "0123456789abcdef";
+ char* result = (char*)malloc(2*m_size+1);
+ int i, j;
+
+ for (i = 0, j = 0; i < m_size; ++i) {
+ result[j++] = hexabet[(m_data[i] & 0xF0) >> 4];
+ result[j++] = hexabet[(m_data[i] & 0x0F)];
+ }
+ result[j] = 0;
+
+ return result;
+}
+
+int Digest::equal(Digest* a, Digest* b) {
+ if (a == b) return 1;
+ if (a == NULL) return 0;
+ if (b == NULL) return 0;
+ int a_size = a->size();
+ if (a_size != b->size()) {
+ return 0;
+ }
+ return (memcmp(a->data(), b->data(), a_size) == 0);
+}
+
+
+const EVP_MD* SHA1Digest::m_md;
+
+SHA1Digest::SHA1Digest() {
+ if (m_md == NULL) {
+ OpenSSL_add_all_digests();
+ m_md = EVP_get_digestbyname("sha1");
+ assert(m_md != NULL);
+ }
+}
+
+SHA1Digest::SHA1Digest(int fd) {
+ if (m_md == NULL) {
+ OpenSSL_add_all_digests();
+ m_md = EVP_get_digestbyname("sha1");
+ assert(m_md != NULL);
+ }
+ digest(m_md, fd);
+}
+
+SHA1Digest::SHA1Digest(const char* filename) {
+ int fd = open(filename, O_RDONLY);
+ if (m_md == NULL) {
+ OpenSSL_add_all_digests();
+ m_md = EVP_get_digestbyname("sha1");
+ assert(m_md != NULL);
+ }
+ digest(m_md, fd);
+}
+
+SHA1Digest::SHA1Digest(uint8_t* data, uint32_t size) {
+ if (m_md == NULL) {
+ OpenSSL_add_all_digests();
+ m_md = EVP_get_digestbyname("sha1");
+ assert(m_md != NULL);
+ }
+ digest(m_md, data, size);
+}
+
+
+SHA1DigestMachO::SHA1DigestMachO(const char* filename) {
+ char* res = NULL;
+ char* error = NULL;
+
+ // Check for Mach-O
+ int type = object_file_type(filename, NULL, &error);
+ if (type == OFT_EXECUTABLE ||
+ type == OFT_DYLIB ||
+ type == OFT_BUNDLE) {
+ // XXX - type == OFT_ARCHIVE?
+ void* block = NULL;
+ unsigned long blocklen = 0;
+ int ret = unprebind(filename,
+ NULL,
+ NULL,
+ &error,
+ 1,
+ NULL,
+ 0,
+ &block,
+ &blocklen);
+ if (ret == REDO_PREBINDING_SUCCESS && block != NULL) {
+ digest(SHA1Digest::m_md, (uint8_t*)block, blocklen);
+ } else {
+ fprintf(stderr, "%s:%d: unexpected unprebind result %d: %s\n", __FILE__, __LINE__, ret, error);
+ }
+ if (block != NULL) {
+ kern_return_t ret = vm_deallocate(mach_task_self(), (vm_address_t)block, (vm_size_t)blocklen);
+ assert(ret == 0);
+ }
+ } else {
+ int fd = open(filename, O_RDONLY);
+ digest(SHA1Digest::m_md, fd);
+ close(fd);
+ }
+}
+
+SHA1DigestSymlink::SHA1DigestSymlink(const char* filename) {
+ char link[PATH_MAX];
+ int res = readlink(filename, link, PATH_MAX);
+ if (res == -1) {
+ fprintf(stderr, "%s:%d: readlink: %s: %s (%d)\n", __FILE__, __LINE__, filename, strerror(errno), errno);
+ } else {
+ digest(SHA1Digest::m_md, (uint8_t*)link, res);
+ }
+}
Property changes on: trunk/darwinup/Digest.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Digest.h
===================================================================
--- trunk/darwinup/Digest.h (rev 0)
+++ trunk/darwinup/Digest.h 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <openssl/evp.h>
+
+////
+// Digest
+//
+// Digest is the root class for all message digest algorithms
+// supported by darwinup.
+//
+// Conceptually it's an abstract class, although that
+// hasn't been formalized.
+//
+// SHA1Digest is the only concrete subclass. There are two
+// subclasses of SHA1Digest which add convenience functions
+// for digesting a canonicalized Mach-O binary, and the
+// target of a symlink obtained by readlink(2).
+//
+// NOTE: It might be more appropriate to use the CommonCrypto
+// implementation of these algorithms rather than the OpenSSL
+// implementation. However, CommonCrypto is only available on
+// Tiger.
+////
+
+struct Digest {
+ Digest();
+ Digest(const EVP_MD* md, int fd);
+ Digest(const EVP_MD* md, uint8_t* data, uint32_t size);
+
+ ////
+ // Accessor functions
+ ////
+
+ // Returns the raw digest.
+ virtual uint8_t* data();
+
+ // Returns the size of the raw digest.
+ virtual uint32_t size();
+
+ // Returns the digest as an ASCIZ string, represented in hexidecimal.
+ virtual char* string();
+
+ ////
+ // Class functions
+ ////
+
+ // Compares two digest objects for equality.
+ // Returns 1 if equal, 0 if not.
+ static int equal(Digest* a, Digest* b);
+
+
+ protected:
+
+ virtual void digest(const EVP_MD* md, int fd);
+ virtual void digest(const EVP_MD* md, uint8_t* data, uint32_t size);
+
+ uint8_t m_data[EVP_MAX_MD_SIZE];
+ uint32_t m_size;
+
+ friend struct Depot;
+};
+
+////
+// SHA1Digest
+////
+struct SHA1Digest : Digest {
+ static const EVP_MD* m_md;
+
+ // Creates an empty digest.
+ SHA1Digest();
+
+ // Computes the SHA-1 digest of data read from the stream.
+ SHA1Digest(int fd);
+
+ // Computes the SHA-1 digest of data in the file.
+ SHA1Digest(const char* filename);
+
+ // Computes the SHA-1 digest of the block of memory.
+ SHA1Digest(uint8_t* data, uint32_t size);
+};
+
+////
+// SHA1DigestMachO
+// Digests of canonicalized Mach-O file formats.
+////
+struct SHA1DigestMachO : SHA1Digest {
+ // Computes the SHA-1 digest of the data in the file.
+ // If the file is a Mach-O executable or dynamic library,
+ // the SHA-1 digest is computed from its canonical
+ // representation.
+ SHA1DigestMachO(const char* filename);
+};
+
+////
+// SHA1DigestSymlink
+// Digests of the target of a symlink.
+////
+struct SHA1DigestSymlink : SHA1Digest {
+ // Computes the SHA-1 digest of the target of the symlink.
+ // The target is obtained via readlink(2).
+ SHA1DigestSymlink(const char* filename);
+};
\ No newline at end of file
Property changes on: trunk/darwinup/Digest.h
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/File.cpp
===================================================================
--- trunk/darwinup/File.cpp (rev 0)
+++ trunk/darwinup/File.cpp 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Archive.h"
+#include "File.h"
+#include "Utils.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+File::File() {
+ m_serial = 0;
+ m_archive = NULL;
+ m_info = FILE_INFO_NONE;
+ m_path = NULL;
+ m_mode = 0;
+ m_uid = 0;
+ m_gid = 0;
+ m_size = 0;
+ m_digest = NULL;
+}
+
+File::File(const char* path) {
+ m_serial = 0;
+ m_archive = NULL;
+ m_info = FILE_INFO_NONE;
+ m_mode = 0;
+ m_uid = 0;
+ m_gid = 0;
+ m_size = 0;
+ m_digest = NULL;
+ if (path) m_path = strdup(path);
+}
+
+File::File(Archive* archive, FTSENT* ent) {
+ char path[PATH_MAX];
+ path[0] = 0;
+ ftsent_filename(ent, path, PATH_MAX);
+ m_path = strdup(path);
+ m_archive = archive;
+ m_info = FILE_INFO_NONE;
+ m_mode = ent->fts_statp->st_mode;
+ m_uid = ent->fts_statp->st_uid;
+ m_gid = ent->fts_statp->st_gid;
+ m_size = ent->fts_statp->st_size;
+
+ m_digest = NULL;
+}
+
+File::File(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest) {
+ m_serial = serial;
+ m_archive = archive;
+ m_info = info;
+ m_path = strdup(path);
+ m_mode = mode;
+ m_uid = uid;
+ m_gid = gid;
+ m_size = size;
+ m_digest = digest;
+}
+
+
+File::~File() {
+ if (m_path) free(m_path);
+ if (m_digest) delete m_digest;
+}
+
+uint64_t File::serial() { return m_serial; }
+Archive* File::archive() { return m_archive; }
+uint32_t File::info() { return m_info; }
+const char* File::path() { return m_path; }
+mode_t File::mode() { return m_mode; }
+uid_t File::uid() { return m_uid; }
+gid_t File::gid() { return m_gid; }
+off_t File::size() { return m_size; }
+Digest* File::digest() { return m_digest; }
+
+void File::info_set(uint32_t flag) { m_info = INFO_SET(m_info, flag); }
+void File::info_clr(uint32_t flag) { m_info = INFO_CLR(m_info, flag); }
+void File::archive(Archive* archive) { m_archive = archive; }
+
+uint32_t File::compare(File* a, File* b) {
+ if (a == b) return FILE_INFO_IDENTICAL; // identity
+ if (a == NULL) return 0xFFFFFFFF; // existent and nonexistent file are infinitely different
+ if (b == NULL) return 0xFFFFFFFF; // existent and nonexistent file are infinitely different
+
+ uint32_t result = FILE_INFO_IDENTICAL;
+ if (a->m_uid != b->m_uid) result |= FILE_INFO_UID_DIFFERS;
+ if (a->m_gid != b->m_gid) result |= FILE_INFO_GID_DIFFERS;
+ if (a->m_mode != b->m_mode) result |= FILE_INFO_MODE_DIFFERS;
+ if ((a->m_mode & S_IFMT) != (b->m_mode & S_IFMT)) result |= FILE_INFO_TYPE_DIFFERS;
+ if ((a->m_mode & ALLPERMS) != (b->m_mode & ALLPERMS)) result |= FILE_INFO_PERM_DIFFERS;
+ //if (a->m_size != b->m_size) result |= FILE_INFO_SIZE_DIFFERS;
+ if (Digest::equal(a->m_digest, b->m_digest) == 0) result |= FILE_INFO_DATA_DIFFERS;
+ return result;
+}
+
+
+void File::print(FILE* stream) {
+ char* dig = m_digest ? m_digest->string() : strdup("");
+
+ char mode_str[12];
+ strmode(m_mode, mode_str);
+
+ fprintf(stream, "%s % 4d % 4d % 40s %s\n", mode_str, m_uid, m_gid, dig, m_path);
+ free(dig);
+}
+
+int File::install(const char* prefix) {
+ int res = 0;
+ Archive* archive = this->archive();
+ assert(archive != NULL);
+ char* dirpath = archive->directory_name(prefix);
+
+ char srcpath[PATH_MAX];
+ const char* dstpath = this->path();
+ if (dirpath) {
+ ssize_t len = snprintf(srcpath, sizeof(srcpath), "%s/%s", dirpath, dstpath);
+ if (len > sizeof(srcpath)) {
+ fprintf(stderr, "ERROR: [install] path too long: %s/%s\n", dirpath, dstpath);
+ return -1;
+ }
+ res = rename(srcpath, dstpath);
+ if (res == -1) {
+ if (errno == ENOENT) {
+ // the file wasn't found, try to do on-demand
+ // expansion of the archive that contains it.
+ if (is_directory(dirpath) == 0) {
+ res = archive->expand_directory(prefix);
+ if (res == 0) res = this->install(prefix);
+ } else {
+ // archive was already expanded, so
+ // the file is truly missing (worry).
+ fprintf(stderr, "%s:%d: %s: %s (%d)\n", __FILE__, __LINE__, srcpath, strerror(errno), errno);
+ }
+ //} else if (errno == ENOTDIR) {
+ // a) some part of destination path does not exist
+ // b) from is a directory, but to is not
+ //} else if (errno == EISDIR) {
+ // to is a directory, but from is not
+ //} else if (errno == ENOTEMPTY) {
+ // to is a directory and is not empty
+ } else {
+ fprintf(stderr, "%s:%d: %s: %s (%d)\n", __FILE__, __LINE__, dstpath, strerror(errno), errno);
+ }
+ } else {
+ IF_DEBUG("[install] rename(%s, %s)\n", srcpath, dstpath);
+ }
+ free(dirpath);
+ } else {
+ res = -1;
+ }
+ return res;
+}
+
+int File::remove() {
+ // not implemented
+ fprintf(stderr, "%s:%d: call to abstract function File::remove\n", __FILE__, __LINE__);
+ return -1;
+}
+
+int File::install_info() {
+ int res = 0;
+ const char* path = this->path();
+ uid_t uid = this->uid();
+ gid_t gid = this->gid();
+ mode_t mode = this->mode() & ALLPERMS;
+
+ IF_DEBUG("[install] chown(%s, %d, %d)\n", path, uid, gid);
+ if (res == 0) res = chown(path, uid, gid);
+ IF_DEBUG("[install] chmod(%s, %04o)\n", path, mode);
+ if (res == 0) res = chmod(path, mode);
+
+ return res;
+}
+
+NoEntry::NoEntry(const char* path) : File(path) {
+ m_info = INFO_SET(m_info, FILE_INFO_NO_ENTRY);
+}
+
+NoEntry::NoEntry(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest) : File(serial, archive, info, path, mode, uid, gid, size, digest) {}
+
+Regular::Regular(Archive* archive, FTSENT* ent) : File(archive, ent) {
+ m_digest = new SHA1DigestMachO(ent->fts_accpath);
+}
+
+Regular::Regular(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest) : File(serial, archive, info, path, mode, uid, gid, size, digest) {
+ if (digest == NULL || serial == 0) {
+ m_digest = new SHA1DigestMachO(path);
+ }
+}
+
+int Regular::remove() {
+ int res = 0;
+ const char* path = this->path();
+ res = unlink(path);
+ if (res == -1 && errno == ENOENT) {
+ // We can safely ignore this because we were going to
+ // remove the file anyway
+ res = 0;
+ } else if (res != 0) {
+ fprintf(stderr, "%s:%d: %s: %s (%d)\n", __FILE__, __LINE__, m_path, strerror(errno), errno);
+ }
+ return res;
+}
+
+Symlink::Symlink(Archive* archive, FTSENT* ent) : File(archive, ent) {
+ m_digest = new SHA1DigestSymlink(ent->fts_accpath);
+}
+
+Symlink::Symlink(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest) : File(serial, archive, info, path, mode, uid, gid, size, digest) {
+ if (digest == NULL || serial == 0) {
+ m_digest = new SHA1DigestSymlink(path);
+ }
+}
+
+int Symlink::remove() {
+ int res = 0;
+ const char* path = this->path();
+ res = unlink(path);
+ if (res == -1 && errno == ENOENT) {
+ // We can safely ignore this because we were going to
+ // remove the file anyway
+ res = 0;
+ } else if (res == -1) {
+ fprintf(stderr, "%s:%d: %s (%d)\n", __FILE__, __LINE__, strerror(errno), errno);
+ }
+ return res;
+}
+
+int Symlink::install_info() {
+ int res = 0;
+ const char* path = this->path();
+ mode_t mode = this->mode() & ALLPERMS;
+ uid_t uid = this->uid();
+ gid_t gid = this->gid();
+ IF_DEBUG("[install] lchown(%d, %d)\n", uid, gid);
+ if (res == 0) res = lchown(path, uid, gid);
+ //IF_DEBUG("[install] lchmod(%o)\n", mode);
+ //if (res == 0) res = lchmod(path, mode);
+ return res;
+}
+
+Directory::Directory(Archive* archive, FTSENT* ent) : File(archive, ent) {}
+
+Directory::Directory(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest) : File(serial, archive, info, path, mode, uid, gid, size, digest) {};
+
+int Directory::install(const char* prefix) {
+ // We create a new directory instead of renaming the
+ // existing one, since that would move the entire
+ // sub-tree, and lead to a lot of ENOENT errors.
+ int res = 0;
+
+ const char* dstpath = this->path();
+ mode_t mode = this->mode() & ALLPERMS;
+ uid_t uid = this->uid();
+ gid_t gid = this->gid();
+
+ IF_DEBUG("[install] mkdir(%s, %04o)\n", dstpath, mode);
+ if (res == 0) res = mkdir(dstpath, mode);
+ if (res != 0) fprintf(stderr, "ERROR: %s:%d: %s: %s (%d)\n", __FILE__, __LINE__, dstpath, strerror(errno), errno);
+ if (res == 0) res = chown(dstpath, uid, gid);
+ if (res != 0) fprintf(stderr, "ERROR: %s:%d: %s: %s (%d)\n", __FILE__, __LINE__, dstpath, strerror(errno), errno);
+ return res;
+}
+
+int Directory::remove() {
+ int res = 0;
+ const char* path = this->path();
+ res = rmdir(path);
+ if (res == -1 && errno == ENOENT) {
+ // We can safely ignore this because we were going to
+ // remove the directory anyway
+ res = 0;
+ } else if (res == -1 && errno == ENOTEMPTY) {
+ res = remove_directory(path);
+ } else if (res == -1) {
+ fprintf(stderr, "%s:%d: %s (%d)\n", __FILE__, __LINE__, strerror(errno), errno);
+ }
+ return res;
+}
+
+
+File* FileFactory(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest) {
+ File* file = NULL;
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ file = new Directory(serial, archive, info, path, mode, uid, gid, size, digest);
+ break;
+ case S_IFREG:
+ file = new Regular(serial, archive, info, path, mode, uid, gid, size, digest);
+ break;
+ case S_IFLNK:
+ file = new Symlink(serial, archive, info, path, mode, uid, gid, size, digest);
+ break;
+ case 0:
+ if (INFO_TEST(info, FILE_INFO_NO_ENTRY)) {
+ file = new NoEntry(serial, archive, info, path, mode, uid, gid, size, digest);
+ break;
+ }
+ default:
+ fprintf(stderr, "%s:%d: unexpected file type %o\n", __FILE__, __LINE__, mode & S_IFMT);
+ break;
+ }
+ return file;
+}
+
+File* FileFactory(Archive* archive, FTSENT* ent) {
+ File* file = NULL;
+ switch (ent->fts_info) {
+ case FTS_D:
+ file = new Directory(archive, ent);
+ break;
+ case FTS_F:
+ file = new Regular(archive, ent);
+ break;
+ case FTS_SL:
+ case FTS_SLNONE:
+ file = new Symlink(archive, ent);
+ break;
+ case FTS_DP:
+ break;
+ case FTS_DEFAULT:
+ case FTS_DNR:
+ fprintf(stderr, "%s:%d: could not read directory. Run as root.\n", __FILE__, __LINE__, ent->fts_info);
+ break;
+ default:
+ fprintf(stderr, "%s:%d: unexpected fts_info type %d\n", __FILE__, __LINE__, ent->fts_info);
+ break;
+ }
+ return file;
+}
+
+File* FileFactory(const char* path) {
+ File* file = NULL;
+ struct stat sb;
+ int res = 0;
+
+ res = lstat(path, &sb);
+ if (res == -1 && errno == ENOENT) {
+ return NULL;
+ } else if (res == -1) {
+ fprintf(stderr, "%s:%d: %s: %s (%d)\n", __FILE__, __LINE__, path, strerror(errno), errno);
+ return NULL;
+ }
+
+ file = FileFactory(0, NULL, FILE_INFO_NONE, path, sb.st_mode, sb.st_uid, sb.st_gid, sb.st_size, NULL);
+ return file;
+}
\ No newline at end of file
Property changes on: trunk/darwinup/File.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/File.h
===================================================================
--- trunk/darwinup/File.h (rev 0)
+++ trunk/darwinup/File.h 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Digest.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fts.h>
+
+//
+// FILE_INFO flags stored in the database
+//
+const uint32_t FILE_INFO_NONE = 0x0000;
+const uint32_t FILE_INFO_BASE_SYSTEM = 0x0001; // file was part of base system, cannot uninstall
+const uint32_t FILE_INFO_NO_ENTRY = 0x0002; // placeholder in the database for non-existent file
+const uint32_t FILE_INFO_INSTALL_DATA = 0x0010; // actually install the file
+const uint32_t FILE_INFO_ROLLBACK_DATA = 0x0020; // file exists in rollback archive
+
+//
+// FILE_INFO flags returned by File::compare()
+//
+const uint32_t FILE_INFO_IDENTICAL = 0x00000000;
+
+const uint32_t FILE_INFO_GID_DIFFERS = 0x00100000;
+const uint32_t FILE_INFO_UID_DIFFERS = 0x00200000;
+
+const uint32_t FILE_INFO_MODE_DIFFERS = 0x01000000; // mode differs overall
+const uint32_t FILE_INFO_TYPE_DIFFERS = 0x02000000; // S_IFMT differs
+const uint32_t FILE_INFO_PERM_DIFFERS = 0x04000000; // ALLPERMS differs
+
+const uint32_t FILE_INFO_SIZE_DIFFERS = 0x10000000;
+const uint32_t FILE_INFO_DATA_DIFFERS = 0x20000000;
+
+
+struct Archive;
+struct File;
+
+////
+// File
+//
+// File is the root class for all filesystem objects.
+// Conceptually it's an abstract class, although that
+// hasn't been formalized.
+//
+// Concrete subclasses exist for specific file types:
+// Regular, Symlink, Directory, and NoEntry, indicating
+// that the given path does not exist.
+//
+// FileFactory functions exist to return the correct
+// concrete subclass for a given filesystem object.
+////
+
+File* FileFactory(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest);
+File* FileFactory(const char* path);
+File* FileFactory(Archive* archive, FTSENT* ent);
+
+
+struct File {
+ File();
+ File(File*);
+ File(const char* path);
+ File(Archive* archive, FTSENT* ent);
+ File(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest);
+ virtual ~File();
+
+ ////
+ // Public Accessor functions
+ ////
+
+ // Unique serial number for the file (used by database).
+ virtual uint64_t serial();
+
+ // FILE_INFO flags.
+ virtual uint32_t info();
+ virtual void info_set(uint32_t);
+ virtual void info_clr(uint32_t);
+
+ // Pointer to the Archive this file belongs to.
+ virtual Archive* archive();
+ virtual void archive(Archive* archive);
+
+ // Path of the file on disk (absolute path).
+ // Do not modify or free(3).
+ virtual const char* path();
+
+ // Mode of the file, including the file type.
+ virtual mode_t mode();
+
+ // Uid of the file.
+ virtual uid_t uid();
+
+ // Gid of the file.
+ virtual gid_t gid();
+
+ // Size of the file.
+ virtual off_t size();
+
+ // Digest of the file's data.
+ virtual Digest* digest();
+
+ ////
+ // Class functions
+ ////
+
+ // Compare two files, setting the appropriate
+ // FILE_INFO bits in the return value.
+ static uint32_t compare(File* a, File* b);
+
+ ////
+ // Member functions
+ ////
+
+ // Installs the file at the given prefix onto the
+ // root volume. i.e., for regular files:
+ // rename(prefix + this->archive()->uuid() + this->path(), this->path());
+ virtual int install(const char* prefix);
+
+ // Sets the mode, uid, and gid of the file on the
+ // root volume.
+ // XXX: rename as repair()?
+ virtual int install_info();
+
+ // Removes the file from the root volume.
+ virtual int remove();
+
+ // Prints one line to the output stream indicating
+ // the file mode, ownership, digest and name.
+ virtual void print(FILE* stream);
+
+ protected:
+
+ uint64_t m_serial;
+ uint32_t m_info;
+ Archive* m_archive;
+ char* m_path;
+ mode_t m_mode;
+ uid_t m_uid;
+ gid_t m_gid;
+ off_t m_size;
+ Digest* m_digest;
+
+ friend struct Depot;
+};
+
+
+////
+// Placeholder for rollback archives in the database.
+// Indicates that the given path had no entry at the time that
+// the archive was created.
+////
+
+struct NoEntry : File {
+ NoEntry(const char* path);
+ NoEntry(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest);
+};
+
+////
+// A regular file.
+// Digest is of the data fork of the file.
+// NOTE: Extended attributes are not detected or preserved.
+////
+struct Regular : File {
+ Regular(Archive* archive, FTSENT* ent);
+ Regular(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest);
+ virtual int remove();
+};
+
+////
+// A symbolic link.
+// Digest is of the target obtained via readlink(2).
+////
+struct Symlink : File {
+ Symlink(Archive* archive, FTSENT* ent);
+ Symlink(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest);
+ virtual int install_info();
+ virtual int remove();
+};
+
+////
+// A directory.
+// Digest is null.
+////
+struct Directory : File {
+ Directory(Archive* archive, FTSENT* ent);
+ Directory(uint64_t serial, Archive* archive, uint32_t info, const char* path, mode_t mode, uid_t uid, gid_t gid, off_t size, Digest* digest);
+ virtual int install(const char* prefix);
+ virtual int remove();
+};
Property changes on: trunk/darwinup/File.h
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Makefile
===================================================================
--- trunk/darwinup/Makefile (rev 0)
+++ trunk/darwinup/Makefile 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,19 @@
+all: darwinup
+
+CXXFLAGS=-g
+
+Archive.o: Archive.cpp Archive.h Depot.h File.h Utils.h
+Depot.o: Depot.cpp Archive.h Depot.h File.h Utils.h
+Digest.o: Digest.cpp Digest.h
+File.o: File.cpp Archive.h Digest.h File.h Utils.h
+Utils.o: Utils.h
+main.o: main.cpp Depot.h Utils.h
+
+# libredo.o is generated from cctools_ofiles
+
+darwinup: Archive.o Depot.o Digest.o File.o SerialSet.o Utils.o main.o \
+ libredo.o
+ g++ -lcrypto -lsqlite3 -o $@ $^
+
+clean:
+ rm -f darwinup Archive.o Depot.o Digest.o File.o SerialSet.o Utils.o main.o
Property changes on: trunk/darwinup/Makefile
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/NOTES
===================================================================
--- trunk/darwinup/NOTES (rev 0)
+++ trunk/darwinup/NOTES 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,110 @@
+darwinup
+01-Sep-2005
+Kevin Van Vechten <kevin at opendarwin.org>
+
+OVERVIEW
+========
+
+The Darwin Update utility provides a transaction-based mechanism to
+install software on the base system that modifies or supersedes existing
+system components. Darwin Update is not a "package manager," and does
+not specify any package format. Updates can be installed from a directory,
+a tar archive, or other formats.
+
+Darwin Update provides the following:
+
+ * Installation of Darwin components (usually built with DarwinBuild)
+ * Verification of components previously installed with Darwin Update
+ * Un-installation of components installed with Darwin Update
+
+It is a design goal of Darwin Update that all components installed by
+this utility can subsequently be uninstalled, leaving the system in its
+original state.
+
+
+THE DEPOT
+=========
+
+The Darwin Update utility creates a directory at the root level of the
+filesystem (/.DarwinDepot) where it stores its database of currently
+installed components, and archived copies of the components they superseded.
+This is known as the depot.
+
+The following files are present in the depot:
+
+/.DarwinDepot/Database-V100
+SQLite database containing information about all of the archives and files
+that have been installed with darwinbuild.
+
+/.DarwinDepot/Archives/
+If an archive has any data to be installed, it will have a corresponding entry
+in this directory. This is known as the backing-store of the archive.
+The backing-store is either in a compressed (.tar.bz2) or expanded (directory)
+state. The compressed state is always authoritative and the expanded state
+is pruned when darwinup exits. The backing store for each archive is named
+according to that archive's UUID in the database.
+
+
+OPERATIONS
+==========
+
+1. INSTALLATION
+
+When Darwin Update is used for installing a tar archive (for example), it
+will first create two entries in the database. One for the archive to be
+installed, and another for the "rollback" archive associated with this
+archive. The rollback archive is where all of the base system files and
+existing user data are preserved before the new archive is installed.
+
+A backing-store directory is created for both the new archive and the
+rollback archive. Darwin Update extracts the tar archive into the newly
+created backing store directory. This effectively pre-allocates space on
+the root volume for the installation.
+
+Next, the location is compared against the software currently installed.
+Records are inserted into the database for each file in the new archive.
+Additionally, records are also inserted into the rollback archive
+representing the initial state of the system. (Note, that if a new file
+is identical to a file that Darwin Update has previously installed, no
+rollback file will be added since the record of the previously installed
+file is sufficient).
+
+Once all the records have been committed to the database, each file that
+was added to the rollback archive is moved into the backing-store. At this
+point the rollback archive backing store directory is compacted into a
+.tar.bz2 archive.
+
+Finally, each new file is moved from the backing store to its location on
+the root filesystem.
+
+Installation is complete.
+
+2. UNINSTALLATION
+
+When Darwin Update is used for uninstalling an archive, it will iterate
+through all the files in that archive as recorded in the database.
+
+If another newer archive has installed a file in the same location, then
+no action is taken because the archive to be uninstalled no longer has a
+claim on that file. However, if there are no newer archives claiming this
+file, it will be uninstalled.
+
+When a file is uninstalled, Darwin Update searches the database for the
+previous version of the file, whether archived from the base system, or
+installed in an earlier archive. If the file is identical to the previous
+version, then no action is taken. However, if the previous version differs
+it will need to be restored.
+
+During restoration, if the file did not previously exist at all, it will be
+deleted. Otherwise, the rollback archive backing store is expanded, and the
+file is moved from teh rollback archive backing store onto the root filesystem.
+The previous file has been restored.
+
+At this point Darwin Update deletes the record of the previous file from the
+database.
+
+Once all files affected by the uninstalled archive have been deleted or
+restored, the archive and its list of files are deleted from the database.
+
+Uninstallation is complete.
+
Property changes on: trunk/darwinup/NOTES
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/SerialSet.cpp
===================================================================
--- trunk/darwinup/SerialSet.cpp (rev 0)
+++ trunk/darwinup/SerialSet.cpp 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "SerialSet.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+SerialSet::SerialSet() {
+ capacity = 0;
+ count = 0;
+ values = (uint64_t*)malloc(0);
+}
+
+SerialSet::~SerialSet() {
+ if (values) free(values);
+}
+
+int SerialSet::add(uint64_t value) {
+ // If the serial already exists in the set, then there's nothing to be done
+ uint32_t i;
+ for (i = 0; i < this->count; ++i) {
+ if (this->values[i] == value) {
+ return 0;
+ }
+ }
+
+ // Otherwise, append it to the end of the set
+ this->count++;
+ if (this->count > this->capacity) {
+ this->capacity += 10;
+ this->values = (uint64_t*)realloc(this->values, this->capacity * sizeof(uint64_t));
+ assert(this->values != NULL);
+ }
+ this->values[this->count-1] = value;
+
+ return 0;
+}
Property changes on: trunk/darwinup/SerialSet.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/SerialSet.h
===================================================================
--- trunk/darwinup/SerialSet.h (rev 0)
+++ trunk/darwinup/SerialSet.h 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+
+// a variably lengthed set of serial numbers from the database
+struct SerialSet {
+ SerialSet();
+ ~SerialSet();
+
+ int add(uint64_t value);
+
+ uint32_t capacity;
+ uint32_t count;
+ uint64_t* values;
+};
Property changes on: trunk/darwinup/SerialSet.h
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Utils.cpp
===================================================================
--- trunk/darwinup/Utils.cpp (rev 0)
+++ trunk/darwinup/Utils.cpp 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Utils.h"
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+extern char** environ;
+
+int fts_compare(const FTSENT **a, const FTSENT **b) {
+ return strcmp((*a)->fts_name, (*b)->fts_name);
+}
+
+int ftsent_filename(FTSENT* ent, char* filename, size_t bufsiz) {
+ if (ent == NULL) return 0;
+ if (ent->fts_level > 1) {
+ bufsiz = ftsent_filename(ent->fts_parent, filename, bufsiz);
+ }
+ strlcat(filename, "/", bufsiz);
+ bufsiz -= 1;
+ if (ent->fts_name) {
+ strlcat(filename, ent->fts_name, bufsiz);
+ bufsiz -= strlen(ent->fts_name);
+ }
+ return bufsiz;
+}
+
+int mkdir_p(const char* path) {
+ int res;
+
+ for (;;) {
+ // Use 0777, let the umask decide.
+ res = mkdir(path, 0777);
+
+ if (res != 0 && errno == ENOENT) {
+ char tmp[PATH_MAX];
+ strncpy(tmp, path, PATH_MAX);
+ char* slash = strrchr(tmp, '/');
+ if (slash) { *slash = 0; }
+ res = mkdir_p(tmp);
+ if (res != 0) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return res;
+}
+
+int remove_directory(const char* directory) {
+ int res = 0;
+ const char* path_argv[] = { directory, NULL };
+ FTS* fts = fts_open((char**)path_argv, FTS_PHYSICAL | FTS_COMFOLLOW | FTS_XDEV, fts_compare);
+ FTSENT* ent = fts_read(fts); // throw away the entry for the DSTROOT itself
+ while (res == 0 && (ent = fts_read(fts)) != NULL) {
+ switch (ent->fts_info) {
+ case FTS_D:
+ break;
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ case FTS_DEFAULT:
+ res = unlink(ent->fts_accpath);
+ break;
+ case FTS_DP:
+ res = rmdir(ent->fts_accpath);
+ break;
+ default:
+ fprintf(stderr, "%s:%d: unexpected fts_info type %d\n", __FILE__, __LINE__, ent->fts_info);
+ break;
+ }
+ }
+ fts_close(fts);
+ return res;
+}
+
+int is_directory(const char* path) {
+ struct stat sb;
+ int res = stat(path, &sb);
+ return (res == 0 && S_ISDIR(sb.st_mode));
+}
+
+int is_regular_file(const char* path) {
+ struct stat sb;
+ int res = stat(path, &sb);
+ return (res == 0 && S_ISREG(sb.st_mode));
+}
+
+int has_suffix(const char* str, const char* sfx) {
+ str = strstr(str, sfx);
+ return (str && strcmp(str, sfx) == 0);
+}
+
+int exec_with_args(const char** args) {
+ int res = 0;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ assert(pid != -1);
+ if (pid == 0) {
+ assert(execve(args[0], (char**)args, environ) != -1);
+ // NOT REACHED
+ }
+ do {
+ res = waitpid(pid, &status, 0);
+ } while (res == -1 && errno == EINTR);
+ if (res != -1) {
+ if (WIFEXITED(status)) {
+ res = WEXITSTATUS(status);
+ } else {
+ res = -1;
+ }
+ }
+ return res;
+}
Property changes on: trunk/darwinup/Utils.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/Utils.h
===================================================================
--- trunk/darwinup/Utils.h (rev 0)
+++ trunk/darwinup/Utils.h 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <fts.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+const uint32_t VERBOSE = 0x1;
+const uint32_t VERBOSE_DEBUG = 0x2;
+
+#define IF_DEBUG(...) do { extern uint32_t verbosity; if (verbosity & VERBOSE_DEBUG) fprintf(stderr, "DEBUG: " __VA_ARGS__); } while (0)
+
+int fts_compare(const FTSENT **a, const FTSENT **b);
+int ftsent_filename(FTSENT* ent, char* filename, size_t bufsiz);
+int mkdir_p(const char* path);
+int remove_directory(const char* path);
+int is_directory(const char* path);
+int is_regular_file(const char* path);
+int has_suffix(const char* str, const char* sfx);
+int exec_with_args(const char** args);
+
+inline int INFO_TEST(uint32_t word, uint32_t flag) { return ((word & flag) != 0); }
+inline int INFO_SET(uint32_t word, uint32_t flag) { return (word | flag); }
+inline int INFO_CLR(uint32_t word, uint32_t flag) { return (word & (~flag)); }
+
Property changes on: trunk/darwinup/Utils.h
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/libredo.o
===================================================================
(Binary files differ)
Property changes on: trunk/darwinup/libredo.o
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: trunk/darwinup/main.cpp
===================================================================
--- trunk/darwinup/main.cpp (rev 0)
+++ trunk/darwinup/main.cpp 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include "Archive.h"
+#include "Depot.h"
+#include "Utils.h"
+
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void usage(char* progname) {
+ char* pad = strdup(progname);
+ int i;
+ for (i = 0; i < strlen(pad); ++i) pad[i] = ' ';
+
+ fprintf(stderr, "usage: %s install <path>\n", progname);
+ fprintf(stderr, " %s list\n", pad, progname);
+ fprintf(stderr, " %s files <uuid>\n", pad, progname);
+ fprintf(stderr, " %s uninstall <uuid>\n", pad, progname);
+ fprintf(stderr, " %s verify <uuid>\n", pad, progname);
+ exit(1);
+}
+
+// our globals
+uint32_t verbosity;
+
+int main(int argc, char* argv[]) {
+ int res = 0;
+ Depot* depot = new Depot("/");
+
+ char* progname = strdup(basename(argv[0]));
+
+ int ch;
+ while ((ch = getopt(argc, argv, "v")) != -1) {
+ switch (ch) {
+ case 'v':
+ verbosity <<= 1;
+ verbosity |= VERBOSE;
+ break;
+ case '?':
+ default:
+ usage(progname);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 2 && strcmp(argv[0], "install") == 0) {
+ char uuid[37];
+ Archive* archive = ArchiveFactory(argv[1]);
+ if (archive) {
+ res = depot->install(archive);
+ if (res == 0) {
+ uuid_unparse_upper(archive->uuid(), uuid);
+ fprintf(stdout, "%s\n", uuid);
+ } else {
+ fprintf(stderr, "An error occurred.\n");
+ res = 1;
+ }
+ } else {
+ fprintf(stderr, "Archive not found: %s\n", argv[1]);
+ }
+ } else if (argc == 1 && strcmp(argv[0], "list") == 0) {
+ depot->list();
+ } else if (argc == 1 && strcmp(argv[0], "dump") == 0) {
+ depot->dump();
+ } else if (argc == 2 && strcmp(argv[0], "files") == 0) {
+ Archive* archive = depot->archive(argv[1]);
+ if (archive) {
+ res = depot->files(archive);
+ delete archive;
+ } else {
+ fprintf(stderr, "Archive not found: %s\n", argv[1]);
+ res = 1;
+ }
+ } else if (argc == 2 && strcmp(argv[0], "uninstall") == 0) {
+ Archive* archive = depot->archive(argv[1]);
+ if (archive) {
+ res = depot->uninstall(archive);
+ if (res != 0) {
+ fprintf(stderr, "An error occurred.\n");
+ res = 1;
+ }
+ delete archive;
+ } else {
+ fprintf(stderr, "Archive not found: %s\n", argv[1]);
+ res = 1;
+ }
+ } else if (argc == 2 && strcmp(argv[0], "verify") == 0) {
+ Archive* archive = depot->archive(argv[1]);
+ if (archive) {
+ res = depot->verify(archive);
+ if (res != 0) {
+ fprintf(stderr, "An error occurred.\n");
+ res = 1;
+ }
+ delete archive;
+ } else {
+ fprintf(stderr, "Archive not found: %s\n", argv[1]);
+ res = 1;
+ }
+ } else {
+ usage(progname);
+ }
+ exit(res);
+ return res;
+}
Property changes on: trunk/darwinup/main.cpp
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/darwinup/redo_prebinding.h
===================================================================
--- trunk/darwinup/redo_prebinding.h (rev 0)
+++ trunk/darwinup/redo_prebinding.h 2006-10-04 09:02:24 UTC (rev 294)
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#define REDO_PREBINDING_VERSION 2
+#include <mach/machine.h>
+/*
+ * For all APIs in this file the parameters program_name and error_message
+ * are used the same. For unrecoverable resource errors like being unable to
+ * allocate memory each API prints a message to stderr precede with program_name
+ * then calls exit(2) with the value EXIT_FAILURE. If an API is unsuccessful
+ * and if error_message pass to it is not NULL it is set to a malloc(3)'ed
+ * buffer with a NULL terminated string with the error message. For all APIs
+ * when they return they release all resources (memory, open file descriptors,
+ * etc).
+ *
+ * The file_name parameter for these APIs may be of the form "foo(bar)" which is
+ * NOT interpreted as an archive name and a member name in that archive. As
+ * these API deal with prebinding and prebound binaries ready for execution
+ * can't be in archives.
+ *
+ * If the executable_path parameter for these APIs is not NULL it is used for
+ * any dependent library has a path that starts with "@executable_path". Then
+ * "@executable_path" is replaced with executable_path.
+ *
+ * If the root_dir parameter is not NULL it is prepended to all the rooted
+ * dependent library paths.
+ */
+
+/*
+ * dependent_libs() takes a file_name of a binary and returns a malloc(3)'ed
+ * array of pointers (NULL terminated) to names (also malloc(3)'ed and '\0'
+ * terminated names) of all the dependent libraries for that binary (not
+ * recursive) for all of the architectures of that binary. If successful
+ * dependent_libs() returns a non NULL value (at minimum a pointer to one NULL
+ * pointer). If unsuccessful dependent_libs() returns NULL.
+ */
+extern
+char **
+dependent_libs(
+const char *file_name,
+const char *program_name,
+char **error_message);
+
+/*
+ * install_name() takes a file_name of a binary and returns a malloc(3)'ed
+ * pointer to a NULL terminated string containing the install_name value for
+ * the binary. If unsuccessful install_name() returns NULL. In particular,
+ * NULL is returned if the binary is not a dylib and there is no error_message
+ * set. If the all of the arch's are dylibs but all the install names don't
+ * match NULL is returned and a error_message is set. If some but not all of
+ * the archs are dylibs NULL is returned and a error_message is set.
+ */
+extern
+char *
+install_name(
+const char *file_name,
+const char *program_name,
+char **error_message);
+
+/* return values for redo_prebinding() */
+enum redo_prebinding_retval {
+ REDO_PREBINDING_SUCCESS,
+ REDO_PREBINDING_FAILURE,
+ /* the following can only be returned if the parameter only_if_needed set */
+ REDO_PREBINDING_NOT_NEEDED,
+ REDO_PREBINDING_NOT_PREBOUND,
+ REDO_PREBINDING_NEEDS_REBUILDING
+};
+
+/*
+ * redo_prebinding() takes a file_name of a binary and redoes the prebinding on
+ * it. If output_file is not NULL the update file is written to output_file,
+ * if not it is written to file_name. If redo_prebinding() is successful it
+ * returns REDO_PREBINDING_SUCCESS otherwise it returns REDO_PREBINDING_FAILURE.
+ * If the parameter allow_missing_architectures is zero and not all
+ * architectures can be updated it is not successful and nothing is done and
+ * this returns REDO_PREBINDING_FAILURE. If the parameter
+ * allow_missing_architectures is non-zero then only problems with missing
+ * architectures for the architecure of the cputype specified by
+ * allow_missing_architectures will cause this call to fail. Other
+ * architectures that could not be prebound due to missing architectures in
+ * depending libraries will not have their prebinding updated but will not
+ * cause this call to fail.
+ * If the slide_to_address parameter is non-zero and the binary is a
+ * dynamic library it is relocated to have that has its prefered address. If
+ * only_if_needed is non-zero the prebinding is checked first and only done if
+ * needed. The checking includes checking the prefered address against the
+ * slide_to_address value if it is non-zero. If only_if_needed is non-zero
+ * and the prebinding does not have to be redone REDO_PREBINDING_NOT_NEEDED is
+ * returned, if the binary is not prebound REDO_PREBINDING_NOT_PREBOUND is
+ * returned and if the new load commands do not fit in the binary and it needs
+ * to be rebuilt REDO_PREBINDING_NEEDS_REBUILDING is returned.
+ * If zero_out_prebind_checksum is non-zero then the cksum field of the
+ * LC_PREBIND_CKSUM load command (if any) is set to zero on output (this should
+ * always be set by B&I tools and never set by the update_prebinding(1)
+ * command).
+ * If throttle is non-NULL it points to a value of the maximum bytes per second
+ * to use for writting the output. If the value is ULONG_MAX then the actual
+ * bytes per second is returned indirectly through *throttle.
+ */
+extern
+enum redo_prebinding_retval
+redo_prebinding(
+const char *file_name,
+const char *executable_path,
+const char *root_dir,
+const char *output_file,
+const char *program_name,
+char **error_message,
+unsigned long slide_to_address,
+int only_if_needed,
+int zero_out_prebind_checksum,
+cpu_type_t allow_missing_architectures,
+unsigned long *throttle);
+
+
+/* return values for needs_redo_prebinding() */
+enum needs_redo_prebinding_retval {
+ PREBINDING_UPTODATE, /* a binary who's prebinding is up todate */
+ PREBINDING_OUTOFDATE, /* a binary who's prebinding is out of date */
+ NOT_PREBOUND, /* a binary, but not built prebound */
+ NOT_PREBINDABLE, /* not a binary or statically linked,
+ prebinding does not apply */
+ PREBINDING_UNKNOWN /* a binary who's prebinding can't be determined
+ because it is malformed, a library it depends
+ on is missing, etc. */
+};
+
+/*
+ * needs_redo_prebinding() takes a file_name and determines if it is a binary
+ * and if its prebinding is uptodate. It returns one of the return values
+ * above depending on the state of the binary and libraries. If the parameter
+ * allow_missing_architectures is zero then the value returned is based on the
+ * first architecture for fat files. If the parameter
+ * allow_missing_architectures is non-zero then the value returned is based on
+ * the cputype specified by allow_missing_architectures. If that architecture
+ * is not present then PREBINDING_UPTODATE is returned. If the parameter
+ * expected_address is not zero and the binary is a dynamic library then the
+ * library is checked to see if it is at the expected_address if not the
+ * prebinding is assumed to be out of date and PREBINDING_OUTOFDATE is returned.
+ */
+extern
+enum needs_redo_prebinding_retval
+needs_redo_prebinding(
+const char *file_name,
+const char *executable_path,
+const char *root_dir,
+const char *program_name,
+char **error_message,
+unsigned long expected_address,
+cpu_type_t allow_missing_architectures);
+
+
+/*
+ * unprebind() takes a file_name of a binary and resets or removes prebinding
+ * information from it. If inbuf is non-NULL, the memory pointed to by inbuf is
+ * used as the input file contents. Otherwise, the contents are loaded from
+ * the file at path file_name. Even if inbuf is non-NULL, a file_name
+ * parameter should be specified for error reporting. Similarly, if outbuf is
+ * non-NULL, upon return, outbuf will point to a buffer containing the
+ * unprebound binary and outlen will point to the length of the output buffer.
+ * This buffer is vm_allocate'd and therefore should be vm_deallocate'd when it
+ * is no longer needed. If outbuf is NULL, and output_file is not NULL the
+ * update file is written to output_file, if outbuf is NULL and output_file is
+ * NULL, it is written to file_name.
+ * If unprebind() is successful it returns REDO_PREBINDING_SUCCESS otherwise it
+ * returns REDO_PREBINDING_FAILURE If the binary is already unprebound (i.e. it
+ * has the MH_PREBINDABLE flag set) then REDO_PREBINDING_NOT_NEEDED is returned.
+ * If the binary is not prebound and not prebindable,
+ * REDO_PREBINDING_NOT_PREBOUND is returned. If zero_checksum is non-zero then
+ * the cksum field the LC_PREBIND_CKSUM load command (if any) is set to zero on
+ * output, otherwise it is left alone.
+ * Unprebinding slides dynamic libraries to address zero, resets prebound
+ * symbols to address zero and type undefined, resets symbol pointers, removes
+ * LC_PREBOUND_DYLIB commands, resets library timestamps, resets two-level hints
+ * and updates relocation entries if necessary. Unprebound binaries have
+ * the MH_PREBINDABLE flag set, but not MH_PREBOUND. It will also set the the
+ * MH_ALLMODSBOUND flag if all two-level libraries were used and all modules
+ * were found to be bound in the LC_PREBOUND_DYLIB commands.
+ * As unprebinding is intended to produce a canonical Mach-O
+ * binary, bundles and non-prebound executables and dylibs are acceptable
+ * as input. For these files, the unprebind operation will zero library
+ * time stamps and version numbers and zero entries in the two-level hints
+ * table. These files will not gain the MH_PREBINDABLE flag.
+ * All resulting binaries successfully processed by unprebind() will have
+ * the MH_CANONICAL flag.
+ */
+extern
+enum redo_prebinding_retval
+unprebind(
+const char *file_name,
+const char *output_file,
+const char *program_name,
+char **error_message,
+int zero_checksum,
+void *inbuf,
+unsigned long inlen,
+void **outbuf,
+unsigned long *outlen);
+
+enum object_file_type_retval {
+ OFT_OTHER,
+ OFT_EXECUTABLE,
+ OFT_DYLIB,
+ OFT_BUNDLE,
+ OFT_ARCHIVE,
+ OFT_INCONSISTENT,
+ OFT_FILE_ERROR
+};
+
+/*
+ * object_file_type() takes a file_name and determines what type of object
+ * file it is. If it is a fat file and the architectures are not of the same
+ * type then OFT_INCONSISTENT is returned. If the file_name can't be opened,
+ * read or malformed then OFT_FILE_ERROR is returned.
+ */
+extern
+enum object_file_type_retval
+object_file_type(
+const char *file_name,
+const char *program_name,
+char **error_message);
+
+struct prebind_cksum_arch {
+ cpu_type_t cputype; /* cpu specifier */
+ cpu_subtype_t cpusubtype; /* machine specifier */
+ unsigned long has_cksum; /* 1 if the arch as an LC_PREBIND_CKSUM */
+ unsigned long cksum; /* value of the cksum in LC_PREBIND_CKSUM */
+};
+
+/*
+ * get_prebind_cksums() takes a file_name that is a Mach-O file or fat file
+ * containing Mach-O files and returns a malloc(3)'ed array of
+ * prebind_cksum_arch structs indirectly through the cksums parameter. The
+ * number of prebind_cksum_arch structs is returned indirectly through the
+ * ncksums parameter. If successful it returns zero else it returns non-zero.
+ */
+extern
+int
+get_prebind_cksums(
+const char *file_name,
+struct prebind_cksum_arch **cksums,
+unsigned long *ncksums,
+const char *program_name,
+char **error_message);
Property changes on: trunk/darwinup/redo_prebinding.h
___________________________________________________________________
Name: svn:eol-style
+ native
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/darwinbuild-changes/attachments/20061004/ea0e85d7/attachment-0001.html
More information about the darwinbuild-changes
mailing list