diff --git a/sql/database-create.sql b/sql/database-create.sql index 392f93d4b1b05485b903cce3d8adb37dd780da38..1b9aaa1cfeea146102badd974e4a08c5009529b5 100644 --- a/sql/database-create.sql +++ b/sql/database-create.sql @@ -128,6 +128,7 @@ DROP TABLE IF EXISTS `blobs`; CREATE TABLE `blobs` ( `uuid` varchar(40) NOT NULL, `filename` tinytext, + `owner_uid` varchar(8) NOT NULL default '', PRIMARY KEY (`uuid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/sql/updates/4/236 b/sql/updates/4/236 new file mode 100644 index 0000000000000000000000000000000000000000..972e54d37ed10d7c461a073f4a0a0ba393a3b3bd --- /dev/null +++ b/sql/updates/4/236 @@ -0,0 +1,19 @@ +# +# Add owner uid to blob table (to allow rmblob) +# +use strict; +use libdb; + +sub DoUpdate($$$) +{ + my ($dbhandle, $dbname, $version) = @_; + + if( !DBSlotExists( "blobs", "owner_uid" ) ) { + DBQueryFatal( "ALTER TABLE blobs ADD " . + "`owner_uid` varchar(8) NOT NULL default ''" ); + } + + return 0; +} + +1; diff --git a/utils/GNUmakefile.in b/utils/GNUmakefile.in index b5efed84e21e070c105dfa8885accc864062522c..674a7c345048e07338628bac3ffb60e1634fcf66 100644 --- a/utils/GNUmakefile.in +++ b/utils/GNUmakefile.in @@ -17,7 +17,7 @@ SUBDIRS = nsgen BIN_SCRIPTS = delay_config sshtb create_image node_admin link_config \ setdest loghole webcopy linkmon_ctl snmp-if-deref.sh \ template_record spewevents \ - wbts_dump mkblob + wbts_dump mkblob rmblob SBIN_SCRIPTS = vlandiff vlansync withadminprivs export_tables cvsupd.pl \ eventping grantnodetype import_commitlog daemon_wrapper \ opsreboot deletenode node_statewait grabwebcams \ diff --git a/utils/mkblob.in b/utils/mkblob.in old mode 100644 new mode 100755 index 4ade53bbf186b58baa62f427c53a1283ffeb9bc2..4e1cf037141ca16dc1d02f331e568bbe10b11816 --- a/utils/mkblob.in +++ b/utils/mkblob.in @@ -36,15 +36,23 @@ use libtestbed; # Handle command-line options. # sub usage() { - print STDERR "Usage: $0 \n"; + print STDERR "Usage: $0 \n"; exit( 1 ); } -usage() unless @ARGV == 1; -my ( $filename ) = @ARGV; +usage() unless @ARGV == 2; +my ( $uid, $filename ) = @ARGV; # # Must taint check! # +if ($uid =~ /^([-\w #%&*+,.\/:;=?@\[\\\]^{|}]+)$/) { + $uid = $1; +} +else { + print STDERR "Bad character in uid\n"; + exit( 1 ); +} + if ($filename =~ /^([-\w #%&*+,.\/:;=?@\[\\\]^{|}]+)$/) { $filename = $1; } @@ -59,7 +67,7 @@ my $uuid = `@UUIDGEN@`; chomp $uuid; my $result = DBQueryWarn( "INSERT INTO blobs SET uuid='$uuid', " . - "filename='$filename';" ); + "filename='$filename', owner_uid='$uid';" ); unless( $result ) { print STDERR "Could not insert record.\n"; exit( 1 ); diff --git a/utils/rmblob.in b/utils/rmblob.in new file mode 100755 index 0000000000000000000000000000000000000000..e2be1b400196e6db68d387a0425121898eaf327d --- /dev/null +++ b/utils/rmblob.in @@ -0,0 +1,82 @@ +#!/usr/bin/perl -w + +# +# EMULAB-COPYRIGHT +# Copyright (c) 2010 University of Utah and the Flux Group. +# All rights reserved. +# + +use English; +use strict; + +# +# Configure variables +# +my $TB = "@prefix@"; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Untaint the path +# +$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/sbin:/usr/sbin"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# +# Testbed Support libraries +# +use lib "@prefix@/lib"; +use libdb; +use libtestbed; + +# +# Handle command-line options. +# +sub usage() { + print STDERR "Usage: $0 \n"; + exit( 1 ); +} + +usage() unless @ARGV == 2; +my ( $uid, $uuid ) = @ARGV; +# +# Must taint check! +# +if ($uid =~ /^([-\w #%&*+,.\/:;=?@\[\\\]^{|}]+)$/) { + $uid = $1; +} +else { + print STDERR "Bad character in uid\n"; + exit( 1 ); +} + +if ($uuid =~ /^([-\w]+)$/) { + $uuid = $1; +} +else { + print STDERR "Bad character in uuid\n"; + exit( 1 ); +} + +my $result = DBQueryWarn( "DELETE FROM blobs WHERE uuid='$uuid' AND " . + "owner_uid='$uid';" ); +unless( $result ) { + print STDERR "Could not delete record.\n"; + exit( 1 ); +} + +unless( $result->affectedrows == 1 ) { + print STDERR "Permission denied.\n"; + exit( 1 ); +} + +# And clean up the blob_files table (which is only a cache), just in case +# we removed the last reference to a file. +DBQueryWarn( "DELETE blob_files FROM blob_files LEFT OUTER JOIN blobs ON " . + "blob_files.filename=blobs.filename WHERE " . + "blobs.filename IS NULL;" ); + +exit( 0 ); diff --git a/xmlrpc/GNUmakefile.in b/xmlrpc/GNUmakefile.in index 3fd254f48b65dc1f584827964d3089a8437deab0..2c7d086e75be7df7ef9448f2fea5421ea4b7f564 100644 --- a/xmlrpc/GNUmakefile.in +++ b/xmlrpc/GNUmakefile.in @@ -35,7 +35,7 @@ SYMLINKS = node_admin node_reboot os_load create_image node_list \ modexp expinfo node_avail tbuisp expwait template_commit \ template_export template_swapin template_swapout \ template_stoprun template_instantiate template_startrun \ - template_checkout node_avail_list mkblob + template_checkout node_avail_list mkblob rmblob # # Force dependencies on the scripts so that they will be rerun through diff --git a/xmlrpc/emulabserver.py.in b/xmlrpc/emulabserver.py.in index 177f95126d5059bc094cef62dc29f5eed181b7dd..3a33c5516d0ce21e70e74c70d7d5cf1b38367aaf 100755 --- a/xmlrpc/emulabserver.py.in +++ b/xmlrpc/emulabserver.py.in @@ -5748,7 +5748,29 @@ class blob: if (argerror): return argerror - (exitval, output) = runcommand( TBDIR + "/bin/mkblob " + argdict[ "filename" ] ) + (exitval, output) = runcommand( TBDIR + "/bin/mkblob " + self.uid + " " + argdict[ "filename" ] ) + + if exitval: + return EmulabResponse( RESPONSE_ERROR, exitval >> 8, output=output ) + else: + return EmulabResponse( RESPONSE_SUCCESS, value=0, output=output ) + + def rmblob( self, version, argdict ): + if version != self.VERSION: + return EmulabResponse(RESPONSE_BADVERSION, + output="Client version mismatch!") + + try: + checknologins() + pass + except NoLoginsError, e: + return EmulabResponse(RESPONSE_REFUSED, output=str(e)) + + argerror = CheckRequiredArgs(argdict, ("uuid",)) + if (argerror): + return argerror + + (exitval, output) = runcommand( TBDIR + "/bin/rmblob " + self.uid + " " + argdict[ "uuid" ] ) if exitval: return EmulabResponse( RESPONSE_ERROR, exitval >> 8, output=output ) diff --git a/xmlrpc/script_wrapper.py.in b/xmlrpc/script_wrapper.py.in index 699d4015e75f9b838b3d8186a1917f1a06eb5588..1a48a8d75af2320db5f8634a7cb157cc51c0ade0 100755 --- a/xmlrpc/script_wrapper.py.in +++ b/xmlrpc/script_wrapper.py.in @@ -150,7 +150,9 @@ API = { "template_stoprun" : { "func" : "template_stoprun", "help" : "Stop current experiment run" }, "mkblob" : { "func" : "mkblob", - "help" : "Create a new blob in the blob store" } + "help" : "Create a new blob in the blob store" }, + "rmblob" : { "func" : "rmblob", + "help" : "Remove a blob from the blob store" } }; # @@ -2640,6 +2642,43 @@ class mkblob: return pass +# +# rmblob +# +class rmblob: + def __init__(self, argv=None): + self.argv = argv; + return + + def apply(self): + try: + opts, req_args = getopt.getopt(self.argv, "h", [ "help" ]); + pass + except getopt.error, e: + print e.args[0] + self.usage(); + return -1; + + for opt, val in opts: + if opt in ("-h", "--help"): + self.usage() + return 0 + + # Do this after so --help is seen. + if len(req_args) != 1: + self.usage(); + return -1; + + rval,response = do_method( "blob", "rmblob", + { "uuid" : req_args[ 0 ] } ); + return rval; + + def usage(self): + print "rmblob [options] uuid" + wrapperoptions(); + return + pass + # # Infer template guid from path #