copyparty 1.15.10__py3-none-any.whl → 1.16.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/__init__.py +1 -0
- copyparty/__main__.py +17 -6
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +47 -21
- copyparty/broker_mp.py +40 -5
- copyparty/broker_mpw.py +20 -19
- copyparty/broker_thr.py +2 -2
- copyparty/cfg.py +4 -0
- copyparty/ftpd.py +1 -0
- copyparty/httpcli.py +159 -9
- copyparty/httpsrv.py +39 -0
- copyparty/metrics.py +3 -0
- copyparty/mtag.py +29 -0
- copyparty/pwhash.py +10 -8
- copyparty/svchub.py +16 -30
- copyparty/szip.py +1 -1
- copyparty/tcpsrv.py +2 -2
- copyparty/tftpd.py +1 -0
- copyparty/th_srv.py +10 -3
- copyparty/up2k.py +333 -64
- copyparty/util.py +45 -5
- copyparty/web/a/partyfuse.py +2 -1
- copyparty/web/a/u2c.py +20 -5
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +12 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/METADATA +8 -4
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/RECORD +38 -38
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/WHEEL +1 -1
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/LICENSE +0 -0
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/top_level.txt +0 -0
copyparty/up2k.py
CHANGED
@@ -86,6 +86,8 @@ zsg = "avif,avifs,bmp,gif,heic,heics,heif,heifs,ico,j2p,j2k,jp2,jpeg,jpg,jpx,png
|
|
86
86
|
CV_EXTS = set(zsg.split(","))
|
87
87
|
|
88
88
|
|
89
|
+
SBUSY = "cannot receive uploads right now;\nserver busy with %s.\nPlease wait; the client will retry..."
|
90
|
+
|
89
91
|
HINT_HISTPATH = "you could try moving the database to another location (preferably an SSD or NVME drive) using either the --hist argument (global option for all volumes), or the hist volflag (just for this volume)"
|
90
92
|
|
91
93
|
|
@@ -122,12 +124,22 @@ class Up2k(object):
|
|
122
124
|
self.args = hub.args
|
123
125
|
self.log_func = hub.log
|
124
126
|
|
127
|
+
self.vfs = self.asrv.vfs
|
128
|
+
self.acct = self.asrv.acct
|
129
|
+
self.iacct = self.asrv.iacct
|
130
|
+
self.grps = self.asrv.grps
|
131
|
+
|
125
132
|
self.salt = self.args.warksalt
|
126
133
|
self.r_hash = re.compile("^[0-9a-zA-Z_-]{44}$")
|
127
134
|
|
128
135
|
self.gid = 0
|
136
|
+
self.gt0 = 0
|
137
|
+
self.gt1 = 0
|
129
138
|
self.stop = False
|
130
139
|
self.mutex = threading.Lock()
|
140
|
+
self.reload_mutex = threading.Lock()
|
141
|
+
self.reload_flag = 0
|
142
|
+
self.reloading = False
|
131
143
|
self.blocked = None
|
132
144
|
self.pp = None
|
133
145
|
self.rescan_cond = threading.Condition()
|
@@ -199,7 +211,38 @@ class Up2k(object):
|
|
199
211
|
|
200
212
|
Daemon(self.deferred_init, "up2k-deferred-init")
|
201
213
|
|
214
|
+
def unpp(self) :
|
215
|
+
self.gt1 = time.time()
|
216
|
+
if self.pp:
|
217
|
+
self.pp.end = True
|
218
|
+
self.pp = None
|
219
|
+
|
202
220
|
def reload(self, rescan_all_vols ) :
|
221
|
+
n = 2 if rescan_all_vols else 1
|
222
|
+
with self.reload_mutex:
|
223
|
+
if self.reload_flag < n:
|
224
|
+
self.reload_flag = n
|
225
|
+
with self.rescan_cond:
|
226
|
+
self.rescan_cond.notify_all()
|
227
|
+
|
228
|
+
def _reload_thr(self) :
|
229
|
+
while self.pp:
|
230
|
+
time.sleep(0.1)
|
231
|
+
while True:
|
232
|
+
with self.reload_mutex:
|
233
|
+
if not self.reload_flag:
|
234
|
+
break
|
235
|
+
rav = self.reload_flag == 2
|
236
|
+
self.reload_flag = 0
|
237
|
+
gt1 = self.gt1
|
238
|
+
with self.mutex:
|
239
|
+
self._reload(rav)
|
240
|
+
while gt1 == self.gt1 or self.pp:
|
241
|
+
time.sleep(0.1)
|
242
|
+
|
243
|
+
self.reloading = False
|
244
|
+
|
245
|
+
def _reload(self, rescan_all_vols ) :
|
203
246
|
"""mutex(main) me"""
|
204
247
|
self.log("reload #{} scheduled".format(self.gid + 1))
|
205
248
|
all_vols = self.asrv.vfs.all_vols
|
@@ -224,10 +267,7 @@ class Up2k(object):
|
|
224
267
|
with self.mutex, self.reg_mutex:
|
225
268
|
self._drop_caches()
|
226
269
|
|
227
|
-
|
228
|
-
self.pp.end = True
|
229
|
-
self.pp = None
|
230
|
-
|
270
|
+
self.unpp()
|
231
271
|
return
|
232
272
|
|
233
273
|
if not self.pp and self.args.exit == "idx":
|
@@ -307,8 +347,8 @@ class Up2k(object):
|
|
307
347
|
|
308
348
|
def _active_uploads(self, uname ) :
|
309
349
|
ret = []
|
310
|
-
for vtop in self.
|
311
|
-
vfs = self.
|
350
|
+
for vtop in self.vfs.aread.get(uname) or []:
|
351
|
+
vfs = self.vfs.all_vols.get(vtop)
|
312
352
|
if not vfs: # dbv only
|
313
353
|
continue
|
314
354
|
ptop = vfs.realpath
|
@@ -481,6 +521,12 @@ class Up2k(object):
|
|
481
521
|
if self.stop:
|
482
522
|
return
|
483
523
|
|
524
|
+
with self.reload_mutex:
|
525
|
+
if self.reload_flag and not self.reloading:
|
526
|
+
self.reloading = True
|
527
|
+
zs = "up2k-reload-%d" % (self.gid,)
|
528
|
+
Daemon(self._reload_thr, zs)
|
529
|
+
|
484
530
|
now = time.time()
|
485
531
|
if now < cooldown:
|
486
532
|
# self.log("SR: cd - now = {:.2f}".format(cooldown - now), 5)
|
@@ -517,7 +563,7 @@ class Up2k(object):
|
|
517
563
|
raise
|
518
564
|
|
519
565
|
with self.mutex:
|
520
|
-
for vp, vol in sorted(self.
|
566
|
+
for vp, vol in sorted(self.vfs.all_vols.items()):
|
521
567
|
maxage = vol.flags.get("scan")
|
522
568
|
if not maxage:
|
523
569
|
continue
|
@@ -550,7 +596,7 @@ class Up2k(object):
|
|
550
596
|
|
551
597
|
if vols:
|
552
598
|
cooldown = now + 10
|
553
|
-
err = self.rescan(self.
|
599
|
+
err = self.rescan(self.vfs.all_vols, vols, False, False)
|
554
600
|
if err:
|
555
601
|
for v in vols:
|
556
602
|
self.need_rescan.add(v)
|
@@ -563,7 +609,7 @@ class Up2k(object):
|
|
563
609
|
def _check_lifetimes(self) :
|
564
610
|
now = time.time()
|
565
611
|
timeout = now + 9001
|
566
|
-
for vp, vol in sorted(self.
|
612
|
+
for vp, vol in sorted(self.vfs.all_vols.items()):
|
567
613
|
lifetime = vol.flags.get("lifetime")
|
568
614
|
if not lifetime:
|
569
615
|
continue
|
@@ -616,7 +662,7 @@ class Up2k(object):
|
|
616
662
|
maxage = self.args.shr_rt * 60
|
617
663
|
low = now - maxage
|
618
664
|
|
619
|
-
vn = self.
|
665
|
+
vn = self.vfs.nodes.get(self.args.shr.strip("/"))
|
620
666
|
active = vn and vn.nodes
|
621
667
|
|
622
668
|
db = sqlite3.connect(self.args.shr_db, timeout=2)
|
@@ -641,7 +687,7 @@ class Up2k(object):
|
|
641
687
|
db.commit()
|
642
688
|
|
643
689
|
if reload:
|
644
|
-
Daemon(self.hub.
|
690
|
+
Daemon(self.hub.reload, "sharedrop", (False, False))
|
645
691
|
|
646
692
|
q = "select min(t1) from sh where t1 > ?"
|
647
693
|
(earliest,) = cur.execute(q, (1,)).fetchone()
|
@@ -667,7 +713,7 @@ class Up2k(object):
|
|
667
713
|
return 2
|
668
714
|
|
669
715
|
ret = 9001
|
670
|
-
for _, vol in sorted(self.
|
716
|
+
for _, vol in sorted(self.vfs.all_vols.items()):
|
671
717
|
rp = vol.realpath
|
672
718
|
cur = self.cur.get(rp)
|
673
719
|
if not cur:
|
@@ -769,6 +815,8 @@ class Up2k(object):
|
|
769
815
|
with self.mutex:
|
770
816
|
gid = self.gid
|
771
817
|
|
818
|
+
self.gt0 = time.time()
|
819
|
+
|
772
820
|
nspin = 0
|
773
821
|
while True:
|
774
822
|
nspin += 1
|
@@ -791,6 +839,11 @@ class Up2k(object):
|
|
791
839
|
if gid:
|
792
840
|
self.log("reload #%d running" % (gid,))
|
793
841
|
|
842
|
+
self.vfs = self.asrv.vfs
|
843
|
+
self.acct = self.asrv.acct
|
844
|
+
self.iacct = self.asrv.iacct
|
845
|
+
self.grps = self.asrv.grps
|
846
|
+
|
794
847
|
vols = list(all_vols.values())
|
795
848
|
t0 = time.time()
|
796
849
|
have_e2d = False
|
@@ -854,7 +907,7 @@ class Up2k(object):
|
|
854
907
|
self._drop_caches()
|
855
908
|
|
856
909
|
for vol in vols:
|
857
|
-
if self.stop:
|
910
|
+
if self.stop or gid != self.gid:
|
858
911
|
break
|
859
912
|
|
860
913
|
en = set(vol.flags.get("mte", {}))
|
@@ -983,7 +1036,7 @@ class Up2k(object):
|
|
983
1036
|
if self.mtag:
|
984
1037
|
Daemon(self._run_all_mtp, "up2k-mtp-scan", (gid,))
|
985
1038
|
else:
|
986
|
-
self.
|
1039
|
+
self.unpp()
|
987
1040
|
|
988
1041
|
return have_e2d
|
989
1042
|
|
@@ -991,7 +1044,7 @@ class Up2k(object):
|
|
991
1044
|
self, ptop , flags
|
992
1045
|
) :
|
993
1046
|
"""mutex(main,reg) me"""
|
994
|
-
histpath = self.
|
1047
|
+
histpath = self.vfs.histtab.get(ptop)
|
995
1048
|
if not histpath:
|
996
1049
|
self.log("no histpath for [{}]".format(ptop))
|
997
1050
|
return None
|
@@ -1004,7 +1057,7 @@ class Up2k(object):
|
|
1004
1057
|
return None
|
1005
1058
|
|
1006
1059
|
vpath = "?"
|
1007
|
-
for k, v in self.
|
1060
|
+
for k, v in self.vfs.all_vols.items():
|
1008
1061
|
if v.realpath == ptop:
|
1009
1062
|
vpath = k
|
1010
1063
|
|
@@ -1171,7 +1224,7 @@ class Up2k(object):
|
|
1171
1224
|
def _verify_db_cache(self, cur , vpath ) :
|
1172
1225
|
# check if list of intersecting volumes changed since last use; drop caches if so
|
1173
1226
|
prefix = (vpath + "/").lstrip("/")
|
1174
|
-
zsl = [x for x in self.
|
1227
|
+
zsl = [x for x in self.vfs.all_vols if x.startswith(prefix)]
|
1175
1228
|
zsl = [x[len(prefix) :] for x in zsl]
|
1176
1229
|
zsl.sort()
|
1177
1230
|
zb = hashlib.sha1("\n".join(zsl).encode("utf-8", "replace")).digest()
|
@@ -1215,7 +1268,7 @@ class Up2k(object):
|
|
1215
1268
|
if d != vol and (d.vpath.startswith(vol.vpath + "/") or not vol.vpath)
|
1216
1269
|
]
|
1217
1270
|
excl += [absreal(x) for x in excl]
|
1218
|
-
excl += list(self.
|
1271
|
+
excl += list(self.vfs.histtab.values())
|
1219
1272
|
if WINDOWS:
|
1220
1273
|
excl = [x.replace("/", "\\") for x in excl]
|
1221
1274
|
else:
|
@@ -1338,7 +1391,7 @@ class Up2k(object):
|
|
1338
1391
|
rds = rd + "/" if rd else ""
|
1339
1392
|
cdirs = cdir + os.sep
|
1340
1393
|
|
1341
|
-
g = statdir(self.log_func, not self.args.no_scandir, True, cdir)
|
1394
|
+
g = statdir(self.log_func, not self.args.no_scandir, True, cdir, False)
|
1342
1395
|
gl = sorted(g)
|
1343
1396
|
partials = set([x[0] for x in gl if "PARTIAL" in x[0]])
|
1344
1397
|
for iname, inf in gl:
|
@@ -1402,7 +1455,7 @@ class Up2k(object):
|
|
1402
1455
|
t = "failed to index subdir [{}]:\n{}"
|
1403
1456
|
self.log(t.format(abspath, min_ex()), c=1)
|
1404
1457
|
elif not stat.S_ISREG(inf.st_mode):
|
1405
|
-
self.log("skip type-
|
1458
|
+
self.log("skip type-0%o file [%s]" % (inf.st_mode, abspath))
|
1406
1459
|
else:
|
1407
1460
|
# self.log("file: {}".format(abspath))
|
1408
1461
|
if rp.endswith(".PARTIAL") and time.time() - lmod < 60:
|
@@ -1723,7 +1776,7 @@ class Up2k(object):
|
|
1723
1776
|
|
1724
1777
|
excl = [
|
1725
1778
|
d[len(vol.vpath) :].lstrip("/")
|
1726
|
-
for d in self.
|
1779
|
+
for d in self.vfs.all_vols
|
1727
1780
|
if d != vol.vpath and (d.startswith(vol.vpath + "/") or not vol.vpath)
|
1728
1781
|
]
|
1729
1782
|
qexa = []
|
@@ -1875,7 +1928,7 @@ class Up2k(object):
|
|
1875
1928
|
def _drop_caches(self) :
|
1876
1929
|
"""mutex(main,reg) me"""
|
1877
1930
|
self.log("dropping caches for a full filesystem scan")
|
1878
|
-
for vol in self.
|
1931
|
+
for vol in self.vfs.all_vols.values():
|
1879
1932
|
reg = self.register_vpath(vol.realpath, vol.flags)
|
1880
1933
|
if not reg:
|
1881
1934
|
continue
|
@@ -2103,7 +2156,7 @@ class Up2k(object):
|
|
2103
2156
|
self._run_one_mtp(ptop, gid)
|
2104
2157
|
|
2105
2158
|
vtop = "\n"
|
2106
|
-
for vol in self.
|
2159
|
+
for vol in self.vfs.all_vols.values():
|
2107
2160
|
if vol.realpath == ptop:
|
2108
2161
|
vtop = vol.vpath
|
2109
2162
|
if "running mtp" in self.volstate.get(vtop, ""):
|
@@ -2113,7 +2166,7 @@ class Up2k(object):
|
|
2113
2166
|
msg = "mtp finished in {:.2f} sec ({})"
|
2114
2167
|
self.log(msg.format(td, s2hms(td, True)))
|
2115
2168
|
|
2116
|
-
self.
|
2169
|
+
self.unpp()
|
2117
2170
|
if self.args.exit == "idx":
|
2118
2171
|
self.hub.sigterm()
|
2119
2172
|
|
@@ -2750,6 +2803,9 @@ class Up2k(object):
|
|
2750
2803
|
) :
|
2751
2804
|
# busy_aps is u2fh (always undefined if -j0) so this is safe
|
2752
2805
|
self.busy_aps = busy_aps
|
2806
|
+
if self.reload_flag or self.reloading:
|
2807
|
+
raise Pebkac(503, SBUSY % ("fs-reload",))
|
2808
|
+
|
2753
2809
|
got_lock = False
|
2754
2810
|
try:
|
2755
2811
|
# bit expensive; 3.9=10x 3.11=2x
|
@@ -2758,8 +2814,7 @@ class Up2k(object):
|
|
2758
2814
|
with self.reg_mutex:
|
2759
2815
|
ret = self._handle_json(cj)
|
2760
2816
|
else:
|
2761
|
-
|
2762
|
-
raise Pebkac(503, t.format(self.blocked or "[unknown]"))
|
2817
|
+
raise Pebkac(503, SBUSY % (self.blocked or "[unknown]",))
|
2763
2818
|
except TypeError:
|
2764
2819
|
if not PY2:
|
2765
2820
|
raise
|
@@ -2801,7 +2856,7 @@ class Up2k(object):
|
|
2801
2856
|
if True:
|
2802
2857
|
jcur = self.cur.get(ptop)
|
2803
2858
|
reg = self.registry[ptop]
|
2804
|
-
vfs = self.
|
2859
|
+
vfs = self.vfs.all_vols[cj["vtop"]]
|
2805
2860
|
n4g = bool(vfs.flags.get("noforget"))
|
2806
2861
|
noclone = bool(vfs.flags.get("noclone"))
|
2807
2862
|
rand = vfs.flags.get("rand") or cj.get("rand")
|
@@ -2825,7 +2880,7 @@ class Up2k(object):
|
|
2825
2880
|
|
2826
2881
|
alts = []
|
2827
2882
|
for ptop, cur in vols:
|
2828
|
-
allv = self.
|
2883
|
+
allv = self.vfs.all_vols
|
2829
2884
|
cvfs = next((v for v in allv.values() if v.realpath == ptop), vfs)
|
2830
2885
|
vtop = cj["vtop"] if cur == jcur else cvfs.vpath
|
2831
2886
|
|
@@ -3067,7 +3122,7 @@ class Up2k(object):
|
|
3067
3122
|
vp,
|
3068
3123
|
job["host"],
|
3069
3124
|
job["user"],
|
3070
|
-
self.
|
3125
|
+
self.vfs.get_perms(job["vtop"], job["user"]),
|
3071
3126
|
job["lmod"],
|
3072
3127
|
job["size"],
|
3073
3128
|
job["addr"],
|
@@ -3079,7 +3134,7 @@ class Up2k(object):
|
|
3079
3134
|
self.log(t, 1)
|
3080
3135
|
raise Pebkac(403, t)
|
3081
3136
|
if hr.get("reloc"):
|
3082
|
-
x = pathmod(self.
|
3137
|
+
x = pathmod(self.vfs, dst, vp, hr["reloc"])
|
3083
3138
|
if x:
|
3084
3139
|
zvfs = vfs
|
3085
3140
|
pdir, _, job["name"], (vfs, rem) = x
|
@@ -3538,7 +3593,7 @@ class Up2k(object):
|
|
3538
3593
|
wake_sr = False
|
3539
3594
|
try:
|
3540
3595
|
flt = job["life"]
|
3541
|
-
vfs = self.
|
3596
|
+
vfs = self.vfs.all_vols[job["vtop"]]
|
3542
3597
|
vlt = vfs.flags["lifetime"]
|
3543
3598
|
if vlt and flt > 1 and flt < vlt:
|
3544
3599
|
upt -= vlt - flt
|
@@ -3711,7 +3766,7 @@ class Up2k(object):
|
|
3711
3766
|
djoin(vtop, rd, fn),
|
3712
3767
|
host,
|
3713
3768
|
usr,
|
3714
|
-
self.
|
3769
|
+
self.vfs.get_perms(djoin(vtop, rd, fn), usr),
|
3715
3770
|
ts,
|
3716
3771
|
sz,
|
3717
3772
|
ip,
|
@@ -3820,13 +3875,13 @@ class Up2k(object):
|
|
3820
3875
|
partial = ""
|
3821
3876
|
if not unpost:
|
3822
3877
|
permsets = [[True, False, False, True]]
|
3823
|
-
|
3824
|
-
vn, rem =
|
3878
|
+
vn0, rem0 = self.vfs.get(vpath, uname, *permsets[0])
|
3879
|
+
vn, rem = vn0.get_dbv(rem0)
|
3825
3880
|
else:
|
3826
3881
|
# unpost with missing permissions? verify with db
|
3827
3882
|
permsets = [[False, True]]
|
3828
|
-
|
3829
|
-
vn, rem =
|
3883
|
+
vn0, rem0 = self.vfs.get(vpath, uname, *permsets[0])
|
3884
|
+
vn, rem = vn0.get_dbv(rem0)
|
3830
3885
|
ptop = vn.realpath
|
3831
3886
|
with self.mutex, self.reg_mutex:
|
3832
3887
|
abrt_cfg = self.flags.get(ptop, {}).get("u2abort", 1)
|
@@ -3882,7 +3937,9 @@ class Up2k(object):
|
|
3882
3937
|
|
3883
3938
|
scandir = not self.args.no_scandir
|
3884
3939
|
if is_dir:
|
3885
|
-
|
3940
|
+
# note: deletion inside shares would require a rewrite here;
|
3941
|
+
# shares necessitate get_dbv which is incompatible with walk
|
3942
|
+
g = vn0.walk("", rem0, [], uname, permsets, True, scandir, True)
|
3886
3943
|
if unpost:
|
3887
3944
|
raise Pebkac(400, "cannot unpost folders")
|
3888
3945
|
elif stat.S_ISLNK(st.st_mode) or stat.S_ISREG(st.st_mode):
|
@@ -3890,7 +3947,7 @@ class Up2k(object):
|
|
3890
3947
|
vpath_dir = vsplit(vpath)[0]
|
3891
3948
|
g = [(vn, voldir, vpath_dir, adir, [(fn, 0)], [], {})] # type: ignore
|
3892
3949
|
else:
|
3893
|
-
self.log("rm: skip type-
|
3950
|
+
self.log("rm: skip type-0%o file [%s]" % (st.st_mode, atop))
|
3894
3951
|
return 0, [], []
|
3895
3952
|
|
3896
3953
|
xbd = vn.flags.get("xbd")
|
@@ -3930,7 +3987,7 @@ class Up2k(object):
|
|
3930
3987
|
vpath,
|
3931
3988
|
"",
|
3932
3989
|
uname,
|
3933
|
-
self.
|
3990
|
+
self.vfs.get_perms(vpath, uname),
|
3934
3991
|
stl.st_mtime,
|
3935
3992
|
st.st_size,
|
3936
3993
|
ip,
|
@@ -3970,7 +4027,7 @@ class Up2k(object):
|
|
3970
4027
|
vpath,
|
3971
4028
|
"",
|
3972
4029
|
uname,
|
3973
|
-
self.
|
4030
|
+
self.vfs.get_perms(vpath, uname),
|
3974
4031
|
stl.st_mtime,
|
3975
4032
|
st.st_size,
|
3976
4033
|
ip,
|
@@ -3990,17 +4047,225 @@ class Up2k(object):
|
|
3990
4047
|
|
3991
4048
|
return n_files, ok + ok2, ng + ng2
|
3992
4049
|
|
4050
|
+
def handle_cp(self, uname , ip , svp , dvp ) :
|
4051
|
+
if svp == dvp or dvp.startswith(svp + "/"):
|
4052
|
+
raise Pebkac(400, "cp: cannot copy parent into subfolder")
|
4053
|
+
|
4054
|
+
svn, srem = self.vfs.get(svp, uname, True, False)
|
4055
|
+
svn_dbv, _ = svn.get_dbv(srem)
|
4056
|
+
sabs = svn.canonical(srem, False)
|
4057
|
+
curs = set()
|
4058
|
+
self.db_act = self.vol_act[svn_dbv.realpath] = time.time()
|
4059
|
+
|
4060
|
+
st = bos.stat(sabs)
|
4061
|
+
if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
|
4062
|
+
with self.mutex:
|
4063
|
+
try:
|
4064
|
+
ret = self._cp_file(uname, ip, svp, dvp, curs)
|
4065
|
+
finally:
|
4066
|
+
for v in curs:
|
4067
|
+
v.connection.commit()
|
4068
|
+
|
4069
|
+
return ret
|
4070
|
+
|
4071
|
+
if not stat.S_ISDIR(st.st_mode):
|
4072
|
+
raise Pebkac(400, "cannot copy type-0%o file" % (st.st_mode,))
|
4073
|
+
|
4074
|
+
permsets = [[True, False]]
|
4075
|
+
scandir = not self.args.no_scandir
|
4076
|
+
|
4077
|
+
# don't use svn_dbv; would skip subvols due to _ls `if not rem:`
|
4078
|
+
g = svn.walk("", srem, [], uname, permsets, True, scandir, True)
|
4079
|
+
with self.mutex:
|
4080
|
+
try:
|
4081
|
+
for dbv, vrem, _, atop, files, rd, vd in g:
|
4082
|
+
for fn in files:
|
4083
|
+
self.db_act = self.vol_act[dbv.realpath] = time.time()
|
4084
|
+
svpf = "/".join(x for x in [dbv.vpath, vrem, fn[0]] if x)
|
4085
|
+
if not svpf.startswith(svp + "/"): # assert
|
4086
|
+
self.log(min_ex(), 1)
|
4087
|
+
t = "cp: bug at %s, top %s%s"
|
4088
|
+
raise Pebkac(500, t % (svpf, svp, SEESLOG))
|
4089
|
+
|
4090
|
+
dvpf = dvp + svpf[len(svp) :]
|
4091
|
+
self._cp_file(uname, ip, svpf, dvpf, curs)
|
4092
|
+
|
4093
|
+
for v in curs:
|
4094
|
+
v.connection.commit()
|
4095
|
+
curs.clear()
|
4096
|
+
finally:
|
4097
|
+
for v in curs:
|
4098
|
+
v.connection.commit()
|
4099
|
+
|
4100
|
+
return "k"
|
4101
|
+
|
4102
|
+
def _cp_file(
|
4103
|
+
self, uname , ip , svp , dvp , curs
|
4104
|
+
) :
|
4105
|
+
"""mutex(main) me; will mutex(reg)"""
|
4106
|
+
svn, srem = self.vfs.get(svp, uname, True, False)
|
4107
|
+
svn_dbv, srem_dbv = svn.get_dbv(srem)
|
4108
|
+
|
4109
|
+
dvn, drem = self.vfs.get(dvp, uname, False, True)
|
4110
|
+
dvn, drem = dvn.get_dbv(drem)
|
4111
|
+
|
4112
|
+
sabs = svn.canonical(srem, False)
|
4113
|
+
dabs = dvn.canonical(drem)
|
4114
|
+
drd, dfn = vsplit(drem)
|
4115
|
+
|
4116
|
+
if bos.path.exists(dabs):
|
4117
|
+
raise Pebkac(400, "cp2: target file exists")
|
4118
|
+
|
4119
|
+
st = stl = bos.lstat(sabs)
|
4120
|
+
if stat.S_ISLNK(stl.st_mode):
|
4121
|
+
is_link = True
|
4122
|
+
try:
|
4123
|
+
st = bos.stat(sabs)
|
4124
|
+
except:
|
4125
|
+
pass # broken symlink; keep as-is
|
4126
|
+
elif not stat.S_ISREG(st.st_mode):
|
4127
|
+
self.log("skipping type-0%o file [%s]" % (st.st_mode, sabs))
|
4128
|
+
return ""
|
4129
|
+
else:
|
4130
|
+
is_link = False
|
4131
|
+
|
4132
|
+
ftime = stl.st_mtime
|
4133
|
+
fsize = st.st_size
|
4134
|
+
|
4135
|
+
xbc = svn.flags.get("xbc")
|
4136
|
+
xac = dvn.flags.get("xac")
|
4137
|
+
if xbc:
|
4138
|
+
if not runhook(
|
4139
|
+
self.log,
|
4140
|
+
None,
|
4141
|
+
self,
|
4142
|
+
"xbc",
|
4143
|
+
xbc,
|
4144
|
+
sabs,
|
4145
|
+
svp,
|
4146
|
+
"",
|
4147
|
+
uname,
|
4148
|
+
self.vfs.get_perms(svp, uname),
|
4149
|
+
ftime,
|
4150
|
+
fsize,
|
4151
|
+
ip,
|
4152
|
+
time.time(),
|
4153
|
+
"",
|
4154
|
+
):
|
4155
|
+
t = "copy blocked by xbr server config: {}".format(svp)
|
4156
|
+
self.log(t, 1)
|
4157
|
+
raise Pebkac(405, t)
|
4158
|
+
|
4159
|
+
bos.makedirs(os.path.dirname(dabs))
|
4160
|
+
|
4161
|
+
c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(
|
4162
|
+
svn_dbv.realpath, srem_dbv
|
4163
|
+
)
|
4164
|
+
c2 = self.cur.get(dvn.realpath)
|
4165
|
+
|
4166
|
+
if w:
|
4167
|
+
if c2 and c2 != c1:
|
4168
|
+
self._copy_tags(c1, c2, w)
|
4169
|
+
|
4170
|
+
curs.add(c1)
|
4171
|
+
|
4172
|
+
if c2:
|
4173
|
+
self.db_add(
|
4174
|
+
c2,
|
4175
|
+
{}, # skip upload hooks
|
4176
|
+
drd,
|
4177
|
+
dfn,
|
4178
|
+
ftime,
|
4179
|
+
fsize,
|
4180
|
+
dvn.realpath,
|
4181
|
+
dvn.vpath,
|
4182
|
+
w,
|
4183
|
+
w,
|
4184
|
+
"",
|
4185
|
+
"",
|
4186
|
+
ip or "",
|
4187
|
+
at or 0,
|
4188
|
+
)
|
4189
|
+
curs.add(c2)
|
4190
|
+
else:
|
4191
|
+
self.log("not found in src db: [{}]".format(svp))
|
4192
|
+
|
4193
|
+
try:
|
4194
|
+
if is_link and st != stl:
|
4195
|
+
# relink non-broken symlinks to still work after the move,
|
4196
|
+
# but only resolve 1st level to maintain relativity
|
4197
|
+
dlink = bos.readlink(sabs)
|
4198
|
+
dlink = os.path.join(os.path.dirname(sabs), dlink)
|
4199
|
+
dlink = bos.path.abspath(dlink)
|
4200
|
+
self._symlink(dlink, dabs, dvn.flags, lmod=ftime)
|
4201
|
+
else:
|
4202
|
+
self._symlink(sabs, dabs, dvn.flags, lmod=ftime)
|
4203
|
+
|
4204
|
+
except OSError as ex:
|
4205
|
+
if ex.errno != errno.EXDEV:
|
4206
|
+
raise
|
4207
|
+
|
4208
|
+
self.log("using plain copy (%s):\n %s\n %s" % (ex.strerror, sabs, dabs))
|
4209
|
+
b1, b2 = fsenc(sabs), fsenc(dabs)
|
4210
|
+
is_link = os.path.islink(b1) # due to _relink
|
4211
|
+
try:
|
4212
|
+
shutil.copy2(b1, b2)
|
4213
|
+
except:
|
4214
|
+
try:
|
4215
|
+
wunlink(self.log, dabs, dvn.flags)
|
4216
|
+
except:
|
4217
|
+
pass
|
4218
|
+
|
4219
|
+
if not is_link:
|
4220
|
+
raise
|
4221
|
+
|
4222
|
+
# broken symlink? keep it as-is
|
4223
|
+
try:
|
4224
|
+
zb = os.readlink(b1)
|
4225
|
+
os.symlink(zb, b2)
|
4226
|
+
except:
|
4227
|
+
wunlink(self.log, dabs, dvn.flags)
|
4228
|
+
raise
|
4229
|
+
|
4230
|
+
if is_link:
|
4231
|
+
try:
|
4232
|
+
times = (int(time.time()), int(ftime))
|
4233
|
+
bos.utime(dabs, times, False)
|
4234
|
+
except:
|
4235
|
+
pass
|
4236
|
+
|
4237
|
+
if xac:
|
4238
|
+
runhook(
|
4239
|
+
self.log,
|
4240
|
+
None,
|
4241
|
+
self,
|
4242
|
+
"xac",
|
4243
|
+
xac,
|
4244
|
+
dabs,
|
4245
|
+
dvp,
|
4246
|
+
"",
|
4247
|
+
uname,
|
4248
|
+
self.vfs.get_perms(dvp, uname),
|
4249
|
+
ftime,
|
4250
|
+
fsize,
|
4251
|
+
ip,
|
4252
|
+
time.time(),
|
4253
|
+
"",
|
4254
|
+
)
|
4255
|
+
|
4256
|
+
return "k"
|
4257
|
+
|
3993
4258
|
def handle_mv(self, uname , ip , svp , dvp ) :
|
3994
4259
|
if svp == dvp or dvp.startswith(svp + "/"):
|
3995
4260
|
raise Pebkac(400, "mv: cannot move parent into subfolder")
|
3996
4261
|
|
3997
|
-
svn, srem = self.
|
3998
|
-
|
4262
|
+
svn, srem = self.vfs.get(svp, uname, True, False, True)
|
4263
|
+
jail, jail_rem = svn.get_dbv(srem)
|
3999
4264
|
sabs = svn.canonical(srem, False)
|
4000
4265
|
curs = set()
|
4001
|
-
self.db_act = self.vol_act[
|
4266
|
+
self.db_act = self.vol_act[jail.realpath] = time.time()
|
4002
4267
|
|
4003
|
-
if not
|
4268
|
+
if not jail_rem:
|
4004
4269
|
raise Pebkac(400, "mv: cannot move a mountpoint")
|
4005
4270
|
|
4006
4271
|
st = bos.lstat(sabs)
|
@@ -4014,7 +4279,9 @@ class Up2k(object):
|
|
4014
4279
|
|
4015
4280
|
return ret
|
4016
4281
|
|
4017
|
-
|
4282
|
+
if not stat.S_ISDIR(st.st_mode):
|
4283
|
+
raise Pebkac(400, "cannot move type-0%o file" % (st.st_mode,))
|
4284
|
+
|
4018
4285
|
permsets = [[True, False, True]]
|
4019
4286
|
scandir = not self.args.no_scandir
|
4020
4287
|
|
@@ -4026,13 +4293,13 @@ class Up2k(object):
|
|
4026
4293
|
raise Pebkac(400, "mv: source folder contains other volumes")
|
4027
4294
|
|
4028
4295
|
g = svn.walk("", srem, [], uname, permsets, True, scandir, True)
|
4029
|
-
|
4030
|
-
|
4031
|
-
|
4032
|
-
|
4296
|
+
with self.mutex:
|
4297
|
+
try:
|
4298
|
+
for dbv, vrem, _, atop, files, rd, vd in g:
|
4299
|
+
if dbv != jail:
|
4300
|
+
# the actual check (avoid toctou)
|
4301
|
+
raise Pebkac(400, "mv: source folder contains other volumes")
|
4033
4302
|
|
4034
|
-
with self.mutex:
|
4035
|
-
try:
|
4036
4303
|
for fn in files:
|
4037
4304
|
self.db_act = self.vol_act[dbv.realpath] = time.time()
|
4038
4305
|
svpf = "/".join(x for x in [dbv.vpath, vrem, fn[0]] if x)
|
@@ -4043,11 +4310,13 @@ class Up2k(object):
|
|
4043
4310
|
|
4044
4311
|
dvpf = dvp + svpf[len(svp) :]
|
4045
4312
|
self._mv_file(uname, ip, svpf, dvpf, curs)
|
4046
|
-
|
4313
|
+
|
4047
4314
|
for v in curs:
|
4048
4315
|
v.connection.commit()
|
4049
|
-
|
4050
|
-
|
4316
|
+
curs.clear()
|
4317
|
+
finally:
|
4318
|
+
for v in curs:
|
4319
|
+
v.connection.commit()
|
4051
4320
|
|
4052
4321
|
rm_ok, rm_ng = rmdirs(self.log_func, scandir, True, sabs, 1)
|
4053
4322
|
|
@@ -4061,7 +4330,7 @@ class Up2k(object):
|
|
4061
4330
|
rem = ap[len(sabs) :].replace(os.sep, "/").lstrip("/")
|
4062
4331
|
vp = vjoin(dvp, rem)
|
4063
4332
|
try:
|
4064
|
-
dvn, drem = self.
|
4333
|
+
dvn, drem = self.vfs.get(vp, uname, False, True)
|
4065
4334
|
bos.mkdir(dvn.canonical(drem))
|
4066
4335
|
except:
|
4067
4336
|
pass
|
@@ -4072,10 +4341,10 @@ class Up2k(object):
|
|
4072
4341
|
self, uname , ip , svp , dvp , curs
|
4073
4342
|
) :
|
4074
4343
|
"""mutex(main) me; will mutex(reg)"""
|
4075
|
-
svn, srem = self.
|
4344
|
+
svn, srem = self.vfs.get(svp, uname, True, False, True)
|
4076
4345
|
svn, srem = svn.get_dbv(srem)
|
4077
4346
|
|
4078
|
-
dvn, drem = self.
|
4347
|
+
dvn, drem = self.vfs.get(dvp, uname, False, True)
|
4079
4348
|
dvn, drem = dvn.get_dbv(drem)
|
4080
4349
|
|
4081
4350
|
sabs = svn.canonical(srem, False)
|
@@ -4119,7 +4388,7 @@ class Up2k(object):
|
|
4119
4388
|
svp,
|
4120
4389
|
"",
|
4121
4390
|
uname,
|
4122
|
-
self.
|
4391
|
+
self.vfs.get_perms(svp, uname),
|
4123
4392
|
ftime,
|
4124
4393
|
fsize,
|
4125
4394
|
ip,
|
@@ -4159,7 +4428,7 @@ class Up2k(object):
|
|
4159
4428
|
dvp,
|
4160
4429
|
"",
|
4161
4430
|
uname,
|
4162
|
-
self.
|
4431
|
+
self.vfs.get_perms(dvp, uname),
|
4163
4432
|
ftime,
|
4164
4433
|
fsize,
|
4165
4434
|
ip,
|
@@ -4271,7 +4540,7 @@ class Up2k(object):
|
|
4271
4540
|
dvp,
|
4272
4541
|
"",
|
4273
4542
|
uname,
|
4274
|
-
self.
|
4543
|
+
self.vfs.get_perms(dvp, uname),
|
4275
4544
|
ftime,
|
4276
4545
|
fsize,
|
4277
4546
|
ip,
|
@@ -4583,7 +4852,7 @@ class Up2k(object):
|
|
4583
4852
|
vp_chk,
|
4584
4853
|
job["host"],
|
4585
4854
|
job["user"],
|
4586
|
-
self.
|
4855
|
+
self.vfs.get_perms(vp_chk, job["user"]),
|
4587
4856
|
job["lmod"],
|
4588
4857
|
job["size"],
|
4589
4858
|
job["addr"],
|
@@ -4595,7 +4864,7 @@ class Up2k(object):
|
|
4595
4864
|
self.log(t, 1)
|
4596
4865
|
raise Pebkac(403, t)
|
4597
4866
|
if hr.get("reloc"):
|
4598
|
-
x = pathmod(self.
|
4867
|
+
x = pathmod(self.vfs, ap_chk, vp_chk, hr["reloc"])
|
4599
4868
|
if x:
|
4600
4869
|
zvfs = vfs
|
4601
4870
|
pdir, _, job["name"], (vfs, rem) = x
|
@@ -4702,7 +4971,7 @@ class Up2k(object):
|
|
4702
4971
|
|
4703
4972
|
def _snap_reg(self, ptop , reg ) :
|
4704
4973
|
now = time.time()
|
4705
|
-
histpath = self.
|
4974
|
+
histpath = self.vfs.histtab.get(ptop)
|
4706
4975
|
if not histpath:
|
4707
4976
|
return
|
4708
4977
|
|
@@ -4950,7 +5219,7 @@ class Up2k(object):
|
|
4950
5219
|
else:
|
4951
5220
|
fvp, fn = vsplit(fvp)
|
4952
5221
|
|
4953
|
-
x = pathmod(self.
|
5222
|
+
x = pathmod(self.vfs, "", req_vp, {"vp": fvp, "fn": fn})
|
4954
5223
|
if not x:
|
4955
5224
|
t = "hook_fx(%s): failed to resolve %s based on %s"
|
4956
5225
|
self.log(t % (act, fvp, req_vp))
|