copyparty 1.12.2__py3-none-any.whl → 1.13.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 +31 -4
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +14 -2
- copyparty/cfg.py +16 -2
- copyparty/fsutil.py +25 -5
- copyparty/httpcli.py +433 -18
- copyparty/httpconn.py +1 -0
- copyparty/httpsrv.py +2 -0
- copyparty/metrics.py +1 -1
- copyparty/svchub.py +5 -1
- copyparty/tcpsrv.py +6 -0
- copyparty/u2idx.py +18 -3
- copyparty/up2k.py +128 -53
- copyparty/util.py +49 -3
- copyparty/web/a/u2c.py +8 -4
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +1 -1
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/md.html +1 -1
- copyparty/web/mde.html +1 -1
- copyparty/web/msg.html +1 -1
- copyparty/web/splash.html +2 -1
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +1 -1
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/METADATA +66 -9
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/RECORD +33 -33
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/LICENSE +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/WHEEL +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/top_level.txt +0 -0
copyparty/metrics.py
CHANGED
copyparty/svchub.py
CHANGED
@@ -520,7 +520,7 @@ class SvcHub(object):
|
|
520
520
|
al.exp_md = odfusion(exp, al.exp_md.replace(" ", ","))
|
521
521
|
al.exp_lg = odfusion(exp, al.exp_lg.replace(" ", ","))
|
522
522
|
|
523
|
-
for k in ["no_hash", "no_idx"]:
|
523
|
+
for k in ["no_hash", "no_idx", "og_ua"]:
|
524
524
|
ptn = getattr(self.args, k)
|
525
525
|
if ptn:
|
526
526
|
setattr(self.args, k, re.compile(ptn))
|
@@ -551,6 +551,10 @@ class SvcHub(object):
|
|
551
551
|
except:
|
552
552
|
raise Exception("invalid --mv-retry [%s]" % (self.args.mv_retry,))
|
553
553
|
|
554
|
+
al.tcolor = al.tcolor.lstrip("#")
|
555
|
+
if len(al.tcolor) == 3: # fc5 => ffcc55
|
556
|
+
al.tcolor = "".join([x * 2 for x in al.tcolor])
|
557
|
+
|
554
558
|
return True
|
555
559
|
|
556
560
|
def _ipa2re(self, txt) :
|
copyparty/tcpsrv.py
CHANGED
@@ -460,6 +460,12 @@ class TcpSrv(object):
|
|
460
460
|
sys.stderr.flush()
|
461
461
|
|
462
462
|
def _qr(self, t1 , t2 ) :
|
463
|
+
t2c = {zs: zli for zs, zli in t2.items() if zs in ("127.0.0.1", "::1")}
|
464
|
+
t2b = {zs: zli for zs, zli in t2.items() if ":" in zs and zs not in t2c}
|
465
|
+
t2 = {zs: zli for zs, zli in t2.items() if zs not in t2b and zs not in t2c}
|
466
|
+
t2.update(t2b) # first ipv4, then ipv6...
|
467
|
+
t2.update(t2c) # ...and finally localhost
|
468
|
+
|
463
469
|
ip = None
|
464
470
|
ips = list(t1) + list(t2)
|
465
471
|
qri = self.args.qri
|
copyparty/u2idx.py
CHANGED
@@ -59,6 +59,17 @@ class U2idx(object):
|
|
59
59
|
def log(self, msg , c = 0) :
|
60
60
|
self.log_func("u2idx", msg, c)
|
61
61
|
|
62
|
+
def shutdown(self) :
|
63
|
+
for cur in self.cur.values():
|
64
|
+
db = cur.connection
|
65
|
+
try:
|
66
|
+
db.interrupt()
|
67
|
+
except:
|
68
|
+
pass
|
69
|
+
|
70
|
+
cur.close()
|
71
|
+
db.close()
|
72
|
+
|
62
73
|
def fsearch(
|
63
74
|
self, uname , vols , body
|
64
75
|
) :
|
@@ -78,14 +89,18 @@ class U2idx(object):
|
|
78
89
|
except:
|
79
90
|
raise Pebkac(500, min_ex())
|
80
91
|
|
81
|
-
def get_cur(self,
|
92
|
+
def get_cur(self, vn ) :
|
82
93
|
if not HAVE_SQLITE3:
|
83
94
|
return None
|
84
95
|
|
85
|
-
cur = self.cur.get(
|
96
|
+
cur = self.cur.get(vn.realpath)
|
86
97
|
if cur:
|
87
98
|
return cur
|
88
99
|
|
100
|
+
if "e2d" not in vn.flags:
|
101
|
+
return None
|
102
|
+
|
103
|
+
ptop = vn.realpath
|
89
104
|
histpath = self.asrv.vfs.histtab.get(ptop)
|
90
105
|
if not histpath:
|
91
106
|
self.log("no histpath for [{}]".format(ptop))
|
@@ -314,7 +329,7 @@ class U2idx(object):
|
|
314
329
|
ptop = vol.realpath
|
315
330
|
flags = vol.flags
|
316
331
|
|
317
|
-
cur = self.get_cur(
|
332
|
+
cur = self.get_cur(vol)
|
318
333
|
if not cur:
|
319
334
|
continue
|
320
335
|
|
copyparty/up2k.py
CHANGED
@@ -136,6 +136,7 @@ class Up2k(object):
|
|
136
136
|
self.need_rescan = set()
|
137
137
|
self.db_act = 0.0
|
138
138
|
|
139
|
+
self.reg_mutex = threading.Lock()
|
139
140
|
self.registry = {}
|
140
141
|
self.flags = {}
|
141
142
|
self.droppable = {}
|
@@ -143,7 +144,7 @@ class Up2k(object):
|
|
143
144
|
self.volsize = {}
|
144
145
|
self.volstate = {}
|
145
146
|
self.vol_act = {}
|
146
|
-
self.busy_aps
|
147
|
+
self.busy_aps = {}
|
147
148
|
self.dupesched = {}
|
148
149
|
self.snap_prev = {}
|
149
150
|
|
@@ -182,7 +183,7 @@ class Up2k(object):
|
|
182
183
|
t = "could not initialize sqlite3, will use in-memory registry only"
|
183
184
|
self.log(t, 3)
|
184
185
|
|
185
|
-
self.fstab = Fstab(self.log_func)
|
186
|
+
self.fstab = Fstab(self.log_func, self.args)
|
186
187
|
self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey
|
187
188
|
|
188
189
|
if self.args.hash_mt < 2:
|
@@ -200,11 +201,15 @@ class Up2k(object):
|
|
200
201
|
Daemon(self.deferred_init, "up2k-deferred-init")
|
201
202
|
|
202
203
|
def reload(self, rescan_all_vols ) :
|
203
|
-
"""mutex me"""
|
204
|
+
"""mutex(main) me"""
|
204
205
|
self.log("reload #{} scheduled".format(self.gid + 1))
|
205
206
|
all_vols = self.asrv.vfs.all_vols
|
206
207
|
|
207
|
-
|
208
|
+
with self.reg_mutex:
|
209
|
+
scan_vols = [
|
210
|
+
k for k, v in all_vols.items() if v.realpath not in self.registry
|
211
|
+
]
|
212
|
+
|
208
213
|
if rescan_all_vols:
|
209
214
|
scan_vols = list(all_vols.keys())
|
210
215
|
|
@@ -217,7 +222,7 @@ class Up2k(object):
|
|
217
222
|
if self.stop:
|
218
223
|
# up-mt consistency not guaranteed if init is interrupted;
|
219
224
|
# drop caches for a full scan on next boot
|
220
|
-
with self.mutex:
|
225
|
+
with self.mutex, self.reg_mutex:
|
221
226
|
self._drop_caches()
|
222
227
|
|
223
228
|
if self.pp:
|
@@ -283,10 +288,27 @@ class Up2k(object):
|
|
283
288
|
min(1000 * 24 * 60 * 60 - 1, time.time() - self.db_act)
|
284
289
|
),
|
285
290
|
}
|
286
|
-
return json.dumps(ret,
|
291
|
+
return json.dumps(ret, separators=(",\n", ": "))
|
292
|
+
|
293
|
+
def find_job_by_ap(self, ptop , ap ) :
|
294
|
+
try:
|
295
|
+
if ANYWIN:
|
296
|
+
ap = ap.replace("\\", "/")
|
297
|
+
|
298
|
+
vp = ap[len(ptop) :].strip("/")
|
299
|
+
dn, fn = vsplit(vp)
|
300
|
+
with self.reg_mutex:
|
301
|
+
tab2 = self.registry[ptop]
|
302
|
+
for job in tab2.values():
|
303
|
+
if job["prel"] == dn and job["name"] == fn:
|
304
|
+
return json.dumps(job, separators=(",\n", ": "))
|
305
|
+
except:
|
306
|
+
pass
|
307
|
+
|
308
|
+
return "{}"
|
287
309
|
|
288
310
|
def get_unfinished_by_user(self, uname, ip) :
|
289
|
-
if PY2 or not self.
|
311
|
+
if PY2 or not self.reg_mutex.acquire(timeout=2):
|
290
312
|
return '[{"timeout":1}]'
|
291
313
|
|
292
314
|
ret = []
|
@@ -315,17 +337,25 @@ class Up2k(object):
|
|
315
337
|
)
|
316
338
|
ret.append(zt5)
|
317
339
|
finally:
|
318
|
-
self.
|
340
|
+
self.reg_mutex.release()
|
341
|
+
|
342
|
+
if ANYWIN:
|
343
|
+
ret = [(x[0], x[1].replace("\\", "/"), x[2], x[3], x[4]) for x in ret]
|
319
344
|
|
320
345
|
ret.sort(reverse=True)
|
321
346
|
ret2 = [
|
322
|
-
{
|
347
|
+
{
|
348
|
+
"at": at,
|
349
|
+
"vp": "/" + quotep(vp),
|
350
|
+
"pd": 100 - ((nn * 100) // (nh or 1)),
|
351
|
+
"sz": sz,
|
352
|
+
}
|
323
353
|
for (at, vp, sz, nn, nh) in ret
|
324
354
|
]
|
325
|
-
return json.dumps(ret2,
|
355
|
+
return json.dumps(ret2, separators=(",\n", ": "))
|
326
356
|
|
327
357
|
def get_unfinished(self) :
|
328
|
-
if PY2 or not self.
|
358
|
+
if PY2 or not self.reg_mutex.acquire(timeout=0.5):
|
329
359
|
return ""
|
330
360
|
|
331
361
|
ret = {}
|
@@ -347,17 +377,17 @@ class Up2k(object):
|
|
347
377
|
|
348
378
|
ret[ptop] = (nbytes, nfiles)
|
349
379
|
finally:
|
350
|
-
self.
|
380
|
+
self.reg_mutex.release()
|
351
381
|
|
352
|
-
return json.dumps(ret,
|
382
|
+
return json.dumps(ret, separators=(",\n", ": "))
|
353
383
|
|
354
384
|
def get_volsize(self, ptop ) :
|
355
|
-
with self.
|
385
|
+
with self.reg_mutex:
|
356
386
|
return self._get_volsize(ptop)
|
357
387
|
|
358
388
|
def get_volsizes(self, ptops ) :
|
359
389
|
ret = []
|
360
|
-
with self.
|
390
|
+
with self.reg_mutex:
|
361
391
|
for ptop in ptops:
|
362
392
|
ret.append(self._get_volsize(ptop))
|
363
393
|
|
@@ -385,7 +415,7 @@ class Up2k(object):
|
|
385
415
|
def _rescan(
|
386
416
|
self, all_vols , scan_vols , wait , fscan
|
387
417
|
) :
|
388
|
-
"""mutex me"""
|
418
|
+
"""mutex(main) me"""
|
389
419
|
if not wait and self.pp:
|
390
420
|
return "cannot initiate; scan is already in progress"
|
391
421
|
|
@@ -667,7 +697,7 @@ class Up2k(object):
|
|
667
697
|
self.log(msg, c=3)
|
668
698
|
|
669
699
|
live_vols = []
|
670
|
-
with self.mutex:
|
700
|
+
with self.mutex, self.reg_mutex:
|
671
701
|
# only need to protect register_vpath but all in one go feels right
|
672
702
|
for vol in vols:
|
673
703
|
try:
|
@@ -709,7 +739,7 @@ class Up2k(object):
|
|
709
739
|
|
710
740
|
if self.args.re_dhash or [zv for zv in vols if "e2tsr" in zv.flags]:
|
711
741
|
self.args.re_dhash = False
|
712
|
-
with self.mutex:
|
742
|
+
with self.mutex, self.reg_mutex:
|
713
743
|
self._drop_caches()
|
714
744
|
|
715
745
|
for vol in vols:
|
@@ -786,7 +816,9 @@ class Up2k(object):
|
|
786
816
|
self.volstate[vol.vpath] = "online (mtp soon)"
|
787
817
|
|
788
818
|
for vol in need_vac:
|
789
|
-
|
819
|
+
with self.mutex, self.reg_mutex:
|
820
|
+
reg = self.register_vpath(vol.realpath, vol.flags)
|
821
|
+
|
790
822
|
assert reg
|
791
823
|
cur, _ = reg
|
792
824
|
with self.mutex:
|
@@ -800,7 +832,9 @@ class Up2k(object):
|
|
800
832
|
if vol.flags["dbd"] == "acid":
|
801
833
|
continue
|
802
834
|
|
803
|
-
|
835
|
+
with self.mutex, self.reg_mutex:
|
836
|
+
reg = self.register_vpath(vol.realpath, vol.flags)
|
837
|
+
|
804
838
|
try:
|
805
839
|
assert reg
|
806
840
|
cur, db_path = reg
|
@@ -847,6 +881,7 @@ class Up2k(object):
|
|
847
881
|
def register_vpath(
|
848
882
|
self, ptop , flags
|
849
883
|
) :
|
884
|
+
"""mutex(main,reg) me"""
|
850
885
|
histpath = self.asrv.vfs.histtab.get(ptop)
|
851
886
|
if not histpath:
|
852
887
|
self.log("no histpath for [{}]".format(ptop))
|
@@ -1030,7 +1065,9 @@ class Up2k(object):
|
|
1030
1065
|
dev = cst.st_dev if vol.flags.get("xdev") else 0
|
1031
1066
|
|
1032
1067
|
with self.mutex:
|
1033
|
-
|
1068
|
+
with self.reg_mutex:
|
1069
|
+
reg = self.register_vpath(top, vol.flags)
|
1070
|
+
|
1034
1071
|
assert reg and self.pp
|
1035
1072
|
cur, db_path = reg
|
1036
1073
|
|
@@ -1627,7 +1664,7 @@ class Up2k(object):
|
|
1627
1664
|
|
1628
1665
|
def _build_tags_index(self, vol ) :
|
1629
1666
|
ptop = vol.realpath
|
1630
|
-
with self.mutex:
|
1667
|
+
with self.mutex, self.reg_mutex:
|
1631
1668
|
reg = self.register_vpath(ptop, vol.flags)
|
1632
1669
|
|
1633
1670
|
assert reg and self.pp
|
@@ -1648,6 +1685,7 @@ class Up2k(object):
|
|
1648
1685
|
return ret
|
1649
1686
|
|
1650
1687
|
def _drop_caches(self) :
|
1688
|
+
"""mutex(main,reg) me"""
|
1651
1689
|
self.log("dropping caches for a full filesystem scan")
|
1652
1690
|
for vol in self.asrv.vfs.all_vols.values():
|
1653
1691
|
reg = self.register_vpath(vol.realpath, vol.flags)
|
@@ -1823,7 +1861,7 @@ class Up2k(object):
|
|
1823
1861
|
params ,
|
1824
1862
|
flt ,
|
1825
1863
|
) :
|
1826
|
-
"""mutex me"""
|
1864
|
+
"""mutex(main) me"""
|
1827
1865
|
n = 0
|
1828
1866
|
c2 = cur.connection.cursor()
|
1829
1867
|
tf = tempfile.SpooledTemporaryFile(1024 * 1024 * 8, "w+b", prefix="cpp-tq-")
|
@@ -2157,7 +2195,7 @@ class Up2k(object):
|
|
2157
2195
|
ip ,
|
2158
2196
|
at ,
|
2159
2197
|
) :
|
2160
|
-
"""will mutex"""
|
2198
|
+
"""will mutex(main)"""
|
2161
2199
|
assert self.mtag
|
2162
2200
|
|
2163
2201
|
try:
|
@@ -2189,7 +2227,7 @@ class Up2k(object):
|
|
2189
2227
|
abspath ,
|
2190
2228
|
tags ,
|
2191
2229
|
) :
|
2192
|
-
"""mutex me"""
|
2230
|
+
"""mutex(main) me"""
|
2193
2231
|
assert self.mtag
|
2194
2232
|
|
2195
2233
|
if not bos.path.isfile(abspath):
|
@@ -2474,28 +2512,36 @@ class Up2k(object):
|
|
2474
2512
|
|
2475
2513
|
cur.connection.commit()
|
2476
2514
|
|
2477
|
-
def
|
2478
|
-
|
2479
|
-
|
2480
|
-
|
2481
|
-
|
2482
|
-
def handle_json(self, cj , busy_aps ) :
|
2515
|
+
def handle_json(
|
2516
|
+
self, cj , busy_aps
|
2517
|
+
) :
|
2518
|
+
# busy_aps is u2fh (always undefined if -j0) so this is safe
|
2483
2519
|
self.busy_aps = busy_aps
|
2520
|
+
got_lock = False
|
2484
2521
|
try:
|
2485
2522
|
# bit expensive; 3.9=10x 3.11=2x
|
2486
2523
|
if self.mutex.acquire(timeout=10):
|
2487
|
-
|
2488
|
-
self.
|
2524
|
+
got_lock = True
|
2525
|
+
with self.reg_mutex:
|
2526
|
+
return self._handle_json(cj)
|
2489
2527
|
else:
|
2490
2528
|
t = "cannot receive uploads right now;\nserver busy with {}.\nPlease wait; the client will retry..."
|
2491
2529
|
raise Pebkac(503, t.format(self.blocked or "[unknown]"))
|
2492
2530
|
except TypeError:
|
2493
2531
|
if not PY2:
|
2494
2532
|
raise
|
2495
|
-
with self.mutex:
|
2496
|
-
self.
|
2533
|
+
with self.mutex, self.reg_mutex:
|
2534
|
+
return self._handle_json(cj)
|
2535
|
+
finally:
|
2536
|
+
if got_lock:
|
2537
|
+
self.mutex.release()
|
2497
2538
|
|
2539
|
+
def _handle_json(self, cj ) :
|
2498
2540
|
ptop = cj["ptop"]
|
2541
|
+
if not self.register_vpath(ptop, cj["vcfg"]):
|
2542
|
+
if ptop not in self.registry:
|
2543
|
+
raise Pebkac(410, "location unavailable")
|
2544
|
+
|
2499
2545
|
cj["name"] = sanitize_fn(cj["name"], "", [".prologue.html", ".epilogue.html"])
|
2500
2546
|
cj["poke"] = now = self.db_act = self.vol_act[ptop] = time.time()
|
2501
2547
|
wark = self._get_wark(cj)
|
@@ -2510,7 +2556,7 @@ class Up2k(object):
|
|
2510
2556
|
# refuse out-of-order / multithreaded uploading if sprs False
|
2511
2557
|
sprs = self.fstab.get(pdir) != "ng"
|
2512
2558
|
|
2513
|
-
|
2559
|
+
if True:
|
2514
2560
|
jcur = self.cur.get(ptop)
|
2515
2561
|
reg = self.registry[ptop]
|
2516
2562
|
vfs = self.asrv.vfs.all_vols[cj["vtop"]]
|
@@ -2948,7 +2994,7 @@ class Up2k(object):
|
|
2948
2994
|
def handle_chunk(
|
2949
2995
|
self, ptop , wark , chash
|
2950
2996
|
) :
|
2951
|
-
with self.mutex:
|
2997
|
+
with self.mutex, self.reg_mutex:
|
2952
2998
|
self.db_act = self.vol_act[ptop] = time.time()
|
2953
2999
|
job = self.registry[ptop].get(wark)
|
2954
3000
|
if not job:
|
@@ -2991,7 +3037,7 @@ class Up2k(object):
|
|
2991
3037
|
return chunksize, ofs, path, job["lmod"], job["sprs"]
|
2992
3038
|
|
2993
3039
|
def release_chunk(self, ptop , wark , chash ) :
|
2994
|
-
with self.
|
3040
|
+
with self.reg_mutex:
|
2995
3041
|
job = self.registry[ptop].get(wark)
|
2996
3042
|
if job:
|
2997
3043
|
job["busy"].pop(chash, None)
|
@@ -2999,7 +3045,7 @@ class Up2k(object):
|
|
2999
3045
|
return True
|
3000
3046
|
|
3001
3047
|
def confirm_chunk(self, ptop , wark , chash ) :
|
3002
|
-
with self.mutex:
|
3048
|
+
with self.mutex, self.reg_mutex:
|
3003
3049
|
self.db_act = self.vol_act[ptop] = time.time()
|
3004
3050
|
try:
|
3005
3051
|
job = self.registry[ptop][wark]
|
@@ -3022,16 +3068,16 @@ class Up2k(object):
|
|
3022
3068
|
|
3023
3069
|
if self.args.nw:
|
3024
3070
|
self.regdrop(ptop, wark)
|
3025
|
-
return ret, dst
|
3026
3071
|
|
3027
3072
|
return ret, dst
|
3028
3073
|
|
3029
3074
|
def finish_upload(self, ptop , wark , busy_aps ) :
|
3030
3075
|
self.busy_aps = busy_aps
|
3031
|
-
with self.mutex:
|
3076
|
+
with self.mutex, self.reg_mutex:
|
3032
3077
|
self._finish_upload(ptop, wark)
|
3033
3078
|
|
3034
3079
|
def _finish_upload(self, ptop , wark ) :
|
3080
|
+
"""mutex(main,reg) me"""
|
3035
3081
|
try:
|
3036
3082
|
job = self.registry[ptop][wark]
|
3037
3083
|
pdir = djoin(job["ptop"], job["prel"])
|
@@ -3104,6 +3150,7 @@ class Up2k(object):
|
|
3104
3150
|
cur.connection.commit()
|
3105
3151
|
|
3106
3152
|
def regdrop(self, ptop , wark ) :
|
3153
|
+
"""mutex(main,reg) me"""
|
3107
3154
|
olds = self.droppable[ptop]
|
3108
3155
|
if wark:
|
3109
3156
|
olds.append(wark)
|
@@ -3198,16 +3245,23 @@ class Up2k(object):
|
|
3198
3245
|
at ,
|
3199
3246
|
skip_xau = False,
|
3200
3247
|
) :
|
3248
|
+
"""mutex(main) me"""
|
3201
3249
|
self.db_rm(db, rd, fn, sz)
|
3202
3250
|
|
3251
|
+
if not ip:
|
3252
|
+
db_ip = ""
|
3253
|
+
else:
|
3254
|
+
# plugins may expect this to look like an actual IP
|
3255
|
+
db_ip = "1.1.1.1" if self.args.no_db_ip else ip
|
3256
|
+
|
3203
3257
|
sql = "insert into up values (?,?,?,?,?,?,?)"
|
3204
|
-
v = (wark, int(ts), sz, rd, fn,
|
3258
|
+
v = (wark, int(ts), sz, rd, fn, db_ip, int(at or 0))
|
3205
3259
|
try:
|
3206
3260
|
db.execute(sql, v)
|
3207
3261
|
except:
|
3208
3262
|
assert self.mem_cur
|
3209
3263
|
rd, fn = s3enc(self.mem_cur, rd, fn)
|
3210
|
-
v = (wark, int(ts), sz, rd, fn,
|
3264
|
+
v = (wark, int(ts), sz, rd, fn, db_ip, int(at or 0))
|
3211
3265
|
db.execute(sql, v)
|
3212
3266
|
|
3213
3267
|
self.volsize[db] += sz
|
@@ -3311,7 +3365,7 @@ class Up2k(object):
|
|
3311
3365
|
vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0])
|
3312
3366
|
vn, rem = vn.get_dbv(rem)
|
3313
3367
|
ptop = vn.realpath
|
3314
|
-
with self.mutex:
|
3368
|
+
with self.mutex, self.reg_mutex:
|
3315
3369
|
abrt_cfg = self.flags.get(ptop, {}).get("u2abort", 1)
|
3316
3370
|
addr = (ip or "\n") if abrt_cfg in (1, 2) else ""
|
3317
3371
|
user = (uname or "\n") if abrt_cfg in (1, 3) else ""
|
@@ -3319,7 +3373,10 @@ class Up2k(object):
|
|
3319
3373
|
for wark, job in reg.items():
|
3320
3374
|
if (user and user != job["user"]) or (addr and addr != job["addr"]):
|
3321
3375
|
continue
|
3322
|
-
|
3376
|
+
jrem = djoin(job["prel"], job["name"])
|
3377
|
+
if ANYWIN:
|
3378
|
+
jrem = jrem.replace("\\", "/")
|
3379
|
+
if jrem == rem:
|
3323
3380
|
if job["ptop"] != ptop:
|
3324
3381
|
t = "job.ptop [%s] != vol.ptop [%s] ??"
|
3325
3382
|
raise Exception(t % (job["ptop"] != ptop))
|
@@ -3415,7 +3472,7 @@ class Up2k(object):
|
|
3415
3472
|
continue
|
3416
3473
|
|
3417
3474
|
n_files += 1
|
3418
|
-
with self.mutex:
|
3475
|
+
with self.mutex, self.reg_mutex:
|
3419
3476
|
cur = None
|
3420
3477
|
try:
|
3421
3478
|
ptop = dbv.realpath
|
@@ -3533,6 +3590,7 @@ class Up2k(object):
|
|
3533
3590
|
def _mv_file(
|
3534
3591
|
self, uname , svp , dvp , curs
|
3535
3592
|
) :
|
3593
|
+
"""mutex(main) me; will mutex(reg)"""
|
3536
3594
|
svn, srem = self.asrv.vfs.get(svp, uname, True, False, True)
|
3537
3595
|
svn, srem = svn.get_dbv(srem)
|
3538
3596
|
|
@@ -3613,7 +3671,9 @@ class Up2k(object):
|
|
3613
3671
|
if c2 and c2 != c1:
|
3614
3672
|
self._copy_tags(c1, c2, w)
|
3615
3673
|
|
3616
|
-
|
3674
|
+
with self.reg_mutex:
|
3675
|
+
has_dupes = self._forget_file(svn.realpath, srem, c1, w, is_xvol, fsize)
|
3676
|
+
|
3617
3677
|
if not is_xvol:
|
3618
3678
|
has_dupes = self._relink(w, svn.realpath, srem, dabs)
|
3619
3679
|
|
@@ -3743,7 +3803,10 @@ class Up2k(object):
|
|
3743
3803
|
drop_tags ,
|
3744
3804
|
sz ,
|
3745
3805
|
) :
|
3746
|
-
"""
|
3806
|
+
"""
|
3807
|
+
mutex(main,reg) me
|
3808
|
+
forgets file in db, fixes symlinks, does not delete
|
3809
|
+
"""
|
3747
3810
|
srd, sfn = vsplit(vrem)
|
3748
3811
|
has_dupes = False
|
3749
3812
|
self.log("forgetting {}".format(vrem))
|
@@ -4068,7 +4131,7 @@ class Up2k(object):
|
|
4068
4131
|
self.do_snapshot()
|
4069
4132
|
|
4070
4133
|
def do_snapshot(self) :
|
4071
|
-
with self.mutex:
|
4134
|
+
with self.mutex, self.reg_mutex:
|
4072
4135
|
for k, reg in self.registry.items():
|
4073
4136
|
self._snap_reg(k, reg)
|
4074
4137
|
|
@@ -4136,7 +4199,7 @@ class Up2k(object):
|
|
4136
4199
|
|
4137
4200
|
path2 = "{}.{}".format(path, os.getpid())
|
4138
4201
|
body = {"droppable": self.droppable[ptop], "registry": reg}
|
4139
|
-
j = json.dumps(body,
|
4202
|
+
j = json.dumps(body, sort_keys=True, separators=(",\n", ": ")).encode("utf-8")
|
4140
4203
|
with gzip.GzipFile(path2, "wb") as f:
|
4141
4204
|
f.write(j)
|
4142
4205
|
|
@@ -4209,7 +4272,7 @@ class Up2k(object):
|
|
4209
4272
|
raise Exception("invalid hash task")
|
4210
4273
|
|
4211
4274
|
try:
|
4212
|
-
if not self._hash_t(task):
|
4275
|
+
if not self._hash_t(task) and self.stop:
|
4213
4276
|
return
|
4214
4277
|
except Exception as ex:
|
4215
4278
|
self.log("failed to hash %s: %s" % (task, ex), 1)
|
@@ -4219,7 +4282,7 @@ class Up2k(object):
|
|
4219
4282
|
) :
|
4220
4283
|
ptop, vtop, flags, rd, fn, ip, at, usr, skip_xau = task
|
4221
4284
|
# self.log("hashq {} pop {}/{}/{}".format(self.n_hashq, ptop, rd, fn))
|
4222
|
-
with self.mutex:
|
4285
|
+
with self.mutex, self.reg_mutex:
|
4223
4286
|
if not self.register_vpath(ptop, flags):
|
4224
4287
|
return True
|
4225
4288
|
|
@@ -4237,7 +4300,7 @@ class Up2k(object):
|
|
4237
4300
|
|
4238
4301
|
wark = up2k_wark_from_hashlist(self.salt, inf.st_size, hashes)
|
4239
4302
|
|
4240
|
-
with self.mutex:
|
4303
|
+
with self.mutex, self.reg_mutex:
|
4241
4304
|
self.idx_wark(
|
4242
4305
|
self.flags[ptop],
|
4243
4306
|
rd,
|
@@ -4313,6 +4376,18 @@ class Up2k(object):
|
|
4313
4376
|
for x in list(self.spools):
|
4314
4377
|
self._unspool(x)
|
4315
4378
|
|
4379
|
+
for cur in self.cur.values():
|
4380
|
+
db = cur.connection
|
4381
|
+
try:
|
4382
|
+
db.interrupt()
|
4383
|
+
except:
|
4384
|
+
pass
|
4385
|
+
|
4386
|
+
cur.close()
|
4387
|
+
db.close()
|
4388
|
+
|
4389
|
+
self.registry = {}
|
4390
|
+
|
4316
4391
|
|
4317
4392
|
def up2k_chunksize(filesize ) :
|
4318
4393
|
chunksize = 1024 * 1024
|
copyparty/util.py
CHANGED
@@ -35,6 +35,9 @@ from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, VT100, WINDOWS
|
|
35
35
|
from .__version__ import S_BUILD_DT, S_VERSION
|
36
36
|
from .stolen import surrogateescape
|
37
37
|
|
38
|
+
ub64dec = base64.urlsafe_b64decode
|
39
|
+
ub64enc = base64.urlsafe_b64encode
|
40
|
+
|
38
41
|
try:
|
39
42
|
from datetime import datetime, timezone
|
40
43
|
|
@@ -738,15 +741,46 @@ class CachedSet(object):
|
|
738
741
|
self.oldest = now
|
739
742
|
|
740
743
|
|
744
|
+
class CachedDict(object):
|
745
|
+
def __init__(self, maxage ) :
|
746
|
+
self.c = {}
|
747
|
+
self.maxage = maxage
|
748
|
+
self.oldest = 0.0
|
749
|
+
|
750
|
+
def set(self, k , v ) :
|
751
|
+
now = time.time()
|
752
|
+
self.c[k] = (now, v)
|
753
|
+
if now - self.oldest < self.maxage:
|
754
|
+
return
|
755
|
+
|
756
|
+
c = self.c = {k: v for k, v in self.c.items() if now - v[0] < self.maxage}
|
757
|
+
try:
|
758
|
+
self.oldest = min([x[0] for x in c.values()])
|
759
|
+
except:
|
760
|
+
self.oldest = now
|
761
|
+
|
762
|
+
def get(self, k ) :
|
763
|
+
try:
|
764
|
+
ts, ret = self.c[k]
|
765
|
+
now = time.time()
|
766
|
+
if now - ts > self.maxage:
|
767
|
+
del self.c[k]
|
768
|
+
return None
|
769
|
+
return ret
|
770
|
+
except:
|
771
|
+
return None
|
772
|
+
|
773
|
+
|
741
774
|
class FHC(object):
|
742
775
|
class CE(object):
|
743
776
|
def __init__(self, fh ) :
|
744
777
|
self.ts = 0
|
745
778
|
self.fhs = [fh]
|
779
|
+
self.all_fhs = set([fh])
|
746
780
|
|
747
781
|
def __init__(self) :
|
748
782
|
self.cache = {}
|
749
|
-
self.aps
|
783
|
+
self.aps = {}
|
750
784
|
|
751
785
|
def close(self, path ) :
|
752
786
|
try:
|
@@ -758,7 +792,7 @@ class FHC(object):
|
|
758
792
|
fh.close()
|
759
793
|
|
760
794
|
del self.cache[path]
|
761
|
-
self.aps
|
795
|
+
del self.aps[path]
|
762
796
|
|
763
797
|
def clean(self) :
|
764
798
|
if not self.cache:
|
@@ -779,9 +813,12 @@ class FHC(object):
|
|
779
813
|
return self.cache[path].fhs.pop()
|
780
814
|
|
781
815
|
def put(self, path , fh ) :
|
782
|
-
self.aps
|
816
|
+
if path not in self.aps:
|
817
|
+
self.aps[path] = 0
|
818
|
+
|
783
819
|
try:
|
784
820
|
ce = self.cache[path]
|
821
|
+
ce.all_fhs.add(fh)
|
785
822
|
ce.fhs.append(fh)
|
786
823
|
except:
|
787
824
|
ce = self.CE(fh)
|
@@ -1976,6 +2013,7 @@ def vsplit(vpath ) :
|
|
1976
2013
|
return vpath.rsplit("/", 1) # type: ignore
|
1977
2014
|
|
1978
2015
|
|
2016
|
+
# vpath-join
|
1979
2017
|
def vjoin(rd , fn ) :
|
1980
2018
|
if rd and fn:
|
1981
2019
|
return rd + "/" + fn
|
@@ -1983,6 +2021,14 @@ def vjoin(rd , fn ) :
|
|
1983
2021
|
return rd or fn
|
1984
2022
|
|
1985
2023
|
|
2024
|
+
# url-join
|
2025
|
+
def ujoin(rd , fn ) :
|
2026
|
+
if rd and fn:
|
2027
|
+
return rd.rstrip("/") + "/" + fn.lstrip("/")
|
2028
|
+
else:
|
2029
|
+
return rd or fn
|
2030
|
+
|
2031
|
+
|
1986
2032
|
def _w8dec2(txt ) :
|
1987
2033
|
"""decodes filesystem-bytes to wtf8"""
|
1988
2034
|
return surrogateescape.decodefilename(txt)
|