copyparty 1.15.0__py3-none-any.whl → 1.15.2__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
@@ -1,7 +1,6 @@
1
1
  # coding: utf-8
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
- import base64
5
4
  import errno
6
5
  import gzip
7
6
  import hashlib
@@ -61,6 +60,7 @@ from .util import (
61
60
  sfsenc,
62
61
  spack,
63
62
  statdir,
63
+ ub64enc,
64
64
  unhumanize,
65
65
  vjoin,
66
66
  vsplit,
@@ -265,19 +265,29 @@ class Up2k(object):
265
265
  if not self.stop:
266
266
  self.log("uploads are now possible", 2)
267
267
 
268
- def get_state(self) :
268
+ def get_state(self, get_q , uname ) :
269
269
  mtpq = 0
270
+ ups = []
271
+ up_en = not self.args.no_up_list
270
272
  q = "select count(w) from mt where k = 't:mtp'"
271
273
  got_lock = False if PY2 else self.mutex.acquire(timeout=0.5)
272
274
  if got_lock:
273
- for cur in self.cur.values():
274
- try:
275
- mtpq += cur.execute(q).fetchone()[0]
276
- except:
277
- pass
278
- self.mutex.release()
275
+ try:
276
+ for cur in self.cur.values() if get_q else []:
277
+ try:
278
+ mtpq += cur.execute(q).fetchone()[0]
279
+ except:
280
+ pass
281
+ if uname and up_en:
282
+ ups = self._active_uploads(uname)
283
+ finally:
284
+ self.mutex.release()
279
285
  else:
280
286
  mtpq = "(?)"
287
+ if up_en:
288
+ ups = [(0, 0, time.time(), "cannot show list (server too busy)")]
289
+
290
+ ups.sort(reverse=True)
281
291
 
282
292
  ret = {
283
293
  "volstate": self.volstate,
@@ -285,6 +295,7 @@ class Up2k(object):
285
295
  "hashq": self.n_hashq,
286
296
  "tagq": self.n_tagq,
287
297
  "mtpq": mtpq,
298
+ "ups": ups,
288
299
  "dbwu": "{:.2f}".format(self.db_act),
289
300
  "dbwt": "{:.2f}".format(
290
301
  min(1000 * 24 * 60 * 60 - 1, time.time() - self.db_act)
@@ -292,6 +303,32 @@ class Up2k(object):
292
303
  }
293
304
  return json.dumps(ret, separators=(",\n", ": "))
294
305
 
306
+ def _active_uploads(self, uname ) :
307
+ ret = []
308
+ for vtop in self.asrv.vfs.aread[uname]:
309
+ vfs = self.asrv.vfs.all_vols.get(vtop)
310
+ if not vfs: # dbv only
311
+ continue
312
+ ptop = vfs.realpath
313
+ tab = self.registry.get(ptop)
314
+ if not tab:
315
+ continue
316
+ for job in tab.values():
317
+ ineed = len(job["need"])
318
+ ihash = len(job["hash"])
319
+ if ineed == ihash or not ineed:
320
+ continue
321
+
322
+ zt = (
323
+ ineed / ihash,
324
+ job["size"],
325
+ int(job["t0"]),
326
+ int(job["poke"]),
327
+ djoin(vtop, job["prel"], job["name"]),
328
+ )
329
+ ret.append(zt)
330
+ return ret
331
+
295
332
  def find_job_by_ap(self, ptop , ap ) :
296
333
  try:
297
334
  if ANYWIN:
@@ -572,7 +609,6 @@ class Up2k(object):
572
609
  return timeout
573
610
 
574
611
  def _check_shares(self) :
575
- assert sqlite3 # type: ignore
576
612
 
577
613
  now = time.time()
578
614
  timeout = now + 9001
@@ -893,7 +929,6 @@ class Up2k(object):
893
929
  with self.mutex, self.reg_mutex:
894
930
  reg = self.register_vpath(vol.realpath, vol.flags)
895
931
 
896
- assert reg
897
932
  cur, _ = reg
898
933
  with self.mutex:
899
934
  cur.connection.commit()
@@ -910,7 +945,6 @@ class Up2k(object):
910
945
  reg = self.register_vpath(vol.realpath, vol.flags)
911
946
 
912
947
  try:
913
- assert reg
914
948
  cur, db_path = reg
915
949
  if bos.path.getsize(db_path + "-wal") < 1024 * 1024 * 5:
916
950
  continue
@@ -1116,7 +1150,7 @@ class Up2k(object):
1116
1150
  zsl = [x[len(prefix) :] for x in zsl]
1117
1151
  zsl.sort()
1118
1152
  zb = hashlib.sha1("\n".join(zsl).encode("utf-8", "replace")).digest()
1119
- vcfg = base64.urlsafe_b64encode(zb[:18]).decode("ascii")
1153
+ vcfg = ub64enc(zb[:18]).decode("ascii")
1120
1154
 
1121
1155
  c = cur.execute("select v from kv where k = 'volcfg'")
1122
1156
  try:
@@ -1145,7 +1179,6 @@ class Up2k(object):
1145
1179
  with self.reg_mutex:
1146
1180
  reg = self.register_vpath(top, vol.flags)
1147
1181
 
1148
- assert reg and self.pp
1149
1182
  cur, db_path = reg
1150
1183
 
1151
1184
  db = Dbw(cur, 0, time.time())
@@ -1164,6 +1197,10 @@ class Up2k(object):
1164
1197
  # ~/.wine/dosdevices/z:/ and such
1165
1198
  excl.extend(("/dev", "/proc", "/run", "/sys"))
1166
1199
 
1200
+ if self.args.re_dirsz:
1201
+ db.c.execute("delete from ds")
1202
+ db.n += 1
1203
+
1167
1204
  rtop = absreal(top)
1168
1205
  n_add = n_rm = 0
1169
1206
  try:
@@ -1172,7 +1209,7 @@ class Up2k(object):
1172
1209
  self.log(t % (vol.vpath, rtop), 6)
1173
1210
  return True, False
1174
1211
 
1175
- n_add = self._build_dir(
1212
+ n_add, _, _ = self._build_dir(
1176
1213
  db,
1177
1214
  top,
1178
1215
  set(excl),
@@ -1246,17 +1283,18 @@ class Up2k(object):
1246
1283
  cst ,
1247
1284
  dev ,
1248
1285
  xvol ,
1249
- ) :
1286
+ ) :
1250
1287
  if xvol and not rcdir.startswith(top):
1251
1288
  self.log("skip xvol: [{}] -> [{}]".format(cdir, rcdir), 6)
1252
- return 0
1289
+ return 0, 0, 0
1253
1290
 
1254
1291
  if rcdir in seen:
1255
1292
  t = "bailing from symlink loop,\n prev: {}\n curr: {}\n from: {}"
1256
1293
  self.log(t.format(seen[-1], rcdir, cdir), 3)
1257
- return 0
1294
+ return 0, 0, 0
1258
1295
 
1259
- ret = 0
1296
+ # total-files-added, total-num-files, recursive-size
1297
+ tfa = tnf = rsz = 0
1260
1298
  seen = seen + [rcdir]
1261
1299
  unreg = []
1262
1300
  files = []
@@ -1266,22 +1304,24 @@ class Up2k(object):
1266
1304
  th_cvd = self.args.th_coversd
1267
1305
  th_cvds = self.args.th_coversd_set
1268
1306
 
1269
- assert self.pp and self.mem_cur
1270
1307
  self.pp.msg = "a%d %s" % (self.pp.n, cdir)
1271
1308
 
1272
1309
  rd = cdir[len(top) :].strip("/")
1273
1310
  if WINDOWS:
1274
1311
  rd = rd.replace("\\", "/").strip("/")
1275
1312
 
1313
+ rds = rd + "/" if rd else ""
1314
+ cdirs = cdir + os.sep
1315
+
1276
1316
  g = statdir(self.log_func, not self.args.no_scandir, True, cdir)
1277
1317
  gl = sorted(g)
1278
1318
  partials = set([x[0] for x in gl if "PARTIAL" in x[0]])
1279
1319
  for iname, inf in gl:
1280
1320
  if self.stop:
1281
- return -1
1321
+ return -1, 0, 0
1282
1322
 
1283
- rp = vjoin(rd, iname)
1284
- abspath = os.path.join(cdir, iname)
1323
+ rp = rds + iname
1324
+ abspath = cdirs + iname
1285
1325
 
1286
1326
  if rei and rei.search(abspath):
1287
1327
  unreg.append(rp)
@@ -1315,7 +1355,7 @@ class Up2k(object):
1315
1355
  continue
1316
1356
  # self.log(" dir: {}".format(abspath))
1317
1357
  try:
1318
- ret += self._build_dir(
1358
+ i1, i2, i3 = self._build_dir(
1319
1359
  db,
1320
1360
  top,
1321
1361
  excl,
@@ -1330,6 +1370,9 @@ class Up2k(object):
1330
1370
  dev,
1331
1371
  xvol,
1332
1372
  )
1373
+ tfa += i1
1374
+ tnf += i2
1375
+ rsz += i3
1333
1376
  except:
1334
1377
  t = "failed to index subdir [{}]:\n{}"
1335
1378
  self.log(t.format(abspath, min_ex()), c=1)
@@ -1348,6 +1391,7 @@ class Up2k(object):
1348
1391
  # placeholder for unfinished upload
1349
1392
  continue
1350
1393
 
1394
+ rsz += sz
1351
1395
  files.append((sz, lmod, iname))
1352
1396
  liname = iname.lower()
1353
1397
  if (
@@ -1369,6 +1413,18 @@ class Up2k(object):
1369
1413
  ):
1370
1414
  cv = iname
1371
1415
 
1416
+ if not self.args.no_dirsz:
1417
+ tnf += len(files)
1418
+ q = "select sz, nf from ds where rd=? limit 1"
1419
+ try:
1420
+ db_sz, db_nf = db.c.execute(q, (rd,)).fetchone() or (-1, -1)
1421
+ if rsz != db_sz or tnf != db_nf:
1422
+ db.c.execute("delete from ds where rd=?", (rd,))
1423
+ db.c.execute("insert into ds values (?,?,?)", (rd, rsz, tnf))
1424
+ db.n += 1
1425
+ except:
1426
+ pass # mojibake rd
1427
+
1372
1428
  # folder of 1000 files = ~1 MiB RAM best-case (tiny filenames);
1373
1429
  # free up stuff we're done with before dhashing
1374
1430
  gl = []
@@ -1382,7 +1438,7 @@ class Up2k(object):
1382
1438
 
1383
1439
  zh.update(cv.encode("utf-8", "replace"))
1384
1440
  zh.update(spack(b"<d", cst.st_mtime))
1385
- dhash = base64.urlsafe_b64encode(zh.digest()[:12]).decode("ascii")
1441
+ dhash = ub64enc(zh.digest()[:12]).decode("ascii")
1386
1442
  sql = "select d from dh where d=? and +h=?"
1387
1443
  try:
1388
1444
  c = db.c.execute(sql, (rd, dhash))
@@ -1392,7 +1448,7 @@ class Up2k(object):
1392
1448
  c = db.c.execute(sql, (drd, dhash))
1393
1449
 
1394
1450
  if c.fetchone():
1395
- return ret
1451
+ return tfa, tnf, rsz
1396
1452
 
1397
1453
  if cv and rd:
1398
1454
  # mojibake not supported (for performance / simplicity):
@@ -1409,10 +1465,10 @@ class Up2k(object):
1409
1465
  seen_files = set([x[2] for x in files]) # for dropcheck
1410
1466
  for sz, lmod, fn in files:
1411
1467
  if self.stop:
1412
- return -1
1468
+ return -1, 0, 0
1413
1469
 
1414
- rp = vjoin(rd, fn)
1415
- abspath = os.path.join(cdir, fn)
1470
+ rp = rds + fn
1471
+ abspath = cdirs + fn
1416
1472
  nohash = reh.search(abspath) if reh else False
1417
1473
 
1418
1474
  sql = "select w, mt, sz, ip, at from up where rd = ? and fn = ?"
@@ -1442,7 +1498,7 @@ class Up2k(object):
1442
1498
  )
1443
1499
  self.log(t)
1444
1500
  self.db_rm(db.c, rd, fn, 0)
1445
- ret += 1
1501
+ tfa += 1
1446
1502
  db.n += 1
1447
1503
  in_db = []
1448
1504
  else:
@@ -1467,7 +1523,7 @@ class Up2k(object):
1467
1523
  continue
1468
1524
 
1469
1525
  if not hashes:
1470
- return -1
1526
+ return -1, 0, 0
1471
1527
 
1472
1528
  wark = up2k_wark_from_hashlist(self.salt, sz, hashes)
1473
1529
 
@@ -1478,7 +1534,7 @@ class Up2k(object):
1478
1534
  # skip upload hooks by not providing vflags
1479
1535
  self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, "", "", ip, at)
1480
1536
  db.n += 1
1481
- ret += 1
1537
+ tfa += 1
1482
1538
  td = time.time() - db.t
1483
1539
  if db.n >= 4096 or td >= 60:
1484
1540
  self.log("commit {} new files".format(db.n))
@@ -1491,33 +1547,37 @@ class Up2k(object):
1491
1547
  db.c.execute("insert into dh values (?,?)", (drd, dhash)) # type: ignore
1492
1548
 
1493
1549
  if self.stop:
1494
- return -1
1550
+ return -1, 0, 0
1495
1551
 
1496
1552
  # drop shadowed folders
1497
1553
  for sh_rd in unreg:
1498
1554
  n = 0
1499
- q = "select count(w) from up where (rd = ? or rd like ?||'%') and at == 0"
1555
+ q = "select count(w) from up where (rd=? or rd like ?||'/%') and +at == 0"
1500
1556
  for sh_erd in [sh_rd, "//" + w8b64enc(sh_rd)]:
1501
1557
  try:
1502
- n = db.c.execute(q, (sh_erd, sh_erd + "/")).fetchone()[0]
1558
+ erd_erd = (sh_erd, sh_erd)
1559
+ n = db.c.execute(q, erd_erd).fetchone()[0]
1503
1560
  break
1504
1561
  except:
1505
1562
  pass
1506
1563
 
1564
+
1507
1565
  if n:
1508
1566
  t = "forgetting {} shadowed autoindexed files in [{}] > [{}]"
1509
1567
  self.log(t.format(n, top, sh_rd))
1510
- assert sh_erd # type: ignore
1511
1568
 
1512
- q = "delete from dh where (d = ? or d like ?||'%')"
1513
- db.c.execute(q, (sh_erd, sh_erd + "/"))
1569
+ q = "delete from dh where (d = ? or d like ?||'/%')"
1570
+ db.c.execute(q, erd_erd)
1571
+
1572
+ q = "delete from up where (rd=? or rd like ?||'/%') and +at == 0"
1573
+ db.c.execute(q, erd_erd)
1574
+ tfa += n
1514
1575
 
1515
- q = "delete from up where (rd = ? or rd like ?||'%') and at == 0"
1516
- db.c.execute(q, (sh_erd, sh_erd + "/"))
1517
- ret += n
1576
+ q = "delete from ds where (rd=? or rd like ?||'/%')"
1577
+ db.c.execute(q, erd_erd)
1518
1578
 
1519
1579
  if n4g:
1520
- return ret
1580
+ return tfa, tnf, rsz
1521
1581
 
1522
1582
  # drop missing files
1523
1583
  q = "select fn from up where rd = ?"
@@ -1535,7 +1595,7 @@ class Up2k(object):
1535
1595
  if n_rm:
1536
1596
  self.log("forgot {} deleted files".format(n_rm))
1537
1597
 
1538
- return ret
1598
+ return tfa, tnf, rsz
1539
1599
 
1540
1600
  def _drop_lost(self, cur , top , excl ) :
1541
1601
  rm = []
@@ -1610,7 +1670,7 @@ class Up2k(object):
1610
1670
 
1611
1671
  # then covers
1612
1672
  n_rm3 = 0
1613
- qu = "select 1 from up where rd=? and +fn=? limit 1"
1673
+ qu = "select 1 from up where rd=? and fn=? limit 1"
1614
1674
  q = "delete from cv where rd=? and dn=? and +fn=?"
1615
1675
  for crd, cdn, fn in cur.execute("select * from cv"):
1616
1676
  urd = vjoin(crd, cdn)
@@ -1753,13 +1813,13 @@ class Up2k(object):
1753
1813
  return 0
1754
1814
 
1755
1815
  with self.mutex:
1816
+ q = "update up set w=?, sz=?, mt=? where rd=? and fn=?"
1756
1817
  for rd, fn, w, sz, mt in rewark:
1757
- q = "update up set w = ?, sz = ?, mt = ? where rd = ? and fn = ? limit 1"
1758
1818
  cur.execute(q, (w, sz, int(mt), rd, fn))
1759
1819
 
1760
- for _, _, w in f404:
1761
- q = "delete from up where w = ? limit 1"
1762
- cur.execute(q, (w,))
1820
+ if f404:
1821
+ q = "delete from up where rd=? and fn=? and +w=?"
1822
+ cur.executemany(q, f404)
1763
1823
 
1764
1824
  cur.connection.commit()
1765
1825
 
@@ -2226,7 +2286,6 @@ class Up2k(object):
2226
2286
  # mp.pool.ThreadPool and concurrent.futures.ThreadPoolExecutor
2227
2287
  # both do crazy runahead so lets reinvent another wheel
2228
2288
  nw = max(1, self.args.mtag_mt)
2229
- assert self.mtag
2230
2289
  if not self.mpool_used:
2231
2290
  self.mpool_used = True
2232
2291
  self.log("using {}x {}".format(nw, self.mtag.backend))
@@ -2300,7 +2359,6 @@ class Up2k(object):
2300
2359
  at ,
2301
2360
  ) :
2302
2361
  """will mutex(main)"""
2303
- assert self.mtag
2304
2362
 
2305
2363
  try:
2306
2364
  st = bos.stat(abspath)
@@ -2332,7 +2390,6 @@ class Up2k(object):
2332
2390
  tags ,
2333
2391
  ) :
2334
2392
  """mutex(main) me"""
2335
- assert self.mtag
2336
2393
 
2337
2394
  if not bos.path.isfile(abspath):
2338
2395
  return 0
@@ -2388,7 +2445,7 @@ class Up2k(object):
2388
2445
  def _log_sqlite_incompat(self, db_path, t0) :
2389
2446
  txt = t0 or ""
2390
2447
  digest = hashlib.sha512(db_path.encode("utf-8", "replace")).digest()
2391
- stackname = base64.urlsafe_b64encode(digest[:9]).decode("utf-8")
2448
+ stackname = ub64enc(digest[:9]).decode("ascii")
2392
2449
  stackpath = os.path.join(E.cfg, "stack-%s.txt" % (stackname,))
2393
2450
 
2394
2451
  t = " the filesystem at %s may not support locking, or is otherwise incompatible with sqlite\n\n %s\n\n"
@@ -2431,12 +2488,11 @@ class Up2k(object):
2431
2488
  self.log("WARN: failed to upgrade from v4", 3)
