+2010-04-04 Keith Marshall <keithmarshall@users.sourceforge.net>
+
+ Implement installer function; use per-package file manifests.
+
+ * src/pkgproc.h (pkgManifest): New class; declare it.
+ (pkgArchiveProcessor::origin): New protected member variable.
+ (pkgArchiveProcessor::sysroot_len): New protected member variable.
+ (pkgArchiveProcessor::installed): Type changed to `pkgManifest *'.
+ (pkgTarArchiveInstaller::Process): Declare specialisation hook.
+ (pkgTarArchiveInstaller::UpdateInstallationManifest): Disused member
+ function; remove redundant declaration from class definition.
+ (pkgInstall): New generalised installation function; declare it.
+ (pkgRegister): New function; declare it.
+
+ * src/pkginst.cpp: New file.
+ (pkgManifest, pkgInstall, pkgRegister): Implement them.
+
+ * src/pkgexec.cpp (pkgActionItem::Execute): Use `pkgInstall' in place
+ of stub formerly provided here.
+
+ * src/pkgkeys.h (manifest_key, reference_key): New keys; declare them.
+ * src/pkgkeys.c (manifest_key, reference_key): Implement them.
+
+ * src/tarproc.cpp: Code refactored.
+ (pkgTarArchiveProcessor::sysroot_len): Initialise it.
+ (pkgTarArchiveProcessor::origin): Initialise it.
+ (pkgTarArchiveProcessor::installed): Initialise; associate it with a
+ pkgManifest class entity, and populate this as appropriate.
+ (pkgTarArchiveProcessor::~pkgTarArchiveProcessor): Add explicit clean
+ up of linked `pkgManifest *' entity referenced by `installed'.
+ (pkgTarArchiveProcessor::Process) [TAR_ENTITY_TYPE_DIRECTORY]: Add
+ braces to demarcate localised inner scope of string scan for removal
+ of trailing directory name separators; (thanks to Cesar Strauss for
+ reporting invalid local pointer declaration error in GCC >= 4.4).
+ (pkgTarArchiveInstaller::UpdateInstallationManifest): Deleted.
+ (pkgTarArchiveInstaller::pkgTarArchiveInstaller): Factor out.
+ (pkgTarArchiveInstaller::ProcessDirectory): Likewise.
+ (pkgTarArchiveInstaller::ProcessDataStream): Likewise.
+ (commit_saved_entity): Likewise.
+
+ * src/tarinst.cpp: New file; it implements...
+ (pkgTarArchiveInstaller::Process): ...this new specialisation
+ of the original pkgTarArchiveProcessor::Process method...
+ (pkgTarArchiveInstaller::pkgTarArchiveInstaller): ...and this
+ refactored code, abstracted from src/tarproc.cpp and modified to
+ use the pkgManifest class for installation tracking.
+ (pkgTarArchiveInstaller::ProcessDirectory): Likewise.
+ (pkgTarArchiveInstaller::ProcessDataStream): Likewise.
+ (commit_saved_entity): Relocated from src/tarproc.cpp.
+
+ * Makefile.in (CORE_DLL_OBJECTS): Add...
+ (pkginst.OBJEXT, tarinst.OBJEXT): ...these new object files.
+
2010-03-31 Keith Marshall <keithmarshall@users.sourceforge.net>
Guard against NULL pointer dereferences in tinyxml code.
pkgbind.$(OBJEXT) pkginet.$(OBJEXT) pkgstrm.$(OBJEXT) pkgname.$(OBJEXT) \
pkgexec.$(OBJEXT) pkgfind.$(OBJEXT) pkginfo.$(OBJEXT) pkgspec.$(OBJEXT) \
sysroot.$(OBJEXT) pkghash.$(OBJEXT) pkgkeys.$(OBJEXT) pkgdeps.$(OBJEXT) \
- mkpath.$(OBJEXT) tarproc.$(OBJEXT) xmlfile.$(OBJEXT) keyword.$(OBJEXT) \
+ mkpath.$(OBJEXT) pkginst.$(OBJEXT) tarinst.$(OBJEXT) tarproc.$(OBJEXT) \
+ xmlfile.$(OBJEXT) keyword.$(OBJEXT) vercmp.$(OBJEXT) dmh.$(OBJEXT) \
tinyxml.$(OBJEXT) tinyxmlparser.$(OBJEXT) \
- tinystr.$(OBJEXT) tinyxmlerror.$(OBJEXT) \
- vercmp.$(OBJEXT) dmh.$(OBJEXT)
+ tinystr.$(OBJEXT) tinyxmlerror.$(OBJEXT)
all: pkginfo$(EXEEXT) mingw-get$(EXEEXT) mingw-get-0.dll
}
if( (current->flags & ACTION_INSTALL) == ACTION_INSTALL )
- {
- /* The selected package has been marked for installation, either explicitly,
+ /*
+ * The selected package has been marked for installation, either explicitly,
* or implicitly to complete a package upgrade.
- *
- * FIXME: Once more, this is a stub, to be extended to provide the working
- * installer implementation.
*/
- //dmh_printf( " installing %s\n", current->Selection()->GetPropVal( tarname_key, "<unknown>" ));
- if( current->Selection( to_remove ) == NULL )
- {
- pkgXmlNode *pkg = current->Selection();
- if( ! match_if_explicit( pkg->ArchiveName(), value_none ) )
- {
- pkgTarArchiveInstaller package( pkg );
- if( package.IsOk() )
- package.Process();
- }
- }
- else
- dmh_notify( DMH_ERROR,
- "package %s is already installed\n",
- current->Selection()->GetPropVal( tarname_key, "<unknown>" )
- );
- }
+ pkgInstall( current );
/* Proceed to next package with scheduled actions.
*/
--- /dev/null
+/*
+ * pkginst.cpp
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2010, MinGW Project
+ *
+ *
+ * Implementation of the primary package installation and package
+ * manifest recording methods.
+ *
+ *
+ * This is free software. Permission is granted to copy, modify and
+ * redistribute this software, under the provisions of the GNU General
+ * Public License, Version 3, (or, at your option, any later version),
+ * as published by the Free Software Foundation; see the file COPYING
+ * for licensing details.
+ *
+ * Note, in particular, that this software is provided "as is", in the
+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ * PARTICULAR PURPOSE. Under no circumstances will the author, or the
+ * MinGW Project, accept liability for any damages, however caused,
+ * arising from the use of this software.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dmh.h"
+
+#include "pkginfo.h"
+#include "pkgkeys.h"
+#include "pkgproc.h"
+
+EXTERN_C const char *hashed_name( int, const char *, const char * );
+
+pkgManifest::pkgManifest( const char *tag, const char *tarname )
+{
+ /* Construct an in-memory image for processing a package manifest.
+ *
+ * We begin by initialising this pair of reference pointers,
+ * assuming that this manifest may become invalid...
+ */
+ manifest = NULL;
+ inventory = NULL;
+
+ /* Then we check that a package tarname has been provided...
+ */
+ if( tarname != NULL )
+ {
+ /* ...in which case, we proceed to set up the reference data...
+ */
+ int retry = 0;
+ while( retry < 16 )
+ {
+ /* Generate a hashed signature for the package manifest
+ * record, and derive an associated database file path name
+ * from it. Note that this hash is returned in 'malloc'ed
+ * memory, which we must later free. Also note that there
+ * are eight possible hashes, to mitigate hash collision,
+ * each of which is denoted by retry modulo eight; we make
+ * an initial pass through the possible hashes, looking for
+ * an existing installation map for this sysroot, loading
+ * it immediately if we find it. Otherwise, we continue
+ * with a second cycle, (retry = 8..15), looking for the
+ * first generated hash with no associated file; we then
+ * use this to create a new installation record file.
+ */
+ pkgXmlDocument *chkfile;
+ const char *signame = hashed_name( retry++, manifest_key, tarname );
+ const char *sigfile = xmlfile( signame, NULL );
+
+ /* Check for an existing file associated with the hash value...
+ */
+ if( (chkfile = new pkgXmlDocument( sigfile ))->IsOk() )
+ {
+ /* ...such a file does exist, but we must still check
+ * that it relates to the specified package...
+ */
+ if( retry < 9 )
+ {
+ /* ...however, we only perform this check during the
+ * first pass through the possible hashes; (second time
+ * through, we are only interested in a hash which does
+ * not have an associated file; note that the first pass
+ * through is for retry = 0..7, but by the time we get
+ * to here we have already incremented 7 to become 8,
+ * hence the check for retry < 9).
+ */
+ pkgXmlNode *root, *rel;
+ const char *pkg_id, *pkg_tarname;
+ if( ((root = chkfile->GetRoot()) != NULL)
+ && ((root->IsElementOfType( tag )))
+ && ((pkg_id = root->GetPropVal( id_key, NULL )) != NULL)
+ && ((strcmp( pkg_id, signame ) == 0))
+ && ((rel = root->FindFirstAssociate( release_key )) != NULL)
+ && ((pkg_tarname = rel->GetPropVal( tarname_key, NULL )) != NULL)
+ && ((pkg_strcmp( pkg_tarname, tarname ))) )
+ {
+ /* This is the manifest file we require...
+ * assign it for return, and force an early exit
+ * from the retry loop.
+ */
+ manifest = chkfile;
+ retry = 16;
+ }
+ }
+ }
+
+ /* Once more noting the prior increment of retry, such
+ * that it has now become 8 for the hash generation with
+ * retry = 7...
+ */
+ else if( retry > 8 )
+ {
+ /* ...we have exhausted all possible hash references,
+ * finding no existing manifest for the specified package;
+ * immediately assign the current (unused) manifest name,
+ * and initialise its root element...
+ */
+ pkgXmlNode *root = new pkgXmlNode( tag );
+ (manifest = chkfile)->AddDeclaration( "1.0", "UTF-8", value_yes );
+ root->SetAttribute( id_key, signame );
+ manifest->SetRoot( root );
+
+ /* ...a container, to be filled later, for recording the
+ * data associated with the specific release of the package
+ * to which this manifest will refer...
+ */
+ pkgXmlNode *ref = new pkgXmlNode( release_key );
+ ref->SetAttribute( tarname_key, tarname );
+ root->AddChild( ref );
+
+ /* ...a further container, in which to record the sysroot
+ * associations for installed instances of this package...
+ */
+ ref = new pkgXmlNode( reference_key );
+ root->AddChild( ref );
+
+ /* ...and one in which to accumulate the content manifest.
+ */
+ inventory = new pkgXmlNode( manifest_key );
+ root->AddChild( inventory );
+
+ /* Finally, having constructed a skeletal manifest,
+ * force an immediate exit from the retry loop...
+ */
+ retry = 16;
+ }
+
+ /* Before abandoning our references to the current hash
+ * signature, and the path name for the associated XML file,
+ * free the memory allocated for them.
+ */
+ free( (void *)(sigfile) );
+ free( (void *)(signame) );
+
+ /* If we have not yet exhausted all possible hashed file names...
+ */
+ if( retry < 16 )
+ /*
+ * ...free the heap memory allocated for the current (unsuitable)
+ * association, so making its "chkfile" reference pointer available
+ * for the next trial, without incurring a memory leak.
+ */
+ delete chkfile;
+ }
+ }
+}
+
+void pkgManifest::BindSysRoot( pkgXmlNode *sysroot, const char *reference_tag )
+{
+ /* Identify the package associated with the current manifest as
+ * having been installed within the specified sysroot, by tagging
+ * with a manifest reference to the sysroot identification key.
+ */
+ const char *id;
+ if( ((id = sysroot->GetPropVal( id_key, NULL )) != NULL) && (*id != '\0') )
+ {
+ /* The specified sysroot has a non-NULL, non-blank key;
+ * check for a prior reference to the same key, within the
+ * "references" section of the manifest...
+ */
+ pkgXmlNode *map, *ref;
+ if( ((ref = manifest->GetRoot()) != NULL)
+ && ((ref->IsElementOfType( reference_tag )))
+ && ((map = ref->FindFirstAssociate( reference_key )) == NULL) )
+ {
+ /* This manifest doesn't yet have a "references" section;
+ * create and add one now...
+ */
+ map = new pkgXmlNode( reference_key );
+ ref->AddChild( map );
+ }
+
+ /* Examine any existing sysroot references within this manifest...
+ */
+ ref = map->FindFirstAssociate( sysroot_key );
+ while( (ref != NULL) && (strcmp( id, ref->GetPropVal( id_key, id )) != 0) )
+ /*
+ * ...progressing to the next reference, if any, until...
+ */
+ ref = ref->FindNextAssociate( sysroot_key );
+
+ /* ...we either matched the required sysroot key, or we
+ * ran out of existing references, without finding a match.
+ */
+ if( ref == NULL )
+ {
+ /* When no existing reference was found, add one.
+ */
+ ref = new pkgXmlNode( sysroot_key );
+ ref->SetAttribute( id_key, id );
+ map->AddChild( ref );
+ }
+ }
+}
+
+void pkgManifest::AddEntry( const char *key, const char *pathname )
+{
+ /* Method invoked by package installers, to add file or directory
+ * entries to the tracked inventory of package content.
+ *
+ * Tracking is enabled only if the manifest structure is valid,
+ * AND an inventory table has been allocated...
+ */
+ if( (this != NULL) && (inventory != NULL) )
+ {
+ /* ...in which case we allocate a new tracking record, with
+ * "dir" or "file" reference key as appropriate, fill it out
+ * with the associated path name attribute, and insert it in
+ * the inventory table.
+ */
+ pkgXmlNode *entry = new pkgXmlNode( key );
+ entry->SetAttribute( pathname_key, pathname );
+ inventory->AddChild( entry );
+ }
+}
+
+pkgManifest::~pkgManifest()
+{
+ /* Destructor for package manifest images; it releases
+ * the memory used while processing a manifest, after first
+ * committing the image to disk storage, or deleting such
+ * a disk image as appropriate.
+ */
+ pkgXmlNode *ref;
+ const char *sigfile;
+
+ /* First confirm that an identification signature has been
+ * assigned for this manifest...
+ */
+ if( ((manifest != NULL)) && ((ref = manifest->GetRoot()) != NULL)
+ && ((sigfile = ref->GetPropVal( id_key, NULL )) != NULL) )
+ {
+ /* ...and map this to a file system reference path name.
+ */
+ sigfile = xmlfile( sigfile );
+
+ /* Check if any current installation, as identified by
+ * its sysroot key, refers to this manifest...
+ */
+ if( ((ref = ref->FindFirstAssociate( reference_key )) != NULL)
+ && ((ref = ref->FindFirstAssociate( sysroot_key )) != NULL) )
+ /*
+ * ...and if so, commit this manifest to disk...
+ */
+ manifest->Save( sigfile );
+
+ else
+ /* ...otherwise, this manifest is defunct, so
+ * delete any current disk copy.
+ */
+ unlink( sigfile );
+
+ /* Release the memory used to identify the path name for
+ * the on-disk copy of this manifest...
+ */
+ free( (void *)(sigfile) );
+ }
+
+ /* ...and finally, expunge its in-memory image.
+ */
+ delete manifest;
+}
+
+static
+void record_dependencies( pkgXmlNode *origin, pkgXmlNode *list )
+{
+ /* Helper function to record dependency call-outs for the package
+ * which is specified by the XML descriptor reference at "origin",
+ * (which should nominally represent a "release" specification for
+ * the package); the call-out references are collected in the XML
+ * container referenced by "list".
+ */
+ if( (origin != NULL) && (list != NULL) )
+ {
+ /* Assume "origin" and "list" represent appropriate XML objects...
+ */
+ if( ! origin->IsElementOfType( package_key ) )
+ /*
+ * ...and walk back through the "origin" tree, until we locate
+ * the top level node in the "package" specification.
+ */
+ record_dependencies( origin->GetParent(), list );
+
+ /* As we unwind this recursive walk back search, we copy all
+ * "requires" elements, at each intervening level, from the top
+ * "package" node until we return to the original "release"...
+ */
+ pkgXmlNode *dep = origin->FindFirstAssociate( requires_key );
+ while( dep != NULL )
+ {
+ /* ...each as a simple clone within the "list" container,
+ * (and all at a single level within the "list")...
+ */
+ list->AddChild( dep->Clone() );
+
+ /* ...repeating for all specified "requires" elements
+ * at each level.
+ */
+ dep = dep->FindNextAssociate( requires_key );
+ }
+ }
+}
+
+EXTERN_C void pkgRegister
+( pkgXmlNode *sysroot, pkgXmlNode *origin, const char *tarname, const char *pkgfile )
+{
+ /* Search the installation records for the current sysroot...
+ */
+ const char *pkg_tarname = NULL;
+ pkgXmlNode *ref = sysroot->FindFirstAssociate( installed_key );
+ while( (ref != NULL)
+ && ((pkg_tarname = ref->GetPropVal( tarname_key, NULL )) != NULL)
+ && ! (pkg_strcmp( tarname, pkg_tarname )) )
+ /*
+ * ...continuing until we either run out of installation records,
+ * or we find an already existing reference to this package.
+ */
+ ref = ref->FindNextAssociate( installed_key );
+
+ /* When we didn't find an appropriate existing installation record,
+ * we instantiate a new one...
+ */
+ if( (ref == NULL) && ((ref = new pkgXmlNode( installed_key )) != NULL) )
+ {
+ /* Fill out its "tarname" attribution...
+ */
+ ref->SetAttribute( tarname_key, tarname );
+ if( pkgfile != tarname )
+ {
+ /* When the real package tarball name isn't identically
+ * the same as the canonical name, then we record the real
+ * file name too.
+ */
+ pkgXmlNode *dl = new pkgXmlNode( download_key );
+ dl->SetAttribute( tarname_key, pkgfile );
+ ref->AddChild( dl );
+ }
+
+ /* Record dependency call-outs for the installed package.
+ */
+ record_dependencies( origin, ref );
+
+ /* Set the 'modified' flag for, and attach the installation
+ * record to, the relevant sysroot record.
+ */
+ sysroot->SetAttribute( modified_key, value_yes );
+ sysroot->AddChild( ref );
+ }
+}
+
+EXTERN_C void pkgInstall( pkgActionItem *current )
+{
+ /* Common handler for all package installation tasks...
+ */
+ pkgXmlNode *pkg;
+ if( (pkg = current->Selection()) != NULL )
+ {
+ /* The current action item has a valid package association...
+ */
+ if( current->Selection( to_remove ) == NULL )
+ {
+ /* The selected package has either not yet been installed,
+ * or any prior installation has been removed in preparation
+ * for re-installation or upgrade.
+ */
+ const char *pkgfile, *tarname;
+ if( match_if_explicit( pkgfile = pkg->ArchiveName(), value_none )
+ && ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL) )
+ {
+ /* In this case, the selected package has no associated archive,
+ * (i.e. it is a "virtual" package); provided we can identify an
+ * associated "sysroot"...
+ */
+ pkgXmlNode *sysroot;
+ pkgSpecs lookup( tarname );
+ if( (sysroot = pkg->GetSysRoot( lookup.GetSubSystemName() )) != NULL )
+ /*
+ * ...the installation process becomes a simple matter of
+ * recording the state of this virtual package as "installed",
+ * in the sysroot manifest, and itemising its prerequisites.
+ */
+ pkgRegister( sysroot, pkg, tarname, pkgfile );
+ }
+ else
+ {
+ /* Here we have a "real" (physical) package to install;
+ * for the time being, we assume it is packaged in our
+ * standard "tar" archive format.
+ */
+ pkgTarArchiveInstaller install( pkg );
+ if( install.IsOk() )
+ install.Process();
+ }
+ }
+ else
+ /* There is a prior installation of the selected package, which
+ * prevents us from proceeding; diagnose and otherwise ignore...
+ */
+ dmh_notify( DMH_ERROR,
+ "package %s is already installed\n",
+ current->Selection()->GetPropVal( tarname_key, "<unknown>" )
+ );
+ }
+}
+
+/* $RCSfile$: end of file */
const char *issue_key = "issue";
const char *le_key = "le";
const char *lt_key = "lt";
+const char *manifest_key = "manifest";
const char *mirror_key = "mirror";
const char *modified_key = "modified";
const char *name_key = "name";
const char *package_list_key = "package-list";
const char *pathname_key = "path";
const char *profile_key = "profile";
+const char *reference_key = "references";
const char *release_key = "release";
const char *repository_key = "repository";
const char *requires_key = "requires";
EXTERN_C_DECL const char *issue_key;
EXTERN_C_DECL const char *le_key;
EXTERN_C_DECL const char *lt_key;
+EXTERN_C_DECL const char *manifest_key;
EXTERN_C_DECL const char *mirror_key;
EXTERN_C_DECL const char *modified_key;
EXTERN_C_DECL const char *name_key;
EXTERN_C_DECL const char *package_list_key;
EXTERN_C_DECL const char *pathname_key;
EXTERN_C_DECL const char *profile_key;
+EXTERN_C_DECL const char *reference_key;
EXTERN_C_DECL const char *release_key;
EXTERN_C_DECL const char *repository_key;
EXTERN_C_DECL const char *requires_key;
#include "pkgbase.h"
#include "pkgstrm.h"
+EXTERN_C void pkgInstall( pkgActionItem* );
+EXTERN_C void pkgRegister( pkgXmlNode*, pkgXmlNode*, const char*, const char* );
+
+class pkgManifest
+{
+ /* A wrapper around the XML document class, with specialised methods
+ * for management of the package installation manifest.
+ */
+ public:
+ pkgManifest( const char*, const char* );
+ ~pkgManifest();
+
+ void AddEntry( const char*, const char* );
+ void BindSysRoot( pkgXmlNode*, const char* );
+ void DetachSysRoot( pkgXmlNode* );
+
+ private:
+ pkgXmlDocument *manifest;
+ pkgXmlNode *inventory;
+};
+
class pkgArchiveProcessor
{
/* A minimal generic abstract base class, from which we derive
virtual int Process() = 0;
protected:
+ int sysroot_len;
+
/* Pointers to the sysroot management records and installation
* path template for a managed package; note that 'tarname' does
* not explicitly refer only to tar archives; it is simply the
* canonical name of the archive file, as recorded in the XML
* 'tarname' property of the package identifier record.
*/
- pkgXmlNode *sysroot;
- const char *sysroot_path;
- pkgXmlNode *installed;
- const char *tarname;
- const char *pkgfile;
+ pkgXmlNode *origin;
+ pkgXmlNode *sysroot;
+ const char *sysroot_path;
+ pkgManifest *installed;
+ const char *tarname;
+ const char *pkgfile;
};
/* Our standard package format specifies the use of tar archives;
pkgTarArchiveInstaller( pkgXmlNode* );
virtual ~pkgTarArchiveInstaller(){}
+ virtual int Process();
+
private:
/* Specialised implementations of the archive processing methods...
*/
virtual int ProcessDirectory( const char* );
virtual int ProcessDataStream( const char* );
- virtual void UpdateInstallationManifest( const char*, const char* );
};
class pkgTarArchiveUninstaller : public pkgTarArchiveProcessor
--- /dev/null
+/*
+ * tarinst.cpp
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2009, 2010, MinGW Project
+ *
+ *
+ * Implementation of package installer methods for installation of
+ * packages which are distributed in the form of tar archives.
+ *
+ *
+ * This is free software. Permission is granted to copy, modify and
+ * redistribute this software, under the provisions of the GNU General
+ * Public License, Version 3, (or, at your option, any later version),
+ * as published by the Free Software Foundation; see the file COPYING
+ * for licensing details.
+ *
+ * Note, in particular, that this software is provided "as is", in the
+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ * PARTICULAR PURPOSE. Under no circumstances will the author, or the
+ * MinGW Project, accept liability for any damages, however caused,
+ * arising from the use of this software.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "dmh.h"
+
+//#include "pkginfo.h"
+#include "pkgkeys.h"
+#include "pkgproc.h"
+
+static int commit_saved_entity( const char *pathname, time_t mtime )
+{
+ /* Helper to set the access and modification times for a file,
+ * after extraction from an archive, to match the specified "mtime";
+ * (typically "mtime" is as recorded within the archive).
+ */
+ struct utimbuf timestamp;
+
+ timestamp.actime = timestamp.modtime = mtime;
+ return utime( pathname, ×tamp );
+}
+
+pkgTarArchiveInstaller::
+pkgTarArchiveInstaller( pkgXmlNode *pkg ):pkgTarArchiveProcessor( pkg )
+{
+ /* Constructor: having successfully set up the pkgTarArchiveProcessor
+ * base class, we attach a pkgManifest to track the installation.
+ */
+ if( (tarname != NULL) && (sysroot != NULL) && stream->IsReady() )
+ installed = new pkgManifest( package_key, tarname );
+}
+
+int pkgTarArchiveInstaller::Process()
+{
+ /* Specialisation of the base class Process() method.
+ */
+ int status;
+ /* First, process the archive as for the base class...
+ */
+ if( (status = pkgTarArchiveProcessor::Process()) == 0 )
+ {
+ /* ...then, on successful completion...
+ *
+ * Update the package installation manifest, to record
+ * the installation in the current sysroot...
+ */
+ installed->BindSysRoot( sysroot, package_key );
+ pkgRegister( sysroot, origin, tarname, pkgfile );
+ }
+ return status;
+}
+
+int pkgTarArchiveInstaller::ProcessDirectory( const char *pathname )
+{
+ /* Create the directory infrastructure required to support
+ * a specific package installation.
+ */
+#if DEBUGLEVEL < 5
+ int status;
+
+ if( (status = mkdir_recursive( pathname, 0755 )) == 0 )
+ /*
+ * Either the specified directory already exists,
+ * or we just successfully created it; attach a reference
+ * in the installation manifest for the current package.
+ */
+ installed->AddEntry( dirname_key, pathname + sysroot_len );
+
+ else
+ /* A required subdirectory could not be created;
+ * diagnose this failure.
+ */
+ dmh_notify( DMH_ERROR, "cannot create directory `%s'\n", pathname );
+
+#else
+ /* Debugging stub...
+ *
+ * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
+ */
+ int status = 0;
+
+ dmh_printf(
+ "FIXME:ProcessDirectory<stub>:not executing: mkdir -p %s\n",
+ pathname
+ );
+# if DEBUGLEVEL > 8
+ installed->AddEntry( dirname_key, pathname + sysroot_len );
+# endif
+#endif
+ return status;
+}
+
+int pkgTarArchiveInstaller::ProcessDataStream( const char *pathname )
+{
+ /* Extract file data from the archive, and copy it to the
+ * associated target file stream, if any.
+ */
+#if DEBUGLEVEL < 5
+ int fd = set_output_stream( pathname, octval( header.field.mode ) );
+ int status = ProcessEntityData( fd );
+ if( fd >= 0 )
+ {
+ /* File stream was written; close it...
+ */
+ close( fd );
+ if( status == 0 )
+ {
+ /* ...and on successful completion, commit it and
+ * record it in the installation database.
+ */
+ commit_saved_entity( pathname, octval( header.field.mtime ) );
+ installed->AddEntry( filename_key, pathname + sysroot_len );
+ }
+
+ else
+ {
+ /* The target file was not successfully and completely
+ * written; discard it, and diagnose failure.
+ */
+ unlink( pathname );
+ dmh_notify( DMH_ERROR, "%s: extraction failed\n", pathname );
+ }
+ }
+ return status;
+
+#else
+ /* Debugging stub...
+ *
+ * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
+ */
+ dmh_printf(
+ "FIXME:ProcessDataStream<stub>:not extracting: %s\n",
+ pathname
+ );
+# if DEBUGLEVEL > 8
+ installed->AddEntry( filename_key, pathname + sysroot_len );
+# endif
+ return ProcessEntityData( -1 );
+#endif
+}
+
+/* $RCSfile$: end of file */
*
* First, we anticipate an invalid initialisation state...
*/
+ sysroot_len = 0;
+
sysroot = NULL;
sysroot_path = NULL;
+ installed = NULL;
stream = NULL;
/* The 'pkg' XML database entry must be non-NULL, must
* represent a package release, and must specify a canonical
* tarname to identify the package...
*/
- if( (pkg != NULL) && pkg->IsElementOfType( release_key )
- && ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL) )
+ if( ((origin = pkg) != NULL) && pkg->IsElementOfType( release_key )
+ && ((tarname = pkg->GetPropVal( tarname_key, NULL )) != NULL) )
{
/* When these pre-conditions are satisfied, we may proceed
* to identify and locate the sysroot record with which this
*/
const char *template_format = "%F%%/M/%%F";
char template_text[mkpath( NULL, template_format, prefix, NULL )];
- mkpath( template_text, template_format, prefix, NULL );
+ sysroot_len = mkpath( template_text, template_format, prefix, NULL ) - 6;
sysroot_path = strdup( template_text );
}
}
pkgTarArchiveProcessor::~pkgTarArchiveProcessor()
{
- /* Destructor must release the heap memory allocated in the
- * constructor, (by strdup), clean up the decompression filter
- * state, and close the archive data stream.
+ /* Destructor must release the heap memory allocated in
+ * the constructor, (by strdup and pkgManifest), clean up
+ * the decompression filter state, and close the archive
+ * data stream.
*/
free( (void *)(sysroot_path) );
+ delete installed;
delete stream;
}
* e.g. we may need to create a directory, or even a sequence
* of directories, to establish a location within the sysroot
* hierarchy...
- *
- * Note: Microsoft's implementation of stat() appears to choke
- * on directory path names with trailing slashes; thus, before
- * we invoke the directory processing routine, (which may need
- * to call stat(), to check if the specified directory already
- * exists), we remove any such trailing slashes.
*/
- char *p = pathname + sizeof( pathname ) - 1;
- while( (p > pathname) && ((*--p == '/') || (*p == '\\')) )
- *p = '\0';
+ { /* Note: Microsoft's implementation of stat() appears to choke
+ * on directory path names with trailing slashes; thus, before
+ * we invoke the directory processing routine, (which may need
+ * to call stat(), to check if the specified directory already
+ * exists), we remove any such trailing slashes.
+ */
+ char *p = pathname + sizeof( pathname ) - 1;
+ while( (p > pathname) && ((*--p == '/') || (*p == '\\')) )
+ *p = '\0';
+ }
/* We are now ready to process the directory path name entry...
*/
return status;
}
-/* Here, we implement the methods for installing software from
- * packages which are distributed in the form of tar archives.
- *
- */
-#include <utime.h>
-
-static int commit_saved_entity( const char *pathname, time_t mtime )
-{
- /* Helper to set the access and modification times for a file,
- * after extraction from an archive, to match the specified "mtime";
- * (typically "mtime" is as recorded within the archive).
- */
- struct utimbuf timestamp;
-
- timestamp.actime = timestamp.modtime = mtime;
- return utime( pathname, ×tamp );
-}
-
-pkgTarArchiveInstaller::
-pkgTarArchiveInstaller( pkgXmlNode *pkg ):pkgTarArchiveProcessor( pkg )
-{
- /* Constructor: having set up the pkgTarArchiveProcessor base class,
- * we add a package installation record to the sysroot entry in the
- * XML database, and mark that sysroot entry as 'modified'.
- */
- if( (tarname != NULL) && (sysroot != NULL) && stream->IsReady() )
- {
- /* The installation record must identify, as a minimum,
- * the canonical name of the package being installed.
- */
- installed = new pkgXmlNode( installed_key );
- installed->SetAttribute( tarname_key, tarname );
- if( pkgfile != tarname )
- {
- /* If the real package tarball name isn't identically
- * the same as the canonical name, we record the real
- * file name too.
- */
- pkgXmlNode *download = new pkgXmlNode( download_key );
- download->SetAttribute( tarname_key, pkgfile );
- installed->AddChild( download );
- }
-
- /* Set the 'modified' flag for, and attach the installation
- * record to, the relevant sysroot record.
- *
- * FIXME: We should defer this until AFTER the archive has
- * been successfully processed, (in the destructor, perhaps?),
- * cleaning up, and not updating the installation manifest,
- * in the event of an archive processing failure.
- */
- sysroot->SetAttribute( modified_key, yes_value );
- sysroot->AddChild( installed );
- }
-}
-
-int pkgTarArchiveInstaller::ProcessDirectory( const char *pathname )
-{
- /* Create the directory infrastructure required to support
- * a specific package installation.
- */
-#if DEBUGLEVEL < 5
- int status;
-
- if( (status = mkdir_recursive( pathname, 0755 )) == 0 )
- /*
- * Either the specified directory already exists,
- * or we just successfully created it; attach a reference
- * in the installation manifest for the current package.
- */
- UpdateInstallationManifest( dirname_key, pathname );
-
- else
- /* A required subdirectory could not be created;
- * diagnose this failure.
- */
- dmh_notify( DMH_ERROR, "cannot create directory `%s'\n", pathname );
-
-#else
- /* Debugging stub...
- *
- * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
- */
- int status = 0;
-
- dmh_printf(
- "FIXME:ProcessDirectory<stub>:not executing: mkdir -p %s\n",
- pathname
- );
-#endif
- return status;
-}
-
-int pkgTarArchiveInstaller::ProcessDataStream( const char *pathname )
-{
- /* Extract file data from the archive, and copy it to the
- * associated target file stream, if any.
- */
-#if DEBUGLEVEL < 5
- int fd = set_output_stream( pathname, octval( header.field.mode ) );
- int status = ProcessEntityData( fd );
- if( fd >= 0 )
- {
- /* File stream was written; close it...
- */
- close( fd );
- if( status == 0 )
- {
- /* ...and on successful completion, commit it and
- * record it in the installation database.
- */
- commit_saved_entity( pathname, octval( header.field.mtime ) );
- UpdateInstallationManifest( filename_key, pathname );
- }
-
- else
- {
- /* The target file was not successfully and completely
- * written; discard it, and diagnose failure.
- */
- unlink( pathname );
- dmh_notify( DMH_ERROR, "%s: extraction failed\n", pathname );
- }
- }
- return status;
-
-#else
- /* Debugging stub...
- *
- * FIXME:maybe adapt for 'dry-run' or 'verbose' use.
- */
- dmh_printf(
- "FIXME:ProcessDataStream<stub>:not extracting: %s\n",
- pathname
- );
- return ProcessEntityData( -1 );
-#endif
-}
-
-void pkgTarArchiveInstaller::
-UpdateInstallationManifest( const char *key, const char *pathname )
-{
- /* Write installation database records for directory
- * and file entities created during archive extraction.
- */
- pkgXmlNode *dir = new pkgXmlNode( key );
- dir->SetAttribute( pathname_key, pathname );
- installed->AddChild( dir );
-}
-
/* $RCSfile$: end of file */