mirror of
https://github.com/release-engineering/dist-git.git
synced 2025-02-23 15:02:54 +00:00
208 lines
6.1 KiB
Python
208 lines
6.1 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# CGI script to handle file updates for the rpms git repository. There
|
|
# is nothing really complex here other than tedious checking of our
|
|
# every step along the way...
|
|
#
|
|
|
|
import os
|
|
import sys
|
|
import errno
|
|
import cgi
|
|
import stat
|
|
import hashlib
|
|
import tempfile
|
|
|
|
# reading buffer size
|
|
BUFFER_SIZE = 4096
|
|
GITREPO = "/srv/git"
|
|
DEFAULT_NAMESPACE = 'rpms'
|
|
|
|
# Lookaside cache directory
|
|
CACHE_DIR = '/srv/cache/lookaside'
|
|
|
|
form = cgi.FieldStorage()
|
|
os.umask(002)
|
|
|
|
def log_msg(*msgs):
|
|
sys.stderr.write(' '.join(map(str,msgs)) + '\n')
|
|
|
|
# abort running the script
|
|
def send_error(text):
|
|
print "Content-type: text/plain\n"
|
|
print text
|
|
sys.exit(1)
|
|
|
|
# prepare to exit graciously
|
|
def send_ok(text):
|
|
print "Content-Type: text/plain"
|
|
print
|
|
if text:
|
|
print text
|
|
|
|
# check and validate that all the fields are present
|
|
def check_form(var, strip=True):
|
|
if not form.has_key(var):
|
|
send_error("required field '%s' is not present" % (var,))
|
|
ret = form.getvalue(var)
|
|
if type(ret) == type([]):
|
|
send_error("Multiple values given for '%s'. Aborting" % (var,))
|
|
if strip:
|
|
ret = os.path.basename(ret) # this is a path component
|
|
return ret
|
|
|
|
|
|
def hardlink(src, dst):
|
|
"""Create a hardlink, making sure the target directory exists."""
|
|
makedirs(os.path.dirname(dst))
|
|
if os.path.exists(dst):
|
|
# The file already exists, let's hardlink over it.
|
|
os.unlink(dst)
|
|
os.link(src, dst)
|
|
log_msg('ln %s %s' % (src, dst))
|
|
|
|
|
|
def makedirs(path, mode=0o2775):
|
|
"""Create a directory with all parents. If it already exists, then do
|
|
nothing."""
|
|
try:
|
|
os.makedirs(path, mode=mode)
|
|
log_msg('mkdir -p %s' % path)
|
|
except OSError as exc:
|
|
if exc.errno != errno.EEXIST:
|
|
send_error('Failed to create directory: %s' % exc)
|
|
|
|
|
|
def check_file_exists(path, filename, upload):
|
|
"""Send message if exists and exit the script."""
|
|
if not os.access(path, os.F_OK | os.R_OK):
|
|
# File does not exist, nothing to do here.
|
|
return
|
|
|
|
if upload is None:
|
|
# File exists and we're just checking.
|
|
message = "Available"
|
|
else:
|
|
# File exists and we wanted to upload it. Just say it's there already.
|
|
upload.file.close()
|
|
s = os.stat(path)
|
|
message = "File %s already exists\nFile: %s Size: %d" % (
|
|
filename, path, s[stat.ST_SIZE])
|
|
send_ok(message)
|
|
sys.exit(0)
|
|
|
|
|
|
# Get correct hashing algorithm
|
|
if 'sha512sum' in form:
|
|
checksum = check_form('sha512sum')
|
|
hash_type = 'sha512'
|
|
elif 'md5sum' in form:
|
|
checksum = check_form('md5sum')
|
|
hash_type = 'md5'
|
|
else:
|
|
send_error('Required checksum is not present')
|
|
|
|
|
|
NAME = check_form("name", strip=False)
|
|
|
|
if '/' not in NAME:
|
|
NAME = '%s/%s' % (DEFAULT_NAMESPACE, NAME)
|
|
|
|
# Is this a submission or a test?
|
|
FILE = None
|
|
FILENAME = None
|
|
if form.has_key("filename"):
|
|
# check the presence of the file
|
|
FILENAME = check_form("filename")
|
|
else:
|
|
if form.has_key("file"):
|
|
FILE = form["file"]
|
|
if not FILE.file:
|
|
send_error("No file given for upload. Aborting")
|
|
try:
|
|
FILENAME = os.path.basename(FILE.filename)
|
|
except:
|
|
send_error("Could not extract the filename for upload. Aborting")
|
|
else:
|
|
send_error("required field '%s' is not present" % ("file", ))
|
|
|
|
# Now that all the fields are valid,, figure out our operating environment
|
|
if not os.environ.has_key("SCRIPT_FILENAME"):
|
|
send_error("My running environment is funky. Aborting")
|
|
|
|
# try to see if we already have this file...
|
|
file_dest = "%s/%s/%s/%s/%s/%s" % (CACHE_DIR, NAME, FILENAME, hash_type, checksum, FILENAME)
|
|
old_path = "%s/%s/%s/%s/%s" % (CACHE_DIR, NAME, FILENAME, checksum, FILENAME)
|
|
|
|
check_file_exists(file_dest, FILENAME, FILE)
|
|
if hash_type == 'md5':
|
|
# If we're using MD5, handle the case of the file only existing in the old
|
|
# path. Once the new checksum is fully deployed, this condition will become
|
|
# obsolete.
|
|
check_file_exists(old_path, FILENAME, FILE)
|
|
|
|
# just checking?
|
|
if FILE is None:
|
|
send_ok("Missing")
|
|
sys.exit(-9)
|
|
|
|
# if a directory exists, check that it has the proper permissions
|
|
def check_dir(tmpdir, wok=os.W_OK):
|
|
if not os.access(tmpdir, os.F_OK):
|
|
return 0
|
|
if not os.access(tmpdir, os.R_OK|wok|os.X_OK):
|
|
send_error("Unable to write to %s repository." % (
|
|
tmpdir,))
|
|
if not os.path.isdir(tmpdir):
|
|
send_error("Path %s is not a directory." % (tmpdir,))
|
|
return 1
|
|
|
|
if not os.environ.has_key("SCRIPT_FILENAME"):
|
|
send_error("My running environment is funky. Aborting")
|
|
# the module's top level directory
|
|
my_moddir = "%s/%s" % (CACHE_DIR, NAME)
|
|
my_filedir = "%s/%s" % (my_moddir, FILENAME)
|
|
hash_dir = "%s/%s/%s" % (my_filedir, hash_type, checksum)
|
|
|
|
# first test if the module really exists
|
|
if not check_dir("%s/%s.git" % (GITREPO, NAME), 0):
|
|
log_msg("Unknown module", NAME)
|
|
send_ok("Module '%s' does not exist!" % (NAME,))
|
|
sys.exit(-9)
|
|
|
|
makedirs(my_moddir)
|
|
|
|
# grab a temporary filename and dump our file in there
|
|
tempfile.tempdir = my_moddir
|
|
tmpfile = tempfile.mktemp(checksum)
|
|
tmpfd = open(tmpfile, "wb+")
|
|
# now read the whole file in
|
|
m = hashlib.new(hash_type)
|
|
FILELENGTH=0
|
|
while 1:
|
|
s = FILE.file.read(BUFFER_SIZE)
|
|
if not s:
|
|
break
|
|
tmpfd.write(s)
|
|
m.update(s)
|
|
FILELENGTH = FILELENGTH + len(s)
|
|
# now we're done reading, check the MD5 sum of what we got
|
|
tmpfd.close()
|
|
check_checksum = m.hexdigest()
|
|
if checksum != check_checksum:
|
|
send_error("%s check failed. Received %s instead of %s" % (
|
|
hash_type.upper(), check_checksum, checksum))
|
|
# wow, even the checksum matches. make sure full path is valid now
|
|
makedirs(hash_dir)
|
|
# and move our file to the final location
|
|
os.rename(tmpfile, file_dest)
|
|
log_msg("Stored %s (%s bytes)" % (file_dest, FILELENGTH))
|
|
send_ok("File %s Size %d STORED OK" % (FILENAME, FILELENGTH))
|
|
|
|
# The file was uploaded with MD5, so we hardlink it to the old location
|
|
# (without hash type). This is a temporary workaround to make sure old clients
|
|
# can access files they uploaded.
|
|
if hash_type == 'md5':
|
|
hardlink(file_dest, old_path)
|
|
|
|
sys.exit(0)
|