...
 
Commits (1)
  • Leigh Stoller's avatar
    Directory based image paths. · bf12ce88
    Leigh Stoller authored
    Soon, we will have images with both full images and deltas, for the same
    image version. To make this possible, the image path will now be a
    directory instead of a file, and all of the versions (ndz,sig,sha1,delta)
    files will reside in the directory.
    
    A new config variable IMAGEDIRECTORIES turns this on, there is also a check
    for the ImageDiretories feature. This is applied only when a brand new
    image is created; a clone version of the image inherits the path it started
    with. Yes, you can have a mix of directory based and file based image
    descriptors.
    
    When it is time to convert all images over, there is a script called
    imagetodir that will go through all image descriptors, create the
    directory, move/rename all the files, and update the descriptors.
    Ultimately, we will not support file based image paths.
    
    I also added versioning to the image metadata descriptors so that going
    forward, old clients can handle a descriptor from a new server.
    bf12ce88
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2012 University of Utah and the Flux Group.
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -490,7 +490,14 @@ elsif (! $isadmin) {
UserError("Path: Invalid Path");
}
}
if ($newimageid_args{"path"} =~ /\/$/) {
if (-e $newimageid_args{"path"} && ! -d $newimageid_args{"path"}) {
UserError("Path: invalid path, it should be a directory");
}
}
elsif (-d $newimageid_args{"path"} =~ /\/$/) {
UserError("Path: invalid path, its a directory");
}
#
# See what node types this image will work on. Must be at least one!
#
......
......@@ -541,7 +541,12 @@ elsif (! $isadmin) {
UserError("Path: Invalid Path");
}
}
if (-d $newimageid_args{"path"}) {
if ($newimageid_args{"path"} =~ /\/$/) {
if (-e $newimageid_args{"path"} && ! -d $newimageid_args{"path"}) {
UserError("Path: invalid path, it should be a directory");
}
}
elsif (-d $newimageid_args{"path"} =~ /\/$/) {
UserError("Path: invalid path, its a directory");
}
......
......@@ -670,6 +670,7 @@ NOVIRTNFSMOUNTS
PROFILEVERSIONS
IMAGEDELTAS
IMAGEPROVENANCE
IMAGEDIRECTORIES
NFSMAPTOUSER
IPV6_SUBNET_PREFIX
IPV6_ENABLED
......@@ -5131,6 +5132,7 @@ MANAGEMENT_NETMASK="255.255.255.0"
MANAGEMENT_ROUTER="10.249.249.253"
NFSMAPTOUSER="root"
IMAGEPROVENANCE=0
IMAGEDIRECTORIES=0
IMAGEDELTAS=0
PROFILEVERSIONS=0
NOVIRTNFSMOUNTS=0
......
......@@ -297,6 +297,7 @@ AC_SUBST(IPV6_ENABLED)
AC_SUBST(IPV6_SUBNET_PREFIX)
AC_SUBST(NFSMAPTOUSER)
AC_SUBST(IMAGEPROVENANCE)
AC_SUBST(IMAGEDIRECTORIES)
AC_SUBST(IMAGEDELTAS)
AC_SUBST(PROFILEVERSIONS)
AC_SUBST(NOVIRTNFSMOUNTS)
......@@ -457,6 +458,7 @@ MANAGEMENT_NETMASK="255.255.255.0"
MANAGEMENT_ROUTER="10.249.249.253"
NFSMAPTOUSER="root"
IMAGEPROVENANCE=0
IMAGEDIRECTORIES=0
IMAGEDELTAS=0
PROFILEVERSIONS=0
NOVIRTNFSMOUNTS=0
......
......@@ -37,6 +37,7 @@ use EmulabConstants;
use libtestbed;
use English;
use Data::Dumper;
use File::Basename;
use overload ('""' => 'Stringify');
# Configure variables
......@@ -86,6 +87,7 @@ sub BlessRow($$)
my $self = {};
my $imageid = $row->{"imageid"};
$self->{'IMAGE'} = $row;
$self->{'HASH'} = {};
bless($self, $class);
return $self;
......@@ -259,8 +261,21 @@ AUTOLOAD {
# A DB row proxy method call.
if (exists($self->{'IMAGE'}->{$name})) {
# Allow update.
if (scalar(@_) == 2) {
$self->{'IMAGE'}->{$name} = $_[1];
}
return $self->{'IMAGE'}->{$name};
}
# Or it is for a local storage slot.
if ($name =~ /^_.*$/) {
if (scalar(@_) == 2) {
return $self->{'HASH'}->{$name} = $_[1];
}
elsif (exists($self->{'HASH'}->{$name})) {
return $self->{'HASH'}->{$name};
}
}
carp("No such slot '$name' field in class $type");
return undef;
}
......@@ -270,6 +285,7 @@ sub DESTROY {
my $self = shift;
$self->{'IMAGE'} = undef;
$self->{'HASH'} = undef;
}
#
......@@ -781,12 +797,14 @@ sub NewVersion($$$$)
# Fix up the path by appending the version number.
#
my $path = $self->path();
if (0) {
if ($path =~ /^(.*):\d+$/) {
$path = $1 . ":${clone_vers}";
}
else {
$path .= ":${clone_vers}";
}
}
if (!$isdataset) {
DBQueryWarn("update $ostablename set ".
......@@ -1566,22 +1584,47 @@ sub MarkUpdate($$;$)
#
# Set the hash.
#
sub SetHash($$)
sub SetFullHash($$)
{
my ($self, $hash) = @_;
return $self->Update({"hash" => $hash});
}
sub SetDeltaHash($$)
{
my ($self, $hash) = @_;
return $self->Update({"deltahash" => $hash});
}
#
# Set the size.
#
sub SetSize($$)
sub SetFullSize($$)
{
my ($self, $size) = @_;
return $self->Update({"size" => $size});
}
sub SetDeltaSize($$)
{
my ($self, $size) = @_;
return $self->Update({"deltasize" => $size});
}
sub SetUploaderPath($$)
{
my ($self, $path) = @_;
return $self->Update({"uploader_path" => $path});
}
sub ClearUploaderPath($)
{
my ($self) = @_;
return $self->Update({"uploader_path" => ''});
}
#
# Set the sector range of an image.
......@@ -2157,5 +2200,102 @@ sub LocalVersionURL($)
return "$TBBASE/image_metadata.php?uuid=$uuid";
}
#
# Path and Directory stuff.
#
# Images are stored as directories now. Inside the directory are base
# and delta images for each version, as well as sig and sha1 files.
#
sub IsDirPath($)
{
my ($self) = @_;
#
# Does the path indicate a directory or a file.
#
if (!defined($self->path())) {
print STDERR "No path is set for $self\n";
return 0;
}
return 1
if ($self->path() =~ /\/$/);
return 0;
}
sub FullImagePath($)
{
my ($self) = @_;
my $path = $self->path();
my $vers = $self->version();
my $name = $self->imagename();
if ($self->IsDirPath()) {
return $path . $name . ".ndz" . ($vers ? ":$vers" : "");
}
return $path;
}
sub DeltaImagePath($)
{
my ($self) = @_;
my $path = $self->path();
my $vers = $self->version();
my $name = $self->imagename();
if ($self->IsDirPath()) {
return $path . $name . ".ddz" . ($vers ? ":$vers" : "");
}
return $path;
}
sub FullImageFile($)
{
my ($self) = @_;
return $self->FullImagePath();
}
sub DeltaImageFile($)
{
my ($self) = @_;
return $self->DeltaImagePath();
}
sub TempImageFile($)
{
my ($self) = @_;
my $path = $self->path();
my $vers = $self->version();
my $name = $self->imagename();
if ($self->IsDirPath()) {
return $path . $name . ".ddz" . ($vers ? ":$vers" : "") . ".tmp";
}
return $path . ".tmp";
}
sub FullImageSHA1File($)
{
my ($self) = @_;
return $self->FullImagePath() . ".sha1";
}
sub DeltaImageSHA1File($)
{
my ($self) = @_;
return $self->DeltaImagePath() . ".sha1";
}
sub FullImageSigFile($)
{
my ($self) = @_;
return $self->FullImagePath() . ".sig";
}
sub DeltaImageSigFile($)
{
my ($self) = @_;
return $self->DeltaImagePath() . ".sig";
}
sub HaveFullImage($)
{
my ($self) = @_;
return $self->size() ? 1 : 0;
}
sub HaveDeltaImage($)
{
my ($self) = @_;
return $self->deltasize() ? 1 : 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -2881,7 +2881,8 @@ sub ImageInfo($)
}
}
else {
my $path = $image->path();
my $path = ($image->HaveDeltaImage() ?
$image->DeltaImageFile() : $image->FullImageFile());
if (-r $path) {
my $size = File::stat::stat($path)->size;
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -68,10 +68,8 @@ my $TBLOADWAIT = (10 * 60);
my $osselect = "$TB/bin/os_select";
my $TBUISP = "$TB/bin/tbuisp";
my $IMAGEINFO = "$TB/sbin/imageinfo";
# Locals
my %imageinfo = (); # Per imageid DB info.
my $debug = 0;
my %children = (); # Child pids in when asyncmode=1
my $remote_mult = 5; # Wait lots longer for remote nodes!
......@@ -97,7 +95,6 @@ sub osload ($$) {
my $failures = 0;
my $usedefault = 1;
my $mereuser = 0;
my $rowref;
my $this_user;
if (!defined($args->{'nodelist'})) {
......@@ -388,26 +385,30 @@ sub osload ($$) {
if $debug;
#
# We try to avoid repeated queries to DB for info that
# does not change by caching the image info on the first
# use. GetImageInfo() will perform various one-time
# checks as well.
#
if (!exists($imageinfo{$image->imageid()}) &&
!GetImageInfo($image,$node)) {
goto failednode;
}
$rowref = $imageinfo{$image->imageid()};
if ($rowref eq 'BADIMAGE') {
# We can have both a full image and/or a delta image. We
# always prefer the full image if we have it.
#
if (! ($image->HaveFullImage() || $image->HaveDeltaImage())) {
tberror "$node: no full or delta image file!";
goto failednode;
}
my $loadpart = $rowref->{'loadpart'};
my $loadlen = $rowref->{'loadlength'};
my $imagepath = $rowref->{'path'};
my $imagepid = $rowref->{'pid'};
$maxwait += $rowref->{'maxloadwait'};
$access_keys[$i] = $rowref->{'access_key'};
my $isfull = $image->HaveFullImage();
my $loadpart = $image->loadpart();
my $loadlen = $image->loadlength();
my $imagepid = $image->pid();
my $imagesize = ($isfull ? $image->size() : $image->deltasize());
#
# Compute a maxwait time based on the image size plus a constant
# factor for the reboot cycle. This is used later in
# WaitTillReloadDone(). Arguably, this should be part of the
# image DB state, so we store it in the imageinfo array too.
#
# size may be > 2^31, shift is unsigned
#
my $chunks = $imagesize >> 20;
$maxwait += int((($chunks / 100.0) * 65)) + $TBLOADWAIT;
$access_keys[$i] = $image->access_key();
#
# Set the default boot OSID.
......@@ -415,8 +416,8 @@ sub osload ($$) {
# last image.
#
if ($i == $imageidxs[-1]) {
$defosid = $rowref->{'default_osid'};
$defvers = $rowref->{'default_vers'};
$defosid = $image->default_osid();
$defvers = $image->default_vers();
my $osinfo = OSinfo->Lookup($defosid, $defvers);
if (!defined($osinfo)) {
......@@ -464,12 +465,12 @@ sub osload ($$) {
# If image MBR is incompatible with what is on the disk right
# now, invalidate all the existing partitions ("...UNLESS" above).
#
if (defined($rowref->{'mbr_version'})) {
if ($rowref->{'mbr_version'} && $curmbrvers &&
$rowref->{'mbr_version'} != $curmbrvers) {
if (defined($image->mbr_version())) {
if ($image->mbr_version() && $curmbrvers &&
$image->mbr_version() != $curmbrvers) {
%partitions = ();
}
$curmbrvers = $rowref->{'mbr_version'};
$curmbrvers = $image->mbr_version();
}
#
......@@ -480,8 +481,8 @@ sub osload ($$) {
my $partname = "part${i}_osid";
my $partvers = "part${i}_vers";
my $osid = $rowref->{$partname};
my $vers = $rowref->{$partvers};
my $osid = $image->DBData()->{$partname};
my $vers = $image->DBData()->{$partvers};
if (defined($osid)) {
my $osinfo = OSinfo->Lookup($osid, $vers);
if (!defined($osinfo)) {
......@@ -583,7 +584,7 @@ sub osload ($$) {
if ($WITHPROVENANCE && $WITHDELTAS) {
my $founddelta = 0;
foreach my $image (@images) {
if ($image->isdelta()) {
if (!$image->HaveFullImage()) {
my $pimage = $image;
my @ilist = ();
do {
......@@ -594,7 +595,7 @@ sub osload ($$) {
goto failednode;
}
push(@ilist, $pimage);
} while ($pimage->isdelta());
} while (!$pimage->HaveFullImage());
push @allimages, reverse(@ilist);
$founddelta = 1;
}
......@@ -655,7 +656,6 @@ sub osload ($$) {
&& !defined($access_keys[$i])) {
$access_keys[$i] = TBGenSecretKey();
$rowref->{'access_key'} = $access_keys[$i];
if ($allimages[$i]->Update({'access_key' => $access_keys[$i]}) != 0) {
tberror "$node: Could not initialize image access key";
goto failednode;
......@@ -897,92 +897,17 @@ sub osload ($$) {
return $failures;
}
#
# Fetch information for a specified image the first time it is used
# (for the indicated node). This info is cached for use by all other
# nodes that require the image. Returns 1 on success, 0 on failure.
#
sub GetImageInfo($$)
sub DumpImageInfo($)
{
my ($image, $node) = @_;
my $imagesize = 0;
my $imageid = $image->imageid();
my $rowref = $image->DBData();
if (!defined($rowref)) {
tberror("No DBData for $image!");
$imageinfo{$imageid} = 'BADIMAGE';
return 0;
}
$imageinfo{$imageid} = $rowref;
my $imagepath = $rowref->{'path'};
#
# Perform a few validity checks: imageid should have a file name
# and that file should exist.
#
if (!defined($imagepath)) {
tberror "No filename associated with $image!";
$imageinfo{$imageid} = 'BADIMAGE';
return 0;
}
if (! -R $imagepath) {
#
# There are two reasons why a legit image might not be readable.
# One is that we are in an elabinelab and the image has just not
# been downloaded yet. The other is that we are attempting to
# access a shared (via the grantimage mechanism) image which the
# caller cannot directly access.
#
# For either case, making a proxy query request via frisbee will
# tell us whether the image is accessible and, if so, its size.
# "imageinfo" makes that call for us.
#
my $frisimageid = $rowref->{'pid'} . "/" . $rowref->{'imagename'};
my $sizestr = `$IMAGEINFO -qs -N $node $frisimageid`;
if ($sizestr =~ /^(\d+)$/) {
$imagesize = $1;
} else {
tberror "$image: access not allowed or image does not exist.";
$imageinfo{$imageid} = 'BADIMAGE';
return 0;
}
} else {
$imagesize = stat($imagepath)->size;
}
#
# A zero-length image cannot be right and will result in much confusion
# if allowed to pass: the image load will succeed, but the disk will be
# unchanged, making it appear that os_load loaded the default image.
#
if ($imagesize == 0) {
tberror "$imagepath is empty!";
$imageinfo{$imageid} = 'BADIMAGE';
return 0;
}
#
# Compute a maxwait time based on the image size plus a constant
# factor for the reboot cycle. This is used later in
# WaitTillReloadDone(). Arguably, this should be part of the
# image DB state, so we store it in the imageinfo array too.
#
if (!defined($rowref->{'maxloadwait'})) {
my $chunks = $imagesize >> 20; # size may be > 2^31, shift is unsigned
$rowref->{'maxloadwait'} = int((($chunks / 100.0) * 65)) + $TBLOADWAIT;
}
my ($image) = @_;
print STDERR
"$image: loadpart=", $rowref->{'loadpart'},
", loadlen=", $rowref->{'loadlength'},
", imagepath=", $rowref->{'path'},
", imagesize=", $imagesize,
", defosid=", $rowref->{'default_osid'},
", maxloadwait=", $rowref->{'maxloadwait'}, "\n"
"$image: loadpart=", $image->loadpart(),
", loadlen=", $image->loadlength(),
", imagepath=", $image->path(),
", imagesize=", $image->size(),
", defosid=", $image->default_osid(),
", maxloadwait=", $image->_maxloadwait(), "\n"
if ($debug);
return 1;
......
......@@ -60,7 +60,6 @@ my $WITHDELTAS = @IMAGEDELTAS@;
# Paths to binaries
my $TBUISP = "$TB/bin/tbuisp";
my $IMAGEINFO = "$TB/sbin/imageinfo";
# XXX windows load
my $MAKECONF = "$TB/sbin/dhcpd_makeconf";
......@@ -98,7 +97,6 @@ sub New($$$;@)
$self->{'FLAGS'} = {};
$self->{'NODEFLAGS'} = {};
$self->{'NODEINFO'} = {};
$self->{'IMAGEINFO'} = {};
$self->{'OSMAP'} = {};
$self->{'FAILED'} = {};
$self->{'FAILCOUNT'} = 0;
......@@ -238,22 +236,6 @@ sub nodeflag($$$;$)
return $retval;
}
# Get/Set the imageid->images mapping. Nice to keep this to avoid lookups.
sub imageinfo($$;$)
{
my ($self,$imageid,$imageinfo) = @_;
if (defined($imageinfo)) {
# set it
$self->{'IMAGEINFO'}->{$imageid} = $imageinfo;
return $imageinfo;
}
return $self->{'IMAGEINFO'}->{$imageid}
if (exists($self->{'IMAGEINFO'}->{$imageid}));
return undef;
}
# Get/Set the osid->osinfo mapping. Nice to keep this to avoid lookups.
sub osmap($$;$)
{
......@@ -680,7 +662,6 @@ sub osload($$$) {
elsif ($nodeobject->isremotenode() && !defined($access_keys{$image})) {
$access_keys{$image} = TBGenSecretKey();
$rowref->{'access_key'} = $access_keys{$image};
if ($image->Update({'access_key' => $access_keys{$image}}) != 0) {
tberror "$self ($node): Could not initialize image access key";
goto failednode;
......@@ -951,86 +932,6 @@ sub AddNode($$$$)
return 0;
}
#
# Fetch information for a specified image the first time it is used.
# This info is cached for use by all other
# nodes that require the image. Returns 1 on success, 0 on failure.
#
sub GetImageInfo($$$$)
{
my ($self, $image, $node, $rowrefptr) = @_;
my $imagesize = 0;
my $imageid = $image->imageid();
my $rowref = $image->DBData();
if (!defined($rowref)) {
tberror("No DBData for $image!");
$$rowrefptr = 'BADIMAGE';
return -1;
}
$$rowrefptr = $rowref;
my $imagepath = $rowref->{'path'};
#
# Perform a few validity checks: imageid should have a file name
# and that file should exist.
#
if (!defined($imagepath)) {
tberror "No filename associated with $image!";
$$rowrefptr = 'BADIMAGE';
return -1;
}
if (! -R $imagepath) {
#
# There are two reasons why a legit image might not be readable.
# One is that we are in an elabinelab and the image has just not
# been downloaded yet. The other is that we are attempting to
# access a shared (via the grantimage mechanism) image which the
# caller cannot directly access.
#
# For either case, making a proxy query request via frisbee will
# tell us whether the image is accessible and, if so, its size.
# "imageinfo" makes that call for us.
#
my $frisimageid = $rowref->{'pid'} . "/" . $rowref->{'imagename'};
my $sizestr = `$IMAGEINFO -qs -N $node $frisimageid`;
if ($sizestr =~ /^(\d+)$/) {
$imagesize = $1;
} else {
tberror "$image: access not allowed or image does not exist.";
$$rowrefptr = 'BADIMAGE';
return -1;
}
}
else {
(undef,undef,undef,undef,undef,undef,undef,$imagesize,
undef,undef,undef,undef,undef) = stat($imagepath);
}
# cache as long as we just looked!
$rowref->{size} = $imagesize;
#
# A zero-length image cannot be right and will result in much confusion
# if allowed to pass: the image load will succeed, but the disk will be
# unchanged, making it appear that os_load loaded the default image.
#
if ($imagesize == 0) {
tberror "$imagepath is empty!";
$$rowrefptr = 'BADIMAGE';
return -1;
}
$self->dprint(3,"GetImageInfo($image): loadpart=", $rowref->{'loadpart'},
", loadlen=", $rowref->{'loadlength'},
", imagepath=", $rowref->{'path'},
", defosid=", $rowref->{'default_osid'});
return 0;
}
# Wait for a reload to finish by watching its state
sub WaitTillReloadDone($$$$$@)
{
......@@ -1825,25 +1726,18 @@ sub _CheckImages($$)
$self->dprint(1,"_CheckImages($node_id): using $image");
#
# We try to avoid repeated queries to DB for info that
# does not change by caching the image info on the first
# use. GetImageInfo() will perform various one-time
# checks as well.
#
my $rowref = $self->imageinfo($imageid);
if (!defined($rowref)) {
my $retval = $self->GetImageInfo($image,$node_id,\$rowref);
# save it off!
$self->imageinfo($imageid,$rowref);
if ($retval) {
return -1;
}
# We can have both a full image and/or a delta image. We
# always prefer the full image if we have it.
#
if (! ($image->HaveFullImage() || $image->HaveDeltaImage())) {
tberror "$image: no full or delta image file!";
return -1;
}
if ($rowref eq 'BADIMAGE') {
if (! ($image->size() || $image->deltasize())) {
tberror "$image: no size info!";
return -1;
}
$self->dprint(2,"_CheckImages($node_id): imageinfo for $imageid: " . Dumper($rowref));
$self->dprint(2,"_CheckImages($node_id): imageinfo for $imageid:\n");
}
return 0;
......@@ -1863,9 +1757,8 @@ sub SetBootOS($$)
#
my $image = $images[-1];
my $imageid = $image->imageid();
my $rowref = $self->imageinfo($imageid);
my $defosid = $rowref->{'default_osid'};
my $defosid = $image->default_osid();
my $osinfo = OSinfo->Lookup($defosid);
if (!defined($osinfo)) {
tberror("$self SetBootOS($node_id): could not map OSID $defosid to its object!");
......@@ -2052,11 +1945,10 @@ sub UpdatePartitions($$)
my $imageid = $imageids[$i];
my $image = $images[$i];
my $rowref = $self->imageinfo($imageid);
my $loadpart = $rowref->{'loadpart'};
my $loadlen = $rowref->{'loadlength'};
my $imagepath = $rowref->{'path'};
my $imagepid = $rowref->{'pid'};
my $loadpart = $image->loadpart();
my $loadlen = $image->loadlength();
my $imagepath = $image->path();
my $imagepid = $image->pid();
#
# Assign partition table entries for each partition in the image
......@@ -2086,12 +1978,12 @@ sub UpdatePartitions($$)
# If image MBR is incompatible with what is on the disk right
# now, invalidate all the existing partitions ("...UNLESS" above).
#
if (defined($rowref->{'mbr_version'})) {
if ($rowref->{'mbr_version'} && $curmbrvers &&
$rowref->{'mbr_version'} != $curmbrvers) {
if (defined($image->mbr_version())) {
if ($image->mbr_version() && $curmbrvers &&
$image->mbr_version() != $curmbrvers) {
%partitions = ();
}
$curmbrvers = $rowref->{'mbr_version'};
$curmbrvers = $image->mbr_version();
}
#
......@@ -2195,9 +2087,7 @@ sub ComputeMaxLoadWaitTime($$)
# WaitTillReloadDone(). Arguably, this should be part of the
# image DB state, so we store it in the imageinfo array too.
#
my $rowref = $self->imageinfo($imageid);
my $chunks = $rowref->{size} >> 20; # size may be > 2^31, shift is unsigned
my $chunks = $image->size() >> 20;# size may be > 2^31, shift is unsigned
# XXX aren't we multi-counting reboots??
# ok, moved constant factor out.
......@@ -2400,12 +2290,7 @@ sub SetupReload($$)
my $zerofree0 = $image->isdelta() ? 0: $zerofree;
# XXX windows load
my $rowref = $self->imageinfo($imageid);
if (!defined($rowref)) {
tberror "$self: Mike make a bad assumption!";
return -1;
}
my $format = $rowref->{'format'};
my $format = $image->format();
if (defined($format) && $format eq "wim") {
$needswinpe++;
}
......@@ -2594,13 +2479,12 @@ sub SetupReload($$)
}
my $imageid = $images[0]->imageid();
my $rowref = $self->imageinfo($imageid);
my $osid = $rowref->{'default_osid'};
my $osid = $image->default_osid();
#
# Get the path to the image
#
my $path = $rowref->{'path'};
my $path = $image->path();
#
# Tell stated that we're about to start reloading
......@@ -2637,13 +2521,12 @@ sub Reload($$)
my @images = @{$self->GetImages($nodeobject)};
my $imageid = $images[0]->imageid();
my $rowref = $self->imageinfo($imageid);
my $osid = $rowref->{'default_osid'};
my $osid = $image->default_osid();
#
# Get the path to the image
#
my $path = $rowref->{'path'};
my $path = $image->path();
TBSetNodeEventState($node_id,TBDB_NODESTATE_RELOADING);
......
......@@ -235,8 +235,7 @@ sub PreSetupReload($$)
my @images = @{$self->GetImages($nodeobject)};
my $newimageid = $images[0]->imageid();
my $newpart = $images[0]->loadpart();
my $rowref = $self->imageinfo($newimageid);
my $newosid = $rowref->{'default_osid'};
my $newosid = $images[0]->default_osid();
#
# Grab the existing partitions first, THEN overwrite them!
......@@ -319,8 +318,7 @@ sub SetupReload($$)
my @images = @{$self->GetImages($nodeobject)};
my $newimageid = $images[0]->imageid();
my $newpart = $images[0]->loadpart();
my $rowref = $self->imageinfo($newimageid);
my $newosid = $rowref->{'default_osid'};
my $newosid = $image->default_osid();
if (@images > 1) {
tbwarn "$self ($node_id): switches can load only one image; using first!";
......@@ -672,9 +670,8 @@ sub Reload($$)
my @images = @{$self->GetImages($nodeobject)};
my $imageid = $images[0]->imageid();
my $rowref = $self->imageinfo($imageid);
my $filename = $rowref->{'path'};
my $osid = $rowref->{'default_osid'};
my $filename = $images[0]->path();
my $osid = $images[0]->default_osid();
if (defined($self->nodeinfo($nodeobject,'reloadchildpid'))) {
tberror "$self Reload($node_id): is a reload already in progress?\n";
......
......@@ -236,8 +236,6 @@ $EUID = 0;
#
DBQueryFatal("delete FROM last_reservation where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM node_reservations where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM images where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM os_info where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM nodetypeXpid_permissions where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM project_stats where pid_idx='$pid_idx'");
DBQueryFatal("delete FROM group_stats where pid_idx='$pid_idx'");
......
......@@ -1685,7 +1685,8 @@ sub doSwapin($) {
return 1;
}
}
if (0) {
if (($update_Eventsys_restart ||
($type != MODIFY && $type != MODIFY_RECOVER))) {
print "Starting the event system.\n";
......@@ -1711,6 +1712,7 @@ sub doSwapin($) {
}
TBDebugTimeStamp("eventsys_control finished");
}
}
}
#
......
......@@ -54,7 +54,7 @@ SBIN_SCRIPTS = vlandiff vlansync withadminprivs export_tables cvsupd.pl \
update_sitevars delete_image sitecheckin sitecheckin_client \
mktestbedtest fixrootcert addservers poolmonitor \
node_exclude managetaint shutdown-shared imagerelease \
runsonxen pxelinux_makeconf
runsonxen pxelinux_makeconf imagetodir
WEB_SBIN_SCRIPTS= webnewnode webdeletenode webspewconlog webarchive_list \
webwanodecheckin webspewimage webdumpdescriptor \
......@@ -72,7 +72,7 @@ CTRLSBIN_SCRIPTS= opsdb_control.proxy daemon_wrapper ec2import.proxy \
SETUID_BIN_SCRIPTS = create_image
SETUID_SBIN_SCRIPTS = grabwebcams checkquota spewconlog opsdb_control suchown \
anonsendmail readblob image_import delete_image \
pxelinux_makeconf
pxelinux_makeconf imageinfo
SETUID_SUEXEC_SCRIPTS = xlogin
......
......@@ -81,7 +81,9 @@ my $GROUPROOT = "@GROUPSROOT_DIR@";
my $CREATEIMAGE = "$TB/bin/create_image";
my $NEWIMAGEEZ = "$TB/bin/newimageid_ez";
my $DOPROVENANCE = @IMAGEPROVENANCE@;
my $DOIMAGEDIRS = @IMAGEDIRECTORIES@;
my $doprovenance = 0;
my $doimagedirs = 0;
#
# Untaint the path
......@@ -235,6 +237,12 @@ if ($DOPROVENANCE) {
}
}
# See if enabled.
if ($DOIMAGEDIRS ||
EmulabFeatures->FeatureEnabled("ImageDirectories", $this_user, $group)) {
$doimagedirs = 1;
}
#
# The simple case is that the descriptor already exists. So it is just
# a simple snapshot to the image file.
......@@ -348,7 +356,11 @@ if (defined($image)) {
if ($image->path() =~ /^$TB/) {
my $path = $PROJROOT . "/" . $image->pid() . "/images/" .
basename($image->path());
# Watch for directory in original path, since basename
# drops he trailing slash.
if ($image->path() =~ /^\/$/) {
$path .= "/";
}
if ($image->Update({"path" => $path})) {
$image->DeleteVersion();
fatal("Could not update path!");
......@@ -429,8 +441,14 @@ if (Image->LookupByName($imagename) && !$this_user->IsAdmin()) {
# Subgroups change the path, but a global image should still
# go into the project image directory.
my $path = ($experiment->pid() eq $experiment->gid() || $global ?
"$PROJROOT/$pid/images/${imagename}.ndz" :
"$GROUPROOT/$pid/$gid/images/${imagename}.ndz");
"$PROJROOT/$pid/images/" :
"$GROUPROOT/$pid/$gid/images/");
if ($doimagedirs) {
$path .= "${imagename}/".
}
else {
$path .= "${imagename}.ndz".
}
#
# Create the image descriptor. We use the backend script to do the
......
......@@ -147,6 +147,7 @@ my $TBLOGS = "@TBLOGSEMAIL@";
my $BOSSIP = "@BOSSNODE_IP@";
my $CONTROL = "@USERNODE@";
my $NONFS = @NOSHAREDFS@;
my $PROJROOT = "@PROJROOT_DIR@";
my $WITHPROVENANCE= @IMAGEPROVENANCE@;
my $WITHDELTAS = @IMAGEDELTAS@;
my $ISFS = ("@BOSSNODE_IP@" eq "@FSNODE_IP@") ? 1 : 0;
......@@ -223,7 +224,6 @@ my $isvirtnode = 0;
my $isxenhost = 0;
my $isec2node = 0;
my $onsharednode= 0;
my $didbackup = 0;
my $node_id;
my $node;
my ($experiment,$pid);
......@@ -529,8 +529,7 @@ else {
#
$experiment = $node->Reservation();
if (!defined($experiment)) {
die("*** $0:\n".
" Could not map $node to its experiment object!\n");
fatal("Could not map $node to its experiment object!");
}
$pid = $experiment->pid();
......@@ -576,7 +575,7 @@ else {
(undef, $srcimage) = $node->RunningOsImage();
}
if (defined($srcimage)) {
$srcsigfile = $srcimage->path() . ".sig";
$srcsigfile = $srcimage->FullImageSigFile();
if (! -e "$srcsigfile") {
# XXX user may not have direct access to a shared image
my $SAVEUID = $UID;
......@@ -602,22 +601,6 @@ else {
}
}
#
# If we are creating a signature file for this image, look up the path
# so we derive the signature file name.
#
if ($signature) {
if (defined($image->path())) {
$dstsigfile = $image->path() . ".sig";
} else {
if ($signature == 1) {
print "*** WARNING: no path for image, ".
"cannot create signature file.\n";
}
$signature = 0;
}
}
#
# To avoid blowing a cavernous hole ("allow all TCP ports to boss")
# in the per-experiment firewall, we don't use the frisbee uploader if
......@@ -640,22 +623,24 @@ else {
# Make sure that the directory exists and is writeable for the user.
# We test this by creating the file. Its going to get wiped anyway.
#
my $filename = $image->path();
my $filename = $image->TempImageFile();
my $isglobal = $image->global();
my $usepath = 0;
my $isdataset = $image->isdataset();
my $hackprefix= $PROJROOT . "/" . $image->pid() . "/images/";
#
# Redirect pathname for global images. See equiv code in clone_image.
# XXX if the project of the experiment creating the image is not emulab-ops,
# we make a feeble attempt to avoid clobbering existing files.
# If we are creating a signature file for this image, get the
# signature file name.
#
my $hackprefix;
if ($pid eq TBOPSPID()) {
$hackprefix = PROJROOT() . "/$pid/images/";
} else {
$hackprefix = PROJROOT() . "/$pid/images/E_O_";
if ($signature) {
# We want to use the temp filename.
$dstsigfile = $filename . ".sig";
}
#
# Redirect pathname for global images. See equiv code in clone_image.
#
if ($isglobal && ($filename =~ /^$TB/)) {
$filename = $hackprefix . basename($filename);
print "*** WARNING: Writing global descriptor to $filename instead!\n";
......@@ -685,9 +670,8 @@ if ($srcsigfile && ($srcsigfile =~ /^$TB/)) {
my $osrcsigfile = $srcsigfile;
$srcsigfile = $hackprefix . basename($srcsigfile);
if (system("cp -fp $osrcsigfile $srcsigfile")) {
die("*** $0:\n".
" Could not copy source signature file ".
"$osrcsigfile to $srcsigfile\n");
fatal("Could not copy source signature file ".
"$osrcsigfile to $srcsigfile");
}
# XXX remember so we can cleanup later
$hacksigfile = $srcsigfile;
......@@ -708,13 +692,11 @@ if ($translated =~ /^([-\w\.\/\+:]+)$/) {
$filename = $1;
}
else {
die("*** $0:\n".
" Bad data returned by realpath: $translated\n");
fatal("Bad data returned by realpath: $translated");
}
# Make sure not a directory.
if (-d $filename) {
die("*** $0:\n".
" $filename is a directory! Must be a plain file.\n");
fatal("$filename is a directory! Must be a plain file.");
}
#
......@@ -723,23 +705,20 @@ if (-d $filename) {
# the user is allowed to use.
#
if (! TBValidUserDir($filename, $ISFS)) {
die("*** $0:\n".
" $filename does not resolve to an allowed directory!\n");
fatal("$filename does not resolve to an allowed directory!");
}
#
# Before we do anything destructive, we lock the image.
#
if ($image->Lock()) {
die("*** $0:\n".
" Image is locked, please try again later!\n");
fatal("Image is locked, please try again later!");
}
$needunlock = 1;
if ($doprovenance && !$isdataset && $image->ready()) {
$image->Unlock();
die("*** $0:\n".
" $image ready flag is set, this is inconsistent!\n");
fatal("$image ready flag is set, this is inconsistent!");
}
# See if a web task is tracking this image creation.
......@@ -761,50 +740,27 @@ if ($pid ne TBOPSPID()) {
}
}
if (-e $filename) {
#
# Back it up in case of failure. Note that the frisbee upload server
# does this, so we do it only for the ssh/nfs case.
#
if (!$usefup) {
system("/bin/mv -f $filename ${filename}.bak");
if ($?) {
fatal("Could not back up $filename");
}
$didbackup = 1;
}
}
#
# We want to truncate the file (we backed it up above), which also
# confirms the user can really create a new file.
#
# XXX The problem is that frisbee upload server does this too, which
# is why we have a lot of zero length backup files. So, in uploader
# mode, make sure the user can create the tmp file that the uploader
# uses.
# We want to confirm the user can create the temp file in the target
# directory, so create a zero length file. But first, need to make
# sure the target directory exists in the image path is a directory.
#
if ($usefup) {
$tmp = "$filename.tmp";
if (-e "$tmp") {
unlink("$tmp") ||
fatal("Could not remove $tmp: $!");
}
} else {
$tmp = $filename;
# Make sure the path directory exists.
if ($image->IsDirPath() && ! -e $image->path() && !mkdir($image->path(), 0775)){
fatal("Could not mkdir " . $image->path() . ": $!");
}
open(FILE, "> $tmp") or
fatal("Could not create $tmp: $!");
open(FILE, "> $filename") or
fatal("Could not create $filename: $!");
close(FILE) or
fatal("Could not truncate $tmp: $!");
fatal("Could not truncate $filename: $!");
#
# XXX this script runs as the user creating the image.
# However, in the uploader case, the uploader runs as the creator of
# the image. In the case those two are not the same, we need to make
# sure that the file we create here is group writable.
#
chmod(0664, $tmp) or
fatal("Could not make $tmp group writable: $!");
chmod(0664, $filename) or
fatal("Could not make $filename group writable: $!");
if (! ($isvirtnode || $isec2node || $isdataset)) {
#
......@@ -1077,6 +1033,10 @@ if (! $foreground) {
#
$needcleanup = 1;
# This tells the master server what uploader path to use.
$image->SetUploaderPath($filename) == 0
or fatal("Could not set the uploader path");
# Clear the bootlog; see below.
$node->ClearBootLog()
if (defined($node));
......@@ -1359,6 +1319,19 @@ if ($delta && $deltapct > 0 && defined($logfile)) {
}
}
#
# The upload completed okay, so move the files into place so that
# imagevalidate finds them in the correct place.
#
if (! (system("/bin/mv -f $filename " .
($delta ?
$image->DeltaImageFile() : $image->FullImageFile())) &&
system("/bin/mv -f $dstsigfile " .
($delta ?
$image->FullImageSigFile() : $image->FullImageSigFile())))) {
fatal("Could not move new files into place");
}
#
# Update fields in the DB related to the image.
#
......@@ -1453,6 +1426,8 @@ sub cleanup ()
if (defined($hacksigfile)) {
unlink($hacksigfile);
}
# Mike says it is a good idea to clear this.
$image->ClearUploaderPath();
if ($isvirtnode || $isec2node || $isdataset) {
#
......@@ -1515,7 +1490,11 @@ sub fatal($)
$logfile->Close();
$image->ClearLogFile();
}
# This is a temporary file.
if (defined($filename)) {
unlink($filename);
}
if (defined($webtask)) {
$webtask->status("failed");
$webtask->imagesize(0);
......@@ -1523,10 +1502,6 @@ sub fatal($)
}
$image->Unlock()
if ($needunlock);
# Restore old image file.
if ($didbackup) {
system("/bin/mv -f ${filename}.bak $filename");
}
exit(-1);
}
......@@ -1547,15 +1522,6 @@ sub check_progress($$)
int($idlewait/60) . " minutes idle.\n";
}
#
# XXX frisbee uploader uploads into a temporary file and then moves
# it into place. So track that tmp file here.
#
my $fname = $filename;
if ($usefup) {
$fname .= ".tmp";
}
#
# Command has finished for better or worse, record status and finish.
#
......@@ -1582,26 +1548,15 @@ sub check_progress($$)
# Also, check to see if the (somewhat arbitrary) maximum filesize has
# been exceeded.
#
my $cursize = (stat($fname))[7];
my $cursize = (stat($filename))[7];
if (!defined($cursize)) {
#
# XXX avoid an ugly race.
# When done, frisuploadd moves foo.tmp -> foo
# If we didn't find foo.tmp, try foo now.
#
if ($usefup) {
$fname =~ s/\.tmp$//;
$cursize = (stat($fname))[7];
}
#
# XXX avoid an ugly uninitialized value.
# This should not happen, since we created the file,
# but just in case, if the file doesn't exist set the size to 0.
# We will eventually timeout.
#
if (!defined($cursize)) {
$cursize = 0;
}
$cursize = 0;
}
if (defined($webtask)) {
$webtask->imagesize($cursize / 1024);
......
......@@ -534,8 +534,8 @@ sub HandleIMDataset()
{
my $global = (defined($read_access) && $read_access eq "global" ? 1 : 0);
my $path = ($pid eq $gid || $global ?
"$TBPROJ_DIR/$pid/images/${lname}.ndz" :
"$TBGROUP_DIR/$pid/$gid/images/${lname}.ndz");
"$TBPROJ_DIR/$pid/images/${lname}/" :
"$TBGROUP_DIR/$pid/$gid/images/${lname}/);
#
# See if we are going to take a snapshot right away, and verify the
......
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -32,19 +32,21 @@ use Data::Dumper;
#
sub usage()
{
print("Usage: delete_image [-p | -r] <imagename>\n".
print("Usage: delete_image [[-f | -F] -p | -r] <imagename>\n".
"Options:\n".
" -p Purge the disk image file(s)\n".
" -r Rename the disk image file(s) instead\n".
" -n Impotent mode, show what would be done.\n".
" -f Force deletion of system image\n".
" -F Force deletion of global system image\n");
exit(-1);
}
my $optlist = "dFprn";
my $optlist = "dFprnf";
my $debug = 0;
my $purge = 0;
my $rename = 0;
my $force = 0;
my $FORCE = 0;
my $impotent = 0;
my $needunlock = 0;
......@@ -111,6 +113,9 @@ if (defined($options{"n"})) {
$impotent = 1;
}
if (defined($options{"F"})) {
$FORCE = 1;
}
if (defined($options{"f"})) {
$force = 1;
}
usage()
......@@ -135,7 +140,7 @@ if (!$image->AccessCheck($this_user, TB_IMAGEID_DESTROY())) {
fatal("You do not have permission to delete this image!");
}
if ($image->pid() eq TBOPSPID() && $image->global() && !$force) {
if ($image->pid() eq TBOPSPID() && $image->global() && !$FORCE) {
fatal("Refusing to delete global system image $image. ".
"Use -F if you are sure.\n");
}
......@@ -178,7 +183,7 @@ if ($?) {
# When IMAGEPROVENANCE is on, we never delete system images, we
# rename them.
#
if ($image->pid() eq TBOPSPID()) {
if ($image->pid() eq TBOPSPID() && !$force) {
if ($purge) {
$purge = 0;
print STDERR "Ignoring purge option for system image. \n";
......@@ -194,6 +199,8 @@ if ($image->pid() eq TBOPSPID()) {
# setuid root. Flip for deleting the image file.
#
if ($purge || $rename) {
my $isdirpath = $image->IsDirPath();
#
# When doing image provenance, we have to deal with all versions
# of the image.
......@@ -202,20 +209,72 @@ if ($purge || $rename) {
if ($image->AllVersions(\@images)) {
fatal("Could not get list of image (versions)");
}
#
# If the path is a directory, we can just do a rename on it.
#
if ($isdirpath) {
my $dirname = $image->path();
if ($purge) {
if ($impotent) {
print "Would remove directory $dirname\n" if (-e $dirname);
}
else {
$EUID = 0;
system("/bin/rm -rf $dirname");
if ($?) {
fatal("Could not remove $dirname");
}
$EUID = $UID;
}
}
else {
my $newname = $dirname .
"/" . $image->imagename() . ":" . $image->imageid();
if ($impotent) {
print "Would rename $dirname to $newname\n" if (-e $dirname);
}
else {
if (-e $dirname) {
$EUID = 0;
system("/bin/mv -fv $dirname $newname");
if ($?) {
fatal("Could not rename $dirname to $newname");
}
$EUID = $UID;
}
# Hmm, need an update all versions method.
foreach my $imageversion (@images) {
$imageversion->Update({"path" => $newname});
}
}
}
#
# Fall into the loop below to clean up stale image versions and
# backup files.
#
}
foreach my $imageversion (@images) {
my @todelete = ();
my @torename = ();
my $filename = $imageversion->path();
my $filename = $imageversion->FullImageFile();
push(@torename, $filename);
push(@todelete, "$filename.bak");
if ($filename =~ /^(.*)\.ndz$/) {
push(@todelete, "$1.sha1");
} else {
push(@todelete, "$filename.sha1");
push(@todelete, "$filename.tmp");
push(@torename, $imageversion->FullImageSHA1File());
push(@torename, $imageversion->FullImageSigFile());
# Backwards compat with non-directory image paths.
if ($filename ne $imageversion->DeltaImageFile()) {
$filename = $imageversion->DeltaImageFile();
push(@torename, $filename);
push(@todelete, "$filename.bak");
push(@todelete, "$filename.tmp");
push(@torename, $imageversion->DeltaImageSHA1File());
}
push(@torename, "$filename.sig");
push(@todelete, "$filename.sig.bak");
# We throw away versions that never came ready or released.
if ($purge ||
......@@ -248,11 +307,20 @@ if ($purge || $rename) {
}
}
}
$EUID = $UID;
#
# Skip this part if we did a directory rename above.
#
next
if ($isdirpath);
#
# Delete with rename; move the current files out of the way
# so that they do not conflict with a later image of the same name.
# We do this by creating a subdir for the files.
#
$EUID = 0;
if (@torename) {
my $dirname = dirname($imageversion->path()) .
"/" . $image->imagename() . ":" . $image->imageid();
......
......@@ -33,13 +33,14 @@ use Data::Dumper;
sub usage()
{
print("Usage: dumpdescriptor ".
"[-d] [-e] [-i <imageid> [-t]] | [-o <osid>]\n");
"[-d] [-e] [-v clientvers] [-i <imageid> [-t]] | [-o <osid>]\n");
exit(-1);
}
my $optlist = "di:o:te";
my $debug = 0;
my $dotypes = 0;
my $export = 0;
my $optlist = "di:o:tev:";
my $debug = 0;
my $dotypes = 0;
my $export = 0;
my $clientvers = 0;
#
# Configure variables
......@@ -51,6 +52,13 @@ my $TBGROUP_DIR = "@GROUPSROOT_DIR@";
my $TBPROJ_DIR = "@PROJROOT_DIR@";
my $TBBASE = "@TBBASE@";
#
# When fetching the metadata, we now tell the server what client
# version of the software we are so it gives something we can handle.
# Be sure to update this if you change the version in image_import
#
my $METADATA_SERVERVERSION = 1;
#
# Untaint the path
#
......@@ -95,6 +103,10 @@ if (defined($options{"t"})) {
if (defined($options{"e"})) {
$export = 1;
}
if (defined($options{"v"})) {
$clientvers = $options{"v"};
}
if (@ARGV) {
usage();
}
......@@ -135,13 +147,22 @@ sub DumpImage($)
$xmlfields{"loadpart"} = $image->loadpart();
$xmlfields{"global"} = $image->global();
$xmlfields{"shared"} = $image->shared();
$xmlfields{"path"} = $image->path()
if (defined($image->path()) && $image->path() ne "");
if (defined($image->path()) && $image->path() ne "") {
#
# Old clients cannot handle directory based paths, so give them
# a filename instead.
#
my $path = $image->path();
if ($clientvers == 0) {
$path .= $image->imagename() . ".ndz" .
($image->version() ? ":" . $image->version() : "");
}
$xmlfields{"path"} = $path;
}
$xmlfields{"hash"} = $image->hash()
if ($export && defined($image->hash()) && $image->hash() ne "");
$xmlfields{"mbr_version"} = $image->mbr_version();
if (0) {
# Not yet, backwards compatability problem.
if ($clientvers > 0) {
$xmlfields{"isdataset"} = $image->isdataset();
}
......@@ -235,8 +256,16 @@ sub DumpImage($)
$xmlfields{"mtype_$type"} = "1";
}
}
print "<image>\n";
#
# Old sites cannot handle a version element.
#
if ($clientvers > 0) {
print "<image metadata_version=\"${METADATA_SERVERVERSION}\">\n";
}
else {
print "<image>\n";
}
foreach my $key (sort keys(%xmlfields)) {
my $val = $xmlfields{$key};
......
......@@ -78,8 +78,16 @@ my $SAVEUID = $UID;
my $IMAGEVALIDATE = "$TB/sbin/imagevalidate";
my $DELETEIMAGE = "$TB/sbin/delete_image";
my $WITHPROVENANCE= @IMAGEPROVENANCE@;
my $DOIMAGEDIRS = @IMAGEDIRECTORIES@;
my $doprovenance = 0;
#
# When fetching the metadata, we now tell the server what client
# version of the software we are so it gives something we can handle.
# Be sure to update this if you change the version in dumpdescriptor.
#
my $METADATA_CLIENTVERSION = 1;
#
# Untaint the path
#
......@@ -293,7 +301,7 @@ if ($getimage) {
}
# Run as root to access /proj
$EUID = $UID = 0;
if (! -e $image->path() || $newhash ne $image->hash() || $force) {
if (! -e $image->FullImageFile() || $newhash ne $image->hash() || $force) {
$EUID = $UID = $SAVEUID;
#
# When updating an image ...
......@@ -439,12 +447,17 @@ sub CreateImage($$$$$)
}
# do not trust path coming in.
if ($global && $user->IsAdmin()) {
$xmlparse->{'attribute'}->{"path"}->{'value'} =
"$TB/images/${imagename}.ndz";
$xmlparse->{'attribute'}->{"path"}->{'value'} = "$TB/images/";
}
else {
$xmlparse->{'attribute'}->{"path"}->{'value'} =
"$TBPROJ_DIR/" . $group->pid() . "/images/${imagename}.ndz";
"$TBPROJ_DIR/" . $group->pid() . "/images/";
}
if ($DOIMAGEDIRS) {
$xmlparse->{'attribute'}->{"path"}->{'value'} .= "${imagename}/".
}
else {
$xmlparse->{'attribute'}->{"path"}->{'value'} .= "${imagename}.ndz".
}
#
......@@ -499,7 +512,7 @@ sub DownLoadImage($$$$)
{
my ($image, $newhash, $user, $group) = @_;
my $image_url = uri_unescape($image->imagefile_url());
my $localfile = $image->path() . ".new";
my $localfile = $image->FullImageFile() . ".new";
if (FetchImageFile($image_url, $localfile)) {
return -1;
......@@ -542,8 +555,8 @@ sub DownLoadImage($$$$)
#
# Now rename the image files and update the hash file.
#
my $hashfile = $image->path() . ".sha1";
my $ndzfile = $image->path();
my $hashfile = $image->FullImageSHA1File();
my $ndzfile = $image->FullImageFile();
unlink($hashfile)
if (-e $hashfile);
system("/bin/mv -f $newhashfile $hashfile");
......@@ -635,6 +648,7 @@ sub FetchImageFile($$)
sub FetchMetadata($)
{
my ($url) = @_;
$url .= "&clientversion=" . $METADATA_CLIENTVERSION;
my $safe_url = User::escapeshellarg($url);
my $xml = "";
my $opts = ($debug ? "" : "-q");
......@@ -679,8 +693,8 @@ sub FetchSigFile($)
{
my ($image) = @_;
my $image_url = uri_unescape($image->imagefile_url()) . "&sigfile=1";
my $localfile = $image->path() . ".sig.new";
my $sigfile = $image->path() . ".sig";
my $localfile = $image->FullImageSigFile() . ".new";
my $sigfile = $image->FullImageSigFile();