preserve mtimes when juggling symlinks

This commit is contained in:
ed 2021-12-04 01:58:04 +01:00
parent f39f575a9c
commit 241ef5b99d
6 changed files with 45 additions and 20 deletions

View file

@ -2,7 +2,7 @@
from __future__ import print_function, unicode_literals
import os
from ..util import fsenc, fsdec
from ..util import fsenc, fsdec, SYMTIME
from . import path
@ -55,5 +55,8 @@ def unlink(p):
return os.unlink(fsenc(p))
def utime(p, times=None):
return os.utime(fsenc(p), times)
def utime(p, times=None, follow_symlinks=True):
if SYMTIME:
return os.utime(fsenc(p), times, follow_symlinks=follow_symlinks)
else:
return os.utime(fsenc(p), times)

View file

@ -2,7 +2,7 @@
from __future__ import print_function, unicode_literals
import os
from ..util import fsenc, fsdec
from ..util import fsenc, fsdec, SYMTIME
def abspath(p):
@ -13,8 +13,11 @@ def exists(p):
return os.path.exists(fsenc(p))
def getmtime(p):
return os.path.getmtime(fsenc(p))
def getmtime(p, follow_symlinks=True):
if not follow_symlinks and SYMTIME:
return os.lstat(fsenc(p)).st_mtime
else:
return os.path.getmtime(fsenc(p))
def getsize(p):

View file

@ -21,6 +21,7 @@ from .util import (
Pebkac,
Queue,
ProgressPrinter,
SYMTIME,
fsdec,
fsenc,
absreal,
@ -1307,7 +1308,7 @@ class Up2k(object):
err = "partial upload exists at a different location; please resume uploading here instead:\n"
err += "/" + quotep(vsrc) + " "
dupe = [cj["prel"], cj["name"]]
dupe = [cj["prel"], cj["name"], cj["lmod"]]
try:
self.dupesched[src].append(dupe)
except:
@ -1332,7 +1333,7 @@ class Up2k(object):
dst = os.path.join(job["ptop"], job["prel"], job["name"])
if not self.args.nw:
bos.unlink(dst) # TODO ed pls
self._symlink(src, dst)
self._symlink(src, dst, lmod=cj["lmod"])
if cur:
a = [cj[x] for x in "prel name lmod size addr".split()]
@ -1404,13 +1405,14 @@ class Up2k(object):
with ren_open(fname, "wb", fdir=fdir, suffix=suffix) as f:
return f["orz"][1]
def _symlink(self, src, dst, verbose=True):
def _symlink(self, src, dst, verbose=True, lmod=None):
if verbose:
self.log("linking dupe:\n {0}\n {1}".format(src, dst))
if self.args.nw:
return
linked = False
try:
if self.args.no_symlink:
raise Exception("disabled in config")
@ -1441,10 +1443,18 @@ class Up2k(object):
hops = len(ndst[nc:]) - 1
lsrc = "../" * hops + "/".join(lsrc)
os.symlink(fsenc(lsrc), fsenc(ldst))
linked = True
except Exception as ex:
self.log("cannot symlink; creating copy: " + repr(ex))
shutil.copy2(fsenc(src), fsenc(dst))
if lmod and (not linked or SYMTIME):
times = (int(time.time()), int(lmod))
if ANYWIN:
self.lastmod_q.put([dst, 0, times])
else:
bos.utime(dst, times, False)
def handle_chunk(self, ptop, wark, chash):
with self.mutex:
job = self.registry[ptop].get(wark)
@ -1551,12 +1561,12 @@ class Up2k(object):
return
cur = self.cur.get(ptop)
for rd, fn in dupes:
for rd, fn, lmod in dupes:
d2 = os.path.join(ptop, rd, fn)
if os.path.exists(d2):
continue
self._symlink(dst, d2)
self._symlink(dst, d2, lmod=lmod)
if cur:
self.db_rm(cur, rd, fn)
self.db_add(cur, wark, rd, fn, *a[-4:])
@ -1773,8 +1783,9 @@ class Up2k(object):
dlabs = absreal(sabs)
m = "moving symlink from [{}] to [{}], target [{}]"
self.log(m.format(sabs, dabs, dlabs))
os.unlink(sabs)
self._symlink(dlabs, dabs, False)
mt = bos.path.getmtime(sabs, False)
bos.unlink(sabs)
self._symlink(dlabs, dabs, False, lmod=mt)
# folders are too scary, schedule rescan of both vols
self.need_rescan[svn.vpath] = 1
@ -1904,25 +1915,30 @@ class Up2k(object):
slabs = list(sorted(links.keys()))[0]
ptop, rem = links.pop(slabs)
self.log("linkswap [{}] and [{}]".format(sabs, slabs))
mt = bos.path.getmtime(slabs, False)
bos.unlink(slabs)
bos.rename(sabs, slabs)
bos.utime(slabs, (int(time.time()), int(mt)), False)
self._symlink(slabs, sabs, False)
full[slabs] = [ptop, rem]
sabs = slabs
if not dabs:
dabs = list(sorted(full.keys()))[0]
for alink in links.keys():
lmod = None
try:
if alink != sabs and absreal(alink) != sabs:
continue
self.log("relinking [{}] to [{}]".format(alink, dabs))
lmod = bos.path.getmtime(alink, False)
bos.unlink(alink)
except:
pass
self._symlink(dabs, alink, False)
self._symlink(dabs, alink, False, lmod=lmod)
return len(full) + len(links)
@ -2028,7 +2044,7 @@ class Up2k(object):
for path, sz, times in ready:
self.log("lmod: setting times {} on {}".format(times, path))
try:
bos.utime(path, times)
bos.utime(path, times, False)
except:
self.log("lmod: failed to utime ({}, {})".format(path, times))

View file

@ -67,8 +67,9 @@ if WINDOWS and PY2:
FS_ENCODING = "utf-8"
HTTP_TS_FMT = "%a, %d %b %Y %H:%M:%S GMT"
SYMTIME = sys.version_info >= (3, 6) and os.supports_follow_symlinks
HTTP_TS_FMT = "%a, %d %b %Y %H:%M:%S GMT"
HTTPCODE = {
200: "OK",

View file

@ -82,10 +82,12 @@ a, #files tbody div a:last-child {
.s0:after,
.s1:after {
content: '⌄';
margin-left: -.1em;
}
.s0r:after,
.s1r:after {
content: '⌃';
margin-left: -.1em;
}
.s0:after,
.s0r:after {
@ -96,7 +98,7 @@ a, #files tbody div a:last-child {
color: #d09;
}
#files thead th:after {
margin-right: -.8em;
margin-right: -.7em;
}
#files tbody tr:hover td {
background: #1c1c1c;

View file

@ -2562,9 +2562,9 @@ var thegrid = (function () {
'<a href="#" class="btn" z="1.2" tt="Hotkey: shift-D">+</a></span> <span>chop: ' +
'<a href="#" class="btn" l="-1" tt="truncate filenames more (show less)">&ndash;</a> ' +
'<a href="#" class="btn" l="1" tt="truncate filenames less (show more)">+</a></span> <span>sort by: ' +
'<a href="#" s="href">name</a>, ' +
'<a href="#" s="sz">size</a>, ' +
'<a href="#" s="ts">date</a>, ' +
'<a href="#" s="href">name</a> ' +
'<a href="#" s="sz">size</a> ' +
'<a href="#" s="ts">date</a> ' +
'<a href="#" s="ext">type</a>' +
'</span></div>' +
'<div id="ggrid"></div>'