mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
partyfuse: cleanup logging and exceptions
windows runs 50% faster with recentlog on infos too...
This commit is contained in:
parent
88a1c5ca5d
commit
8b942ea237
275
bin/partyfuse.py
275
bin/partyfuse.py
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import print_function, unicode_literals
|
|
||||||
|
|
||||||
"""partyfuse: remote copyparty as a local filesystem"""
|
"""partyfuse: remote copyparty as a local filesystem"""
|
||||||
__author__ = "ed <copyparty@ocv.me>"
|
__author__ = "ed <copyparty@ocv.me>"
|
||||||
|
@ -29,27 +28,28 @@ get server cert:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
import stat
|
|
||||||
import errno
|
|
||||||
import struct
|
|
||||||
import codecs
|
|
||||||
import builtins
|
|
||||||
import platform
|
|
||||||
import argparse
|
import argparse
|
||||||
import threading
|
|
||||||
import traceback
|
|
||||||
import http.client # py2: httplib
|
|
||||||
import urllib.parse
|
|
||||||
import calendar
|
import calendar
|
||||||
|
import codecs
|
||||||
|
import errno
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import stat
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
import urllib.parse
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from urllib.parse import quote_from_bytes as quote
|
from urllib.parse import quote_from_bytes as quote
|
||||||
from urllib.parse import unquote_to_bytes as unquote
|
from urllib.parse import unquote_to_bytes as unquote
|
||||||
|
|
||||||
|
import builtins
|
||||||
|
import http.client
|
||||||
|
|
||||||
WINDOWS = sys.platform == "win32"
|
WINDOWS = sys.platform == "win32"
|
||||||
MACOS = platform.system() == "Darwin"
|
MACOS = platform.system() == "Darwin"
|
||||||
UTC = timezone.utc
|
UTC = timezone.utc
|
||||||
|
@ -71,11 +71,12 @@ print(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def null_log(msg):
|
def nullfun(*a):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
info = log = dbg = null_log
|
info = dbg = nullfun
|
||||||
|
is_dbg = False
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -106,29 +107,28 @@ def termsafe(txt):
|
||||||
return txt.encode(sys.stdout.encoding, "replace").decode(sys.stdout.encoding)
|
return txt.encode(sys.stdout.encoding, "replace").decode(sys.stdout.encoding)
|
||||||
|
|
||||||
|
|
||||||
def threadless_log(msg):
|
def threadless_log(fmt, *a):
|
||||||
print(msg + "\n", end="")
|
fmt += "\n"
|
||||||
|
print(fmt % a if a else fmt, end="")
|
||||||
|
|
||||||
|
|
||||||
def boring_log(msg):
|
riced_tids = {}
|
||||||
msg = "\033[36m{:012x}\033[0m {}\n".format(threading.current_thread().ident, msg)
|
|
||||||
print(msg[4:], end="")
|
|
||||||
|
|
||||||
|
|
||||||
def rice_tid():
|
def rice_tid():
|
||||||
tid = threading.current_thread().ident
|
tid = threading.current_thread().ident
|
||||||
|
try:
|
||||||
|
return riced_tids[tid]
|
||||||
|
except:
|
||||||
c = struct.unpack(b"B" * 5, struct.pack(b">Q", tid)[-5:])
|
c = struct.unpack(b"B" * 5, struct.pack(b">Q", tid)[-5:])
|
||||||
return "".join("\033[1;37;48;5;{}m{:02x}".format(x, x) for x in c) + "\033[0m"
|
ret = "".join("\033[1;37;48;5;%dm%02x" % (x, x) for x in c) + "\033[0m"
|
||||||
|
riced_tids[tid] = ret
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def fancy_log(msg):
|
def fancy_log(fmt, *a):
|
||||||
print("{:10.6f} {} {}\n".format(time.time() % 900, rice_tid(), msg), end="")
|
msg = fmt % a if a else fmt
|
||||||
|
print("%10.6f %s %s\n" % (time.time() % 900, rice_tid(), msg), end="")
|
||||||
|
|
||||||
def hexler(binary):
|
|
||||||
return binary.replace("\r", "\\r").replace("\n", "\\n")
|
|
||||||
return " ".join(["{}\033[36m{:02x}\033[0m".format(b, ord(b)) for b in binary])
|
|
||||||
return " ".join(map(lambda b: format(ord(b), "02x"), binary))
|
|
||||||
|
|
||||||
|
|
||||||
def register_wtf8():
|
def register_wtf8():
|
||||||
|
@ -167,19 +167,28 @@ def dewin(txt):
|
||||||
|
|
||||||
|
|
||||||
class RecentLog(object):
|
class RecentLog(object):
|
||||||
def __init__(self):
|
def __init__(self, ar):
|
||||||
|
self.ar = ar
|
||||||
self.mtx = threading.Lock()
|
self.mtx = threading.Lock()
|
||||||
self.f = None # open("partyfuse.log", "wb")
|
self.f = open(ar.logf, "wb") if ar.logf else None
|
||||||
self.q = []
|
self.q = []
|
||||||
|
|
||||||
thr = threading.Thread(target=self.printer)
|
thr = threading.Thread(target=self.printer)
|
||||||
thr.daemon = True
|
thr.daemon = True
|
||||||
thr.start()
|
thr.start()
|
||||||
|
|
||||||
def put(self, msg):
|
def put(self, fmt, *a):
|
||||||
msg = "{:10.6f} {} {}\n".format(time.time() % 900, rice_tid(), msg)
|
msg = fmt % a if a else fmt
|
||||||
|
msg = "%10.6f %s %s\n" % (time.time() % 900, rice_tid(), msg)
|
||||||
if self.f:
|
if self.f:
|
||||||
fmsg = " ".join([datetime.now(UTC).strftime("%H%M%S.%f"), str(msg)])
|
zd = datetime.now(UTC)
|
||||||
|
fmsg = "%d-%04d-%06d.%06d %s" % (
|
||||||
|
zd.year,
|
||||||
|
zd.month * 100 + zd.day,
|
||||||
|
(zd.hour * 100 + zd.minute) * 100 + zd.second,
|
||||||
|
zd.microsecond,
|
||||||
|
msg,
|
||||||
|
)
|
||||||
self.f.write(fmsg.encode("utf-8"))
|
self.f.write(fmsg.encode("utf-8"))
|
||||||
|
|
||||||
with self.mtx:
|
with self.mtx:
|
||||||
|
@ -315,8 +324,8 @@ class Gateway(object):
|
||||||
c = self.getconn(tid)
|
c = self.getconn(tid)
|
||||||
c.request(meth, path, headers=headers, **kwargs)
|
c.request(meth, path, headers=headers, **kwargs)
|
||||||
return c.getresponse()
|
return c.getresponse()
|
||||||
except:
|
except Exception as ex:
|
||||||
dbg("bad conn")
|
info("HTTP %r", ex)
|
||||||
|
|
||||||
self.closeconn(tid)
|
self.closeconn(tid)
|
||||||
try:
|
try:
|
||||||
|
@ -341,11 +350,7 @@ class Gateway(object):
|
||||||
r = self.sendreq("GET", web_path, {})
|
r = self.sendreq("GET", web_path, {})
|
||||||
if r.status != 200:
|
if r.status != 200:
|
||||||
self.closeconn()
|
self.closeconn()
|
||||||
log(
|
info("http error %s reading dir %r", r.status, web_path)
|
||||||
"http error {} reading dir {} in {}".format(
|
|
||||||
r.status, web_path, rice_tid()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
raise FuseOSError(errno.ENOENT)
|
raise FuseOSError(errno.ENOENT)
|
||||||
|
|
||||||
ctype = r.getheader("Content-Type", "")
|
ctype = r.getheader("Content-Type", "")
|
||||||
|
@ -354,14 +359,14 @@ class Gateway(object):
|
||||||
elif ctype.startswith("text/html"):
|
elif ctype.startswith("text/html"):
|
||||||
parser = self.parse_html
|
parser = self.parse_html
|
||||||
else:
|
else:
|
||||||
log("listdir on file: {}".format(path))
|
info("listdir on file (%s): %r", ctype, path)
|
||||||
raise FuseOSError(errno.ENOENT)
|
raise FuseOSError(errno.ENOENT)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return parser(r)
|
return parser(r)
|
||||||
except:
|
except:
|
||||||
info(repr(path) + "\n" + traceback.format_exc())
|
info("parser: %r\n%s", path, traceback.format_exc())
|
||||||
raise
|
raise FuseOSError(errno.EIO)
|
||||||
|
|
||||||
def download_file_range(self, path, ofs1, ofs2):
|
def download_file_range(self, path, ofs1, ofs2):
|
||||||
if bad_good:
|
if bad_good:
|
||||||
|
@ -369,20 +374,15 @@ class Gateway(object):
|
||||||
|
|
||||||
web_path = self.quotep("/" + "/".join([self.web_root, path])) + "?raw"
|
web_path = self.quotep("/" + "/".join([self.web_root, path])) + "?raw"
|
||||||
hdr_range = "bytes={}-{}".format(ofs1, ofs2 - 1)
|
hdr_range = "bytes={}-{}".format(ofs1, ofs2 - 1)
|
||||||
info(
|
t = "DL %4.0fK\033[36m%9d-%-9d\033[0m%r"
|
||||||
"DL {:4.0f}K\033[36m{:>9}-{:<9}\033[0m{}".format(
|
info(t, (ofs2 - ofs1) / 1024.0, ofs1, ofs2 - 1, path)
|
||||||
(ofs2 - ofs1) / 1024.0, ofs1, ofs2 - 1, hexler(path)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
r = self.sendreq("GET", web_path, {"Range": hdr_range})
|
r = self.sendreq("GET", web_path, {"Range": hdr_range})
|
||||||
if r.status != http.client.PARTIAL_CONTENT:
|
if r.status != http.client.PARTIAL_CONTENT:
|
||||||
|
t = "http error %d reading file %r range %s in %s"
|
||||||
|
info(t, r.status, web_path, hdr_range, rice_tid())
|
||||||
self.closeconn()
|
self.closeconn()
|
||||||
raise Exception(
|
raise FuseOSError(errno.EIO)
|
||||||
"http error {} reading file {} range {} in {}".format(
|
|
||||||
r.status, web_path, hdr_range, rice_tid()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return r.read()
|
return r.read()
|
||||||
|
|
||||||
|
@ -433,7 +433,6 @@ class Gateway(object):
|
||||||
for line in lines:
|
for line in lines:
|
||||||
m = ptn.match(line)
|
m = ptn.match(line)
|
||||||
if not m:
|
if not m:
|
||||||
# print(line)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ftype, furl, fname, fsize, fdate = m.groups()
|
ftype, furl, fname, fsize, fdate = m.groups()
|
||||||
|
@ -449,7 +448,7 @@ class Gateway(object):
|
||||||
sz = int(fsize)
|
sz = int(fsize)
|
||||||
ts = calendar.timegm(time.strptime(fdate, "%Y-%m-%d %H:%M:%S"))
|
ts = calendar.timegm(time.strptime(fdate, "%Y-%m-%d %H:%M:%S"))
|
||||||
except:
|
except:
|
||||||
info("bad HTML or OS [{}] [{}]".format(fdate, fsize))
|
info("bad HTML or OS [%r] [%r]", fdate, fsize)
|
||||||
# python cannot strptime(1959-01-01) on windows
|
# python cannot strptime(1959-01-01) on windows
|
||||||
|
|
||||||
if ftype != "DIR":
|
if ftype != "DIR":
|
||||||
|
@ -500,19 +499,20 @@ class CPPF(Operations):
|
||||||
info("up")
|
info("up")
|
||||||
|
|
||||||
def _describe(self):
|
def _describe(self):
|
||||||
msg = ""
|
msg = []
|
||||||
with self.filecache_mtx:
|
with self.filecache_mtx:
|
||||||
for n, cn in enumerate(self.filecache):
|
for n, cn in enumerate(self.filecache):
|
||||||
cache_path, cache1 = cn.tag
|
cache_path, cache1 = cn.tag
|
||||||
cache2 = cache1 + len(cn.data)
|
cache2 = cache1 + len(cn.data)
|
||||||
msg += "\n{:<2} {:>7} {:>10}:{:<9} {}".format(
|
t = "\n{:<2} {:>7} {:>10}:{:<9} {}".format(
|
||||||
n,
|
n,
|
||||||
len(cn.data),
|
len(cn.data),
|
||||||
cache1,
|
cache1,
|
||||||
cache2,
|
cache2,
|
||||||
cache_path.replace("\r", "\\r").replace("\n", "\\n"),
|
cache_path.replace("\r", "\\r").replace("\n", "\\n"),
|
||||||
)
|
)
|
||||||
return msg
|
msg.append(t)
|
||||||
|
return "".join(msg)
|
||||||
|
|
||||||
def clean_dircache(self):
|
def clean_dircache(self):
|
||||||
"""not threadsafe"""
|
"""not threadsafe"""
|
||||||
|
@ -565,7 +565,8 @@ class CPPF(Operations):
|
||||||
car = None
|
car = None
|
||||||
cdr = None
|
cdr = None
|
||||||
ncn = -1
|
ncn = -1
|
||||||
dbg("cache request {}:{} |{}|".format(get1, get2, file_sz) + self._describe())
|
if is_dbg:
|
||||||
|
dbg("cache request %d:%d |%d|%s", get1, get2, file_sz, self._describe())
|
||||||
with self.filecache_mtx:
|
with self.filecache_mtx:
|
||||||
for cn in self.filecache:
|
for cn in self.filecache:
|
||||||
ncn += 1
|
ncn += 1
|
||||||
|
@ -592,7 +593,7 @@ class CPPF(Operations):
|
||||||
buf_ofs = get1 - cache1
|
buf_ofs = get1 - cache1
|
||||||
buf_end = buf_ofs + (get2 - get1)
|
buf_end = buf_ofs + (get2 - get1)
|
||||||
dbg(
|
dbg(
|
||||||
"found all (#{} {}:{} |{}|) [{}:{}] = {}".format(
|
"found all (#%d %d:%d |%d|) [%d:%d] = %d",
|
||||||
ncn,
|
ncn,
|
||||||
cache1,
|
cache1,
|
||||||
cache2,
|
cache2,
|
||||||
|
@ -601,14 +602,13 @@ class CPPF(Operations):
|
||||||
buf_end,
|
buf_end,
|
||||||
buf_end - buf_ofs,
|
buf_end - buf_ofs,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
return cn.data[buf_ofs:buf_end]
|
return cn.data[buf_ofs:buf_end]
|
||||||
|
|
||||||
if get2 <= cache2:
|
if get2 <= cache2:
|
||||||
x = cn.data[: get2 - cache1]
|
x = cn.data[: get2 - cache1]
|
||||||
if not cdr or len(cdr) < len(x):
|
if not cdr or len(cdr) < len(x):
|
||||||
dbg(
|
dbg(
|
||||||
"found cdr (#{} {}:{} |{}|) [:{}-{}] = [:{}] = {}".format(
|
"found cdr (#%d %d:%d |%d|) [:%d-%d] = [:%d] = %d",
|
||||||
ncn,
|
ncn,
|
||||||
cache1,
|
cache1,
|
||||||
cache2,
|
cache2,
|
||||||
|
@ -618,7 +618,6 @@ class CPPF(Operations):
|
||||||
get2 - cache1,
|
get2 - cache1,
|
||||||
len(x),
|
len(x),
|
||||||
)
|
)
|
||||||
)
|
|
||||||
cdr = x
|
cdr = x
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
@ -627,7 +626,7 @@ class CPPF(Operations):
|
||||||
x = cn.data[-(max(0, cache2 - get1)) :]
|
x = cn.data[-(max(0, cache2 - get1)) :]
|
||||||
if not car or len(car) < len(x):
|
if not car or len(car) < len(x):
|
||||||
dbg(
|
dbg(
|
||||||
"found car (#{} {}:{} |{}|) [-({}-{}):] = [-{}:] = {}".format(
|
"found car (#%d %d:%d |%d|) [-(%d-%d):] = [-%d:] = %d",
|
||||||
ncn,
|
ncn,
|
||||||
cache1,
|
cache1,
|
||||||
cache2,
|
cache2,
|
||||||
|
@ -637,12 +636,11 @@ class CPPF(Operations):
|
||||||
cache2 - get1,
|
cache2 - get1,
|
||||||
len(x),
|
len(x),
|
||||||
)
|
)
|
||||||
)
|
|
||||||
car = x
|
car = x
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
msg = "cache fallthrough\n{} {} {}\n{} {} {}\n{} {} --\n".format(
|
msg = "cache fallthrough\n%d %d %d\n%d %d %d\n%d %d --\n%s" % (
|
||||||
get1,
|
get1,
|
||||||
get2,
|
get2,
|
||||||
get2 - get1,
|
get2 - get1,
|
||||||
|
@ -651,9 +649,10 @@ class CPPF(Operations):
|
||||||
cache2 - cache1,
|
cache2 - cache1,
|
||||||
get1 - cache1,
|
get1 - cache1,
|
||||||
get2 - cache2,
|
get2 - cache2,
|
||||||
|
self._describe(),
|
||||||
)
|
)
|
||||||
msg += self._describe()
|
info(msg)
|
||||||
raise Exception(msg)
|
raise FuseOSError(errno.EIO)
|
||||||
|
|
||||||
if car and cdr and len(car) + len(cdr) == get2 - get1:
|
if car and cdr and len(car) + len(cdr) == get2 - get1:
|
||||||
dbg("<cache> have both")
|
dbg("<cache> have both")
|
||||||
|
@ -668,22 +667,17 @@ class CPPF(Operations):
|
||||||
|
|
||||||
buf_ofs = get1 - h_ofs
|
buf_ofs = get1 - h_ofs
|
||||||
|
|
||||||
dbg(
|
if dbg:
|
||||||
"<cache> cdr {}, car {}:{} |{}| [{}:]".format(
|
t = "<cache> cdr %d, car %d:%d |%d| [%d:]"
|
||||||
len(cdr), h_ofs, h_end, h_end - h_ofs, buf_ofs
|
dbg(t, len(cdr), h_ofs, h_end, h_end - h_ofs, buf_ofs)
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
||||||
if len(buf) == h_end - h_ofs:
|
if len(buf) == h_end - h_ofs:
|
||||||
ret = buf[buf_ofs:] + cdr
|
ret = buf[buf_ofs:] + cdr
|
||||||
else:
|
else:
|
||||||
ret = buf[get1 - h_ofs :]
|
ret = buf[get1 - h_ofs :]
|
||||||
info(
|
t = "remote truncated %d:%d to |%d|, will return |%d|"
|
||||||
"remote truncated {}:{} to |{}|, will return |{}|".format(
|
info(t, h_ofs, h_end, len(buf), len(ret))
|
||||||
h_ofs, h_end, len(buf), len(ret)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif car:
|
elif car:
|
||||||
h_ofs = get1 + len(car)
|
h_ofs = get1 + len(car)
|
||||||
|
@ -694,11 +688,8 @@ class CPPF(Operations):
|
||||||
|
|
||||||
buf_ofs = (get2 - get1) - len(car)
|
buf_ofs = (get2 - get1) - len(car)
|
||||||
|
|
||||||
dbg(
|
t = "<cache> car %d, cdr %d:%d |%d| [:%d]"
|
||||||
"<cache> car {}, cdr {}:{} |{}| [:{}]".format(
|
dbg(t, len(car), h_ofs, h_end, h_end - h_ofs, buf_ofs)
|
||||||
len(car), h_ofs, h_end, h_end - h_ofs, buf_ofs
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
||||||
ret = car + buf[:buf_ofs]
|
ret = car + buf[:buf_ofs]
|
||||||
|
@ -731,11 +722,8 @@ class CPPF(Operations):
|
||||||
buf_ofs = get1 - h_ofs
|
buf_ofs = get1 - h_ofs
|
||||||
buf_end = buf_ofs + get2 - get1
|
buf_end = buf_ofs + get2 - get1
|
||||||
|
|
||||||
dbg(
|
t = "<cache> %d:%d |%d| [%d:%d]"
|
||||||
"<cache> {}:{} |{}| [{}:{}]".format(
|
dbg(t, h_ofs, h_end, h_end - h_ofs, buf_ofs, buf_end)
|
||||||
h_ofs, h_end, h_end - h_ofs, buf_ofs, buf_end
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
||||||
ret = buf[buf_ofs:buf_end]
|
ret = buf[buf_ofs:buf_end]
|
||||||
|
@ -751,7 +739,7 @@ class CPPF(Operations):
|
||||||
|
|
||||||
def _readdir(self, path, fh=None):
|
def _readdir(self, path, fh=None):
|
||||||
path = path.strip("/")
|
path = path.strip("/")
|
||||||
log("readdir [{}] [{}]".format(hexler(path), fh))
|
dbg("readdir %r [%s]", path, fh)
|
||||||
|
|
||||||
ret = self.gw.listdir(path)
|
ret = self.gw.listdir(path)
|
||||||
if not self.n_dircache:
|
if not self.n_dircache:
|
||||||
|
@ -773,20 +761,17 @@ class CPPF(Operations):
|
||||||
cache_max = 1024 * 1024 * 2
|
cache_max = 1024 * 1024 * 2
|
||||||
if length > req_max:
|
if length > req_max:
|
||||||
# windows actually doing 240 MiB read calls, sausage
|
# windows actually doing 240 MiB read calls, sausage
|
||||||
info("truncate |{}| to {}MiB".format(length, req_max >> 20))
|
info("truncate |%d| to %dMiB", length, req_max >> 20)
|
||||||
length = req_max
|
length = req_max
|
||||||
|
|
||||||
path = path.strip("/")
|
path = path.strip("/")
|
||||||
ofs2 = offset + length
|
ofs2 = offset + length
|
||||||
file_sz = self.getattr(path)["st_size"]
|
file_sz = self.getattr(path)["st_size"]
|
||||||
log(
|
dbg("read %r |%d| %d:%d max %d", path, length, offset, ofs2, file_sz)
|
||||||
"read {} |{}| {}:{} max {}".format(
|
|
||||||
hexler(path), length, offset, ofs2, file_sz
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if ofs2 > file_sz:
|
if ofs2 > file_sz:
|
||||||
ofs2 = file_sz
|
ofs2 = file_sz
|
||||||
log("truncate to |{}| :{}".format(ofs2 - offset, ofs2))
|
dbg("truncate to |%d| :%d", ofs2 - offset, ofs2)
|
||||||
|
|
||||||
if file_sz == 0 or offset >= ofs2:
|
if file_sz == 0 or offset >= ofs2:
|
||||||
return b""
|
return b""
|
||||||
|
@ -822,7 +807,7 @@ class CPPF(Operations):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def getattr(self, path, fh=None):
|
def getattr(self, path, fh=None):
|
||||||
log("getattr [{}]".format(hexler(path)))
|
dbg("getattr %r", path)
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
path = enwin(path) # windows occasionally decodes f0xx to xx
|
path = enwin(path) # windows occasionally decodes f0xx to xx
|
||||||
|
|
||||||
|
@ -835,12 +820,11 @@ class CPPF(Operations):
|
||||||
|
|
||||||
if not path:
|
if not path:
|
||||||
ret = self.gw.stat_dir(time.time())
|
ret = self.gw.stat_dir(time.time())
|
||||||
# dbg("=" + repr(ret))
|
dbg("=%r", ret)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
cn = self.get_cached_dir(dirpath)
|
cn = self.get_cached_dir(dirpath)
|
||||||
if cn:
|
if cn:
|
||||||
log("cache ok")
|
|
||||||
dents = cn.data
|
dents = cn.data
|
||||||
else:
|
else:
|
||||||
dbg("cache miss")
|
dbg("cache miss")
|
||||||
|
@ -864,7 +848,7 @@ class CPPF(Operations):
|
||||||
if MACOS and path.split("/")[-1].startswith("._"):
|
if MACOS and path.split("/")[-1].startswith("._"):
|
||||||
fun = dbg
|
fun = dbg
|
||||||
|
|
||||||
fun("=ENOENT ({})".format(hexler(path)))
|
fun("=ENOENT %r", path)
|
||||||
raise FuseOSError(errno.ENOENT)
|
raise FuseOSError(errno.ENOENT)
|
||||||
|
|
||||||
access = None
|
access = None
|
||||||
|
@ -880,39 +864,39 @@ class CPPF(Operations):
|
||||||
if False:
|
if False:
|
||||||
# incorrect semantics but good for debugging stuff like samba and msys2
|
# incorrect semantics but good for debugging stuff like samba and msys2
|
||||||
def access(self, path, mode):
|
def access(self, path, mode):
|
||||||
log("@@ access [{}] [{}]".format(path, mode))
|
dbg("@@ access [{}] [{}]".format(path, mode))
|
||||||
return 1 if self.getattr(path) else 0
|
return 1 if self.getattr(path) else 0
|
||||||
|
|
||||||
def flush(self, path, fh):
|
def flush(self, path, fh):
|
||||||
log("@@ flush [{}] [{}]".format(path, fh))
|
dbg("@@ flush [{}] [{}]".format(path, fh))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def getxattr(self, *args):
|
def getxattr(self, *args):
|
||||||
log("@@ getxattr [{}]".format("] [".join(str(x) for x in args)))
|
dbg("@@ getxattr [{}]".format("] [".join(str(x) for x in args)))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def listxattr(self, *args):
|
def listxattr(self, *args):
|
||||||
log("@@ listxattr [{}]".format("] [".join(str(x) for x in args)))
|
dbg("@@ listxattr [{}]".format("] [".join(str(x) for x in args)))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def open(self, path, flags):
|
def open(self, path, flags):
|
||||||
log("@@ open [{}] [{}]".format(path, flags))
|
dbg("@@ open [{}] [{}]".format(path, flags))
|
||||||
return 42
|
return 42
|
||||||
|
|
||||||
def opendir(self, fh):
|
def opendir(self, fh):
|
||||||
log("@@ opendir [{}]".format(fh))
|
dbg("@@ opendir [{}]".format(fh))
|
||||||
return 69
|
return 69
|
||||||
|
|
||||||
def release(self, ino, fi):
|
def release(self, ino, fi):
|
||||||
log("@@ release [{}] [{}]".format(ino, fi))
|
dbg("@@ release [{}] [{}]".format(ino, fi))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def releasedir(self, ino, fi):
|
def releasedir(self, ino, fi):
|
||||||
log("@@ releasedir [{}] [{}]".format(ino, fi))
|
dbg("@@ releasedir [{}] [{}]".format(ino, fi))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def statfs(self, path):
|
def statfs(self, path):
|
||||||
log("@@ statfs [{}]".format(path))
|
dbg("@@ statfs [{}]".format(path))
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
|
@ -930,28 +914,28 @@ class CPPF(Operations):
|
||||||
return self.junk_fh_ctr
|
return self.junk_fh_ctr
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
log("open ERR {}".format(repr(ex)))
|
info("open ERR %r", ex)
|
||||||
raise FuseOSError(errno.ENOENT)
|
raise FuseOSError(errno.ENOENT)
|
||||||
|
|
||||||
def open(self, path, flags):
|
def open(self, path, flags):
|
||||||
dbg("open [{}] [{}]".format(hexler(path), flags))
|
dbg("open %r [%s]", path, flags)
|
||||||
return self._open(path)
|
return self._open(path)
|
||||||
|
|
||||||
def opendir(self, path):
|
def opendir(self, path):
|
||||||
dbg("opendir [{}]".format(hexler(path)))
|
dbg("opendir %r", path)
|
||||||
return self._open(path)
|
return self._open(path)
|
||||||
|
|
||||||
def flush(self, path, fh):
|
def flush(self, path, fh):
|
||||||
dbg("flush [{}] [{}]".format(hexler(path), fh))
|
dbg("flush %r [%s]", path, fh)
|
||||||
|
|
||||||
def release(self, ino, fi):
|
def release(self, ino, fi):
|
||||||
dbg("release [{}] [{}]".format(hexler(ino), fi))
|
dbg("release %r [%s]", ino, fi)
|
||||||
|
|
||||||
def releasedir(self, ino, fi):
|
def releasedir(self, ino, fi):
|
||||||
dbg("releasedir [{}] [{}]".format(hexler(ino), fi))
|
dbg("releasedir %r [%s]", ino, fi)
|
||||||
|
|
||||||
def access(self, path, mode):
|
def access(self, path, mode):
|
||||||
dbg("access [{}] [{}]".format(hexler(path), mode))
|
dbg("access %r [%s]", path, mode)
|
||||||
try:
|
try:
|
||||||
x = self.getattr(path)
|
x = self.getattr(path)
|
||||||
if x["st_mode"] <= 0:
|
if x["st_mode"] <= 0:
|
||||||
|
@ -967,7 +951,7 @@ class TheArgparseFormatter(
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global info, log, dbg
|
global info, dbg, is_dbg
|
||||||
time.strptime("19970815", "%Y%m%d") # python#7980
|
time.strptime("19970815", "%Y%m%d") # python#7980
|
||||||
|
|
||||||
# filecache helps for reads that are ~64k or smaller;
|
# filecache helps for reads that are ~64k or smaller;
|
||||||
|
@ -994,37 +978,44 @@ def main():
|
||||||
formatter_class=TheArgparseFormatter,
|
formatter_class=TheArgparseFormatter,
|
||||||
epilog="example:" + ex_pre + ex_pre.join(examples),
|
epilog="example:" + ex_pre + ex_pre.join(examples),
|
||||||
)
|
)
|
||||||
ap.add_argument(
|
# fmt: off
|
||||||
"-cd", metavar="NUM_SECONDS", type=float, default=nd, help="directory cache"
|
|
||||||
)
|
|
||||||
ap.add_argument(
|
|
||||||
"-cf", metavar="NUM_BLOCKS", type=int, default=nf, help="file cache"
|
|
||||||
)
|
|
||||||
ap.add_argument("-a", metavar="PASSWORD", help="password or $filepath")
|
|
||||||
ap.add_argument("-d", action="store_true", help="enable debug")
|
|
||||||
ap.add_argument("-te", metavar="PEM_FILE", help="certificate to expect/verify")
|
|
||||||
ap.add_argument("-td", action="store_true", help="disable certificate check")
|
|
||||||
ap.add_argument("base_url", type=str, help="remote copyparty URL to mount")
|
ap.add_argument("base_url", type=str, help="remote copyparty URL to mount")
|
||||||
ap.add_argument("local_path", type=str, help=where + " to mount it on")
|
ap.add_argument("local_path", type=str, help=where + " to mount it on")
|
||||||
ar = ap.parse_args()
|
ap.add_argument("-a", metavar="PASSWORD", help="password or $filepath")
|
||||||
|
|
||||||
|
ap2 = ap.add_argument_group("https/TLS")
|
||||||
|
ap2.add_argument("-te", metavar="PEMFILE", help="certificate to expect/verify")
|
||||||
|
ap2.add_argument("-td", action="store_true", help="disable certificate check")
|
||||||
|
|
||||||
|
ap2 = ap.add_argument_group("cache/perf")
|
||||||
|
ap2.add_argument("-cd", metavar="SECS", type=float, default=nd, help="directory-cache, expiration time")
|
||||||
|
ap2.add_argument("-cf", metavar="BLOCKS", type=int, default=nf, help="file cache; each block is <= 1 MiB")
|
||||||
|
|
||||||
|
ap2 = ap.add_argument_group("logging")
|
||||||
|
ap2.add_argument("-q", action="store_true", help="quiet")
|
||||||
|
ap2.add_argument("-d", action="store_true", help="debug/verbose")
|
||||||
|
ap2.add_argument("--slowterm", action="store_true", help="only most recent msgs; good for windows")
|
||||||
|
ap2.add_argument("--logf", metavar="FILE", type=str, default="", help="log to FILE; enables --slowterm")
|
||||||
|
|
||||||
|
ar = ap.parse_args()
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
if ar.logf:
|
||||||
|
ar.slowterm = True
|
||||||
|
|
||||||
if ar.d:
|
|
||||||
# windows terminals are slow (cmd.exe, mintty)
|
# windows terminals are slow (cmd.exe, mintty)
|
||||||
# otoh fancy_log beats RecentLog on linux
|
# otoh fancy_log beats RecentLog on linux
|
||||||
logger = RecentLog().put if WINDOWS else fancy_log
|
logger = RecentLog(ar).put if ar.slowterm else fancy_log
|
||||||
|
if ar.d:
|
||||||
info = logger
|
info = logger
|
||||||
log = logger
|
|
||||||
dbg = logger
|
dbg = logger
|
||||||
else:
|
is_dbg = True
|
||||||
# debug=off, speed is dontcare
|
elif not ar.q:
|
||||||
info = fancy_log
|
info = logger
|
||||||
log = null_log
|
|
||||||
dbg = null_log
|
|
||||||
|
|
||||||
if ar.a and ar.a.startswith("$"):
|
if ar.a and ar.a.startswith("$"):
|
||||||
fn = ar.a[1:]
|
fn = ar.a[1:]
|
||||||
log("reading password from file [{}]".format(fn))
|
info("reading password from file %r", fn)
|
||||||
with open(fn, "rb") as f:
|
with open(fn, "rb") as f:
|
||||||
ar.a = f.read().decode("utf-8").strip()
|
ar.a = f.read().decode("utf-8").strip()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue