mirror of
https://github.com/9001/copyparty.git
synced 2026-11-10 12:38:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ccc999f39 | ||
|
|
16a3f5d51a | ||
|
|
35af7c9fef | ||
|
|
4d1f2a9301 | ||
|
|
229159027b | ||
|
|
ec4a95e1a5 | ||
|
|
eb4f1dddce | ||
|
|
097b83056e | ||
|
|
c688bb2e46 | ||
|
|
c92435c2d2 | ||
|
|
22afaa7586 | ||
|
|
5ddbce31fc | ||
|
|
ec8ec0f1a6 | ||
|
|
558f6aee44 | ||
|
|
6867ccc511 | ||
|
|
947d73eeb6 | ||
|
|
735d6e00de | ||
|
|
fd4f434192 | ||
|
|
971a62cc89 | ||
|
|
bfc3cff848 | ||
|
|
8967726f61 | ||
|
|
ad9994bef3 | ||
|
|
e5e2e97910 | ||
|
|
e42e01563e |
No files matched your search
@@ -219,6 +219,8 @@ other places to download copyparty from (non-github links):
|
||||
* https://copyparty.eu/enz = https://copyparty.eu/copyparty-en.pyz = the enterprise pyz
|
||||
* https://copyparty.eu/cli = online cli helptext
|
||||
|
||||
DO NOT download from [sourceforge](https://copyparty.eu/unsanctioned/sourceforge.txt)
|
||||
|
||||
|
||||
### at home
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver="1.20.21"
|
||||
pkgver="1.20.23"
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
@@ -24,8 +24,9 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
||||
"python-argon2-cffi: hashed passwords in config"
|
||||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("etc/${pkgname}/copyparty.conf" )
|
||||
sha256sums=("654ef47555bb402e27d8905c900c99f54b7f1d6db874a749effdf9ab23cf4223")
|
||||
backup=("etc/copyparty.conf")
|
||||
install=copyparty.install
|
||||
sha256sums=("c0957ce077b0494292541116b9473fdbda1e6597b5d9158b2fc470db0a1834dd")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
@@ -36,12 +37,11 @@ package() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
python -m installer --destdir="$pkgdir" dist/*.whl
|
||||
|
||||
install -dm755 "${pkgdir}/etc/${pkgname}"
|
||||
install -Dm755 "bin/prisonparty.sh" "${pkgdir}/usr/bin/prisonparty"
|
||||
install -Dm644 "contrib/systemd/${pkgname}.conf" "${pkgdir}/etc/${pkgname}/copyparty.conf"
|
||||
install -Dm644 "contrib/systemd/${pkgname}.conf" "${pkgdir}/etc/copyparty.conf"
|
||||
install -Dm644 "contrib/systemd/${pkgname}@.service" "${pkgdir}/usr/lib/systemd/system/${pkgname}@.service"
|
||||
install -Dm644 "contrib/systemd/${pkgname}-user.service" "${pkgdir}/usr/lib/systemd/user/${pkgname}.service"
|
||||
install -Dm644 "contrib/systemd/prisonparty@.service" "${pkgdir}/usr/lib/systemd/system/prisonparty@.service"
|
||||
install -Dm644 "contrib/systemd/index.md" "${pkgdir}/var/lib/${pkgname}-jail/README.md"
|
||||
install -Dm644 "contrib/systemd/index.md" "${pkgdir}/var/lib/${pkgname}-jail/srv/README.md"
|
||||
install -Dm644 "LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
post_upgrade() {
|
||||
|
||||
# v1.20.22 moved the config-file, affecting the system-service only
|
||||
if [[ $(vercmp "$2" 1.20.22) -lt 0 ]]; then
|
||||
|
||||
oldpath="/etc/copyparty/copyparty.conf"
|
||||
newpath="/etc/copyparty.conf"
|
||||
|
||||
echo "==> Migrating config-file from $oldpath to $newpath"
|
||||
|
||||
if [[ ! -f "$oldpath" ]]; then
|
||||
oldpath="$oldpath.pacsave"
|
||||
fi
|
||||
|
||||
if [[ -f "$newpath.pacnew" ]]; then
|
||||
echo "==> WARNING: A config-file already existed in the new location [$newpath]. The existing file will be used as-is."
|
||||
|
||||
elif [[ -f "$oldpath" ]]; then
|
||||
mv -v "$oldpath" "$newpath"
|
||||
rmdir /etc/copyparty 2>/dev/null || true
|
||||
fi
|
||||
|
||||
systemctl cat copyparty@.service | awk '/=PRTY_CONFIG=/ && !/=\/etc\/copyparty.conf/ {exit 1}' || {
|
||||
echo "==> WARNING: The copyparty system-service has been edited by hand since installation, so it will not read config from the new default config path [$newpath]. Please use PRTY_CONFIG=$newpath if that is correct for your setup."
|
||||
}
|
||||
fi
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver=1.20.21
|
||||
pkgver=1.20.23
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
@@ -21,7 +21,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
||||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("/etc/${pkgname}.d/init" )
|
||||
sha256sums=("654ef47555bb402e27d8905c900c99f54b7f1d6db874a749effdf9ab23cf4223")
|
||||
sha256sums=("c0957ce077b0494292541116b9473fdbda1e6597b5d9158b2fc470db0a1834dd")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.21/copyparty-1.20.21.tar.gz",
|
||||
"version": "1.20.21",
|
||||
"hash": "sha256-ZU70dVW7QC4n2JBckAyZ9Ut/HW24dKdJ7/35qyPPQiM="
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.23/copyparty-1.20.23.tar.gz",
|
||||
"version": "1.20.23",
|
||||
"hash": "sha256-wJV84HewSUKSVBEWuUc/29oeZZe12RWLL8Rw2woYNN0="
|
||||
}
|
||||
+84
-13
@@ -687,6 +687,7 @@ def get_sects():
|
||||
\033[32m-i 127.0.0.1\033[0m listens on IPv4 localhost only
|
||||
\033[32m-i 127.1\033[0m listens on IPv4 localhost only
|
||||
\033[32m-i 127.1,192.168.123.1\033[0m = IPv4 localhost and 192.168.123.1
|
||||
\033[32m-i no\033[0m disable webserver
|
||||
|
||||
\033[33m-p\033[0m takes a comma-separated list of tcp ports to listen on;
|
||||
the default is \033[32m-p 3923\033[0m but as root you can \033[32m-p 80,443,3923\033[0m
|
||||
@@ -847,6 +848,74 @@ def get_sects():
|
||||
).rstrip()
|
||||
+ build_flags_desc(),
|
||||
],
|
||||
[
|
||||
"dedup",
|
||||
"file deduplication on upload",
|
||||
dedent(
|
||||
"""
|
||||
when just \033[36m--dedup\033[0m or volflag "\033[36mdedup\033[0m" is enabled, files will be
|
||||
deduplicated by means of \033[1msymlinks,\033[0m which demand some caution:
|
||||
|
||||
the available dedup approaches:
|
||||
|
||||
* symlinks work on most filesystems, but if another software
|
||||
moves either the symlink itself or the file that it points to,
|
||||
the link will break and the file "disappears";
|
||||
only safe if only copyparty is used to rename/move/delete
|
||||
|
||||
* hardlinks work on Linux filesystems and \033[1mlook\033[0m like regular files,
|
||||
except if the contents of one copy of that file is edited, then
|
||||
this will affect \033[1mall the dupes too!\033[0m
|
||||
and they share the same last-modified-time
|
||||
|
||||
* reflinks only work on btrfs/zfs/xfs but is the safest/best choice;
|
||||
deduplicated files behave just like regular files,
|
||||
and can be modified without affecting other copies/dupes
|
||||
|
||||
to change approach, add one of the following in addition to "\033[36mdedup\033[0m":
|
||||
|
||||
\033[36m--reflink\033[0m or volflag "\033[36mreflink\033[0m"
|
||||
if reflink is not possible (unsupp/cross-fs), a full copy is made
|
||||
|
||||
\033[36m--hardlink-only\033[0m or volflag "\033[36mhardlinkonly\033[0m"
|
||||
if hardlink is not possible (unsupp/cross-fs), a full copy is made
|
||||
|
||||
\033[36m--hardlink\033[0m or volflag "\033[36mhardlink\033[0m"
|
||||
if hardlink is not possible (unsupp/cross-fs), a symlink is made
|
||||
|
||||
note: dedup works best when combined with e2ds or e2dsa
|
||||
|
||||
to deduplicate existing files on-disk, see \033[36m--help-redup\033[0m
|
||||
"""
|
||||
),
|
||||
],
|
||||
[
|
||||
"redup",
|
||||
"deduplicating existing files on disk",
|
||||
dedent(
|
||||
"""
|
||||
\033[36m--redup\033[0m can convert on-disk files between deduplication-approaches,
|
||||
for example turning redundant full copies into reflinks to save space
|
||||
|
||||
converts \033[1mall files\033[0m in each volume! not just copyparty-created ones
|
||||
|
||||
example: --redup \033[32mno,sym,hard\033[0m=\033[35mref\033[0m will find all files of type
|
||||
\033[32mfullcopy/symlink/hardlink\033[0m, and convert them into \033[35mreflinks\033[0m
|
||||
(see \033[36m--help-dedup\033[0m for a summary on the types)
|
||||
|
||||
converting \033[1mfrom\033[0m symlinks/hardlinks is fairly fast and simple;
|
||||
in this case, the obvious target type/approach is reflinks
|
||||
(which only works on btrfs/zfs/xfs)
|
||||
|
||||
converting \033[1mto\033[0m symlinks or hardlinks is \033[3mpossible\033[0m, but
|
||||
not-recommended due to risks explained in \033[36m--help-dedup\033[0m
|
||||
(interaction with other softwares can result in data loss)
|
||||
|
||||
converting \033[1mfrom\033[0m 'no' (full redundant copies) is hella slow;
|
||||
checksums each file to avoid corruption from bad RAM
|
||||
"""
|
||||
),
|
||||
],
|
||||
[
|
||||
"thumb-ex",
|
||||
"extract thumbnails from custom file formats with python scripts",
|
||||
@@ -1212,9 +1281,9 @@ def get_sects():
|
||||
"file/folder permissions",
|
||||
dedent(
|
||||
"""
|
||||
global-option \033[33m--chmod-f\033[0m and volflag \033[33mchmod_f\033[0m specifies the unix-permission to use when creating a new file
|
||||
global-option \033[33m--chmod-f\033[0m and volflag "\033[33mchmod_f\033[0m" specifies the unix-permission to use when creating a new file
|
||||
|
||||
similarly, \033[33m--chmod-d\033[0m and \033[33mchmod_d\033[0m sets the directory/folder perm
|
||||
similarly, \033[33m--chmod-d\033[0m and "\033[33mchmod_d\033[0m" sets the directory/folder perm
|
||||
|
||||
the value is a three- or four-digit octal number such as \033[32m755\033[0m, \033[32m0644\033[0m, \033[32m2750\033[0m, etc.
|
||||
|
||||
@@ -1381,8 +1450,8 @@ def add_qr(ap, tty):
|
||||
ap2.add_argument("--qrz", metavar="N", type=int, default=0, help="[\033[32m1\033[0m]=1x, [\033[32m2\033[0m]=2x, [\033[32m0\033[0m]=auto (try [\033[32m2\033[0m] on broken fonts)")
|
||||
ap2.add_argument("--qr-pin", metavar="N", type=int, default=0, help="sticky/pin the qr-code to always stay on-screen; [\033[32m0\033[0m]=disabled, [\033[32m1\033[0m]=with-url, [\033[32m2\033[0m]=just-qr")
|
||||
ap2.add_argument("--qr-wait", metavar="SEC", type=float, default=0, help="wait \033[33mSEC\033[0m before printing the qr-code to the log")
|
||||
ap2.add_argument("--qr-every", metavar="SEC", type=float, default=0, help="print the qr-code every \033[33mSEC\033[0m (try this with/without --qr-pin in case of issues)")
|
||||
ap2.add_argument("--qr-winch", metavar="SEC", type=float, default=0, help="when --qr-pin is enabled, check for terminal size change every \033[33mSEC\033[0m")
|
||||
ap2.add_argument("--qr-every", metavar="SEC", type=float, default=0, help="print the qr-code every \033[33mSEC\033[0m (try this with/without \033[33m--qr-pin\033[0m in case of issues)")
|
||||
ap2.add_argument("--qr-winch", metavar="SEC", type=float, default=0, help="when \033[33m--qr-pin\033[0m is enabled, check for terminal size change every \033[33mSEC\033[0m")
|
||||
ap2.add_argument("--qr-file", metavar="TXT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m write qr-code to file.\n └─To create txt or svg, \033[33mTXT\033[0m is Filepath:Zoom:Pad, for example [\033[32mqr.txt:1:2\033[0m]\n └─To create png or gif, \033[33mTXT\033[0m is Filepath:Zoom:Pad:Foreground:Background, for example [\033[32mqr.png:8:2:333333:ffcc55\033[0m], or [\033[32mqr.png:8:2::ffcc55\033[0m] for transparent")
|
||||
ap2.add_argument("--qr-stdout", action="store_true", help="always display the QR-code on STDOUT in the terminal, even if \033[33m-q\033[0m")
|
||||
ap2.add_argument("--qr-stderr", action="store_true", help="always display the QR-code on STDERR in the terminal, even if \033[33m-q\033[0m")
|
||||
@@ -1427,16 +1496,18 @@ def add_upload(ap):
|
||||
ap2.add_argument("--reg-cap", metavar="N", type=int, default=38400, help="max number of uploads to keep in memory when running without \033[33m-e2d\033[0m; roughly 1 MiB RAM per 600")
|
||||
ap2.add_argument("--no-fpool", action="store_true", help="disable file-handle pooling -- instead, repeatedly close and reopen files during upload (bad idea to enable this on windows and/or cow filesystems)")
|
||||
ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)")
|
||||
ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see --help-chmod. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)")
|
||||
ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m2750\033[0m] = setgid + owner-RW + group-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
|
||||
ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see \033[33m--help-chmod\033[0m. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)")
|
||||
ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see \033[33m--help-chmod\033[0m. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m2750\033[0m] = setgid + owner-RW + group-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
|
||||
ap2.add_argument("--uid", metavar="N", type=int, default=-1, help="unix user-id to chown new files/folders to; default = -1 = do-not-change (volflag=uid)")
|
||||
ap2.add_argument("--gid", metavar="N", type=int, default=-1, help="unix group-id to chown new files/folders to; default = -1 = do-not-change (volflag=gid)")
|
||||
ap2.add_argument("--wram", action="store_true", help="allow uploading even if a volume is inside a ramdisk, meaning that all data will be lost on the next server reboot (volflag=wram)")
|
||||
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
|
||||
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication; see \033[33m--help-dedup\033[0m (volflag=dedup)")
|
||||
ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
|
||||
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
|
||||
ap2.add_argument("--hardlink-only", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made (volflag=hardlinkonly)")
|
||||
ap2.add_argument("--reflink", action="store_true", help="enable reflink-based dedup; will fallback on full copies when that is impossible (non-CoW filesystem) (volflag=reflink)")
|
||||
ap2.add_argument("--redup", metavar="A[,B]=T", help="convert dedup-type for existing files; \033[33mA,B\033[0m is dedup-types to convert from (no/ref/sym/hard), \033[33mT\033[0m is target type; converting from sym/hard is fast-ish, from no/ref is slooow, to sym/hard is jank; see \033[33m--help-redup\033[0m. Example: [\033[32msym,hard=ref\033[0m] (volflag=redup)")
|
||||
ap2.add_argument("--redup-dry", action="store_true", help="dry-run; makes \033[33m--redup\033[0m not apply changes (volflag=redup_dry)")
|
||||
ap2.add_argument("--no-dupe", action="store_true", help="reject duplicate files during upload; only matches within the same volume (volflag=nodupe)")
|
||||
ap2.add_argument("--no-dupe-m", action="store_true", help="also reject dupes when moving a file into another volume (volflag=nodupem)")
|
||||
ap2.add_argument("--no-clone", action="store_true", help="do not use existing data on disk to satisfy dupe uploads; reduces server HDD reads in exchange for much more network load (volflag=noclone)")
|
||||
@@ -1530,7 +1601,7 @@ def add_auth(ap):
|
||||
ses_db = os.path.join(E.cfg, "sessions.db")
|
||||
ap2 = ap.add_argument_group("IdP / identity provider / user authentication options")
|
||||
ap2.add_argument("--idp-h-usr", metavar="HN", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m contains a username to associate the request with (for use with authentik/oauth/...)\n\033[1;31mWARNING:\033[0m if you enable this, make sure clients are unable to specify this header themselves; must be washed away and replaced by a reverse-proxy")
|
||||
ap2.add_argument("--idp-hm-usr", metavar="T", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mT\033[0m is provided, and its value exists in a mapping defined by this option; see --help-idp")
|
||||
ap2.add_argument("--idp-hm-usr", metavar="T", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mT\033[0m is provided, and its value exists in a mapping defined by this option; see \033[33m--help-idp\033[0m")
|
||||
ap2.add_argument("--idp-h-grp", metavar="HN", type=u, default="", help="assume the request-header \033[33mHN\033[0m contains the groupname of the requesting user; can be referenced in config files for group-based access control")
|
||||
ap2.add_argument("--idp-h-key", metavar="HN", type=u, default="", help="optional but recommended safeguard; your reverse-proxy will insert a secret header named \033[33mHN\033[0m into all requests, and the other IdP headers will be ignored if this header is not present")
|
||||
ap2.add_argument("--idp-gsep", metavar="RE", type=u, default="|:;+,", help="if there are multiple groups in \033[33m--idp-h-grp\033[0m, they are separated by one of the characters in \033[33mRE\033[0m")
|
||||
@@ -1542,7 +1613,7 @@ def add_auth(ap):
|
||||
ap2.add_argument("--idp-login", metavar="L", type=u, default="", help="replace all login-buttons with a link to URL \033[33mL\033[0m (unless \033[32mpw\033[0m is in \033[33m--auth-ord\033[0m then both will be shown); [\033[32m{dst}\033[0m] expands to url of current page")
|
||||
ap2.add_argument("--idp-login-t", metavar="T", type=u, default="Login with SSO", help="the label/text for the idp-login button")
|
||||
ap2.add_argument("--idp-logout", metavar="L", type=u, default="", help="replace all logout-buttons with a link to URL \033[33mL\033[0m")
|
||||
ap2.add_argument("--auth-ord", metavar="TXT", type=u, default="idp,ipu", help="controls auth precedence; examples: [\033[32mpw,idp,ipu\033[0m], [\033[32mipu,pw,idp\033[0m], see --help-auth-ord")
|
||||
ap2.add_argument("--auth-ord", metavar="TXT", type=u, default="idp,ipu", help="controls auth precedence; examples: [\033[32mpw,idp,ipu\033[0m], [\033[32mipu,pw,idp\033[0m], see \033[33m--help-auth-ord\033[0m")
|
||||
ap2.add_argument("--pw-hdr", metavar="NAME", type=u, default="pw", help="lowercase name of password-header (NAME: foo); \033[1;31mWARNING:\033[0m Changing this will break support for many clients")
|
||||
ap2.add_argument("--pw-urlp", metavar="NAME", type=u, default="pw", help="lowercase name of password url-param (?NAME=foo); \033[1;31mWARNING:\033[0m Changing this will break support for many clients")
|
||||
ap2.add_argument("--no-bauth", action="store_true", help="disable basic-authentication support; do not accept passwords from the 'Authenticate' header at all. NOTE: This breaks support for the android app")
|
||||
@@ -1824,7 +1895,7 @@ def add_safety(ap):
|
||||
ap2.add_argument("--csp-ui", metavar="TXT", default="script-src 'unsafe-eval' 'nonce-{{ js_nonce }}'; worker-src 'self'", help="content-security-policy to apply for the web-UI; default helps prevent XSS by blocking <script> / onclick / ... (volflag=csp_ui)")
|
||||
ap2.add_argument("--csp-dl", metavar="TXT", default="", help="content-security-policy to apply for static files (volflag=csp_dl)")
|
||||
ap2.add_argument("--no-script", action="store_true", help="disables javascript in html files; helps prevent XSS but kills interactive websites; this will override \033[33m--csp-dl\033[0m with [\033[32mscript-src 'none'\033[0m] (volflag=noscript)")
|
||||
ap2.add_argument("--no-html", action="store_true", help="show html-files as plain text; helps prevent XSS but kills websites/blogs, also enables --no-script (volflag=nohtml)")
|
||||
ap2.add_argument("--no-html", action="store_true", help="show html-files as plain text; helps prevent XSS but kills websites/blogs, also enables \033[33m--no-script\033[0m (volflag=nohtml)")
|
||||
ap2.add_argument("--no-mime", action="store_true", help="disallow changing the response mimetype with url-parameter ?mime=... (volflag=nomime)")
|
||||
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise). \033[1;31mWARNING:\033[0m Not compatible with WebDAV")
|
||||
ap2.add_argument("--force-js", action="store_true", help="don't send folder listings as HTML, force clients to use the embedded json instead -- slight protection against misbehaving search engines which ignore \033[33m--no-robots\033[0m")
|
||||
@@ -1969,7 +2040,7 @@ def add_thumbnail(ap):
|
||||
|
||||
ap2.add_argument("--th-spec-cnv", metavar="T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)")
|
||||
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz, epub=jpg.epub, kra=png.kra, ora=png.ora", help="audio/image formats to decompress before passing to ffmpeg")
|
||||
ap2.add_argument("--th-extract", metavar="T", type=u, action="append", help="\033[34mREPEATABLE:\033[0m list of file extensions to thumbnail using a custom plugin (a python script); example: [\033[32mmdf,iso,dmg=/thumbs/diskimg.py\033[0m], see --help-thumb-ex")
|
||||
ap2.add_argument("--th-extract", metavar="T", type=u, action="append", help="\033[34mREPEATABLE:\033[0m list of file extensions to thumbnail using a custom plugin (a python script); example: [\033[32mmdf,iso,dmg=/thumbs/diskimg.py\033[0m], see \033[33m--help-thumb-ex\033[0m")
|
||||
ap2.add_argument("--th-extr-sz", metavar="M", type=int, default=16, help="max num megabytes to allow \033[33m--th-extract\033[0m plugins to extract from each file")
|
||||
ap2.add_argument("--hot-th-extr", action="store_true", help="recompile extractors on each thumbnail extraction -- expensive but convenient when hacking on stuff")
|
||||
|
||||
@@ -2126,7 +2197,7 @@ def add_ui(ap, retry: int):
|
||||
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
|
||||
ap2.add_argument("--js-other", metavar="L", type=u, default="", help="URL to additional JS to include in all other pages")
|
||||
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages (except for basic-browser); can be @PATH to send the contents of a file at PATH, and/or begin with %% to render as jinja2 template; \033[32m<script>\033[0m will not work, use \033[32m<script nonce=\"{{ js_nonce }}\">\033[0m (volflag=html_head)")
|
||||
ap2.add_argument("--html-head-s", metavar="T", type=u, default="", help="text to append to the <head> of all HTML pages (except for basic-browser); similar to (and can be combined with) --html-head but only accepts static text (volflag=html_head_s)")
|
||||
ap2.add_argument("--html-head-s", metavar="T", type=u, default="", help="text to append to the <head> of all HTML pages (except for basic-browser); similar to (and can be combined with) \033[33m--html-head\033[0m but only accepts static text (volflag=html_head_s)")
|
||||
ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI, or add ?v to URL for override)")
|
||||
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
|
||||
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
|
||||
@@ -2137,7 +2208,7 @@ def add_ui(ap, retry: int):
|
||||
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty @ --name", help="title / service-name to show in html documents")
|
||||
ap2.add_argument("--bname", metavar="TXT", type=u, default="--name", help="server name (displayed in filebrowser document title)")
|
||||
ap2.add_argument("--pb-url", metavar="URL", type=u, default=URL_PRJ, help="powered-by link; disable with \033[33m-nb\033[0m")
|
||||
ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m). This is the same as --ver-who all")
|
||||
ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m). This is the same as \033[33m--ver-who all\033[0m")
|
||||
ap2.add_argument("--ver-who", metavar="TXT", type=u, default="no", help="only show version for: [\033[32ma\033[0m]=admin-permission-anywhere, [\033[32mauth\033[0m]=authenticated, [\033[32mall\033[0m]=anyone")
|
||||
ap2.add_argument("--du-who", metavar="TXT", type=u, default="all", help="only show disk usage for: [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admin-permission, [\033[32mrw\033[0m]=read-write, [\033[32mw\033[0m]=write, [\033[32mauth\033[0m]=authenticated, [\033[32mall\033[0m]=anyone (volflag=du_who)")
|
||||
ap2.add_argument("--ver-iwho", type=int, default=0, help=argparse.SUPPRESS)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 20, 22)
|
||||
VERSION = (1, 20, 24)
|
||||
CODENAME = "sftp is fine too"
|
||||
BUILD_DT = (2026, 9, 6)
|
||||
BUILD_DT = (2026, 9, 19)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
||||
+17
-2
@@ -107,6 +107,7 @@ SEESLOG = " (see fileserver log for details)"
|
||||
SSEELOG = " ({})".format(SEE_LOG)
|
||||
BAD_CFG = "invalid config; {}".format(SEE_LOG)
|
||||
SBADCFG = " ({})".format(BAD_CFG)
|
||||
REDUP_E2 = "WARNING: [/%s] needs 'e2ds' for redup from 'ref' and/or to 'sym'"
|
||||
|
||||
PTN_U_GRP = re.compile(r"\$\{u(%[+-][^}]+)\}")
|
||||
PTN_G_GRP = re.compile(r"\$\{g(%[+-][^}]+)\}")
|
||||
@@ -2915,6 +2916,17 @@ class AuthSrv(object):
|
||||
if ccs == "y":
|
||||
vol.flags["bcasechk"] = True
|
||||
|
||||
ptn = re.compile(r"^(,no|,sym|,hard|,ref)+=(sym|hard|ref)$")
|
||||
ptn2 = re.compile(r"(no|ref).*=|=sym")
|
||||
for vol in vfs.all_nodes.values():
|
||||
zs = vol.flags.get("redup")
|
||||
if zs and not ptn.match("," + zs):
|
||||
t = "invalid redup for volume /%s: %r"
|
||||
self.log(t % (vol.vpath, zs), 1)
|
||||
errors = True
|
||||
if zs and "e2ds" not in vol.flags and ptn2.search(zs):
|
||||
self.log(REDUP_E2 % (vol.vpath,), 3)
|
||||
|
||||
tags = self.args.mtp or []
|
||||
tags = [x.split("=")[0] for x in tags]
|
||||
tags = [y for x in tags for y in x.split(",")]
|
||||
@@ -2957,6 +2969,9 @@ class AuthSrv(object):
|
||||
errors = True
|
||||
|
||||
for vol in vfs.all_nodes.values():
|
||||
for zs in vf_bmap().values():
|
||||
if vol.flags.get(zs) in (None, False, ""):
|
||||
vol.flags.pop(zs, None)
|
||||
for k in list(vol.flags.keys()):
|
||||
if re.match("^-[^-]+$", k):
|
||||
vol.flags.pop(k)
|
||||
@@ -3294,7 +3309,7 @@ class AuthSrv(object):
|
||||
"dth3x": vf["th3x"],
|
||||
"u2ts": vf["u2ts"],
|
||||
"shr_who": vf["shr_who"],
|
||||
"frand": bool(vf.get("rand")),
|
||||
"frand": "rand" in vf,
|
||||
"lifetime": vf.get("lifetime") or 0,
|
||||
"unlist": vf.get("unlist") or "",
|
||||
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
|
||||
@@ -3352,7 +3367,7 @@ class AuthSrv(object):
|
||||
"u2sz": self.args.u2sz,
|
||||
"u2ts": vf["u2ts"],
|
||||
"u2ow": vf["u2ow"],
|
||||
"frand": bool(vf.get("rand")),
|
||||
"frand": "rand" in vf,
|
||||
"lifetime": vn.js_ls["lifetime"],
|
||||
"u2sort": self.args.u2sort,
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ def vf_bmap() -> dict[str, str]:
|
||||
"og_s_title",
|
||||
"opds",
|
||||
"rand",
|
||||
"redup_dry",
|
||||
"reflink",
|
||||
"rm_partial",
|
||||
"rmagic",
|
||||
@@ -146,6 +147,7 @@ def vf_vmap() -> dict[str, str]:
|
||||
"put_ck",
|
||||
"put_name",
|
||||
"readmes",
|
||||
"redup",
|
||||
"rotf_tz",
|
||||
"mv_retry",
|
||||
"rm_retry",
|
||||
@@ -229,6 +231,8 @@ flagcats = {
|
||||
"hardlinkonly": "dedup with hardlink only, never symlink;\nmake a full copy if hardlink is impossible",
|
||||
"reflink": "enable reflink-based file deduplication,\nwith fallback on full copy when that is impossible",
|
||||
"safededup": "verify on-disk data before using it for dedup",
|
||||
"redup=A,B=T": "convert dedup-type for existing files",
|
||||
"redup_dry": "dry-run mode for redup",
|
||||
"noclone": "take dupe data from clients, even if available on HDD",
|
||||
"nodupe": "rejects existing files (instead of linking/cloning them)",
|
||||
"nodupem": "rejects existing files during moves as well",
|
||||
|
||||
@@ -3251,6 +3251,8 @@ class HttpCli(object):
|
||||
dst = vfs.canonical(rem)
|
||||
try:
|
||||
if not bos.path.isdir(dst):
|
||||
if "nosub" in vfs.flags:
|
||||
raise Pebkac(500, "no subdirectories allowed")
|
||||
bos.makedirs(dst, vf=vfs.flags)
|
||||
except OSError as ex:
|
||||
self.log("makedirs failed %r" % (dst,))
|
||||
@@ -3262,6 +3264,8 @@ class HttpCli(object):
|
||||
raise Pebkac(400, "some file got your folder name")
|
||||
|
||||
raise Pebkac(500, min_ex())
|
||||
except Pebkac:
|
||||
raise
|
||||
except:
|
||||
raise Pebkac(500, min_ex())
|
||||
|
||||
@@ -3888,7 +3892,7 @@ class HttpCli(object):
|
||||
rnd = 0
|
||||
else:
|
||||
rnd = int(self.uparam.get("rand") or self.headers.get("rand") or 0)
|
||||
if vfs.flags.get("rand"): # force-enable
|
||||
if "rand" in vfs.flags: # force-enable
|
||||
rnd = max(rnd, vfs.flags["nrand"])
|
||||
|
||||
zs = self.uparam.get("life", self.headers.get("life", ""))
|
||||
@@ -7079,17 +7083,16 @@ class HttpCli(object):
|
||||
e2d = "e2d" in vn.flags
|
||||
e2t = "e2t" in vn.flags
|
||||
|
||||
og_fn = ""
|
||||
add_og = "og" in vn.flags
|
||||
if add_og:
|
||||
if "th" in self.uparam or "raw" in self.uparam or "opds" in self.uparam:
|
||||
add_og = False
|
||||
elif vn.flags["og_ua"]:
|
||||
add_og = vn.flags["og_ua"].search(self.ua)
|
||||
og_fn = ""
|
||||
|
||||
if "v" in self.uparam:
|
||||
add_og = True
|
||||
og_fn = ""
|
||||
|
||||
if "b" in self.uparam and "norobots" not in vn.flags:
|
||||
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
||||
@@ -7610,6 +7613,7 @@ class HttpCli(object):
|
||||
return self.tx_file("oh_f", ap) # is no-cache
|
||||
|
||||
if icur:
|
||||
assert idx # type: ignore # !rm
|
||||
mte = vn.flags.get("mte") or {}
|
||||
tagset: set[str] = set()
|
||||
rd = vrem
|
||||
@@ -7978,7 +7982,7 @@ class HttpCli(object):
|
||||
else:
|
||||
j2a["og_url"] = j2a["og_raw"] = url_base
|
||||
|
||||
if not vn.flags.get("og_no_head"):
|
||||
if "og_no_head" not in vn.flags:
|
||||
ogh = {"twitter:card": "summary"}
|
||||
|
||||
title = str(vn.flags.get("og_title") or "")
|
||||
@@ -8047,7 +8051,7 @@ class HttpCli(object):
|
||||
while title.endswith(" - "):
|
||||
title = title[:3]
|
||||
|
||||
if vn.flags.get("og_s_title") or not title:
|
||||
if "og_s_title" in vn.flags or not title:
|
||||
title = str(vn.flags.get("og_title") or "")
|
||||
|
||||
for tag, hname in tagmap.items():
|
||||
|
||||
@@ -87,6 +87,9 @@ class TcpSrv(object):
|
||||
successful_binds = 0
|
||||
try:
|
||||
for ipa in ips:
|
||||
if ipa == "no":
|
||||
self.args.ign_ebind_all = True
|
||||
continue
|
||||
try:
|
||||
self._listen(ipa, port)
|
||||
ok[ipa].append(port)
|
||||
|
||||
+2
-2
@@ -201,11 +201,11 @@ try:
|
||||
# reduces glibc RAM usage from 4.7 to 3.5 GiB ...yep, still bonkers
|
||||
os.environ["VIPS_CONCURRENCY"] = "1"
|
||||
|
||||
HAVE_VIPS = True
|
||||
import pyvips
|
||||
|
||||
pyvips.cache_set_max(0)
|
||||
logging.getLogger("pyvips").setLevel(logging.WARNING)
|
||||
HAVE_VIPS = True
|
||||
except Exception as e:
|
||||
HAVE_VIPS = False
|
||||
if not isinstance(e, ImportError):
|
||||
@@ -219,10 +219,10 @@ try:
|
||||
if PRTY_NO_RAWPY:
|
||||
raise Exception()
|
||||
|
||||
HAVE_RAWPY = True
|
||||
import rawpy
|
||||
|
||||
logging.getLogger("rawpy").setLevel(logging.WARNING)
|
||||
HAVE_RAWPY = True
|
||||
except:
|
||||
HAVE_RAWPY = False
|
||||
|
||||
|
||||
+191
-41
@@ -17,7 +17,7 @@ from copy import deepcopy
|
||||
from queue import Queue
|
||||
|
||||
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, UNIX, WINDOWS, E
|
||||
from .authsrv import LEELOO_DALLAS, SEESLOG, VFS, AuthSrv
|
||||
from .authsrv import LEELOO_DALLAS, REDUP_E2, SEESLOG, VFS, AuthSrv
|
||||
from .bos import bos
|
||||
from .cfg import vf_bmap, vf_cmap, vf_vmap
|
||||
from .fsutil import Fstab
|
||||
@@ -887,13 +887,10 @@ class Up2k(object):
|
||||
|
||||
def _expr_idx_filter(self, flags: dict[str, Any]) -> tuple[bool, dict[str, Any]]:
|
||||
if not self.no_expr_idx:
|
||||
return False, flags
|
||||
return False, dict(flags)
|
||||
|
||||
ret = {k: v for k, v in flags.items() if not k.startswith("e2t")}
|
||||
if len(ret) == len(flags):
|
||||
return False, flags
|
||||
|
||||
return True, ret
|
||||
return len(ret) != len(flags), flags
|
||||
|
||||
def init_indexes(
|
||||
self, all_vols: dict[str, VFS], scan_vols: list[str], fscan: bool, gid: int = 0
|
||||
@@ -912,7 +909,7 @@ class Up2k(object):
|
||||
|
||||
with self.mutex:
|
||||
if gid != self.gid:
|
||||
return False
|
||||
return
|
||||
|
||||
if self.pp:
|
||||
continue
|
||||
@@ -951,6 +948,9 @@ class Up2k(object):
|
||||
with self.mutex, self.reg_mutex:
|
||||
# only need to protect register_vpath but all in one go feels right
|
||||
for vol in vols:
|
||||
if vol.realpath:
|
||||
self.flags[vol.realpath] = dict(vol.flags)
|
||||
|
||||
if bos.path.isfile(vol.realpath):
|
||||
self.volstate[vol.vpath] = "online (just-a-file)"
|
||||
t = "NOTE: volume [/%s] is a file, not a folder"
|
||||
@@ -1000,6 +1000,21 @@ class Up2k(object):
|
||||
with self.mutex, self.reg_mutex:
|
||||
self._drop_caches()
|
||||
|
||||
def setstate(vol, n):
|
||||
t = ""
|
||||
if n < 1 and "e2v" in vol.flags:
|
||||
t += "+e2v"
|
||||
if n < 2 and "redup" in vol.flags:
|
||||
t += "+redup"
|
||||
if n < 3 and "e2ts" in vol.flags:
|
||||
t += "+tags"
|
||||
|
||||
if t:
|
||||
t = "online (%s pending)" % (t[1:],)
|
||||
else:
|
||||
t = "online, idle"
|
||||
self.volstate[vol.vpath] = t
|
||||
|
||||
for vol in vols:
|
||||
if self.stop or gid != self.gid:
|
||||
break
|
||||
@@ -1016,26 +1031,16 @@ class Up2k(object):
|
||||
if vac:
|
||||
need_vac[vol] = True
|
||||
|
||||
if "e2v" in vol.flags:
|
||||
t = "online (integrity-check pending)"
|
||||
elif "e2ts" in vol.flags:
|
||||
t = "online (tags pending)"
|
||||
else:
|
||||
t = "online, idle"
|
||||
|
||||
self.volstate[vol.vpath] = t
|
||||
setstate(vol, 0)
|
||||
|
||||
self._unblock()
|
||||
|
||||
# file contents verification
|
||||
for vol in vols:
|
||||
if self.stop:
|
||||
break
|
||||
|
||||
if "e2v" not in vol.flags:
|
||||
if self.stop or "e2v" not in vol.flags:
|
||||
continue
|
||||
|
||||
t = "online (verifying integrity)"
|
||||
t = "online (doing e2v)"
|
||||
self.volstate[vol.vpath] = t
|
||||
self.log("{} [{}]".format(t, vol.realpath))
|
||||
|
||||
@@ -1044,12 +1049,23 @@ class Up2k(object):
|
||||
self.log("modified {} entries in the db".format(nmod), 3)
|
||||
need_vac[vol] = True
|
||||
|
||||
if "e2ts" in vol.flags:
|
||||
t = "online (tags pending)"
|
||||
else:
|
||||
t = "online, idle"
|
||||
setstate(vol, 1)
|
||||
|
||||
# convert dedup type
|
||||
for vol in vols:
|
||||
if self.stop or not vol.flags.get("redup"):
|
||||
continue
|
||||
|
||||
t = "online (doing redup)"
|
||||
self.volstate[vol.vpath] = t
|
||||
self.log("{} [{}]".format(t, vol.realpath))
|
||||
|
||||
try:
|
||||
with self.mutex, self.reg_mutex:
|
||||
self._redup(vol, vols)
|
||||
except:
|
||||
self.log("redup failed: " + min_ex(), 1)
|
||||
setstate(vol, 2)
|
||||
|
||||
# open the rest + do any e2ts(a)
|
||||
needed_mutagen = False
|
||||
@@ -1084,7 +1100,7 @@ class Up2k(object):
|
||||
cur.execute("vacuum")
|
||||
|
||||
if self.stop:
|
||||
return False
|
||||
return
|
||||
|
||||
for vol in all_vols.values():
|
||||
if vol.flags["dbd"] == "acid":
|
||||
@@ -1114,7 +1130,7 @@ class Up2k(object):
|
||||
self.log("checkpoint failed: {}".format(ex), 3)
|
||||
|
||||
if self.stop:
|
||||
return False
|
||||
return
|
||||
|
||||
self.pp.end = True
|
||||
|
||||
@@ -1158,7 +1174,7 @@ class Up2k(object):
|
||||
vpath = k
|
||||
|
||||
_, flags = self._expr_idx_filter(flags)
|
||||
n4g = bool(flags.get("noforget"))
|
||||
n4g = "noforget" in flags
|
||||
|
||||
ft = "\033[0;32m{}{:.0}"
|
||||
ff = "\033[0;35m{}{:.0}"
|
||||
@@ -1297,11 +1313,14 @@ class Up2k(object):
|
||||
self.log(t % (vpath, ex), 1)
|
||||
return None
|
||||
|
||||
if dir_is_empty(self.log_func, not self.args.no_scandir, histpath) and not (
|
||||
ANYWIN or UNIX or "hist_cow" in flags
|
||||
if not (
|
||||
ANYWIN or UNIX or "hist_cow" in flags or "up2k.db" in os.listdir(histpath)
|
||||
):
|
||||
try:
|
||||
runcmd([b"chattr", b"+C", fsenc(histpath)], 1)
|
||||
if self.fstab.get(ptop)[0] != "btrfs":
|
||||
raise Exception()
|
||||
runcmd([b"chattr", b"-c", fsenc(histpath)], 1) # compression and...
|
||||
runcmd([b"chattr", b"+C", fsenc(histpath)], 1) # no-cow are incompat
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -1388,14 +1407,15 @@ class Up2k(object):
|
||||
top = vol.realpath
|
||||
rei = vol.flags.get("noidx")
|
||||
reh = vol.flags.get("nohash")
|
||||
n4g = bool(vol.flags.get("noforget"))
|
||||
n4g = "noforget" in vol.flags
|
||||
ffat = "fat32" in vol.flags
|
||||
cst = bos.stat(top)
|
||||
dev = cst.st_dev if vol.flags.get("xdev") else 0
|
||||
dev = cst.st_dev if "xdev" in vol.flags else 0
|
||||
|
||||
with self.mutex:
|
||||
with self.reg_mutex:
|
||||
reg = self.register_vpath(top, vol.flags)
|
||||
self.flags[vol.realpath] = dict(vol.flags)
|
||||
|
||||
assert reg and self.pp # !rm
|
||||
cur, db_path = reg
|
||||
@@ -1450,7 +1470,7 @@ class Up2k(object):
|
||||
[],
|
||||
cst,
|
||||
dev,
|
||||
bool(vol.flags.get("xvol")),
|
||||
"xvol" in vol.flags,
|
||||
)
|
||||
if not n4g:
|
||||
n_rm = self._drop_lost(db.c, top, excl)
|
||||
@@ -2088,6 +2108,127 @@ class Up2k(object):
|
||||
|
||||
return len(rewark) + len(f404)
|
||||
|
||||
def _redup(self, vol: VFS, vols: list[VFS]) -> None:
|
||||
logmsg = "redup: replacing %r with %r"
|
||||
dry = "redup_dry" in vol.flags
|
||||
if dry:
|
||||
logmsg += " #dry"
|
||||
|
||||
zs, to = vol.flags["redup"].split("=")
|
||||
zsl = zs.split(",")
|
||||
cref = "no" in zsl or "ref" in zsl
|
||||
csym = "sym" in zsl
|
||||
chard = "hard" in zsl
|
||||
chard0 = chard
|
||||
|
||||
vf = vol.flags.copy()
|
||||
vf["dedup"] = 1
|
||||
for zs in "hardlink hardlinkonly reflink".split():
|
||||
vf.pop(zs, None)
|
||||
|
||||
if to == "ref":
|
||||
vf["reflink"] = 1
|
||||
elif to == "hard":
|
||||
vf["hardlinkonly"] = vf["hardlink"] = 1
|
||||
elif to == "sym":
|
||||
pass
|
||||
else:
|
||||
raise Exception("target type not implemented: " + to)
|
||||
|
||||
if (csym or chard) and to != "sym":
|
||||
self.log("redup: stage 1 begin")
|
||||
if to == "hard":
|
||||
chard = False
|
||||
for x in vol.walk("", "", [], LEELOO_DALLAS, [[]], 2, True, True, True):
|
||||
vn, _, _, atop, lsf, lsd, lsv = x
|
||||
if vn is not vol:
|
||||
lsd[:] = []
|
||||
lsv.clear()
|
||||
continue
|
||||
for fn, st in lsf:
|
||||
if (csym and stat.S_ISLNK(st.st_mode)) or (
|
||||
chard and st.st_nlink > 1
|
||||
):
|
||||
ap = os.path.join(atop, fn)
|
||||
self.log(logmsg % (ap, absreal(ap)))
|
||||
if dry:
|
||||
continue
|
||||
ap2 = tempfile.NamedTemporaryFile(
|
||||
prefix="r,", dir=atop, delete=False
|
||||
).name
|
||||
self._symlink(ap, ap2, vf, True, True, st.st_mtime)
|
||||
st1 = bos.stat(ap)
|
||||
st2 = bos.lstat(ap2)
|
||||
if st1.st_size != st2.st_size or stat.S_ISLNK(st2.st_mode):
|
||||
wunlink(self.log, ap2, vf)
|
||||
raise Exception("redup: clone failed; giving up")
|
||||
wunlink(self.log, ap, vf)
|
||||
bos.rename(ap2, ap)
|
||||
|
||||
if cref or to == "sym":
|
||||
self.log("redup: stage 2 begin")
|
||||
if "e2ds" not in vol.flags:
|
||||
self.log(REDUP_E2 % (vol.vpath,), 3)
|
||||
cur = self.cur[vol.realpath]
|
||||
curs = [(vol, cur.connection.cursor())]
|
||||
if to != "sym":
|
||||
for v2 in vols:
|
||||
if vol is not v2 and v2.realpath in self.cur:
|
||||
curs += [(v2, self.cur[v2.realpath])]
|
||||
nrem = cur.execute("select count(w) from up").fetchone()[0]
|
||||
self.log("redup [/%s]: %d files left" % (vol.vpath, nrem))
|
||||
q2 = "select rd, fn from up where substr(w,1,16) = ? and w=?"
|
||||
for w, rd, fn in cur.execute("select w, rd, fn from up"):
|
||||
w16 = w[:16]
|
||||
for v2, c2 in curs:
|
||||
hit = c2.execute(q2, (w16, w)).fetchone()
|
||||
if not hit:
|
||||
continue
|
||||
rd2, fn2 = hit
|
||||
if fn == fn2 and rd == rd2 and vol is v2:
|
||||
continue
|
||||
apt = ""
|
||||
try:
|
||||
rd, fn = s3dec(rd, fn)
|
||||
rd2, fn2 = s3dec(rd2, fn2)
|
||||
fp1 = os.path.join(vol.realpath, rd, fn)
|
||||
fp2 = os.path.join(v2.realpath, rd2, fn2)
|
||||
st1 = bos.lstat(fp1)
|
||||
if fp1 == fp2 or stat.S_ISLNK(st1.st_mode):
|
||||
continue # self(?), or redup failed during walk
|
||||
ap1 = absreal(fp1)
|
||||
ap2 = absreal(fp2)
|
||||
st2 = bos.lstat(ap2)
|
||||
if stat.S_ISLNK(st2.st_mode):
|
||||
continue # dead hit
|
||||
for ap in (ap1,) if ap1 == ap2 else (ap1, ap2):
|
||||
self.log("redup: integrity-checking %r" % (ap,))
|
||||
zsl, st = self._hashlist_from_file(ap)
|
||||
w2 = up2k_wark_from_hashlist(self.salt, st.st_size, zsl)
|
||||
if w2 != w:
|
||||
t = "db desync:\n%s %r\n%s db"
|
||||
raise Exception(t % (w2, ap, w))
|
||||
|
||||
self.log(logmsg % (ap1, ap2))
|
||||
if dry:
|
||||
continue
|
||||
apt = tempfile.NamedTemporaryFile(
|
||||
prefix="r,", dir=os.path.dirname(fp1), delete=False
|
||||
).name
|
||||
self._symlink(ap2, apt, vf, True, True, st1.st_mtime)
|
||||
zsl, st = self._hashlist_from_file(apt)
|
||||
w2 = up2k_wark_from_hashlist(self.salt, st.st_size, zsl)
|
||||
if w != w2:
|
||||
raise Exception("bad checksum after copy?!")
|
||||
wunlink(self.log, ap1, vf)
|
||||
bos.rename(apt, ap1)
|
||||
break
|
||||
except Exception as ex:
|
||||
self.log("redup: skipping match due to " + str(ex))
|
||||
if apt and os.path.exists(apt):
|
||||
os.unlink(apt)
|
||||
continue
|
||||
|
||||
def _build_tags_index(self, vol: VFS) -> tuple[int, int, bool]:
|
||||
ptop = vol.realpath
|
||||
with self.mutex, self.reg_mutex:
|
||||
@@ -3075,17 +3216,20 @@ class Up2k(object):
|
||||
jcur = self.cur.get(ptop)
|
||||
reg = self.registry[ptop]
|
||||
vfs = self.vfs.all_vols[cj["vtop"]]
|
||||
n4g = bool(vfs.flags.get("noforget"))
|
||||
noclone = bool(vfs.flags.get("noclone"))
|
||||
rand = vfs.flags.get("rand") or cj.get("rand")
|
||||
n4g = "noforget" in vfs.flags
|
||||
noclone = "noclone" in vfs.flags
|
||||
rand = "rand" in vfs.flags or cj.get("rand")
|
||||
lost: list[tuple["sqlite3.Cursor", str, str]] = []
|
||||
|
||||
safe_dedup = vfs.flags.get("safededup") or 50
|
||||
data_ok = safe_dedup < 10 or n4g
|
||||
|
||||
vols = [(ptop, jcur)] if jcur else []
|
||||
if vfs.flags.get("xlink"):
|
||||
if vols and ("xlink" in vfs.flags or "reflink" in vfs.flags):
|
||||
vols += [(k, v) for k, v in self.cur.items() if k != ptop]
|
||||
if "xlink" not in vfs.flags:
|
||||
zs = self.fstab.get(ptop)[1]
|
||||
vols = [x for x in vols if self.fstab.get(x[0])[1] == zs]
|
||||
|
||||
if noclone:
|
||||
wark = up2k_wark_from_metadata(
|
||||
@@ -3575,7 +3719,7 @@ class Up2k(object):
|
||||
st = bos.stat(fp)
|
||||
try:
|
||||
vrel = vjoin(job["prel"], fname)
|
||||
xlink = bool(vf.get("xlink"))
|
||||
xlink = "xlink" in vf
|
||||
cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, vrel)
|
||||
self._forget_file(ptop, vrel, vf, cur, wark, True, st.st_size, xlink)
|
||||
except Exception as ex:
|
||||
@@ -3630,7 +3774,7 @@ class Up2k(object):
|
||||
if rm and bos.path.exists(dst):
|
||||
wunlink(self.log, dst, flags)
|
||||
|
||||
if not is_mv and not flags.get("dedup"):
|
||||
if not is_mv and "dedup" not in flags:
|
||||
raise Exception("dedup is disabled in config")
|
||||
|
||||
if "reflink" in flags:
|
||||
@@ -4331,7 +4475,7 @@ class Up2k(object):
|
||||
cur = None
|
||||
try:
|
||||
ptop = dbv.realpath
|
||||
xlink = bool(dbv.flags.get("xlink"))
|
||||
xlink = "xlink" in dbv.flags
|
||||
cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, volpath)
|
||||
self._forget_file(
|
||||
ptop, volpath, dbv.flags, cur, wark, True, st.st_size, xlink
|
||||
@@ -4535,6 +4679,12 @@ class Up2k(object):
|
||||
self.log("not found in src db: %r" % (svp,))
|
||||
|
||||
try:
|
||||
if svn_dbv != dvn and not (
|
||||
"xlink" in dvn.flags
|
||||
or "reflink" in dvn.flags
|
||||
or "hardlinkonly" in dvn.flags
|
||||
):
|
||||
raise OSError(errno.EXDEV, "different volumes")
|
||||
if is_link and st != stl:
|
||||
# relink non-broken symlinks to still work after the move,
|
||||
# but only resolve 1st level to maintain relativity
|
||||
@@ -4812,7 +4962,7 @@ class Up2k(object):
|
||||
raise Pebkac(400, t)
|
||||
self._copy_tags(c1, c2, w)
|
||||
|
||||
xlink = bool(svn.flags.get("xlink"))
|
||||
xlink = "xlink" in svn.flags
|
||||
|
||||
with self.reg_mutex:
|
||||
has_dupes = self._forget_file(
|
||||
|
||||
+7
-4
@@ -188,6 +188,7 @@ try:
|
||||
HAVE_IFADDR = True
|
||||
except:
|
||||
HAVE_IFADDR = False
|
||||
IFADDR_VND = False
|
||||
|
||||
def get_adapters(include_unconfigured=False):
|
||||
return []
|
||||
@@ -288,7 +289,6 @@ try:
|
||||
raise Exception()
|
||||
|
||||
socket.inet_pton(socket.AF_INET6, "::1")
|
||||
HAVE_IPV6 = True
|
||||
|
||||
if GRAAL:
|
||||
try:
|
||||
@@ -303,13 +303,15 @@ try:
|
||||
return _inet_pton(fam, ip)
|
||||
|
||||
socket.inet_pton = inet_pton
|
||||
|
||||
HAVE_IPV6 = True
|
||||
except:
|
||||
HAVE_IPV6 = False
|
||||
|
||||
def inet_pton(fam, ip):
|
||||
return socket.inet_aton(ip)
|
||||
|
||||
socket.inet_pton = inet_pton
|
||||
HAVE_IPV6 = False
|
||||
|
||||
|
||||
try:
|
||||
@@ -450,6 +452,7 @@ IMPLICATIONS = [
|
||||
["no_logue", "no_logues"], # user-typo
|
||||
["nologues", "no_logues"], # user-typo
|
||||
["nologue", "no_logues"], # user-typo
|
||||
["nw", "redup_dry"],
|
||||
["plainlogue", "plainlogues"], # user-typo
|
||||
["plainreadmes", "plainreadme"], # user-typo
|
||||
["noscript", "plainlogues"],
|
||||
@@ -4049,9 +4052,9 @@ def runihook(
|
||||
}
|
||||
for x, vp, ap in zip(ups, vps, aps)
|
||||
]
|
||||
sp_ka["sin"] = json.dumps(ja).encode("utf-8", "replace")
|
||||
sp_ka["sin"] = json.dumps(ja).encode("utf-8", "replace") + b"\n"
|
||||
else:
|
||||
sp_ka["sin"] = b"\n".join(fsenc(x) for x in aps)
|
||||
sp_ka["sin"] = b"\n".join(fsenc(x) for x in aps) + b"\n"
|
||||
|
||||
if acmd[0].startswith("zmq:"):
|
||||
try:
|
||||
|
||||
+31
-13
@@ -3559,7 +3559,7 @@ function eval_hash() {
|
||||
if (hash0 && window.og_fn) {
|
||||
var all = msel.getall(), mi;
|
||||
for (var a = 0; a < all.length; a++)
|
||||
if (og_fn == uricom_dec(vsplit(all[a].vp)[1].split('?')[0])) {
|
||||
if (og_fn == all[a].fn) {
|
||||
mi = all[a];
|
||||
break;
|
||||
}
|
||||
@@ -4254,9 +4254,6 @@ var fileman = (function () {
|
||||
s2d[a] = all.indexOf(sel[a]);
|
||||
|
||||
var vp = sel[a].vp;
|
||||
if (vp.endsWith('/'))
|
||||
vp = vp.slice(0, -1);
|
||||
|
||||
var vsp = vsplit(vp);
|
||||
if (base != vsp[0])
|
||||
return toast.err(0, esc('bug:\n' + base + '\n' + vsp[0]));
|
||||
@@ -4601,6 +4598,9 @@ var fileman = (function () {
|
||||
if (err !== 'xbd')
|
||||
toast.ok(2, L.fd_ok);
|
||||
|
||||
if (QS('#seldoc.sel'))
|
||||
thegrid.setvis(true);
|
||||
|
||||
treectl.goto();
|
||||
return;
|
||||
}
|
||||
@@ -5077,11 +5077,22 @@ var showfile = (function () {
|
||||
r.nmap = {
|
||||
'dockerfile': 'docker'
|
||||
};
|
||||
r.pmap = {
|
||||
'sh': 'bash',
|
||||
'ash': 'bash',
|
||||
'dash': 'bash',
|
||||
'luajit': 'lua',
|
||||
'node': 'js',
|
||||
};
|
||||
var x = txt_ext + ' ans c cfg conf cpp cs css diff glsl go html ini java js json jsx kt kts latex less lisp lua makefile md nasm nim nix py r rss rb ruby sass scss sql svg swift tex toml ts vhdl xml yaml zig';
|
||||
x = x.split(/ +/g);
|
||||
for (var a = 0; a < x.length; a++)
|
||||
if (!r.map["." + x[a]])
|
||||
r.map["." + x[a]] = x[a];
|
||||
x = 'awk bash lua make perl python ruby swift';
|
||||
x = x.split(/ +/g);
|
||||
for (var a = 0; a < x.length; a++)
|
||||
r.pmap[x[a]] = x[a];
|
||||
|
||||
r.sname = function (srch) {
|
||||
return srch.split(/[?&]doc=/)[1].split('&')[0];
|
||||
@@ -5090,7 +5101,7 @@ var showfile = (function () {
|
||||
if (window.og_fn) {
|
||||
var ext = og_fn.split(/\./g).pop();
|
||||
if (r.map['.' + ext])
|
||||
hist_replace(get_evpath() + '?doc=' + og_fn);
|
||||
hist_replace(get_evpath() + '?doc=' + uricom_enc(og_fn));
|
||||
}
|
||||
|
||||
window.Prism = { 'manual': true };
|
||||
@@ -5101,7 +5112,7 @@ var showfile = (function () {
|
||||
var m = /[?&]doc=([^&]+)/.exec(location.search);
|
||||
if (m) {
|
||||
setTimeout(function () {
|
||||
r.show(uricom_dec(m[1]), true);
|
||||
r.show(m[1], true);
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
@@ -5121,11 +5132,17 @@ var showfile = (function () {
|
||||
return !!/[?&]doc=/.exec(location.search);
|
||||
};
|
||||
|
||||
r.getlang = function (fn) {
|
||||
r.getlang = function (fn, txt) {
|
||||
fn = fn.toLowerCase();
|
||||
var ext = fn.slice(fn.lastIndexOf('.'));
|
||||
return r.map[ext] || r.nmap[fn];
|
||||
}
|
||||
var prog = txt && txt.startsWith('#!') && txt.split('\n')[0].replace(
|
||||
'#!/usr/bin/env', '').replace(
|
||||
'#!/usr/bin/', '').replace(
|
||||
'#!/bin/', '').trim().split(' ')[0];
|
||||
if (prog)
|
||||
prog = r.pmap[prog] || r.pmap[prog.replace(/[0-9].*/, '')];
|
||||
return r.map[ext] || r.nmap[fn] || prog;
|
||||
};
|
||||
|
||||
r.addlinks = function () {
|
||||
r.files = [];
|
||||
@@ -5138,7 +5155,7 @@ var showfile = (function () {
|
||||
if (!lang)
|
||||
continue;
|
||||
|
||||
r.files.push({ 'id': link.id, 'name': uricom_dec(fn) });
|
||||
r.files.push({ 'id': link.id, 'name': link.fn });
|
||||
|
||||
var ah = ebi(link.id),
|
||||
td = ah.closest('tr').getElementsByTagName('td')[0];
|
||||
@@ -5274,7 +5291,7 @@ var showfile = (function () {
|
||||
txt = doc[2],
|
||||
name = url.split('?')[0].split('/').pop(),
|
||||
tname = uricom_dec(name),
|
||||
lang = r.getlang(name),
|
||||
lang = r.getlang(name, txt),
|
||||
is_md = lang == 'md';
|
||||
|
||||
ebi('files').style.display = ebi('gfiles').style.display = ebi('lazy').style.display = ebi('pro').style.display = ebi('epi').style.display = 'none';
|
||||
@@ -5463,7 +5480,7 @@ var showfile = (function () {
|
||||
sel = false;
|
||||
|
||||
for (var b = 0; b < sels.length; b++)
|
||||
if (vsplit(sels[b].vp)[1] == lin)
|
||||
if (sels[b].fn == lin)
|
||||
sel = true;
|
||||
|
||||
clmod(lis[a], 'hl', lin == fn);
|
||||
@@ -8768,6 +8785,7 @@ var msel = (function () {
|
||||
item.id = links[a].getAttribute('id');
|
||||
item.sel = clgot(links[a].closest('tr'), 'sel');
|
||||
item.vp = href.indexOf('/') !== -1 ? href : vbase + href;
|
||||
item.fn = uricom_dec(href.split('/').pop());
|
||||
|
||||
if (dk) {
|
||||
var m = /[?&](k=[^&#]+)/.exec(qhref);
|
||||
@@ -9912,7 +9930,7 @@ var rcm = (function () {
|
||||
clmod(ebi('rtxt'), 'hide', !selFile.id);
|
||||
clmod(ebi('rs1'), 'hide', !selFile.path);
|
||||
clmod(ebi('rmd'), 'hide', !selFile.name || ext != "md");
|
||||
clmod(ebi('rwopi'), 'hide', !selFile.name || !wopi_set.has(ext));
|
||||
clmod(ebi('rwopi'), 'hide', !selFile.name || !wopi_set || !wopi_set.has(ext));
|
||||
clmod(ebi('rcpl'), 'hide', !selFile.path);
|
||||
clmod(ebi('rdl'), 'hide', !has_sel);
|
||||
clmod(ebi('rzip'), 'hide', !has_sel);
|
||||
|
||||
+14
-9
@@ -1012,6 +1012,11 @@ function up2k_init(subtle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function unxhr(xhr) {
|
||||
xhr.onload = xhr.onerror = xhr.ontimeout = null;
|
||||
apop(st.xhr, xhr);
|
||||
}
|
||||
|
||||
var pvis = new U2pvis("bz", '#u2cards', uc, st),
|
||||
donut = new Donut(uc, st);
|
||||
|
||||
@@ -1934,10 +1939,8 @@ function up2k_init(subtle) {
|
||||
if (!st.deaf)
|
||||
console.log('xhr: bad apple');
|
||||
|
||||
xhr.onerror = xhr.ontimeout = null;
|
||||
xhr.onload();
|
||||
xhr.abort();
|
||||
st.deaf = true;
|
||||
xhr.onload();
|
||||
}
|
||||
|
||||
if (st.bytes.inflight && (st.bytes.inflight < 0 || !st.busy.upload.length)) {
|
||||
@@ -2498,12 +2501,12 @@ function up2k_init(subtle) {
|
||||
if (!toast.visible)
|
||||
toast.warn(9.98, L.u_enethd + "\n\nfile: " + esc(t.name), t);
|
||||
|
||||
apop(st.xhr, xhr);
|
||||
unxhr(xhr);
|
||||
apop(st.busy.head, t);
|
||||
st.todo.head.unshift(t);
|
||||
};
|
||||
function orz(e) {
|
||||
apop(st.xhr, xhr);
|
||||
unxhr(xhr);
|
||||
if (t.done)
|
||||
return console.log('done; skip head2', t.name, t);
|
||||
|
||||
@@ -2571,7 +2574,7 @@ function up2k_init(subtle) {
|
||||
|
||||
var xhr = new XHR();
|
||||
xhr.onerror = xhr.ontimeout = function () {
|
||||
apop(st.xhr, xhr);
|
||||
unxhr(xhr);
|
||||
if (t.t_busied != me) // t.done ok
|
||||
return console.log('zombie handshake onerror', t.name, t);
|
||||
|
||||
@@ -2584,7 +2587,7 @@ function up2k_init(subtle) {
|
||||
t.keepalive = keepalive;
|
||||
};
|
||||
var orz = function (e) {
|
||||
apop(st.xhr, xhr);
|
||||
unxhr(xhr);
|
||||
if (t.t_busied != me || t.done)
|
||||
return console.log('zombie handshake onload', t.name, t);
|
||||
|
||||
@@ -2963,7 +2966,7 @@ function up2k_init(subtle) {
|
||||
pcar == pcdr ? pcar : ('' + pcar + '~' + pcdr);
|
||||
|
||||
var orz = function (xhr) {
|
||||
apop(st.xhr, xhr);
|
||||
unxhr(xhr);
|
||||
st.bytes.inflight -= xhr.bsent;
|
||||
var txt = unpre((xhr.response && xhr.response.err) || xhr.responseText);
|
||||
if (txt.indexOf('upload blocked by x') + 1) {
|
||||
@@ -3014,7 +3017,9 @@ function up2k_init(subtle) {
|
||||
orz2(xhr);
|
||||
}
|
||||
var orz2 = function (xhr) {
|
||||
apop(st.xhr, xhr);
|
||||
unxhr(xhr);
|
||||
if (st.deaf)
|
||||
xhr.abort();
|
||||
apop(st.busy.upload, upt);
|
||||
for (var a = pcar; a <= pcdr; a++)
|
||||
apop(t.postlist, a);
|
||||
|
||||
@@ -1,3 +1,57 @@
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2026-0906-2249 `v1.20.23` rcm once again
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* the custom right-click-menu didn't like being enabled ad9994be
|
||||
* this release is a hotfix for that; see v1.20.22 for all the other new stuff
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2026-0906-2112 `v1.20.22` hello fedora
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* iPhone: new bug in iOS breaks uploading; add workaround 3ea671ff
|
||||
* apple broke XHR/fetch in a recent iOS version by introducing some wtf race-conditions in response handling; under high network load, the browser simply forgets to tell js that there's a response, so we're basically flying blind
|
||||
* this workaround makes iOS uploads 50x faster than before but still not perfect (impossible given the situation); will be stuttery until apple fixes iOS
|
||||
* apple will probably fix it very soon given the severity of the bug, but at least one copyparty user is forever-stuck on iOS-18.x which will never be fixed by apple, so a workaround is justified
|
||||
* #1617 new plugin to thumbnail office documents with collabora (thx @kamaeff!) d4a1ce48
|
||||
* new hook: [phonecam-sorter.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/hooks/phonecam-sorter.py) to automate organizing of pics/vids synced from phone to nas 9de09026
|
||||
* dirkeys: allow non-recursive download-as-zip with just `dk` 3e3401f4
|
||||
* `--no-mime` / volflag `nomime` disables `?mime=` for specifying custom response mimetype 370a05ab
|
||||
* btrfs-specific: nocow .hist to improve sqlite performance dd9cf645
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* two low-severity vulns in different components, but surprisingly similar synopses:
|
||||
* GHSA-mc69-pxc8-4xf4 dirkeys (volflag `dk`) did not prevent descending into subdirs if an attacker could guess the name of the subdir 55969b8e
|
||||
* GHSA-3fhv-rhjw-7hrg sftp did not fully enforce volflags xvol/xdev; an attacker could read a file inside the symlink destination if they could guess the name inside b80a210e
|
||||
* not important enough to be listed in "recent important news", but will be detected by the (default-disabled) [version-checker](https://github.com/9001/copyparty/#version-checker)
|
||||
* #1628 fix http206 range-request for last-n-bytes bdf8b793
|
||||
* #1610 autogrid didn't count jxl images (thx @sylfn!) 7796c2d0
|
||||
* when running without `e2d`, a config-reload would block uploads 0767d956
|
||||
* really old chrome versions (before ver.62) was only able to upload over https ab791921
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* #1632 connect-page: adjust rclone commands to support long passwords d7d30f26
|
||||
* packaging: fix jank in source tarballs b854b8f5
|
||||
* packaging: don't list licenses of unvendored modules 704af697
|
||||
* #1631 systemd-examples: move config to `/etc/copyparty.conf` 185087ee
|
||||
* reduce binary-garbage in logs from scrapers/scanners f00d19c5
|
||||
* sfx: prefer `~/.cache/` (set `PRTY_XD=/tmp` to override) f870cf3c
|
||||
* sfx: mention https://copyparty.eu/sfx-wtf/ in the header 80165120
|
||||
* reduce complaining in log about default/unsafe tls-certs when not relevant 80cf7bfd
|
||||
|
||||
## 🌠 fun facts
|
||||
|
||||
* #887 copyparty has been packaged for Fedora 44! And EPEL-10 is on the way too... Thx @supakeen o/
|
||||
* [verified at RevSpace NL](https://a.ocv.me/pub/g/2026/09/20260901_151949.jpg?cache)
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2026-0817-2157 `v1.20.21` thumbex
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ echo warming up cache
|
||||
cat 1 >/dev/null
|
||||
|
||||
echo ok lets go
|
||||
$pybin "$sfx" -p39204 -e2dsa --dbd=yolo --exit=idx -lo=t -q "$@" && err= || err=$?
|
||||
$pybin "$sfx" -i no -e2dsa --dbd=yolo --exit=idx -lo=t -q "$@" && err= || err=$?
|
||||
[ $win ] && [ $err = 15 ] && err= # sigterm doesn't hook on windows, ah whatever
|
||||
[ $err ] && echo ERROR $err && exit $err
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ FROM alpine:3.23
|
||||
WORKDIR /z
|
||||
ENV ver_hashwasm=4.12.0 \
|
||||
ver_marked=4.3.0 \
|
||||
ver_dompf=3.4.6 \
|
||||
ver_dompf=3.4.15 \
|
||||
ver_mde=2.18.0 \
|
||||
ver_codemirror=5.65.18 \
|
||||
ver_fontawesome=5.13.0 \
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ command -v gsort >/dev/null && {
|
||||
|
||||
cd ../dist
|
||||
|
||||
kwds='-bind -accounts -auth -auth-ord -flags -thumb-ex -handlers -hooks -idp -urlform -exp -rlo -ls -dbd -chmod -pwhash -zm'
|
||||
kwds='-bind -accounts -auth -auth-ord -flags -dedup -redup -thumb-ex -handlers -hooks -idp -urlform -exp -rlo -ls -dbd -chmod -pwhash -zm'
|
||||
|
||||
export PRTY_FULL_HELP=1
|
||||
export PRTY_NO_PARAMIKO=1
|
||||
|
||||
@@ -8,7 +8,7 @@ import subprocess as sp
|
||||
|
||||
# to convert the copyparty --help to html, run this in xfce4-terminal @ 140x43:
|
||||
_ = r""""
|
||||
echo; for a in '' -bind -accounts -auth -auth-ord -flags -handlers -hooks -idp -urlform -exp -ls -dbd -chmod -pwhash -zm; do
|
||||
echo; for a in '' -bind -accounts -auth -auth-ord -flags -dedup -redup -thumb-ex -handlers -hooks -idp -urlform -exp -rlo -ls -dbd -chmod -pwhash -zm; do
|
||||
./copyparty-sfx.py --help$a 2>/dev/null; printf '\n\n\n%0139d\n\n\n'; done # xfce4-terminal @ 140x43
|
||||
"""
|
||||
# click [edit] => [select all]
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ exit 0
|
||||
|
||||
|
||||
# first open an infinitely wide console (this is why you own an ultrawide) and copypaste this into it:
|
||||
for a in '' -bind -accounts -auth -auth-ord -flags -handlers -hooks -idp -urlform -exp -ls -dbd -chmod -pwhash -zm; do
|
||||
for a in '' -bind -accounts -auth -auth-ord -flags -dedup -redup -thumb-ex -handlers -hooks -idp -urlform -exp -rlo -ls -dbd -chmod -pwhash -zm; do
|
||||
./copyparty-sfx.py --help$a 2>/dev/null; printf '\n\n\n%0255d\n\n\n'; done
|
||||
|
||||
# then copypaste all of the output by pressing ctrl-shift-a, ctrl-shift-c
|
||||
|
||||
@@ -70,6 +70,9 @@ done
|
||||
[ "$1" ] || {
|
||||
python3 ../scripts/test/smoketest.py &
|
||||
pids+=($!)
|
||||
|
||||
bash ../scripts/test/redup.sh &
|
||||
pids+=($!)
|
||||
}
|
||||
|
||||
for pid in ${pids[@]}; do
|
||||
|
||||
Executable
+104
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
td=redump-test
|
||||
|
||||
st_orig() { cat <<'EOF'
|
||||
f1,1,'f1',3660
|
||||
f2,1,'f2',3720
|
||||
f3,2,'f3',3960
|
||||
f4,1,'f4',3840
|
||||
f5,1,'f5' -> 'f2',3900
|
||||
f6,2,'f6',3960
|
||||
f7,1,'f7',4020
|
||||
f8,1,'f8',4080
|
||||
f9,1,'f9',4140
|
||||
EOF
|
||||
}
|
||||
st_ref() { cat <<'EOF'
|
||||
f1,1,'f1',3660
|
||||
f2,1,'f2',3720
|
||||
f3,1,'f3',3960
|
||||
f4,1,'f4',3840
|
||||
f5,1,'f5',3900
|
||||
f6,1,'f6',3960
|
||||
f7,1,'f7',4020
|
||||
f8,1,'f8',4080
|
||||
f9,1,'f9',4140
|
||||
EOF
|
||||
}
|
||||
st_sym() { cat <<'EOF'
|
||||
f1,1,'f1',3660
|
||||
f2,1,'f2',3720
|
||||
f3,1,'f3',3960
|
||||
f4,1,'f4',3840
|
||||
f5,1,'f5' -> 'f2',3900
|
||||
f6,1,'f6' -> 'f3',3960
|
||||
f7,1,'f7' -> 'f4',4020
|
||||
f8,1,'f8' -> 'f1',4080
|
||||
f9,1,'f9' -> 'f1',4140
|
||||
EOF
|
||||
}
|
||||
st_hard() { cat <<'EOF'
|
||||
f1,3,'f1',3600
|
||||
f2,2,'f2',3600
|
||||
f3,2,'f3',3600
|
||||
f4,2,'f4',3600
|
||||
f5,2,'f5',3600
|
||||
f6,2,'f6',3600
|
||||
f7,2,'f7',3600
|
||||
f8,3,'f8',3600
|
||||
f9,3,'f9',3600
|
||||
EOF
|
||||
}
|
||||
statchk() {
|
||||
(cd $td;stat -c%n,%h,%N,%Y * | sort -n | diff -U9 <($1) -) && return
|
||||
cat $td/* | grep -vE '^f1f2f3f4f2f3f4f1f1$' || return
|
||||
cat $td.l
|
||||
exit 1
|
||||
}
|
||||
setup() {
|
||||
rm -rf $td
|
||||
mkdir $td
|
||||
th=19700101010 #..M
|
||||
tu=978310800 #unix
|
||||
( cd "$td"
|
||||
for n in {1..4}; do echo -n f$n > f$n; done
|
||||
ln -s f2 f5
|
||||
ln f3 f6
|
||||
cp --reflink=always f4 f7
|
||||
cp --reflink=never f1 f8
|
||||
cp --reflink=never f1 f9
|
||||
for n in {1..9}; do touch -ht $th$n f$n; done
|
||||
);statchk st_orig
|
||||
}
|
||||
export PRTY_NO_ARGON2=1
|
||||
export PRTY_NO_CFSSL=1
|
||||
export PRTY_NO_FFMPEG=1
|
||||
export PRTY_NO_FFPROBE=1
|
||||
export PRTY_NO_IFADDR=1
|
||||
export PRTY_NO_IMPRESO=1
|
||||
export PRTY_NO_MAGIC=1
|
||||
export PRTY_NO_PARAMIKO=1
|
||||
export PRTY_NO_PARTFTPY=1
|
||||
export PRTY_NO_PIL=1
|
||||
export PRTY_NO_PSUTIL=1
|
||||
export PRTY_NO_PYFTPD=1
|
||||
export PRTY_NO_RAW=1
|
||||
export PRTY_NO_VIPS=1
|
||||
|
||||
cmd="python -m copyparty -i no --ign-ebind-all --no-ses --no-fastboot --no-voldump --exit=idx --ansi -v $td::A"
|
||||
|
||||
fs=$(stat -fc%T .)
|
||||
if [ "$fs" = btrfs ]; then
|
||||
echo ref:; setup; $cmd -e2dsa --redup sym,hard,ref=ref >$td.l; statchk st_ref
|
||||
else
|
||||
printf 'detected filesystem [%s] which is not btrfs, will NOT run reflink test\n' "$fs"
|
||||
fi
|
||||
|
||||
echo sym:; setup; $cmd -e2dsa --redup sym,hard,ref=sym >$td.l; statchk st_sym
|
||||
|
||||
echo hard:; setup; $cmd -e2dsa --redup sym,hard,ref=hard >$td.l; touch -ht 197001010100 $td/*; statchk st_hard
|
||||
# `- ign hard mtimes because redup is order-undefined (or sqlite-row-order rather but ye)
|
||||
|
||||
rm -rf $td $td.l
|
||||
+2
-2
@@ -145,7 +145,7 @@ class Cfg(Namespace):
|
||||
def __init__(self, a=None, v=None, c=None, **ka0):
|
||||
ka = {}
|
||||
|
||||
ex = "allow_flac allow_wav allow_svg_js chpw cookie_lax daw dav_auth dav_mac dav_rt dlni dothidden e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only http_no_tcp ih ihead localtime log_badxml magic md_no_br nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_dupe_m no_fnugg no_html no_lifetime no_logues no_mime no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_script no_tail no_tarcmp no_thumb no_vthumb no_u2abrt no_zip no_zls nrand nsort nw og og_no_head og_s_title ohead opds q rand re_dirsz reflink rm_partial rmagic rss show_hist smb srch_dbg srch_excl srch_nfkc srch_icase stats ui_noacci ui_nocpla ui_noctxb ui_nolbar ui_nombar ui_nonav ui_notree ui_norepl ui_nosrvi uqe usernames vague_403 vc ver vol_nospawn vol_or_crash wo_up_readme wopi write_uplog xdev xlink xvol zipmaxu zs"
|
||||
ex = "allow_flac allow_wav allow_svg_js chpw cookie_lax daw dav_auth dav_mac dav_rt dlni dothidden e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only http_no_tcp ih ihead localtime log_badxml magic md_no_br nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_dupe_m no_fnugg no_html no_lifetime no_logues no_mime no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_script no_tail no_tarcmp no_thumb no_vthumb no_u2abrt no_zip no_zls nrand nsort nw og og_no_head og_s_title ohead opds q rand re_dirsz redup_dry reflink rm_partial rmagic rss show_hist smb srch_dbg srch_excl srch_nfkc srch_icase stats ui_noacci ui_nocpla ui_noctxb ui_nolbar ui_nombar ui_nonav ui_notree ui_norepl ui_nosrvi uqe usernames vague_403 vc ver vol_nospawn vol_or_crash wo_up_readme wopi write_uplog xdev xlink xvol zipmaxu zs"
|
||||
ka.update(**{k: False for k in ex.split()})
|
||||
|
||||
ex = "dav_inf dedup dotpart dotsrch hist_cow hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump wram re_dhash see_dots plain_ip"
|
||||
@@ -166,7 +166,7 @@ class Cfg(Namespace):
|
||||
ex = "ctl_re db_act forget_ip gauto idp_cookie idp_store k304 loris md_nhist no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs"
|
||||
ka.update(**{k: 0 for k in ex.split()})
|
||||
|
||||
ex = "ah_alg bname chdir chmod_f chpw_db csp_dl csp_ui db_xattr doctitle df epilogues exit favico fika ipa ipar html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_date log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts preadmes prologues readmes shr shr1 shr_site site smsg tcolor textfiles th_pregen txt_eol ufavico ufavico_h unlist up_site vc_url vname xff_src zipmaxt R RS SR"
|
||||
ex = "ah_alg bname chdir chmod_f chpw_db csp_dl csp_ui db_xattr doctitle df epilogues exit favico fika ipa ipar html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_date log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts preadmes prologues readmes redup shr shr1 shr_site site smsg tcolor textfiles th_pregen txt_eol ufavico ufavico_h unlist up_site vc_url vname xff_src zipmaxt R RS SR"
|
||||
ka.update(**{k: "" for k in ex.split()})
|
||||
|
||||
ex = "apnd_who ban_403 ban_404 ban_422 ban_pw ban_pwc ban_url dont_ban cachectl http_vary rcm rss_fmt_d rss_fmt_t spinner"
|
||||
|
||||
Reference in new issue
Block a user