mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 17:12:13 -06:00
fuse: support windows/msys2
This commit is contained in:
parent
f8a51b68e7
commit
b2b083fd0a
|
@ -13,6 +13,7 @@ import time
|
||||||
import stat
|
import stat
|
||||||
import errno
|
import errno
|
||||||
import struct
|
import struct
|
||||||
|
import builtins
|
||||||
import threading
|
import threading
|
||||||
import http.client # py2: httplib
|
import http.client # py2: httplib
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
@ -48,6 +49,20 @@ MB/s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def print(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
builtins.print(*list(args), **kwargs)
|
||||||
|
except:
|
||||||
|
builtins.print(termsafe(' '.join(str(x) for x in args)), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def termsafe(txt):
|
||||||
|
try:
|
||||||
|
return txt.encode(sys.stdout.encoding, 'backslashreplace').decode(sys.stdout.encoding)
|
||||||
|
except:
|
||||||
|
return txt.encode(sys.stdout.encoding, 'replace').decode(sys.stdout.encoding)
|
||||||
|
|
||||||
|
|
||||||
def threadless_log(msg):
|
def threadless_log(msg):
|
||||||
print(msg + "\n", end="")
|
print(msg + "\n", end="")
|
||||||
|
|
||||||
|
@ -82,6 +97,10 @@ def get_tid():
|
||||||
return threading.current_thread().ident
|
return threading.current_thread().ident
|
||||||
|
|
||||||
|
|
||||||
|
def html_dec(txt):
|
||||||
|
return txt.replace('<', '<').replace('>', '>').replace('"', '"').replace('&', '&')
|
||||||
|
|
||||||
|
|
||||||
class CacheNode(object):
|
class CacheNode(object):
|
||||||
def __init__(self, tag, data):
|
def __init__(self, tag, data):
|
||||||
self.tag = tag
|
self.tag = tag
|
||||||
|
@ -147,9 +166,8 @@ class Gateway(object):
|
||||||
return c.getresponse()
|
return c.getresponse()
|
||||||
|
|
||||||
def listdir(self, path):
|
def listdir(self, path):
|
||||||
web_path = "/" + "/".join([self.web_root, path]) + "?dots"
|
web_path = self.quotep("/" + "/".join([self.web_root, path])) + "?dots"
|
||||||
|
r = self.sendreq("GET", web_path)
|
||||||
r = self.sendreq("GET", self.quotep(web_path))
|
|
||||||
if r.status != 200:
|
if r.status != 200:
|
||||||
self.closeconn()
|
self.closeconn()
|
||||||
raise Exception(
|
raise Exception(
|
||||||
|
@ -161,11 +179,11 @@ class Gateway(object):
|
||||||
return self.parse_html(r)
|
return self.parse_html(r)
|
||||||
|
|
||||||
def download_file_range(self, path, ofs1, ofs2):
|
def download_file_range(self, path, ofs1, ofs2):
|
||||||
web_path = "/" + "/".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)
|
||||||
log("downloading {}".format(hdr_range))
|
log("downloading {}".format(hdr_range))
|
||||||
|
|
||||||
r = self.sendreq("GET", self.quotep(web_path), headers={"Range": hdr_range})
|
r = self.sendreq("GET", web_path, headers={"Range": hdr_range})
|
||||||
if r.status != http.client.PARTIAL_CONTENT:
|
if r.status != http.client.PARTIAL_CONTENT:
|
||||||
self.closeconn()
|
self.closeconn()
|
||||||
raise Exception(
|
raise Exception(
|
||||||
|
@ -203,6 +221,7 @@ class Gateway(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ftype, fname, fsize, fdate = m.groups()
|
ftype, fname, fsize, fdate = m.groups()
|
||||||
|
fname = html_dec(fname)
|
||||||
ts = datetime.strptime(fdate, "%Y-%m-%d %H:%M:%S").timestamp()
|
ts = datetime.strptime(fdate, "%Y-%m-%d %H:%M:%S").timestamp()
|
||||||
sz = int(fsize)
|
sz = int(fsize)
|
||||||
if ftype == "-":
|
if ftype == "-":
|
||||||
|
@ -214,7 +233,7 @@ class Gateway(object):
|
||||||
|
|
||||||
def stat_dir(self, ts, sz=4096):
|
def stat_dir(self, ts, sz=4096):
|
||||||
return {
|
return {
|
||||||
"st_mode": 0o555 | stat.S_IFDIR,
|
"st_mode": stat.S_IFDIR | 0o555,
|
||||||
"st_uid": 1000,
|
"st_uid": 1000,
|
||||||
"st_gid": 1000,
|
"st_gid": 1000,
|
||||||
"st_size": sz,
|
"st_size": sz,
|
||||||
|
@ -226,7 +245,7 @@ class Gateway(object):
|
||||||
|
|
||||||
def stat_file(self, ts, sz):
|
def stat_file(self, ts, sz):
|
||||||
return {
|
return {
|
||||||
"st_mode": 0o444 | stat.S_IFREG,
|
"st_mode": stat.S_IFREG | 0o444,
|
||||||
"st_uid": 1000,
|
"st_uid": 1000,
|
||||||
"st_gid": 1000,
|
"st_gid": 1000,
|
||||||
"st_size": sz,
|
"st_size": sz,
|
||||||
|
@ -240,6 +259,7 @@ class Gateway(object):
|
||||||
class CPPF(Operations):
|
class CPPF(Operations):
|
||||||
def __init__(self, base_url):
|
def __init__(self, base_url):
|
||||||
self.gw = Gateway(base_url)
|
self.gw = Gateway(base_url)
|
||||||
|
self.junk_fh_ctr = 3
|
||||||
|
|
||||||
self.dircache = []
|
self.dircache = []
|
||||||
self.dircache_mtx = threading.Lock()
|
self.dircache_mtx = threading.Lock()
|
||||||
|
@ -485,6 +505,8 @@ class CPPF(Operations):
|
||||||
return self.gw.download_file_range(path, offset, ofs2)
|
return self.gw.download_file_range(path, offset, ofs2)
|
||||||
|
|
||||||
def getattr(self, path, fh=None):
|
def getattr(self, path, fh=None):
|
||||||
|
log("getattr [{}]".format(path))
|
||||||
|
|
||||||
path = path.strip("/")
|
path = path.strip("/")
|
||||||
try:
|
try:
|
||||||
dirpath, fname = path.rsplit("/", 1)
|
dirpath, fname = path.rsplit("/", 1)
|
||||||
|
@ -492,10 +514,10 @@ class CPPF(Operations):
|
||||||
dirpath = ""
|
dirpath = ""
|
||||||
fname = path
|
fname = path
|
||||||
|
|
||||||
log("getattr {}".format(path))
|
|
||||||
|
|
||||||
if not path:
|
if not path:
|
||||||
return self.gw.stat_dir(time.time())
|
ret = self.gw.stat_dir(time.time())
|
||||||
|
dbg("=" + repr(ret))
|
||||||
|
return ret
|
||||||
|
|
||||||
cn = self.get_cached_dir(dirpath)
|
cn = self.get_cached_dir(dirpath)
|
||||||
if cn:
|
if cn:
|
||||||
|
@ -507,8 +529,10 @@ class CPPF(Operations):
|
||||||
|
|
||||||
for cache_name, cache_stat, _ in dents:
|
for cache_name, cache_stat, _ in dents:
|
||||||
if cache_name == fname:
|
if cache_name == fname:
|
||||||
|
dbg("=" + repr(cache_stat))
|
||||||
return cache_stat
|
return cache_stat
|
||||||
|
|
||||||
|
log("=404")
|
||||||
raise FuseOSError(errno.ENOENT)
|
raise FuseOSError(errno.ENOENT)
|
||||||
|
|
||||||
access = None
|
access = None
|
||||||
|
@ -520,6 +544,71 @@ class CPPF(Operations):
|
||||||
release = None
|
release = None
|
||||||
releasedir = None
|
releasedir = None
|
||||||
statfs = None
|
statfs = None
|
||||||
|
|
||||||
|
if False:
|
||||||
|
# incorrect semantics but good for debugging stuff like samba and msys2
|
||||||
|
def access(self, path, mode):
|
||||||
|
log("@@ access [{}] [{}]".format(path, mode))
|
||||||
|
return 1 if self.getattr(path) else 0
|
||||||
|
|
||||||
|
def flush(self, path, fh):
|
||||||
|
log("@@ flush [{}] [{}]".format(path, fh))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getxattr(self, *args):
|
||||||
|
log("@@ getxattr [{}]".format('] ['.join(str(x) for x in args)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def listxattr(self, *args):
|
||||||
|
log("@@ listxattr [{}]".format('] ['.join(str(x) for x in args)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def open(self, path, flags):
|
||||||
|
log("@@ open [{}] [{}]".format(path, flags))
|
||||||
|
return 42
|
||||||
|
|
||||||
|
def opendir(self, fh):
|
||||||
|
log("@@ opendir [{}]".format(fh))
|
||||||
|
return 69
|
||||||
|
|
||||||
|
def release(self, ino, fi):
|
||||||
|
log("@@ release [{}] [{}]".format(ino, fi))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def releasedir(self, ino, fi):
|
||||||
|
log("@@ releasedir [{}] [{}]".format(ino, fi))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def statfs(self, path):
|
||||||
|
log("@@ statfs [{}]".format(path))
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# quick compat for /mingw64/bin/python3 (msys2)
|
||||||
|
def open(self, path, flags):
|
||||||
|
log("open [{}] [{}]".format(path, flags))
|
||||||
|
try:
|
||||||
|
x = self.getattr(path)
|
||||||
|
if x["st_mode"] <= 0:
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
self.junk_fh_ctr += 1
|
||||||
|
if self.junk_fh_ctr > 32000: # TODO untested
|
||||||
|
self.junk_fh_ctr = 4
|
||||||
|
|
||||||
|
return self.junk_fh_ctr
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
log("open ERR {}".format(repr(ex)))
|
||||||
|
raise FuseOSError(errno.ENOENT)
|
||||||
|
|
||||||
|
def flush(self, path, fh):
|
||||||
|
log("flush [{}] [{}]".format(path, fh))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def release(self, ino, fi):
|
||||||
|
log("release [{}] [{}]".format(ino, fi))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -530,7 +619,7 @@ def main():
|
||||||
print("need arg 2: root url")
|
print("need arg 2: root url")
|
||||||
return
|
return
|
||||||
|
|
||||||
FUSE(CPPF(remote), local, foreground=True, nothreads=True)
|
FUSE(CPPF(remote), local, foreground=True, nothreads=True, allow_other=True, nonempty=True)
|
||||||
# if nothreads=False also uncomment the `with *_mtx` things
|
# if nothreads=False also uncomment the `with *_mtx` things
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue