centralize mojibake support stuff

This commit is contained in:
ed 2021-07-24 21:56:55 +02:00
parent 4451485664
commit d020527c6f
14 changed files with 176 additions and 83 deletions

View file

@ -11,6 +11,7 @@ import threading
from .__init__ import WINDOWS
from .util import IMPLICATIONS, uncyg, undot, absreal, Pebkac, fsdec, fsenc, statdir
from .bos import bos
class AXS(object):
@ -555,7 +556,7 @@ class AuthSrv(object):
for nch in range(len(hid)):
hpath = os.path.join(self.args.hist, hid[: nch + 1])
try:
os.makedirs(hpath)
bos.makedirs(hpath)
except:
pass
@ -579,7 +580,7 @@ class AuthSrv(object):
vol.histpath = absreal(vol.histpath)
if vol.dbv:
if os.path.exists(os.path.join(vol.histpath, "up2k.db")):
if bos.path.exists(os.path.join(vol.histpath, "up2k.db")):
promote.append(vol)
vol.dbv = None
else:

View file

54
copyparty/bos/bos.py Normal file
View file

@ -0,0 +1,54 @@
# coding: utf-8
from __future__ import print_function, unicode_literals
import os
from ..util import fsenc, fsdec
from . import path
# grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
# printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
def chmod(p, mode, follow_symlinks=True):
return os.chmod(fsenc(p), mode, follow_symlinks=follow_symlinks)
def listdir(p="."):
return [fsdec(x) for x in os.listdir(fsenc(p))]
def lstat(p):
return os.lstat(fsenc(p))
def makedirs(name, mode=0o755, exist_ok=None):
return os.makedirs(fsenc(name), mode=mode, exist_ok=exist_ok)
def mkdir(p, mode=0o755):
return os.mkdir(fsenc(p), mode=mode)
def rename(src, dst):
return os.rename(fsenc(src), fsenc(dst))
def replace(src, dst):
return os.replace(fsenc(src), fsenc(dst))
def rmdir(p):
return os.rmdir(fsenc(p))
def stat(p, follow_symlinks=True):
return os.stat(fsenc(p), follow_symlinks=follow_symlinks)
def unlink(p):
return os.unlink(fsenc(p))
def utime(p, times=None, follow_symlinks=True):
return os.utime(fsenc(p), times, follow_symlinks=follow_symlinks)

29
copyparty/bos/path.py Normal file
View file

@ -0,0 +1,29 @@
# coding: utf-8
from __future__ import print_function, unicode_literals
import os
from ..util import fsenc, fsdec
def exists(p):
return os.path.exists(fsenc(p))
def getmtime(p):
return os.path.getmtime(fsenc(p))
def getsize(p):
return os.path.getsize(fsenc(p))
def isdir(p):
return os.path.isdir(fsenc(p))
def islink(p):
return os.path.islink(fsenc(p))
def realpath(p):
return fsdec(os.path.realpath(fsenc(p)))

View file

@ -15,6 +15,7 @@ import calendar
from .__init__ import E, PY2, WINDOWS, ANYWIN, unicode
from .util import * # noqa # pylint: disable=unused-wildcard-import
from .bos import bos
from .authsrv import AuthSrv
from .szip import StreamZip
from .star import StreamTar
@ -615,11 +616,11 @@ class HttpCli(object):
if sub:
try:
dst = os.path.join(vfs.realpath, rem)
if not os.path.isdir(fsenc(dst)):
os.makedirs(fsenc(dst))
if not bos.path.isdir(dst):
bos.makedirs(dst)
except OSError as ex:
self.log("makedirs failed [{}]".format(dst))
if not os.path.isdir(fsenc(dst)):
if not bos.path.isdir(dst):
if ex.errno == 13:
raise Pebkac(500, "the server OS denied write-access")
@ -765,7 +766,7 @@ class HttpCli(object):
times = (int(time.time()), int(lastmod))
self.log("no more chunks, setting times {}".format(times))
try:
os.utime(fsenc(path), times)
bos.utime(path, times)
except:
self.log("failed to utime ({}, {})".format(path, times))
@ -810,14 +811,14 @@ class HttpCli(object):
fdir = os.path.join(vfs.realpath, rem)
fn = os.path.join(fdir, sanitized)
if not os.path.isdir(fsenc(fdir)):
if not bos.path.isdir(fdir):
raise Pebkac(500, "parent folder does not exist")
if os.path.isdir(fsenc(fn)):
if bos.path.isdir(fn):
raise Pebkac(500, "that folder exists already")
try:
os.mkdir(fsenc(fn))
bos.mkdir(fn)
except OSError as ex:
if ex.errno == 13:
raise Pebkac(500, "the server OS denied write-access")
@ -847,7 +848,7 @@ class HttpCli(object):
fdir = os.path.join(vfs.realpath, rem)
fn = os.path.join(fdir, sanitized)
if os.path.exists(fsenc(fn)):
if bos.path.exists(fn):
raise Pebkac(500, "that file exists already")
with open(fsenc(fn), "wb") as f:
@ -877,7 +878,7 @@ class HttpCli(object):
p_file, "", [".prologue.html", ".epilogue.html"]
)
if not os.path.isdir(fsenc(fdir)):
if not bos.path.isdir(fdir):
raise Pebkac(404, "that folder does not exist")
suffix = ".{:.6f}-{}".format(time.time(), self.ip)
@ -916,10 +917,10 @@ class HttpCli(object):
suffix = ".PARTIAL"
try:
os.rename(fsenc(fp), fsenc(fp2 + suffix))
bos.rename(fp, fp2 + suffix)
except:
fp2 = fp2[: -len(suffix) - 1]
os.rename(fsenc(fp), fsenc(fp2 + suffix))
bos.rename(fp, fp2 + suffix)
raise
@ -1017,7 +1018,7 @@ class HttpCli(object):
fp = os.path.join(vfs.realpath, rem)
srv_lastmod = srv_lastmod3 = -1
try:
st = os.stat(fsenc(fp))
st = bos.stat(fp)
srv_lastmod = st.st_mtime
srv_lastmod3 = int(srv_lastmod * 1000)
except OSError as ex:
@ -1056,10 +1057,10 @@ class HttpCli(object):
mdir, mfile = os.path.split(fp)
mfile2 = "{}.{:.3f}.md".format(mfile[:-3], srv_lastmod)
try:
os.mkdir(fsenc(os.path.join(mdir, ".hist")))
bos.mkdir(os.path.join(mdir, ".hist"))
except:
pass
os.rename(fsenc(fp), fsenc(os.path.join(mdir, ".hist", mfile2)))
bos.rename(fp, os.path.join(mdir, ".hist", mfile2))
p_field, _, p_data = next(self.parser.gen)
if p_field != "body":
@ -1068,7 +1069,7 @@ class HttpCli(object):
with open(fsenc(fp), "wb", 512 * 1024) as f:
sz, sha512, _ = hashcopy(p_data, f)
new_lastmod = os.stat(fsenc(fp)).st_mtime
new_lastmod = bos.stat(fp).st_mtime
new_lastmod3 = int(new_lastmod * 1000)
sha512 = sha512[:56]
@ -1113,7 +1114,7 @@ class HttpCli(object):
for ext in ["", ".gz", ".br"]:
try:
fs_path = req_path + ext
st = os.stat(fsenc(fs_path))
st = bos.stat(fs_path)
file_ts = max(file_ts, st.st_mtime)
editions[ext or "plain"] = [fs_path, st.st_size]
except:
@ -1365,10 +1366,10 @@ class HttpCli(object):
html_path = os.path.join(E.mod, "web", "{}.html".format(tpl))
template = self.j2(tpl)
st = os.stat(fsenc(fs_path))
st = bos.stat(fs_path)
ts_md = st.st_mtime
st = os.stat(fsenc(html_path))
st = bos.stat(html_path)
ts_html = st.st_mtime
sz_md = 0
@ -1585,7 +1586,7 @@ class HttpCli(object):
dbv, vrem = vn.get_dbv(rem)
try:
st = os.stat(fsenc(abspath))
st = bos.stat(abspath)
except:
raise Pebkac(404)
@ -1601,7 +1602,7 @@ class HttpCli(object):
if is_dir:
for fn in self.args.th_covers.split(","):
fp = os.path.join(abspath, fn)
if os.path.exists(fp):
if bos.path.exists(fp):
vrem = "{}/{}".format(vrem.rstrip("/"), fn)
is_dir = False
break
@ -1675,7 +1676,7 @@ class HttpCli(object):
logues = ["", ""]
for n, fn in enumerate([".prologue.html", ".epilogue.html"]):
fn = os.path.join(abspath, fn)
if os.path.exists(fsenc(fn)):
if bos.path.exists(fn):
with open(fsenc(fn), "rb") as f:
logues[n] = f.read().decode("utf-8")
@ -1739,7 +1740,7 @@ class HttpCli(object):
histdir = os.path.join(fsroot, ".hist")
ptn = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[^\.]+)$")
try:
for hfn in os.listdir(histdir):
for hfn in bos.listdir(histdir):
m = ptn.match(hfn)
if not m:
continue
@ -1780,7 +1781,7 @@ class HttpCli(object):
fspath = fsroot + "/" + fn
try:
inf = stats.get(fn) or os.stat(fsenc(fspath))
inf = stats.get(fn) or bos.stat(fspath)
except:
self.log("broken symlink: {}".format(repr(fspath)))
continue

View file

@ -28,6 +28,7 @@ except ImportError:
from .__init__ import E, PY2, MACOS
from .util import spack, min_ex, start_stackmon, start_log_thrs
from .bos import bos
from .httpconn import HttpConn
if PY2:
@ -73,7 +74,7 @@ class HttpSrv(object):
}
cert_path = os.path.join(E.cfg, "cert.pem")
if os.path.exists(cert_path):
if bos.path.exists(cert_path):
self.cert_path = cert_path
else:
self.cert_path = None

View file

@ -9,6 +9,7 @@ import subprocess as sp
from .__init__ import PY2, WINDOWS, unicode
from .util import fsenc, fsdec, uncyg, REKOBO_LKEY
from .bos import bos
def have_ff(cmd):
@ -44,7 +45,7 @@ class MParser(object):
if WINDOWS:
bp = uncyg(bp)
if os.path.exists(bp):
if bos.path.exists(bp):
self.bin = bp
return
except:
@ -420,7 +421,7 @@ class MTag(object):
except Exception as ex:
return self.get_ffprobe(abspath) if self.can_ffprobe else {}
sz = os.path.getsize(fsenc(abspath))
sz = bos.path.getsize(abspath)
ret = {".q": [0, int((sz / md.info.length) / 128)]}
for attr, k, norm in [

View file

@ -1,12 +1,12 @@
# coding: utf-8
from __future__ import print_function, unicode_literals
import os
import tarfile
import threading
from .sutil import errdesc
from .util import Queue, fsenc
from .bos import bos
class QFile(object):
@ -61,7 +61,7 @@ class StreamTar(object):
yield None
if self.errf:
os.unlink(self.errf["ap"])
bos.unlink(self.errf["ap"])
def ser(self, f):
name = f["vp"]

View file

@ -1,11 +1,12 @@
# coding: utf-8
from __future__ import print_function, unicode_literals
import os
import time
import tempfile
from datetime import datetime
from .bos import bos
def errdesc(errors):
report = ["copyparty failed to add the following files to the archive:", ""]
@ -20,9 +21,9 @@ def errdesc(errors):
dt = datetime.utcfromtimestamp(time.time())
dt = dt.strftime("%Y-%m%d-%H%M%S")
os.chmod(tf_path, 0o444)
bos.chmod(tf_path, 0o444)
return {
"vp": "archive-errors-{}.txt".format(dt),
"ap": tf_path,
"st": os.stat(tf_path),
"st": bos.stat(tf_path),
}, report

View file

@ -8,6 +8,7 @@ from datetime import datetime
from .sutil import errdesc
from .util import yieldfile, sanitize_fn, spack, sunpack
from .bos import bos
def dostime2unix(buf):
@ -271,4 +272,4 @@ class StreamZip(object):
yield self._ct(ecdr)
if errors:
os.unlink(errf["ap"])
bos.unlink(errf["ap"])

View file

@ -5,6 +5,7 @@ import os
from .util import Cooldown
from .th_srv import thumb_path, THUMBABLE, FMT_FF
from .bos import bos
class ThumbCli(object):
@ -36,7 +37,7 @@ class ThumbCli(object):
tpath = thumb_path(histpath, rem, mtime, fmt)
ret = None
try:
st = os.stat(tpath)
st = bos.stat(tpath)
if st.st_size:
ret = tpath
else:

View file

@ -11,6 +11,7 @@ import subprocess as sp
from .__init__ import PY2, unicode
from .util import fsenc, vsplit, runcmd, Queue, Cooldown, BytesIO, min_ex
from .bos import bos
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, ffprobe
@ -155,12 +156,12 @@ class ThumbSrv(object):
except:
thdir = os.path.dirname(tpath)
try:
os.makedirs(thdir)
bos.makedirs(thdir)
except:
pass
inf_path = os.path.join(thdir, "dir.txt")
if not os.path.exists(inf_path):
if not bos.path.exists(inf_path):
with open(inf_path, "wb") as f:
f.write(fsenc(os.path.dirname(abspath)))
@ -180,7 +181,7 @@ class ThumbSrv(object):
cond.wait(3)
try:
st = os.stat(tpath)
st = bos.stat(tpath)
if st.st_size:
return tpath
except:
@ -197,7 +198,7 @@ class ThumbSrv(object):
abspath, tpath = task
ext = abspath.split(".")[-1].lower()
fun = None
if not os.path.exists(tpath):
if not bos.path.exists(tpath):
if ext in FMT_PIL:
fun = self.conv_pil
elif ext in FMT_FF:
@ -323,7 +324,7 @@ class ThumbSrv(object):
p1 = os.path.dirname(tdir)
p2 = os.path.dirname(p1)
for dp in [tdir, p1, p2]:
os.utime(fsenc(dp), (ts, ts))
bos.utime(dp, (ts, ts))
except:
pass
@ -350,7 +351,7 @@ class ThumbSrv(object):
prev_b64 = None
prev_fp = None
try:
ents = os.listdir(thumbpath)
ents = bos.listdir(thumbpath)
except:
return 0
@ -361,7 +362,7 @@ class ThumbSrv(object):
# "top" or b64 prefix/full (a folder)
if len(f) <= 3 or len(f) == 24:
age = now - os.path.getmtime(fp)
age = now - bos.path.getmtime(fp)
if age > maxage:
with self.mutex:
safe = True
@ -393,7 +394,7 @@ class ThumbSrv(object):
if b64 == prev_b64:
self.log("rm replaced [{}]".format(fp))
os.unlink(prev_fp)
bos.unlink(prev_fp)
prev_b64 = b64
prev_fp = fp

View file

@ -9,6 +9,7 @@ from datetime import datetime
from .__init__ import unicode
from .util import s3dec, Pebkac, min_ex
from .bos import bos
from .up2k import up2k_wark_from_hashlist
@ -67,7 +68,7 @@ class U2idx(object):
histpath = self.asrv.vfs.histtab[ptop]
db_path = os.path.join(histpath, "up2k.db")
if not os.path.exists(db_path):
if not bos.path.exists(db_path):
return None
cur = sqlite3.connect(db_path, 2).cursor()

View file

@ -34,6 +34,7 @@ from .util import (
s2hms,
min_ex,
)
from .bos import bos
from .authsrv import AuthSrv
from .mtag import MTag, MParser
@ -214,7 +215,7 @@ class Up2k(object):
# only need to protect register_vpath but all in one go feels right
for vol in vols:
try:
os.listdir(vol.realpath)
bos.listdir(vol.realpath)
except:
self.volstate[vol.vpath] = "OFFLINE (cannot access folder)"
self.log("cannot access " + vol.realpath, c=1)
@ -352,14 +353,14 @@ class Up2k(object):
reg = {}
path = os.path.join(histpath, "up2k.snap")
if "e2d" in flags and os.path.exists(path):
if "e2d" in flags and bos.path.exists(path):
with gzip.GzipFile(path, "rb") as f:
j = f.read().decode("utf-8")
reg2 = json.loads(j)
for k, job in reg2.items():
path = os.path.join(job["ptop"], job["prel"], job["name"])
if os.path.exists(fsenc(path)):
if bos.path.exists(path):
reg[k] = job
job["poke"] = time.time()
else:
@ -375,7 +376,7 @@ class Up2k(object):
return None
try:
os.makedirs(histpath)
bos.makedirs(histpath)
except:
pass
@ -519,7 +520,7 @@ class Up2k(object):
# almost zero overhead dw
self.pp.msg = "b{} {}".format(nfiles - nchecked, abspath)
try:
if not os.path.exists(fsenc(abspath)):
if not bos.path.exists(abspath):
rm.append([drd, dfn])
except Exception as ex:
self.log("stat-rm: {} @ [{}]".format(repr(ex), abspath))
@ -907,7 +908,7 @@ class Up2k(object):
# x.set_trace_callback(trace)
def _open_db(self, db_path):
existed = os.path.exists(db_path)
existed = bos.path.exists(db_path)
cur = self._orz(db_path)
ver = self._read_ver(cur)
if not existed and ver is None:
@ -933,7 +934,7 @@ class Up2k(object):
db = cur.connection
cur.close()
db.close()
os.unlink(db_path)
bos.unlink(db_path)
return self._create_db(db_path, None)
def _backup_db(self, db_path, cur, ver, msg):
@ -1029,7 +1030,7 @@ class Up2k(object):
dp_abs = "/".join([cj["ptop"], dp_dir, dp_fn])
# relying on path.exists to return false on broken symlinks
if os.path.exists(fsenc(dp_abs)):
if bos.path.exists(dp_abs):
job = {
"name": dp_fn,
"prel": dp_dir,
@ -1053,7 +1054,7 @@ class Up2k(object):
for fn in names:
path = os.path.join(job["ptop"], job["prel"], fn)
try:
if os.path.getsize(fsenc(path)) > 0:
if bos.path.getsize(path) > 0:
# upload completed or both present
break
except:
@ -1087,7 +1088,7 @@ class Up2k(object):
job["name"] = self._untaken(pdir, cj["name"], now, cj["addr"])
dst = os.path.join(job["ptop"], job["prel"], job["name"])
if not self.args.nw:
os.unlink(fsenc(dst)) # TODO ed pls
bos.unlink(dst) # TODO ed pls
self._symlink(src, dst)
if cur:
@ -1152,8 +1153,8 @@ class Up2k(object):
try:
lsrc = src
ldst = dst
fs1 = os.stat(fsenc(os.path.split(src)[0])).st_dev
fs2 = os.stat(fsenc(os.path.split(dst)[0])).st_dev
fs1 = bos.stat(os.path.split(src)[0]).st_dev
fs2 = bos.stat(os.path.split(dst)[0]).st_dev
if fs1 == 0:
# py2 on winxp or other unsupported combination
raise OSError()
@ -1283,8 +1284,8 @@ class Up2k(object):
atop = vn.canonical(rem)
adir, fn = os.path.split(atop)
fun = os.lstat if os.supports_follow_symlinks else os.stat
st = fun(fsenc(atop))
fun = bos.lstat if os.supports_follow_symlinks else bos.stat
st = fun(atop)
if stat.S_ISLNK(st.st_mode) or stat.S_ISREG(st.st_mode):
g = [[os.path.dirname(vpath), adir, [[fn, 0]], [], []]]
else:
@ -1307,12 +1308,12 @@ class Up2k(object):
cur, wark = self._find_from_vpath(ptop, vrem)
self._forget_file(ptop, vpath, cur, wark)
os.unlink(abspath)
bos.unlink(abspath)
n_dirs = 0
for d in dirs.keys():
try:
os.rmdir(d)
bos.rmdir(d)
n_dirs += 1
except:
pass
@ -1329,7 +1330,7 @@ class Up2k(object):
if not srem:
raise Pebkac(400, "mv: cannot move a mountpoint")
if os.path.exists(dabs):
if bos.path.exists(dabs):
raise Pebkac(400, "mv: target file exists")
c1, w = self._find_from_vpath(svn.realpath, srem)
@ -1345,15 +1346,15 @@ class Up2k(object):
slabs = "{}/{}".join(rd, fn).strip("/")
slabs = absreal(os.path.join(dvn.realpath, slabs))
if os.path.exists(fsenc(slabs)):
if bos.path.exists(slabs):
self.log("mv: quick relink, nice")
self._symlink(fsenc(slabs), fsenc(dabs))
st = os.stat(fsenc(sabs))
self._symlink(slabs, dabs)
st = bos.stat(sabs)
self.db_add(c2, w, drd, dfn, st.st_mtime, st.st_size)
os.unlink(fsenc(sabs))
bos.unlink(sabs)
else:
self.log("mv: file in db missing? whatever, fixed")
os.rename(fsenc(sabs), fsenc(slabs))
bos.rename(sabs, slabs)
self._forget_file(svn.realpath, srem, c1, w)
return "k"
@ -1362,9 +1363,9 @@ class Up2k(object):
self.log("mv: plain move")
self._copy_tags(c1, c2, w)
self._forget_file(svn.realpath, srem, c1, w)
st = os.stat(fsenc(sabs))
st = bos.stat(sabs)
self.db_add(c2, w, drd, dfn, st.st_mtime, st.st_size)
os.rename(fsenc(sabs), fsenc(dabs))
bos.rename(sabs, dabs)
return "k"
def _copy_tags(self, csrc, cdst, wark):
@ -1455,7 +1456,7 @@ class Up2k(object):
links = {}
for vp in dupes:
ap = gabs(vp)
d = links if os.path.islink(ap) else full
d = links if bos.path.islink(ap) else full
d[vp] = ap
if not vp2 and not full:
@ -1464,17 +1465,17 @@ class Up2k(object):
dabs = links.pop(dvp)
sabs = gabs(vp1)
self.log("linkswap [{}] and [{}]".format(sabs, dabs))
os.unlink(dabs)
os.rename(sabs, dabs)
os.link(sabs, dabs)
bos.unlink(dabs)
bos.rename(sabs, dabs)
self._symlink(sabs, dabs)
full[vp1] = sabs
dvp = vp2 if vp2 else full.keys()[0]
dabs = gabs(dvp)
for alink in links.values():
self.log("relinking [{}] to [{}]".format(alink, dabs))
os.unlink(alink)
os.link(alink, dabs)
bos.unlink(alink)
self._symlink(alink, dabs)
def _get_wark(self, cj):
if len(cj["name"]) > 1024 or len(cj["hash"]) > 512 * 1024: # 16TiB
@ -1497,7 +1498,7 @@ class Up2k(object):
def _hashlist_from_file(self, path):
pp = self.pp if hasattr(self, "pp") else None
fsz = os.path.getsize(fsenc(path))
fsz = bos.path.getsize(path)
csz = up2k_chunksize(fsz)
ret = []
with open(fsenc(path), "rb", 512 * 1024) as f:
@ -1565,7 +1566,7 @@ class Up2k(object):
for path, sz, times in ready:
self.log("lmod: setting times {} on {}".format(times, path))
try:
os.utime(fsenc(path), times)
bos.utime(path, times)
except:
self.log("lmod: failed to utime ({}, {})".format(path, times))
@ -1601,13 +1602,13 @@ class Up2k(object):
try:
# remove the filename reservation
path = os.path.join(job["ptop"], job["prel"], job["name"])
if os.path.getsize(fsenc(path)) == 0:
os.unlink(fsenc(path))
if bos.path.getsize(path) == 0:
bos.unlink(path)
if len(job["hash"]) == len(job["need"]):
# PARTIAL is empty, delete that too
path = os.path.join(job["ptop"], job["prel"], job["tnam"])
os.unlink(fsenc(path))
bos.unlink(path)
except:
pass
@ -1615,8 +1616,8 @@ class Up2k(object):
if not reg:
if ptop not in self.snap_prev or self.snap_prev[ptop] is not None:
self.snap_prev[ptop] = None
if os.path.exists(fsenc(path)):
os.unlink(fsenc(path))
if bos.path.exists(path):
bos.unlink(path)
return
newest = max(x["poke"] for _, x in reg.items()) if reg else 0
@ -1625,7 +1626,7 @@ class Up2k(object):
return
try:
os.makedirs(histpath)
bos.makedirs(histpath)
except:
pass
@ -1692,7 +1693,7 @@ class Up2k(object):
abspath = os.path.join(ptop, rd, fn)
self.log("hashing " + abspath)
inf = os.stat(fsenc(abspath))
inf = bos.stat(abspath)
hashes = self._hashlist_from_file(abspath)
wark = up2k_wark_from_hashlist(self.salt, inf.st_size, hashes)
with self.mutex: