copyparty 1.7.6__py3-none-any.whl → 1.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- copyparty/__main__.py +125 -11
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +96 -6
- copyparty/broker_mp.py +14 -1
- copyparty/cert.py +3 -1
- copyparty/cfg.py +7 -1
- copyparty/ftpd.py +7 -4
- copyparty/httpcli.py +122 -14
- copyparty/ico.py +17 -4
- copyparty/pwhash.py +145 -0
- copyparty/u2idx.py +1 -1
- copyparty/up2k.py +82 -15
- copyparty/util.py +34 -1
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.7.6.dist-info → copyparty-1.8.1.dist-info}/METADATA +41 -1
- {copyparty-1.7.6.dist-info → copyparty-1.8.1.dist-info}/RECORD +23 -22
- {copyparty-1.7.6.dist-info → copyparty-1.8.1.dist-info}/LICENSE +0 -0
- {copyparty-1.7.6.dist-info → copyparty-1.8.1.dist-info}/WHEEL +0 -0
- {copyparty-1.7.6.dist-info → copyparty-1.8.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.7.6.dist-info → copyparty-1.8.1.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
@@ -58,6 +58,7 @@ from .util import (
|
|
58
58
|
html_escape,
|
59
59
|
humansize,
|
60
60
|
ipnorm,
|
61
|
+
loadpy,
|
61
62
|
min_ex,
|
62
63
|
quotep,
|
63
64
|
rand_name,
|
@@ -169,13 +170,16 @@ class HttpCli(object):
|
|
169
170
|
def log(self, msg , c = 0) :
|
170
171
|
ptn = self.asrv.re_pwd
|
171
172
|
if ptn and ptn.search(msg):
|
172
|
-
|
173
|
+
if self.asrv.ah.on:
|
174
|
+
msg = ptn.sub("\033[7m pw \033[27m", msg)
|
175
|
+
else:
|
176
|
+
msg = ptn.sub(self.unpwd, msg)
|
173
177
|
|
174
178
|
self.log_func(self.log_src, msg, c)
|
175
179
|
|
176
180
|
def unpwd(self, m ) :
|
177
|
-
a, b = m.groups()
|
178
|
-
return "
|
181
|
+
a, b, c = m.groups()
|
182
|
+
return "{}\033[7m {} \033[27m{}".format(a, self.asrv.iacct[b], c)
|
179
183
|
|
180
184
|
def _check_nonfatal(self, ex , post ) :
|
181
185
|
if post:
|
@@ -379,13 +383,14 @@ class HttpCli(object):
|
|
379
383
|
zs = base64.b64decode(zb).decode("utf-8")
|
380
384
|
# try "pwd", "x:pwd", "pwd:x"
|
381
385
|
for bauth in [zs] + zs.split(":", 1)[::-1]:
|
382
|
-
|
386
|
+
hpw = self.asrv.ah.hash(bauth)
|
387
|
+
if self.asrv.iacct.get(hpw):
|
383
388
|
break
|
384
389
|
except:
|
385
390
|
pass
|
386
391
|
|
387
392
|
self.pw = uparam.get("pw") or self.headers.get("pw") or bauth or cookie_pw
|
388
|
-
self.uname = self.asrv.iacct.get(self.pw) or "*"
|
393
|
+
self.uname = self.asrv.iacct.get(self.asrv.ah.hash(self.pw)) or "*"
|
389
394
|
self.rvol = self.asrv.vfs.aread[self.uname]
|
390
395
|
self.wvol = self.asrv.vfs.awrite[self.uname]
|
391
396
|
self.mvol = self.asrv.vfs.amove[self.uname]
|
@@ -759,11 +764,27 @@ class HttpCli(object):
|
|
759
764
|
return True
|
760
765
|
|
761
766
|
if not self.can_read and not self.can_write and not self.can_get:
|
762
|
-
|
763
|
-
|
764
|
-
|
767
|
+
t = "@{} has no access to [{}]"
|
768
|
+
self.log(t.format(self.uname, self.vpath))
|
769
|
+
|
770
|
+
if self.avn and "on403" in self.avn.flags:
|
771
|
+
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
772
|
+
ret = self.on40x(vn.flags["on403"], vn, rem)
|
773
|
+
if ret == "true":
|
774
|
+
return True
|
775
|
+
elif ret == "false":
|
776
|
+
return False
|
777
|
+
elif ret == "allow":
|
778
|
+
self.log("plugin override; access permitted")
|
779
|
+
self.can_read = self.can_write = self.can_move = True
|
780
|
+
self.can_delete = self.can_get = self.can_upget = True
|
781
|
+
else:
|
782
|
+
return self.tx_404(True)
|
783
|
+
else:
|
784
|
+
if self.vpath:
|
785
|
+
return self.tx_404(True)
|
765
786
|
|
766
|
-
|
787
|
+
self.uparam["h"] = ""
|
767
788
|
|
768
789
|
if "tree" in self.uparam:
|
769
790
|
return self.tx_tree()
|
@@ -1354,7 +1375,9 @@ class HttpCli(object):
|
|
1354
1375
|
lim = vfs.get_dbv(rem)[0].lim
|
1355
1376
|
fdir = vfs.canonical(rem)
|
1356
1377
|
if lim:
|
1357
|
-
fdir, rem = lim.all(
|
1378
|
+
fdir, rem = lim.all(
|
1379
|
+
self.ip, rem, remains, vfs.realpath, fdir, self.conn.hsrv.broker
|
1380
|
+
)
|
1358
1381
|
|
1359
1382
|
fn = None
|
1360
1383
|
if rem and not self.trailing_slash and not bos.path.isdir(fdir):
|
@@ -1487,6 +1510,7 @@ class HttpCli(object):
|
|
1487
1510
|
lim.bup(self.ip, post_sz)
|
1488
1511
|
try:
|
1489
1512
|
lim.chk_sz(post_sz)
|
1513
|
+
lim.chk_vsz(self.conn.hsrv.broker, vfs.realpath, post_sz)
|
1490
1514
|
except:
|
1491
1515
|
bos.unlink(path)
|
1492
1516
|
raise
|
@@ -1961,7 +1985,7 @@ class HttpCli(object):
|
|
1961
1985
|
return True
|
1962
1986
|
|
1963
1987
|
def get_pwd_cookie(self, pwd ) :
|
1964
|
-
if pwd in self.asrv.iacct:
|
1988
|
+
if self.asrv.ah.hash(pwd) in self.asrv.iacct:
|
1965
1989
|
msg = "login ok"
|
1966
1990
|
dur = int(60 * 60 * self.args.logout)
|
1967
1991
|
else:
|
@@ -2097,7 +2121,9 @@ class HttpCli(object):
|
|
2097
2121
|
lim = vfs.get_dbv(rem)[0].lim
|
2098
2122
|
fdir_base = vfs.canonical(rem)
|
2099
2123
|
if lim:
|
2100
|
-
fdir_base, rem = lim.all(
|
2124
|
+
fdir_base, rem = lim.all(
|
2125
|
+
self.ip, rem, -1, vfs.realpath, fdir_base, self.conn.hsrv.broker
|
2126
|
+
)
|
2101
2127
|
upload_vpath = "{}/{}".format(vfs.vpath, rem).strip("/")
|
2102
2128
|
if not nullwrite:
|
2103
2129
|
bos.makedirs(fdir_base)
|
@@ -2190,6 +2216,7 @@ class HttpCli(object):
|
|
2190
2216
|
try:
|
2191
2217
|
lim.chk_df(tabspath, sz, True)
|
2192
2218
|
lim.chk_sz(sz)
|
2219
|
+
lim.chk_vsz(self.conn.hsrv.broker, vfs.realpath, sz)
|
2193
2220
|
lim.chk_bup(self.ip)
|
2194
2221
|
lim.chk_nup(self.ip)
|
2195
2222
|
except:
|
@@ -2365,7 +2392,7 @@ class HttpCli(object):
|
|
2365
2392
|
fp = vfs.canonical(rp)
|
2366
2393
|
lim = vfs.get_dbv(rem)[0].lim
|
2367
2394
|
if lim:
|
2368
|
-
fp, rp = lim.all(self.ip, rp, clen, fp)
|
2395
|
+
fp, rp = lim.all(self.ip, rp, clen, vfs.realpath, fp, self.conn.hsrv.broker)
|
2369
2396
|
bos.makedirs(fp)
|
2370
2397
|
|
2371
2398
|
fp = os.path.join(fp, fn)
|
@@ -2436,6 +2463,25 @@ class HttpCli(object):
|
|
2436
2463
|
if p_field != "body":
|
2437
2464
|
raise Pebkac(400, "expected body, got {}".format(p_field))
|
2438
2465
|
|
2466
|
+
xbu = vfs.flags.get("xbu")
|
2467
|
+
if xbu:
|
2468
|
+
if not runhook(
|
2469
|
+
self.log,
|
2470
|
+
xbu,
|
2471
|
+
fp,
|
2472
|
+
self.vpath,
|
2473
|
+
self.host,
|
2474
|
+
self.uname,
|
2475
|
+
time.time(),
|
2476
|
+
0,
|
2477
|
+
self.ip,
|
2478
|
+
time.time(),
|
2479
|
+
"",
|
2480
|
+
):
|
2481
|
+
t = "save blocked by xbu server config"
|
2482
|
+
self.log(t, 1)
|
2483
|
+
raise Pebkac(403, t)
|
2484
|
+
|
2439
2485
|
if bos.path.exists(fp):
|
2440
2486
|
bos.unlink(fp)
|
2441
2487
|
|
@@ -2447,6 +2493,7 @@ class HttpCli(object):
|
|
2447
2493
|
lim.bup(self.ip, sz)
|
2448
2494
|
try:
|
2449
2495
|
lim.chk_sz(sz)
|
2496
|
+
lim.chk_vsz(self.conn.hsrv.broker, vfs.realpath, sz)
|
2450
2497
|
except:
|
2451
2498
|
bos.unlink(fp)
|
2452
2499
|
raise
|
@@ -2455,6 +2502,39 @@ class HttpCli(object):
|
|
2455
2502
|
new_lastmod3 = int(new_lastmod * 1000)
|
2456
2503
|
sha512 = sha512[:56]
|
2457
2504
|
|
2505
|
+
xau = vfs.flags.get("xau")
|
2506
|
+
if xau and not runhook(
|
2507
|
+
self.log,
|
2508
|
+
xau,
|
2509
|
+
fp,
|
2510
|
+
self.vpath,
|
2511
|
+
self.host,
|
2512
|
+
self.uname,
|
2513
|
+
new_lastmod,
|
2514
|
+
sz,
|
2515
|
+
self.ip,
|
2516
|
+
new_lastmod,
|
2517
|
+
"",
|
2518
|
+
):
|
2519
|
+
t = "save blocked by xau server config"
|
2520
|
+
self.log(t, 1)
|
2521
|
+
os.unlink(fp)
|
2522
|
+
raise Pebkac(403, t)
|
2523
|
+
|
2524
|
+
vfs, rem = vfs.get_dbv(rem)
|
2525
|
+
self.conn.hsrv.broker.say(
|
2526
|
+
"up2k.hash_file",
|
2527
|
+
vfs.realpath,
|
2528
|
+
vfs.vpath,
|
2529
|
+
vfs.flags,
|
2530
|
+
vsplit(rem)[0],
|
2531
|
+
fn,
|
2532
|
+
self.ip,
|
2533
|
+
new_lastmod,
|
2534
|
+
self.uname,
|
2535
|
+
True,
|
2536
|
+
)
|
2537
|
+
|
2458
2538
|
response = json.dumps(
|
2459
2539
|
{"ok": True, "lastmod": new_lastmod3, "size": sz, "sha512": sha512}
|
2460
2540
|
)
|
@@ -2989,6 +3069,20 @@ class HttpCli(object):
|
|
2989
3069
|
self.reply(html.encode("utf-8"), status=rc)
|
2990
3070
|
return True
|
2991
3071
|
|
3072
|
+
def on40x(self, mods , vn , rem ) :
|
3073
|
+
for mpath in mods:
|
3074
|
+
try:
|
3075
|
+
mod = loadpy(mpath, self.args.hot_handlers)
|
3076
|
+
except Exception as ex:
|
3077
|
+
self.log("import failed: {!r}".format(ex))
|
3078
|
+
continue
|
3079
|
+
|
3080
|
+
ret = mod.main(self, vn, rem)
|
3081
|
+
if ret:
|
3082
|
+
return ret.lower()
|
3083
|
+
|
3084
|
+
return "" # unhandled / fallthrough
|
3085
|
+
|
2992
3086
|
def scanvol(self) :
|
2993
3087
|
if not self.can_read or not self.can_write:
|
2994
3088
|
raise Pebkac(403, "not allowed for user " + self.uname)
|
@@ -3306,7 +3400,21 @@ class HttpCli(object):
|
|
3306
3400
|
try:
|
3307
3401
|
st = bos.stat(abspath)
|
3308
3402
|
except:
|
3309
|
-
|
3403
|
+
if "on404" not in vn.flags:
|
3404
|
+
return self.tx_404()
|
3405
|
+
|
3406
|
+
ret = self.on40x(vn.flags["on404"], vn, rem)
|
3407
|
+
if ret == "true":
|
3408
|
+
return True
|
3409
|
+
elif ret == "false":
|
3410
|
+
return False
|
3411
|
+
elif ret == "retry":
|
3412
|
+
try:
|
3413
|
+
st = bos.stat(abspath)
|
3414
|
+
except:
|
3415
|
+
return self.tx_404()
|
3416
|
+
else:
|
3417
|
+
return self.tx_404()
|
3310
3418
|
|
3311
3419
|
if rem.startswith(".hist/up2k.") or (
|
3312
3420
|
rem.endswith("/dir.txt") and rem.startswith(".hist/th/")
|
copyparty/ico.py
CHANGED
@@ -17,7 +17,9 @@ class Ico(object):
|
|
17
17
|
def get(self, ext , as_thumb , chrome ) :
|
18
18
|
"""placeholder to make thumbnails not break"""
|
19
19
|
|
20
|
-
|
20
|
+
bext = ext.encode("ascii", "replace")
|
21
|
+
ext = bext.decode("utf-8")
|
22
|
+
zb = hashlib.sha1(bext).digest()[2:4]
|
21
23
|
if PY2:
|
22
24
|
zb = [ord(x) for x in zb]
|
23
25
|
|
@@ -33,7 +35,7 @@ class Ico(object):
|
|
33
35
|
h = int(100 / (float(sw) / float(sh)))
|
34
36
|
w = 100
|
35
37
|
|
36
|
-
if chrome
|
38
|
+
if chrome:
|
37
39
|
# cannot handle more than ~2000 unique SVGs
|
38
40
|
if HAVE_PIL:
|
39
41
|
# svg: 3s, cache: 6s, this: 8s
|
@@ -43,8 +45,19 @@ class Ico(object):
|
|
43
45
|
w = 64
|
44
46
|
img = Image.new("RGB", (w, h), "#" + c[:6])
|
45
47
|
pb = ImageDraw.Draw(img)
|
46
|
-
|
47
|
-
|
48
|
+
try:
|
49
|
+
_, _, tw, th = pb.textbbox((0, 0), ext)
|
50
|
+
except:
|
51
|
+
tw, th = pb.textsize(ext)
|
52
|
+
|
53
|
+
tw += len(ext)
|
54
|
+
cw = tw // len(ext)
|
55
|
+
x = ((w - tw) // 2) - (cw * 2) // 3
|
56
|
+
fill = "#" + c[6:]
|
57
|
+
for ch in ext:
|
58
|
+
pb.text((x, (h - th) // 2), " %s " % (ch,), fill=fill)
|
59
|
+
x += cw
|
60
|
+
|
48
61
|
img = img.resize((w * 3, h * 3), Image.NEAREST)
|
49
62
|
|
50
63
|
buf = BytesIO()
|
copyparty/pwhash.py
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
from __future__ import print_function, unicode_literals
|
3
|
+
|
4
|
+
import argparse
|
5
|
+
import base64
|
6
|
+
import hashlib
|
7
|
+
import sys
|
8
|
+
import threading
|
9
|
+
|
10
|
+
from .__init__ import unicode
|
11
|
+
|
12
|
+
|
13
|
+
class PWHash(object):
|
14
|
+
def __init__(self, args ):
|
15
|
+
self.args = args
|
16
|
+
|
17
|
+
try:
|
18
|
+
alg, ac = args.ah_alg.split(",")
|
19
|
+
except:
|
20
|
+
alg = args.ah_alg
|
21
|
+
ac = {}
|
22
|
+
|
23
|
+
if alg == "none":
|
24
|
+
alg = ""
|
25
|
+
|
26
|
+
self.alg = alg
|
27
|
+
self.ac = ac
|
28
|
+
if not alg:
|
29
|
+
self.on = False
|
30
|
+
self.hash = unicode
|
31
|
+
return
|
32
|
+
|
33
|
+
self.on = True
|
34
|
+
self.salt = args.ah_salt.encode("utf-8")
|
35
|
+
self.cache = {}
|
36
|
+
self.mutex = threading.Lock()
|
37
|
+
self.hash = self._cache_hash
|
38
|
+
|
39
|
+
if alg == "sha2":
|
40
|
+
self._hash = self._gen_sha2
|
41
|
+
elif alg == "scrypt":
|
42
|
+
self._hash = self._gen_scrypt
|
43
|
+
elif alg == "argon2":
|
44
|
+
self._hash = self._gen_argon2
|
45
|
+
else:
|
46
|
+
t = "unsupported password hashing algorithm [{}], must be one of these: argon2 scrypt sha2 none"
|
47
|
+
raise Exception(t.format(alg))
|
48
|
+
|
49
|
+
def _cache_hash(self, plain ) :
|
50
|
+
with self.mutex:
|
51
|
+
try:
|
52
|
+
return self.cache[plain]
|
53
|
+
except:
|
54
|
+
pass
|
55
|
+
|
56
|
+
if not plain:
|
57
|
+
return ""
|
58
|
+
|
59
|
+
if len(plain) > 255:
|
60
|
+
raise Exception("password too long")
|
61
|
+
|
62
|
+
if len(self.cache) > 9000:
|
63
|
+
self.cache = {}
|
64
|
+
|
65
|
+
ret = self._hash(plain)
|
66
|
+
self.cache[plain] = ret
|
67
|
+
return ret
|
68
|
+
|
69
|
+
def _gen_sha2(self, plain ) :
|
70
|
+
its = int(self.ac[0]) if self.ac else 424242
|
71
|
+
bplain = plain.encode("utf-8")
|
72
|
+
ret = b"\n"
|
73
|
+
for _ in range(its):
|
74
|
+
ret = hashlib.sha512(self.salt + bplain + ret).digest()
|
75
|
+
|
76
|
+
return "+" + base64.urlsafe_b64encode(ret[:24]).decode("utf-8")
|
77
|
+
|
78
|
+
def _gen_scrypt(self, plain ) :
|
79
|
+
cost = 2 << 13
|
80
|
+
its = 2
|
81
|
+
blksz = 8
|
82
|
+
para = 4
|
83
|
+
try:
|
84
|
+
cost = 2 << int(self.ac[0])
|
85
|
+
its = int(self.ac[1])
|
86
|
+
blksz = int(self.ac[2])
|
87
|
+
para = int(self.ac[3])
|
88
|
+
except:
|
89
|
+
pass
|
90
|
+
|
91
|
+
ret = plain.encode("utf-8")
|
92
|
+
for _ in range(its):
|
93
|
+
ret = hashlib.scrypt(ret, salt=self.salt, n=cost, r=blksz, p=para, dklen=24)
|
94
|
+
|
95
|
+
return "+" + base64.urlsafe_b64encode(ret).decode("utf-8")
|
96
|
+
|
97
|
+
def _gen_argon2(self, plain ) :
|
98
|
+
from argon2.low_level import Type as ArgonType
|
99
|
+
from argon2.low_level import hash_secret
|
100
|
+
|
101
|
+
time_cost = 3
|
102
|
+
mem_cost = 256
|
103
|
+
parallelism = 4
|
104
|
+
version = 19
|
105
|
+
try:
|
106
|
+
time_cost = int(self.ac[0])
|
107
|
+
mem_cost = int(self.ac[1])
|
108
|
+
parallelism = int(self.ac[2])
|
109
|
+
version = int(self.ac[3])
|
110
|
+
except:
|
111
|
+
pass
|
112
|
+
|
113
|
+
bplain = plain.encode("utf-8")
|
114
|
+
|
115
|
+
bret = hash_secret(
|
116
|
+
secret=bplain,
|
117
|
+
salt=self.salt,
|
118
|
+
time_cost=time_cost,
|
119
|
+
memory_cost=mem_cost * 1024,
|
120
|
+
parallelism=parallelism,
|
121
|
+
hash_len=24,
|
122
|
+
type=ArgonType.ID,
|
123
|
+
version=version,
|
124
|
+
)
|
125
|
+
ret = bret.split(b"$")[-1].decode("utf-8")
|
126
|
+
return "+" + ret.replace("/", "_").replace("+", "-")
|
127
|
+
|
128
|
+
def stdin(self) :
|
129
|
+
while True:
|
130
|
+
ln = sys.stdin.readline().strip()
|
131
|
+
if not ln:
|
132
|
+
break
|
133
|
+
print(self.hash(ln))
|
134
|
+
|
135
|
+
def cli(self) :
|
136
|
+
import getpass
|
137
|
+
|
138
|
+
while True:
|
139
|
+
p1 = getpass.getpass("password> ")
|
140
|
+
p2 = getpass.getpass("again or just hit ENTER> ")
|
141
|
+
if p2 and p1 != p2:
|
142
|
+
print("\033[31minputs don't match; try again\033[0m", file=sys.stderr)
|
143
|
+
continue
|
144
|
+
print(self.hash(p1))
|
145
|
+
print()
|
copyparty/u2idx.py
CHANGED
@@ -66,7 +66,7 @@ class U2idx(object):
|
|
66
66
|
|
67
67
|
fsize = body["size"]
|
68
68
|
fhash = body["hash"]
|
69
|
-
wark = up2k_wark_from_hashlist(self.args.
|
69
|
+
wark = up2k_wark_from_hashlist(self.args.warksalt, fsize, fhash)
|
70
70
|
|
71
71
|
uq = "substr(w,1,16) = ? and w = ?"
|
72
72
|
uv = [wark[:16], wark]
|
copyparty/up2k.py
CHANGED
@@ -41,6 +41,7 @@ from .util import (
|
|
41
41
|
gen_filekey,
|
42
42
|
gen_filekey_dbg,
|
43
43
|
hidedir,
|
44
|
+
humansize,
|
44
45
|
min_ex,
|
45
46
|
quotep,
|
46
47
|
rand_name,
|
@@ -56,6 +57,7 @@ from .util import (
|
|
56
57
|
sfsenc,
|
57
58
|
spack,
|
58
59
|
statdir,
|
60
|
+
unhumanize,
|
59
61
|
vjoin,
|
60
62
|
vsplit,
|
61
63
|
w8b64dec,
|
@@ -107,7 +109,7 @@ class Up2k(object):
|
|
107
109
|
self.args = hub.args
|
108
110
|
self.log_func = hub.log
|
109
111
|
|
110
|
-
self.salt = self.args.
|
112
|
+
self.salt = self.args.warksalt
|
111
113
|
self.r_hash = re.compile("^[0-9a-zA-Z_-]{44}$")
|
112
114
|
|
113
115
|
self.gid = 0
|
@@ -122,6 +124,8 @@ class Up2k(object):
|
|
122
124
|
self.registry = {}
|
123
125
|
self.flags = {}
|
124
126
|
self.droppable = {}
|
127
|
+
self.volnfiles = {}
|
128
|
+
self.volsize = {}
|
125
129
|
self.volstate = {}
|
126
130
|
self.vol_act = {}
|
127
131
|
self.busy_aps = set()
|
@@ -258,6 +262,20 @@ class Up2k(object):
|
|
258
262
|
}
|
259
263
|
return json.dumps(ret, indent=4)
|
260
264
|
|
265
|
+
def get_volsize(self, ptop ) :
|
266
|
+
with self.mutex:
|
267
|
+
return self._get_volsize(ptop)
|
268
|
+
|
269
|
+
def _get_volsize(self, ptop ) :
|
270
|
+
cur = self.cur[ptop]
|
271
|
+
nbytes = self.volsize[cur]
|
272
|
+
nfiles = self.volnfiles[cur]
|
273
|
+
for j in list(self.registry.get(ptop, {}).values()):
|
274
|
+
nbytes += j["size"]
|
275
|
+
nfiles += 1
|
276
|
+
|
277
|
+
return (nbytes, nfiles)
|
278
|
+
|
261
279
|
def rescan(
|
262
280
|
self, all_vols , scan_vols , wait , fscan
|
263
281
|
) :
|
@@ -807,6 +825,8 @@ class Up2k(object):
|
|
807
825
|
try:
|
808
826
|
cur = self._open_db(db_path)
|
809
827
|
self.cur[ptop] = cur
|
828
|
+
self.volsize[cur] = 0
|
829
|
+
self.volnfiles[cur] = 0
|
810
830
|
|
811
831
|
# speeds measured uploading 520 small files on a WD20SPZX (SMR 2.5" 5400rpm 4kb)
|
812
832
|
dbd = flags["dbd"]
|
@@ -914,6 +934,24 @@ class Up2k(object):
|
|
914
934
|
|
915
935
|
db.c.connection.commit()
|
916
936
|
|
937
|
+
if vol.flags.get("vmaxb") or vol.flags.get("vmaxn"):
|
938
|
+
zs = "select count(sz), sum(sz) from up"
|
939
|
+
vn, vb = db.c.execute(zs).fetchone()
|
940
|
+
vb = vb or 0
|
941
|
+
vb += vn * 2048
|
942
|
+
self.volsize[db.c] = vb
|
943
|
+
self.volnfiles[db.c] = vn
|
944
|
+
vmaxb = unhumanize(vol.flags.get("vmaxb") or "0")
|
945
|
+
vmaxn = unhumanize(vol.flags.get("vmaxn") or "0")
|
946
|
+
t = "{} / {} ( {} / {} files) in {}".format(
|
947
|
+
humansize(vb, True),
|
948
|
+
humansize(vmaxb, True),
|
949
|
+
humansize(vn, True).rstrip("B"),
|
950
|
+
humansize(vmaxn, True).rstrip("B"),
|
951
|
+
vol.realpath,
|
952
|
+
)
|
953
|
+
self.log(t)
|
954
|
+
|
917
955
|
return True, bool(n_add or n_rm or do_vac)
|
918
956
|
|
919
957
|
def _build_dir(
|
@@ -1089,7 +1127,7 @@ class Up2k(object):
|
|
1089
1127
|
top, rp, dts, lmod, dsz, sz
|
1090
1128
|
)
|
1091
1129
|
self.log(t)
|
1092
|
-
self.db_rm(db.c, rd, fn)
|
1130
|
+
self.db_rm(db.c, rd, fn, 0)
|
1093
1131
|
ret += 1
|
1094
1132
|
db.n += 1
|
1095
1133
|
in_db = []
|
@@ -1172,7 +1210,7 @@ class Up2k(object):
|
|
1172
1210
|
rm_files = [x for x in hits if x not in seen_files]
|
1173
1211
|
n_rm = len(rm_files)
|
1174
1212
|
for fn in rm_files:
|
1175
|
-
self.db_rm(db.c, rd, fn)
|
1213
|
+
self.db_rm(db.c, rd, fn, 0)
|
1176
1214
|
|
1177
1215
|
if n_rm:
|
1178
1216
|
self.log("forgot {} deleted files".format(n_rm))
|
@@ -2281,7 +2319,9 @@ class Up2k(object):
|
|
2281
2319
|
if lost:
|
2282
2320
|
c2 = None
|
2283
2321
|
for cur, dp_dir, dp_fn in lost:
|
2284
|
-
|
2322
|
+
t = "forgetting deleted file: /{}"
|
2323
|
+
self.log(t.format(vjoin(vjoin(vfs.vpath, dp_dir), dp_fn)))
|
2324
|
+
self.db_rm(cur, dp_dir, dp_fn, cj["size"])
|
2285
2325
|
if c2 and c2 != cur:
|
2286
2326
|
c2.connection.commit()
|
2287
2327
|
|
@@ -2415,7 +2455,14 @@ class Up2k(object):
|
|
2415
2455
|
|
2416
2456
|
if vfs.lim:
|
2417
2457
|
ap2, cj["prel"] = vfs.lim.all(
|
2418
|
-
cj["addr"],
|
2458
|
+
cj["addr"],
|
2459
|
+
cj["prel"],
|
2460
|
+
cj["size"],
|
2461
|
+
cj["ptop"],
|
2462
|
+
ap1,
|
2463
|
+
self.hub.broker,
|
2464
|
+
reg,
|
2465
|
+
"up2k._get_volsize",
|
2419
2466
|
)
|
2420
2467
|
bos.makedirs(ap2)
|
2421
2468
|
vfs.lim.nup(cj["addr"])
|
@@ -2733,7 +2780,7 @@ class Up2k(object):
|
|
2733
2780
|
|
2734
2781
|
self._symlink(dst, d2, self.flags[ptop], lmod=lmod)
|
2735
2782
|
if cur:
|
2736
|
-
self.db_rm(cur, rd, fn)
|
2783
|
+
self.db_rm(cur, rd, fn, job["size"])
|
2737
2784
|
self.db_add(cur, vflags, rd, fn, lmod, *z2[3:])
|
2738
2785
|
|
2739
2786
|
if cur:
|
@@ -2776,7 +2823,7 @@ class Up2k(object):
|
|
2776
2823
|
|
2777
2824
|
self.db_act = self.vol_act[ptop] = time.time()
|
2778
2825
|
try:
|
2779
|
-
self.db_rm(cur, rd, fn)
|
2826
|
+
self.db_rm(cur, rd, fn, sz)
|
2780
2827
|
self.db_add(
|
2781
2828
|
cur,
|
2782
2829
|
vflags,
|
@@ -2806,13 +2853,17 @@ class Up2k(object):
|
|
2806
2853
|
|
2807
2854
|
return True
|
2808
2855
|
|
2809
|
-
def db_rm(self, db , rd , fn ) :
|
2856
|
+
def db_rm(self, db , rd , fn , sz ) :
|
2810
2857
|
sql = "delete from up where rd = ? and fn = ?"
|
2811
2858
|
try:
|
2812
|
-
db.execute(sql, (rd, fn))
|
2859
|
+
r = db.execute(sql, (rd, fn))
|
2813
2860
|
except:
|
2814
2861
|
assert self.mem_cur
|
2815
|
-
db.execute(sql, s3enc(self.mem_cur, rd, fn))
|
2862
|
+
r = db.execute(sql, s3enc(self.mem_cur, rd, fn))
|
2863
|
+
|
2864
|
+
if r.rowcount:
|
2865
|
+
self.volsize[db] -= sz
|
2866
|
+
self.volnfiles[db] -= 1
|
2816
2867
|
|
2817
2868
|
def db_add(
|
2818
2869
|
self,
|
@@ -2841,6 +2892,9 @@ class Up2k(object):
|
|
2841
2892
|
v = (wark, int(ts), sz, rd, fn, ip or "", int(at or 0))
|
2842
2893
|
db.execute(sql, v)
|
2843
2894
|
|
2895
|
+
self.volsize[db] += sz
|
2896
|
+
self.volnfiles[db] += 1
|
2897
|
+
|
2844
2898
|
xau = False if skip_xau else vflags.get("xau")
|
2845
2899
|
dst = djoin(ptop, rd, fn)
|
2846
2900
|
if xau and not runhook(
|
@@ -2988,12 +3042,12 @@ class Up2k(object):
|
|
2988
3042
|
break
|
2989
3043
|
|
2990
3044
|
abspath = djoin(adir, fn)
|
3045
|
+
st = bos.stat(abspath)
|
2991
3046
|
volpath = "{}/{}".format(vrem, fn).strip("/")
|
2992
3047
|
vpath = "{}/{}".format(dbv.vpath, volpath).strip("/")
|
2993
3048
|
self.log("rm {}\n {}".format(vpath, abspath))
|
2994
3049
|
_ = dbv.get(volpath, uname, *permsets[0])
|
2995
3050
|
if xbd:
|
2996
|
-
st = bos.stat(abspath)
|
2997
3051
|
if not runhook(
|
2998
3052
|
self.log,
|
2999
3053
|
xbd,
|
@@ -3017,14 +3071,26 @@ class Up2k(object):
|
|
3017
3071
|
try:
|
3018
3072
|
ptop = dbv.realpath
|
3019
3073
|
cur, wark, _, _, _, _ = self._find_from_vpath(ptop, volpath)
|
3020
|
-
self._forget_file(ptop, volpath, cur, wark, True)
|
3074
|
+
self._forget_file(ptop, volpath, cur, wark, True, st.st_size)
|
3021
3075
|
finally:
|
3022
3076
|
if cur:
|
3023
3077
|
cur.connection.commit()
|
3024
3078
|
|
3025
3079
|
bos.unlink(abspath)
|
3026
3080
|
if xad:
|
3027
|
-
runhook(
|
3081
|
+
runhook(
|
3082
|
+
self.log,
|
3083
|
+
xad,
|
3084
|
+
abspath,
|
3085
|
+
vpath,
|
3086
|
+
"",
|
3087
|
+
uname,
|
3088
|
+
st.st_mtime,
|
3089
|
+
st.st_size,
|
3090
|
+
ip,
|
3091
|
+
0,
|
3092
|
+
"",
|
3093
|
+
)
|
3028
3094
|
|
3029
3095
|
if is_dir:
|
3030
3096
|
ok, ng = rmdirs(self.log_func, scandir, True, atop, 1)
|
@@ -3200,7 +3266,7 @@ class Up2k(object):
|
|
3200
3266
|
if c2 and c2 != c1:
|
3201
3267
|
self._copy_tags(c1, c2, w)
|
3202
3268
|
|
3203
|
-
self._forget_file(svn.realpath, srem, c1, w, c1 != c2)
|
3269
|
+
self._forget_file(svn.realpath, srem, c1, w, c1 != c2, fsize)
|
3204
3270
|
self._relink(w, svn.realpath, srem, dabs)
|
3205
3271
|
curs.add(c1)
|
3206
3272
|
|
@@ -3276,6 +3342,7 @@ class Up2k(object):
|
|
3276
3342
|
cur ,
|
3277
3343
|
wark ,
|
3278
3344
|
drop_tags ,
|
3345
|
+
sz ,
|
3279
3346
|
) :
|
3280
3347
|
"""forgets file in db, fixes symlinks, does not delete"""
|
3281
3348
|
srd, sfn = vsplit(vrem)
|
@@ -3290,7 +3357,7 @@ class Up2k(object):
|
|
3290
3357
|
q = "delete from mt where w=?"
|
3291
3358
|
cur.execute(q, (wark[:16],))
|
3292
3359
|
|
3293
|
-
self.db_rm(cur, srd, sfn)
|
3360
|
+
self.db_rm(cur, srd, sfn, sz)
|
3294
3361
|
|
3295
3362
|
reg = self.registry.get(ptop)
|
3296
3363
|
if reg:
|