mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-04 04:04:10 +00:00
146 lines
5.0 KiB
Python
146 lines
5.0 KiB
Python
|
|
||
|
import os.path
|
||
|
import urllib
|
||
|
import simplejson
|
||
|
from allmydata.scripts.common import get_alias, escape_path, DefaultAliasMarker
|
||
|
from allmydata.scripts.common_http import do_http
|
||
|
|
||
|
def ascii_or_none(s):
|
||
|
if s is None:
|
||
|
return s
|
||
|
return str(s)
|
||
|
|
||
|
def get_info(nodeurl, aliases, target):
|
||
|
rootcap, path = get_alias(aliases, target, None)
|
||
|
if rootcap == DefaultAliasMarker:
|
||
|
# this is a local file
|
||
|
pathname = os.path.abspath(os.path.expanduser(path))
|
||
|
if not os.path.exists(pathname):
|
||
|
return ("empty", "local", pathname)
|
||
|
if os.path.isdir(pathname):
|
||
|
return ("directory", "local", pathname)
|
||
|
else:
|
||
|
assert os.path.isfile(pathname)
|
||
|
return ("file", "local", pathname)
|
||
|
else:
|
||
|
# this is a tahoe object
|
||
|
url = nodeurl + "uri/%s" % urllib.quote(rootcap)
|
||
|
if path:
|
||
|
url += "/" + escape_path(path)
|
||
|
resp = do_http("GET", url + "?t=json")
|
||
|
if resp.status == 404:
|
||
|
# doesn't exist yet
|
||
|
return ("empty", "tahoe", False, None, None, url)
|
||
|
parsed = simplejson.loads(resp.read())
|
||
|
nodetype, d = parsed
|
||
|
mutable = d.get("mutable", False) # older nodes don't provide 'mutable'
|
||
|
rw_uri = ascii_or_none(d.get("rw_uri"))
|
||
|
ro_uri = ascii_or_none(d.get("ro_uri"))
|
||
|
if nodetype == "dirnode":
|
||
|
return ("directory", "tahoe", mutable, rw_uri, ro_uri, url)
|
||
|
else:
|
||
|
return ("file", "tahoe", mutable, rw_uri, ro_uri, url)
|
||
|
|
||
|
def copy(nodeurl, config, aliases, sources, destination,
|
||
|
verbosity, stdout, stderr):
|
||
|
if nodeurl[-1] != "/":
|
||
|
nodeurl += "/"
|
||
|
recursive = config["recursive"]
|
||
|
|
||
|
#print "sources:", sources
|
||
|
#print "dest:", destination
|
||
|
|
||
|
target = get_info(nodeurl, aliases, destination)
|
||
|
#print target
|
||
|
|
||
|
source_info = dict([(get_info(nodeurl, aliases, source), source)
|
||
|
for source in sources])
|
||
|
source_files = [s for s in source_info if s[0] == "file"]
|
||
|
source_dirs = [s for s in source_info if s[0] == "directory"]
|
||
|
empty_sources = [s for s in source_info if s[0] == "empty"]
|
||
|
if empty_sources:
|
||
|
for s in empty_sources:
|
||
|
print >>stderr, "no such file or directory %s" % source_info[s]
|
||
|
return 1
|
||
|
|
||
|
#print "source_files", " ".join([source_info[s] for s in source_files])
|
||
|
#print "source_dirs", " ".join([source_info[s] for s in source_dirs])
|
||
|
|
||
|
if source_dirs and not recursive:
|
||
|
print >>stderr, "cannot copy directories without --recursive"
|
||
|
return 1
|
||
|
|
||
|
if target[0] == "file":
|
||
|
# cp STUFF foo.txt, where foo.txt already exists. This limits the
|
||
|
# possibilities considerably.
|
||
|
if len(sources) > 1:
|
||
|
print >>stderr, "target '%s' is not a directory" % destination
|
||
|
return 1
|
||
|
if source_dirs:
|
||
|
print >>stderr, "cannot copy directory into a file"
|
||
|
return 1
|
||
|
return copy_to_file(source_files[0], target)
|
||
|
|
||
|
if target[0] == "empty":
|
||
|
if recursive:
|
||
|
return copy_to_directory(source_files, source_dirs, target)
|
||
|
if len(sources) > 1:
|
||
|
# if we have -r, we'll auto-create the target directory. Without
|
||
|
# it, we'll only create a file.
|
||
|
print >>stderr, "cannot copy multiple files into a file without -r"
|
||
|
return 1
|
||
|
# cp file1 newfile
|
||
|
return copy_to_file(source_files[0], target)
|
||
|
|
||
|
if target[0] == "directory":
|
||
|
return copy_to_directory(source_files, source_dirs, target)
|
||
|
|
||
|
print >>stderr, "unknown target"
|
||
|
return 1
|
||
|
|
||
|
|
||
|
def get_file_data(source):
|
||
|
assert source[0] == "file"
|
||
|
if source[1] == "local":
|
||
|
return open(source[2], "rb").read()
|
||
|
return do_http("GET", source[-1]).read()
|
||
|
|
||
|
class WriteError(Exception):
|
||
|
pass
|
||
|
|
||
|
def check_PUT(resp):
|
||
|
if resp.status in (200, 201):
|
||
|
return True
|
||
|
raise WriteError("Error during PUT: %s %s %s" % (resp.status, resp.reason,
|
||
|
resp.read()))
|
||
|
|
||
|
def put_file_data(data, target):
|
||
|
if target[1] == "local":
|
||
|
open(target[2], "wb").write(data)
|
||
|
return True
|
||
|
resp = do_http("PUT", target[-1], data)
|
||
|
return check_PUT(resp)
|
||
|
|
||
|
def put_uri(uri, target):
|
||
|
resp = do_http("PUT", target[-1] + "?t=uri", uri)
|
||
|
return check_PUT(resp)
|
||
|
|
||
|
def copy_to_file(source, target):
|
||
|
assert source[0] == "file"
|
||
|
# do we need to copy bytes?
|
||
|
if source[1] == "local" or source[2] == True or target[1] == "local":
|
||
|
# yes
|
||
|
data = get_file_data(source)
|
||
|
put_file_data(data, target)
|
||
|
return
|
||
|
# no, we're getting data from an immutable source, and we're copying into
|
||
|
# the tahoe grid, so we can just copy the URI.
|
||
|
uri = source[3] or source[4] # prefer rw_uri, fall back to ro_uri
|
||
|
# TODO: if the original was mutable, and we're creating the target,
|
||
|
# should be we create a mutable file to match? At the moment we always
|
||
|
# create immutable files.
|
||
|
put_uri(uri, target)
|
||
|
|
||
|
def copy_to_directory(source_files, source_dirs, target):
|
||
|
NotImplementedError
|