don't modify untracked files

This commit is contained in:
ed 2020-04-20 14:49:25 +00:00
parent c2016ba037
commit 53f22c25c9
3 changed files with 39 additions and 14 deletions

View file

@ -275,7 +275,7 @@ class HttpCli(object):
body["vdir"] = self.vpath body["vdir"] = self.vpath
body["rdir"] = os.path.join(vfs.realpath, rem) body["rdir"] = os.path.join(vfs.realpath, rem)
body["addr"] = self.conn.addr[0] body["addr"] = self.addr[0]
x = self.conn.hsrv.broker.put(True, "up2k.handle_json", body) x = self.conn.hsrv.broker.put(True, "up2k.handle_json", body)
response = x.get() response = x.get()
@ -435,10 +435,12 @@ class HttpCli(object):
if not os.path.isdir(fsenc(fdir)): if not os.path.isdir(fsenc(fdir)):
raise Pebkac(404, "that folder does not exist") raise Pebkac(404, "that folder does not exist")
# TODO broker which avoid this race # TODO broker which avoid this race and
# and provides a new filename if taken # provides a new filename if taken (same as up2k)
if os.path.exists(fsenc(fn)): if os.path.exists(fsenc(fn)):
fn += ".{:.6f}".format(time.time()) fn += ".{:.6f}-{}".format(time.time(), self.addr[0])
# using current-time instead of t0 cause clients
# may reuse a name for multiple files in one post
try: try:
with open(fsenc(fn), "wb") as f: with open(fsenc(fn), "wb") as f:

View file

@ -13,7 +13,7 @@ import threading
from copy import deepcopy from copy import deepcopy
from .__init__ import WINDOWS from .__init__ import WINDOWS
from .util import Pebkac, Queue from .util import Pebkac, Queue, fsenc
class Up2k(object): class Up2k(object):
@ -49,6 +49,7 @@ class Up2k(object):
def handle_json(self, cj): def handle_json(self, cj):
wark = self._get_wark(cj) wark = self._get_wark(cj)
now = time.time()
with self.mutex: with self.mutex:
# TODO use registry persistence here to symlink any matching wark # TODO use registry persistence here to symlink any matching wark
if wark in self.registry: if wark in self.registry:
@ -63,11 +64,16 @@ class Up2k(object):
) )
raise Pebkac(400, err) raise Pebkac(400, err)
else: else:
self._symlink(src, dst) # symlink to the client-provided name,
# returning the previous upload info
job = deepcopy(job)
suffix = self._suffix(dst, now, job["addr"])
job["name"] = cj["name"] + suffix
self._symlink(src, dst + suffix)
else: else:
job = { job = {
"wark": wark, "wark": wark,
"t0": int(time.time()), "t0": now,
"addr": cj["addr"], "addr": cj["addr"],
"vdir": cj["vdir"], "vdir": cj["vdir"],
"rdir": cj["rdir"], "rdir": cj["rdir"],
@ -78,6 +84,9 @@ class Up2k(object):
"hash": deepcopy(cj["hash"]), "hash": deepcopy(cj["hash"]),
} }
path = os.path.join(job["rdir"], job["name"])
job["name"] += self._suffix(path, now, cj["addr"])
# one chunk may occur multiple times in a file; # one chunk may occur multiple times in a file;
# filter to unique values for the list of missing chunks # filter to unique values for the list of missing chunks
# (preserve order to reduce disk thrashing) # (preserve order to reduce disk thrashing)
@ -98,14 +107,22 @@ class Up2k(object):
"wark": wark, "wark": wark,
} }
def _suffix(self, fpath, ts, ip):
# TODO broker which avoid this race and
# provides a new filename if taken (same as bup)
if not os.path.exists(fsenc(fpath)):
return ""
return ".{:.6f}-{}".format(ts, ip)
def _symlink(self, src, dst): def _symlink(self, src, dst):
# TODO store this in linktab so we never delete src if there are links to it # TODO store this in linktab so we never delete src if there are links to it
self.log("up2k", "linking dupe:\n {0}\n {1}".format(src, dst)) self.log("up2k", "linking dupe:\n {0}\n {1}".format(src, dst))
try: try:
lsrc = src lsrc = src
ldst = dst ldst = dst
fs1 = os.stat(os.path.split(src)[0]).st_dev fs1 = os.stat(fsenc(os.path.split(src)[0])).st_dev
fs2 = os.stat(os.path.split(dst)[0]).st_dev fs2 = os.stat(fsenc(os.path.split(dst)[0])).st_dev
if fs1 == 0: if fs1 == 0:
# py2 on winxp or other unsupported combination # py2 on winxp or other unsupported combination
raise OSError() raise OSError()
@ -121,10 +138,10 @@ class Up2k(object):
if nc > 1: if nc > 1:
lsrc = nsrc[nc:] lsrc = nsrc[nc:]
lsrc = "../" * (len(lsrc) - 1) + "/".join(lsrc) lsrc = "../" * (len(lsrc) - 1) + "/".join(lsrc)
os.symlink(lsrc, ldst) os.symlink(fsenc(lsrc), fsenc(ldst))
except (AttributeError, OSError) as ex: except (AttributeError, OSError) as ex:
self.log("up2k", "cannot symlink; creating copy") self.log("up2k", "cannot symlink; creating copy")
shutil.copy2(src, dst) shutil.copy2(fsenc(src), fsenc(dst))
def handle_chunk(self, wark, chash): def handle_chunk(self, wark, chash):
with self.mutex: with self.mutex:
@ -201,7 +218,7 @@ class Up2k(object):
def _new_upload(self, job): def _new_upload(self, job):
self.registry[job["wark"]] = job self.registry[job["wark"]] = job
path = os.path.join(job["rdir"], job["name"]) path = os.path.join(job["rdir"], job["name"])
with open(path, "wb") as f: with open(fsenc(path), "wb") as f:
f.seek(job["size"] - 1) f.seek(job["size"] - 1)
f.write(b"e") f.write(b"e")
@ -215,6 +232,6 @@ class Up2k(object):
time.sleep(5) time.sleep(5)
for path, times in ready: for path, times in ready:
try: try:
os.utime(path, times) os.utime(fsenc(path), times)
except: except:
self.log("lmod", "failed to utime ({}, {})".format(path, times)) self.log("lmod", "failed to utime ({}, {})".format(path, times))

View file

@ -338,7 +338,7 @@ function up2k_init(have_crypto) {
continue; continue;
var tr = document.createElement('tr'); var tr = document.createElement('tr');
tr.innerHTML = '<td></td><td id="f{0}t">hashing</td><td id="f{0}p" class="prog"></td>'.format(st.files.length); tr.innerHTML = '<td id="f{0}n"></td><td id="f{0}t">hashing</td><td id="f{0}p" class="prog"></td>'.format(st.files.length);
tr.getElementsByTagName('td')[0].textContent = entry.name; tr.getElementsByTagName('td')[0].textContent = entry.name;
o('u2tab').appendChild(tr); o('u2tab').appendChild(tr);
@ -686,6 +686,12 @@ function up2k_init(have_crypto) {
if (xhr.status == 200) { if (xhr.status == 200) {
var response = JSON.parse(xhr.responseText); var response = JSON.parse(xhr.responseText);
if (response.name !== t.name) {
// file exists; server renamed us
t.name = response.name;
o('f{0}n'.format(t.n)).textContent = t.name;
}
t.postlist = []; t.postlist = [];
t.wark = response.wark; t.wark = response.wark;
var missing = response.hash; var missing = response.hash;