From 129d33f1a04baee34243a2f85028ec0dba1408d6 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 27 Jul 2021 19:15:58 +0200 Subject: [PATCH] mv/del: recursive rmdir --- copyparty/up2k.py | 31 +++++++++---------------------- copyparty/util.py | 21 +++++++++++++++++++++ copyparty/web/browser.js | 8 +++++--- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/copyparty/up2k.py b/copyparty/up2k.py index 181234c1..012cba71 100644 --- a/copyparty/up2k.py +++ b/copyparty/up2k.py @@ -30,6 +30,7 @@ from .util import ( vsplit, s3enc, s3dec, + rmdirs, statdir, s2hms, min_ex, @@ -1305,27 +1306,25 @@ class Up2k(object): db.execute(sql, v) def handle_rm(self, uname, vpath): - dirs = {} permsets = [[True, False, False, True]] vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0]) ptop = vn.realpath atop = vn.canonical(rem) adir, fn = os.path.split(atop) st = bos.lstat(atop) + scandir = not self.args.no_scandir if stat.S_ISLNK(st.st_mode) or stat.S_ISREG(st.st_mode): dbv, vrem = self.asrv.vfs.get(vpath, uname, *permsets[0]) dbv, vrem = dbv.get_dbv(vrem) g = [[dbv, vrem, os.path.dirname(vpath), adir, [[fn, 0]], [], []]] else: - scandir = not self.args.no_scandir g = vn.walk("", rem, [], uname, permsets, True, scandir, True) n_files = 0 - for dbv, vrem, _, atop, files, rd, vd in g: - dirs[atop] = 1 + for dbv, vrem, _, adir, files, rd, vd in g: for fn in [x[0] for x in files]: n_files += 1 - abspath = os.path.join(atop, fn) + abspath = os.path.join(adir, fn) vpath = "{}/{}".format(vrem, fn).strip("/") self.log("rm {}\n {}".format(vpath, abspath)) _ = dbv.get(vrem, uname, *permsets[0]) @@ -1339,15 +1338,10 @@ class Up2k(object): bos.unlink(abspath) - n_dirs = 0 - for d in dirs.keys(): - try: - bos.rmdir(d) - n_dirs += 1 - except: - pass - - return "deleted {} files (and {}/{} folders)".format(n_files, n_dirs, len(dirs)) + rm = rmdirs(self.log_func, scandir, True, atop) + ok = len(rm[0]) + ng = len(rm[1]) + return "deleted {} files (and {}/{} folders)".format(n_files, ok, ok + ng) def handle_mv(self, uname, svp, dvp): svn, srem = self.asrv.vfs.get(svp, uname, True, False, True) @@ -1372,14 +1366,12 @@ class Up2k(object): # fail early (prevent partial moves) raise Pebkac(400, "mv: source folder contains other volumes") - dirs = {} g = svn.walk("", srem, [], uname, permsets, True, scandir, True) for dbv, vrem, _, atop, files, rd, vd in g: if dbv != jail: # the actual check (avoid toctou) raise Pebkac(400, "mv: source folder contains other volumes") - dirs[atop] = 1 for fn in files: svpf = "/".join(x for x in [dbv.vpath, vrem, fn[0]] if x) if not svpf.startswith(svp + "/"): # assert @@ -1388,12 +1380,7 @@ class Up2k(object): dvpf = dvp + svpf[len(svp) :] self._mv_file(uname, svpf, dvpf) - for d in list(dirs.keys()) + [sabs]: - try: - bos.rmdir(d) - except: - pass - + rmdirs(self.log_func, scandir, True, sabs) return "k" def _mv_file(self, uname, svp, dvp): diff --git a/copyparty/util.py b/copyparty/util.py index 38b31654..bac3cf53 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals import re import os import sys +import stat import time import base64 import select @@ -1061,6 +1062,26 @@ def statdir(logger, scandir, lstat, top): logger(src, "{} @ {}".format(repr(ex), top), 1) +def rmdirs(logger, scandir, lstat, top): + dirs = statdir(logger, scandir, lstat, top) + dirs = [x[0] for x in dirs if stat.S_ISDIR(x[1].st_mode)] + dirs = [os.path.join(top, x) for x in dirs] + ok = [] + ng = [] + for d in dirs[::-1]: + a, b = rmdirs(logger, scandir, lstat, d) + ok += a + ng += b + + try: + os.rmdir(fsenc(top)) + ok.append(top) + except: + ng.append(top) + + return ok, ng + + def unescape_cookie(orig): # mw=idk; doot=qwe%2Crty%3Basd+fgh%2Bjkl%25zxc%26vbn # qwe,rty;asd fgh+jkl%zxc&vbn ret = "" diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 1368ab39..eecfdab0 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -1555,7 +1555,7 @@ var fileman = (function () { treectl.goto(get_evpath()); return; } - toast.inf(2, 'deleting ' + (vps.length + 1) + ' items\n\n' + vp); + toast.inf(0, 'deleting ' + (vps.length + 1) + ' items\n\n' + vp); xhr.open('GET', vp + '?delete', true); xhr.onreadystatechange = delete_cb; @@ -1642,7 +1642,7 @@ var fileman = (function () { r.tx(srcdir); return; } - toast.inf(2, 'pasting ' + (req.length + 1) + ' items\n\n' + vp); + toast.inf(0, 'pasting ' + (req.length + 1) + ' items\n\n' + vp); var dst = get_evpath() + vp.split('/').slice(-1)[0]; @@ -2034,8 +2034,10 @@ document.onkeydown = function (e) { if (ctrl(e)) document.documentElement.scrollTop += (d == 'next' ? 1 : -1) * el.offsetHeight; - if (e.shiftKey) + if (e.shiftKey) { clmod(el, 'sel', 't'); + msel.selui(); + } return ev(e); }