mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
preserve empty folders (closes #23):
* when deleting files, do not cascade upwards through empty folders * when moving folders, also move any empty folders inside the only remaining action which autoremoves empty folders is files getting deleted as they expire volume lifetimes also prevents accidentally moving parent folders into subfolders (even though that actually worked surprisingly well)
This commit is contained in:
parent
c44f5f5701
commit
83178d0836
|
@ -271,7 +271,7 @@ class FtpFs(AbstractedFS):
|
||||||
|
|
||||||
vp = join(self.cwd, path).lstrip("/")
|
vp = join(self.cwd, path).lstrip("/")
|
||||||
try:
|
try:
|
||||||
self.hub.up2k.handle_rm(self.uname, self.h.cli_ip, [vp], [])
|
self.hub.up2k.handle_rm(self.uname, self.h.cli_ip, [vp], [], False)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise FSE(str(ex))
|
raise FSE(str(ex))
|
||||||
|
|
||||||
|
|
|
@ -1152,18 +1152,9 @@ class HttpCli(object):
|
||||||
dst = self.headers["destination"]
|
dst = self.headers["destination"]
|
||||||
dst = re.sub("^https?://[^/]+", "", dst).lstrip()
|
dst = re.sub("^https?://[^/]+", "", dst).lstrip()
|
||||||
dst = unquotep(dst)
|
dst = unquotep(dst)
|
||||||
if not self._mv(self.vpath, dst):
|
if not self._mv(self.vpath, dst.lstrip("/")):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# up2k only cares about files and removes all empty folders;
|
|
||||||
# clients naturally expect empty folders to survive a rename
|
|
||||||
vn, rem = self.asrv.vfs.get(dst, self.uname, False, False)
|
|
||||||
dabs = vn.canonical(rem)
|
|
||||||
try:
|
|
||||||
bos.makedirs(dabs)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _applesan(self) -> bool:
|
def _applesan(self) -> bool:
|
||||||
|
@ -3054,7 +3045,7 @@ class HttpCli(object):
|
||||||
ret = self.gen_tree(top, dst)
|
ret = self.gen_tree(top, dst)
|
||||||
if self.is_vproxied:
|
if self.is_vproxied:
|
||||||
parents = self.args.R.split("/")
|
parents = self.args.R.split("/")
|
||||||
for parent in parents[::-1]:
|
for parent in reversed(parents):
|
||||||
ret = {"k%s" % (parent,): ret, "a": []}
|
ret = {"k%s" % (parent,): ret, "a": []}
|
||||||
|
|
||||||
zs = json.dumps(ret)
|
zs = json.dumps(ret)
|
||||||
|
@ -3193,7 +3184,9 @@ class HttpCli(object):
|
||||||
nlim = int(self.uparam.get("lim") or 0)
|
nlim = int(self.uparam.get("lim") or 0)
|
||||||
lim = [nlim, nlim] if nlim else []
|
lim = [nlim, nlim] if nlim else []
|
||||||
|
|
||||||
x = self.conn.hsrv.broker.ask("up2k.handle_rm", self.uname, self.ip, req, lim)
|
x = self.conn.hsrv.broker.ask(
|
||||||
|
"up2k.handle_rm", self.uname, self.ip, req, lim, False
|
||||||
|
)
|
||||||
self.loud_reply(x.get())
|
self.loud_reply(x.get())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -3210,7 +3203,7 @@ class HttpCli(object):
|
||||||
# x-www-form-urlencoded (url query part) uses
|
# x-www-form-urlencoded (url query part) uses
|
||||||
# either + or %20 for 0x20 so handle both
|
# either + or %20 for 0x20 so handle both
|
||||||
dst = unquotep(dst.replace("+", " "))
|
dst = unquotep(dst.replace("+", " "))
|
||||||
return self._mv(self.vpath, dst)
|
return self._mv(self.vpath, dst.lstrip("/"))
|
||||||
|
|
||||||
def _mv(self, vsrc: str, vdst: str) -> bool:
|
def _mv(self, vsrc: str, vdst: str) -> bool:
|
||||||
if not self.can_move:
|
if not self.can_move:
|
||||||
|
|
|
@ -261,7 +261,7 @@ class SMB(object):
|
||||||
yeet("blocked delete (no-del-acc): " + vpath)
|
yeet("blocked delete (no-del-acc): " + vpath)
|
||||||
|
|
||||||
vpath = vpath.replace("\\", "/").lstrip("/")
|
vpath = vpath.replace("\\", "/").lstrip("/")
|
||||||
self.hub.up2k.handle_rm(LEELOO_DALLAS, "1.7.6.2", [vpath], [])
|
self.hub.up2k.handle_rm(LEELOO_DALLAS, "1.7.6.2", [vpath], [], False)
|
||||||
|
|
||||||
def _utime(self, vpath: str, times: tuple[float, float]) -> None:
|
def _utime(self, vpath: str, times: tuple[float, float]) -> None:
|
||||||
if not self.args.smbw:
|
if not self.args.smbw:
|
||||||
|
|
|
@ -384,7 +384,7 @@ class Up2k(object):
|
||||||
if vp:
|
if vp:
|
||||||
fvp = "%s/%s" % (vp, fvp)
|
fvp = "%s/%s" % (vp, fvp)
|
||||||
|
|
||||||
self._handle_rm(LEELOO_DALLAS, "", fvp, [])
|
self._handle_rm(LEELOO_DALLAS, "", fvp, [], True)
|
||||||
nrm += 1
|
nrm += 1
|
||||||
|
|
||||||
if nrm:
|
if nrm:
|
||||||
|
@ -2897,7 +2897,9 @@ class Up2k(object):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def handle_rm(self, uname: str, ip: str, vpaths: list[str], lim: list[int]) -> str:
|
def handle_rm(
|
||||||
|
self, uname: str, ip: str, vpaths: list[str], lim: list[int], rm_up: bool
|
||||||
|
) -> str:
|
||||||
n_files = 0
|
n_files = 0
|
||||||
ok = {}
|
ok = {}
|
||||||
ng = {}
|
ng = {}
|
||||||
|
@ -2906,7 +2908,7 @@ class Up2k(object):
|
||||||
self.log("hit delete limit of {} files".format(lim[1]), 3)
|
self.log("hit delete limit of {} files".format(lim[1]), 3)
|
||||||
break
|
break
|
||||||
|
|
||||||
a, b, c = self._handle_rm(uname, ip, vp, lim)
|
a, b, c = self._handle_rm(uname, ip, vp, lim, rm_up)
|
||||||
n_files += a
|
n_files += a
|
||||||
for k in b:
|
for k in b:
|
||||||
ok[k] = 1
|
ok[k] = 1
|
||||||
|
@ -2920,7 +2922,7 @@ class Up2k(object):
|
||||||
return "deleted {} files (and {}/{} folders)".format(n_files, iok, iok + ing)
|
return "deleted {} files (and {}/{} folders)".format(n_files, iok, iok + ing)
|
||||||
|
|
||||||
def _handle_rm(
|
def _handle_rm(
|
||||||
self, uname: str, ip: str, vpath: str, lim: list[int]
|
self, uname: str, ip: str, vpath: str, lim: list[int], rm_up: bool
|
||||||
) -> tuple[int, list[str], list[str]]:
|
) -> tuple[int, list[str], list[str]]:
|
||||||
self.db_act = time.time()
|
self.db_act = time.time()
|
||||||
try:
|
try:
|
||||||
|
@ -3027,16 +3029,22 @@ class Up2k(object):
|
||||||
if xad:
|
if xad:
|
||||||
runhook(self.log, xad, abspath, vpath, "", uname, 0, 0, ip, 0, "")
|
runhook(self.log, xad, abspath, vpath, "", uname, 0, 0, ip, 0, "")
|
||||||
|
|
||||||
ok: list[str] = []
|
|
||||||
ng: list[str] = []
|
|
||||||
if is_dir:
|
if is_dir:
|
||||||
ok, ng = rmdirs(self.log_func, scandir, True, atop, 1)
|
ok, ng = rmdirs(self.log_func, scandir, True, atop, 1)
|
||||||
|
else:
|
||||||
|
ok = ng = []
|
||||||
|
|
||||||
ok2, ng2 = rmdirs_up(os.path.dirname(atop), ptop)
|
if rm_up:
|
||||||
|
ok2, ng2 = rmdirs_up(os.path.dirname(atop), ptop)
|
||||||
|
else:
|
||||||
|
ok2 = ng2 = []
|
||||||
|
|
||||||
return n_files, ok + ok2, ng + ng2
|
return n_files, ok + ok2, ng + ng2
|
||||||
|
|
||||||
def handle_mv(self, uname: str, svp: str, dvp: str) -> str:
|
def handle_mv(self, uname: str, svp: str, dvp: str) -> str:
|
||||||
|
if svp == dvp or dvp.startswith(svp + "/"):
|
||||||
|
raise Pebkac(400, "mv: cannot move parent into subfolder")
|
||||||
|
|
||||||
svn, srem = self.asrv.vfs.get(svp, uname, True, False, True)
|
svn, srem = self.asrv.vfs.get(svp, uname, True, False, True)
|
||||||
svn, srem = svn.get_dbv(srem)
|
svn, srem = svn.get_dbv(srem)
|
||||||
sabs = svn.canonical(srem, False)
|
sabs = svn.canonical(srem, False)
|
||||||
|
@ -3090,8 +3098,21 @@ class Up2k(object):
|
||||||
|
|
||||||
curs.clear()
|
curs.clear()
|
||||||
|
|
||||||
rmdirs(self.log_func, scandir, True, sabs, 1)
|
rm_ok, rm_ng = rmdirs(self.log_func, scandir, True, sabs, 1)
|
||||||
rmdirs_up(os.path.dirname(sabs), svn.realpath)
|
|
||||||
|
for zsl in (rm_ok, rm_ng):
|
||||||
|
for ap in reversed(zsl):
|
||||||
|
if not ap.startswith(sabs):
|
||||||
|
raise Pebkac(500, "mv_d: bug at {}, top {}".format(ap, sabs))
|
||||||
|
|
||||||
|
rem = ap[len(sabs) :].replace(os.sep, "/").lstrip("/")
|
||||||
|
vp = vjoin(dvp, rem)
|
||||||
|
try:
|
||||||
|
dvn, drem = self.asrv.vfs.get(vp, uname, False, True)
|
||||||
|
bos.mkdir(dvn.canonical(drem))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
return "k"
|
return "k"
|
||||||
|
|
||||||
def _mv_file(
|
def _mv_file(
|
||||||
|
|
|
@ -2270,7 +2270,7 @@ def rmdirs(
|
||||||
dirs = [os.path.join(top, x) for x in dirs]
|
dirs = [os.path.join(top, x) for x in dirs]
|
||||||
ok = []
|
ok = []
|
||||||
ng = []
|
ng = []
|
||||||
for d in dirs[::-1]:
|
for d in reversed(dirs):
|
||||||
a, b = rmdirs(logger, scandir, lstat, d, depth + 1)
|
a, b = rmdirs(logger, scandir, lstat, d, depth + 1)
|
||||||
ok += a
|
ok += a
|
||||||
ng += b
|
ng += b
|
||||||
|
|
Loading…
Reference in a new issue