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/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
- if self.pp:
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.asrv.vfs.aread[uname]:
311
- vfs = self.asrv.vfs.all_vols.get(vtop)
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.asrv.vfs.all_vols.items()):
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.asrv.vfs.all_vols, vols, False, False)
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.asrv.vfs.all_vols.items()):
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.asrv.vfs.nodes.get(self.args.shr.strip("/"))
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._reload_blocking, "sharedrop", (False, False))
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.asrv.vfs.all_vols.items()):
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.pp = None
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.asrv.vfs.histtab.get(ptop)
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.asrv.vfs.all_vols.items():
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.asrv.vfs.all_vols if x.startswith(prefix)]
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.asrv.vfs.histtab.values())
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-{:x} file [{}]".format(inf.st_mode, abspath))
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.asrv.vfs.all_vols
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.asrv.vfs.all_vols.values():
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.asrv.vfs.all_vols.values():
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.pp = None
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
- t = "cannot receive uploads right now;\nserver busy with {}.\nPlease wait; the client will retry..."
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.asrv.vfs.all_vols[cj["vtop"]]
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.asrv.vfs.all_vols
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.asrv.vfs.get_perms(job["vtop"], job["user"]),
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.asrv.vfs, dst, vp, hr["reloc"])
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.asrv.vfs.all_vols[job["vtop"]]
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.asrv.vfs.get_perms(djoin(vtop, rd, fn), usr),
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
- vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0])
3824
- vn, rem = vn.get_dbv(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
- vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0])
3829
- vn, rem = vn.get_dbv(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
- g = vn.walk("", rem, [], uname, permsets, True, scandir, True)
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-{:x} file [{}]".format(st.st_mode, atop))
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.asrv.vfs.get_perms(vpath, uname),
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.asrv.vfs.get_perms(vpath, uname),
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.asrv.vfs.get(svp, uname, True, False, True)
3998
- svn, srem = svn.get_dbv(srem)
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[svn.realpath] = time.time()
4266
+ self.db_act = self.vol_act[jail.realpath] = time.time()
4002
4267
 
4003
- if not srem:
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
- jail = svn.get_dbv(srem)[0]
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
- for dbv, vrem, _, atop, files, rd, vd in g:
4030
- if dbv != jail:
4031
- # the actual check (avoid toctou)
4032
- raise Pebkac(400, "mv: source folder contains other volumes")
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
- finally:
4313
+
4047
4314
  for v in curs:
4048
4315
  v.connection.commit()
4049
-
4050
- curs.clear()
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.asrv.vfs.get(vp, uname, False, True)
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.asrv.vfs.get(svp, uname, True, False, True)
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.asrv.vfs.get(dvp, uname, False, True)
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.asrv.vfs.get_perms(svp, uname),
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.asrv.vfs.get_perms(dvp, uname),
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.asrv.vfs.get_perms(dvp, uname),
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.asrv.vfs.get_perms(vp_chk, job["user"]),
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.asrv.vfs, ap_chk, vp_chk, hr["reloc"])
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.asrv.vfs.histtab.get(ptop)
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.asrv.vfs, "", req_vp, {"vp": fvp, "fn": fn})
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))