2432
2489
 
2433
2490
  if ver == DB_VER:
2434
- try:
2435
- self._add_cv_tab(cur)
2436
- self._add_xiu_tab(cur)
2437
- self._add_dhash_tab(cur)
2438
- except:
2439
- pass
2491
+ self._add_dhash_tab(cur)
2492
+ self._add_xiu_tab(cur)
2493
+ self._add_cv_tab(cur)
2494
+ self._add_idx_up_vp(cur, db_path)
2495
+ self._add_ds_tab(cur)
2440
2496
 
2441
2497
  try:
2442
2498
  nfiles = next(cur.execute("select count(w) from up"))[0]
@@ -2533,9 +2589,10 @@ class Up2k(object):
2533
2589
 
2534
2590
  for cmd in [
2535
2591
  r"create table up (w text, mt int, sz int, rd text, fn text, ip text, at int)",
2536
- r"create index up_rd on up(rd)",
2592
+ r"create index up_vp on up(rd, fn)",
2537
2593
  r"create index up_fn on up(fn)",
2538
2594
  r"create index up_ip on up(ip)",
2595
+ r"create index up_at on up(at)",
2539
2596
  idx,
2540
2597
  r"create table mt (w text, k text, v int)",
2541
2598
  r"create index mt_w on mt(w)",
@@ -2549,6 +2606,7 @@ class Up2k(object):
2549
2606
  self._add_dhash_tab(cur)
2550
2607
  self._add_xiu_tab(cur)
2551
2608
  self._add_cv_tab(cur)
2609
+ self._add_ds_tab(cur)
2552
2610
  self.log("created DB at {}".format(db_path))
2553
2611
  return cur
2554
2612
 
@@ -2565,6 +2623,12 @@ class Up2k(object):
2565
2623
 
2566
2624
  def _add_dhash_tab(self, cur ) :
2567
2625
  # v5 -> v5a
2626
+ try:
2627
+ cur.execute("select d, h from dh limit 1").fetchone()
2628
+ return
2629
+ except:
2630
+ pass
2631
+
2568
2632
  for cmd in [
2569
2633
  r"create table dh (d text, h text)",
2570
2634
  r"create index dh_d on dh(d)",
@@ -2618,6 +2682,40 @@ class Up2k(object):
2618
2682
 
2619
2683
  cur.connection.commit()
2620
2684
 
2685
+ def _add_idx_up_vp(self, cur , db_path ) :
2686
+ # v5c -> v5d
2687
+ try:
2688
+ cur.execute("drop index up_rd")
2689
+ except:
2690
+ return
2691
+
2692
+ for cmd in [
2693
+ r"create index up_vp on up(rd, fn)",
2694
+ r"create index up_at on up(at)",
2695
+ ]:
2696
+ self.log("upgrading db [%s]: %s" % (db_path, cmd[:18]))
2697
+ cur.execute(cmd)
2698
+
2699
+ self.log("upgrading db [%s]: writing to disk..." % (db_path,))
2700
+ cur.connection.commit()
2701
+ cur.execute("vacuum")
2702
+
2703
+ def _add_ds_tab(self, cur ) :
2704
+ # v5d -> v5e
2705
+ try:
2706
+ cur.execute("select rd, sz from ds limit 1").fetchone()
2707
+ return
2708
+ except:
2709
+ pass
2710
+
2711
+ for cmd in [
2712
+ r"create table ds (rd text, sz int, nf int)",
2713
+ r"create index ds_rd on ds(rd)",
2714
+ ]:
2715
+ cur.execute(cmd)
2716
+
2717
+ cur.connection.commit()
2718
+
2621
2719
  def wake_rescanner(self):
2622
2720
  with self.rescan_cond:
2623
2721
  self.rescan_cond.notify_all()
@@ -2802,7 +2900,6 @@ class Up2k(object):
2802
2900
 
2803
2901
  c2 = cur
2804
2902
 
2805
- assert c2
2806
2903
  c2.connection.commit()
2807
2904
 
2808
2905
  cur = jcur
@@ -2843,7 +2940,7 @@ class Up2k(object):
2843
2940
  self.log(t)
2844
2941
  del reg[wark]
2845
2942
 
2846
- elif inc_ap != orig_ap and not data_ok:
2943
+ elif inc_ap != orig_ap and not data_ok and "done" in reg[wark]:
2847
2944
  self.log("asserting contents of %s" % (orig_ap,))
2848
2945
  dhashes, _ = self._hashlist_from_file(orig_ap)
2849
2946
  dwark = up2k_wark_from_hashlist(self.salt, st.st_size, dhashes)
@@ -2907,9 +3004,12 @@ class Up2k(object):
2907
3004
  job = deepcopy(job)
2908
3005
  job["wark"] = wark
2909
3006
  job["at"] = cj.get("at") or time.time()
2910
- zs = "lmod ptop vtop prel name host user addr poke"
3007
+ zs = "vtop ptop prel name lmod host user addr poke"
2911
3008
  for k in zs.split():
2912
3009
  job[k] = cj.get(k) or ""
3010
+ for k in ("life", "replace"):
3011
+ if k in cj:
3012
+ job[k] = cj[k]
2913
3013
 
2914
3014
  pdir = djoin(cj["ptop"], cj["prel"])
2915
3015
  if rand:
@@ -3010,18 +3110,8 @@ class Up2k(object):
3010
3110
  "busy": {},
3011
3111
  }
3012
3112
  # client-provided, sanitized by _get_wark: name, size, lmod
3013
- for k in [
3014
- "host",
3015
- "user",
3016
- "addr",
3017
- "vtop",
3018
- "ptop",
3019
- "prel",
3020
- "name",
3021
- "size",
3022
- "lmod",
3023
- "poke",
3024
- ]:
3113
+ zs = "vtop ptop prel name size lmod host user addr poke"
3114
+ for k in zs.split():
3025
3115
  job[k] = cj[k]
3026
3116
 
3027
3117
  for k in ["life", "replace"]:
@@ -3104,7 +3194,22 @@ class Up2k(object):
3104
3194
  fp = djoin(fdir, fname)
3105
3195
  if job.get("replace") and bos.path.exists(fp):
3106
3196
  self.log("replacing existing file at {}".format(fp))
3107
- wunlink(self.log, fp, self.flags.get(job["ptop"]) or {})
3197
+ cur = None
3198
+ ptop = job["ptop"]
3199
+ vf = self.flags.get(ptop) or {}
3200
+ st = bos.stat(fp)
3201
+ try:
3202
+ vrel = vjoin(job["prel"], fname)
3203
+ xlink = bool(vf.get("xlink"))
3204
+ cur, wark, _, _, _, _ = self._find_from_vpath(ptop, vrel)
3205
+ self._forget_file(ptop, vrel, cur, wark, True, st.st_size, xlink)
3206
+ except Exception as ex:
3207
+ self.log("skipping replace-relink: %r" % (ex,))
3208
+ finally:
3209
+ if cur:
3210
+ cur.connection.commit()
3211
+
3212
+ wunlink(self.log, fp, vf)
3108
3213
 
3109
3214
  if self.args.plain_ip:
3110
3215
  dip = ip.replace(":", ".")
@@ -3112,8 +3217,9 @@ class Up2k(object):
3112
3217
  dip = self.hub.iphash.s(ip)
3113
3218
 
3114
3219
  suffix = "-%.6f-%s" % (ts, dip)
3115
- with ren_open(fname, "wb", fdir=fdir, suffix=suffix) as zfw:
3116
- return zfw["orz"][1]
3220
+ f, ret = ren_open(fname, "wb", fdir=fdir, suffix=suffix)
3221
+ f.close()
3222
+ return ret
3117
3223
 
3118
3224
  def _symlink(
3119
3225
  self,
@@ -3260,7 +3366,6 @@ class Up2k(object):
3260
3366
  t = "that chunk is already being written to:\n {}\n {} {}/{}\n {}"
3261
3367
  raise Pebkac(400, t.format(wark, chash, idx, nh, job["name"]))
3262
3368
 
3263
- assert chash # type: ignore
3264
3369
  chunksize = up2k_chunksize(job["size"])
3265
3370
 
3266
3371
  coffsets = []
@@ -3295,19 +3400,30 @@ class Up2k(object):
3295
3400
 
3296
3401
  return chashes, chunksize, coffsets, path, job["lmod"], job["sprs"]
3297
3402
 
3298
- def release_chunks(self, ptop , wark , chashes ) :
3299
- with self.reg_mutex:
3300
- job = self.registry[ptop].get(wark)
3301
- if job:
3302
- for chash in chashes:
3303
- job["busy"].pop(chash, None)
3304
-
3305
- return True
3403
+ def fast_confirm_chunks(
3404
+ self, ptop , wark , chashes
3405
+ ) :
3406
+ if not self.mutex.acquire(False):
3407
+ return -1, ""
3408
+ if not self.reg_mutex.acquire(False):
3409
+ self.mutex.release()
3410
+ return -1, ""
3411
+ try:
3412
+ return self._confirm_chunks(ptop, wark, chashes)
3413
+ finally:
3414
+ self.reg_mutex.release()
3415
+ self.mutex.release()
3306
3416
 
3307
3417
  def confirm_chunks(
3308
3418
  self, ptop , wark , chashes
3309
3419
  ) :
3310
3420
  with self.mutex, self.reg_mutex:
3421
+ return self._confirm_chunks(ptop, wark, chashes)
3422
+
3423
+ def _confirm_chunks(
3424
+ self, ptop , wark , chashes
3425
+ ) :
3426
+ if True:
3311
3427
  self.db_act = self.vol_act[ptop] = time.time()
3312
3428
  try:
3313
3429
  job = self.registry[ptop][wark]
@@ -3315,7 +3431,7 @@ class Up2k(object):
3315
3431
  src = djoin(pdir, job["tnam"])
3316
3432
  dst = djoin(pdir, job["name"])
3317
3433
  except Exception as ex:
3318
- return "confirm_chunk, wark(%r)" % (ex,) # type: ignore
3434
+ return -2, "confirm_chunk, wark(%r)" % (ex,) # type: ignore
3319
3435
 
3320
3436
  for chash in chashes:
3321
3437
  job["busy"].pop(chash, None)
@@ -3324,7 +3440,7 @@ class Up2k(object):
3324
3440
  for chash in chashes:
3325
3441
  job["need"].remove(chash)
3326
3442
  except Exception as ex:
3327
- return "confirm_chunk, chash(%s) %r" % (chash, ex) # type: ignore
3443
+ return -2, "confirm_chunk, chash(%s) %r" % (chash, ex) # type: ignore
3328
3444
 
3329
3445
  ret = len(job["need"])
3330
3446
  if ret > 0:
@@ -3473,7 +3589,6 @@ class Up2k(object):
3473
3589
  cur.connection.commit()
3474
3590
  except Exception as ex:
3475
3591
  x = self.register_vpath(ptop, {})
3476
- assert x
3477
3592
  db_ex_chk(self.log, ex, x[1])
3478
3593
  raise
3479
3594
 
@@ -3488,7 +3603,6 @@ class Up2k(object):
3488
3603
  try:
3489
3604
  r = db.execute(sql, (rd, fn))
3490
3605
  except:
3491
- assert self.mem_cur
3492
3606
  r = db.execute(sql, s3enc(self.mem_cur, rd, fn))
3493
3607
 
3494
3608
  if r.rowcount:
@@ -3526,7 +3640,6 @@ class Up2k(object):
3526
3640
  try:
3527
3641
  db.execute(sql, v)
3528
3642
  except:
3529
- assert self.mem_cur
3530
3643
  rd, fn = s3enc(self.mem_cur, rd, fn)
3531
3644
  v = (wark, int(ts), sz, rd, fn, db_ip, int(at or 0))
3532
3645
  db.execute(sql, v)
@@ -3574,7 +3687,6 @@ class Up2k(object):
3574
3687
  try:
3575
3688
  db.execute(q, (cd, wark[:16], rd, fn))
3576
3689
  except:
3577
- assert self.mem_cur
3578
3690
  rd, fn = s3enc(self.mem_cur, rd, fn)
3579
3691
  db.execute(q, (cd, wark[:16], rd, fn))
3580
3692
 
@@ -3607,6 +3719,19 @@ class Up2k(object):
3607
3719
  except:
3608
3720
  pass
3609
3721
 
3722
+ if "nodirsz" not in vflags:
3723
+ try:
3724
+ q = "update ds set nf=nf+1, sz=sz+? where rd=?"
3725
+ q2 = "insert into ds values(?,?,1)"
3726
+ while True:
3727
+ if not db.execute(q, (sz, rd)).rowcount:
3728
+ db.execute(q2, (rd, sz))
3729
+ if not rd:
3730
+ break
3731
+ rd = rd.rsplit("/", 1)[0] if "/" in rd else ""
3732
+ except:
3733
+ pass
3734
+
3610
3735
  def handle_rm(
3611
3736
  self,
3612
3737
  uname ,
@@ -3991,7 +4116,6 @@ class Up2k(object):
3991
4116
 
3992
4117
  has_dupes = False
3993
4118
  if w:
3994
- assert c1
3995
4119
  if c2 and c2 != c1:
3996
4120
  self._copy_tags(c1, c2, w)
3997
4121
 
@@ -4129,7 +4253,6 @@ class Up2k(object):
4129
4253
  try:
4130
4254
  c = cur.execute(q, (rd, fn))
4131
4255
  except:
4132
- assert self.mem_cur
4133
4256
  c = cur.execute(q, s3enc(self.mem_cur, rd, fn))
4134
4257
 
4135
4258
  hit = c.fetchone()
@@ -4372,8 +4495,7 @@ class Up2k(object):
4372
4495
  rem -= len(buf)
4373
4496
 
4374
4497
  digest = hashobj.digest()[:33]
4375
- digest = base64.urlsafe_b64encode(digest)
4376
- ret.append(digest.decode("utf-8"))
4498
+ ret.append(ub64enc(digest).decode("ascii"))
4377
4499
 
4378
4500
  return ret, st
4379
4501
 
@@ -4445,8 +4567,8 @@ class Up2k(object):
4445
4567
  dip = self.hub.iphash.s(job["addr"])
4446
4568
 
4447
4569
  suffix = "-%.6f-%s" % (job["t0"], dip)
4448
- with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as zfw:
4449
- f, job["tnam"] = zfw["orz"]
4570
+ f, job["tnam"] = ren_open(tnam, "wb", fdir=pdir, suffix=suffix)
4571
+ try:
4450
4572
  abspath = djoin(pdir, job["tnam"])
4451
4573
  sprs = job["sprs"]
4452
4574
  sz = job["size"]
@@ -4493,6 +4615,8 @@ class Up2k(object):
4493
4615
  if job["hash"] and sprs:
4494
4616
  f.seek(sz - 1)
4495
4617
  f.write(b"e")
4618
+ finally:
4619
+ f.close()
4496
4620
 
4497
4621
  if not job["hash"]:
4498
4622
  self._finish_upload(job["ptop"], job["wark"])
@@ -4835,11 +4959,10 @@ def up2k_wark_from_hashlist(salt , filesize , hashes ) :
4835
4959
  vstr = "\n".join(values)
4836
4960
 
4837
4961
  wark = hashlib.sha512(vstr.encode("utf-8")).digest()[:33]
4838
- wark = base64.urlsafe_b64encode(wark)
4839
- return wark.decode("ascii")
4962
+ return ub64enc(wark).decode("ascii")
4840
4963
 
4841
4964
 
4842
4965
  def up2k_wark_from_metadata(salt , sz , lastmod , rd , fn ) :
4843
4966
  ret = sfsenc("%s\n%d\n%d\n%s\n%s" % (salt, lastmod, sz, rd, fn))
4844
- ret = base64.urlsafe_b64encode(hashlib.sha512(ret).digest())
4967
+ ret = ub64enc(hashlib.sha512(ret).digest())
4845
4968
  return ("#%s" % (ret.decode("ascii"),))[:44]