copyparty 1.19.8__tar.gz → 1.19.10__tar.gz
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-1.19.8 → copyparty-1.19.10}/PKG-INFO +7 -3
- {copyparty-1.19.8 → copyparty-1.19.10}/README.md +6 -2
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/__main__.py +26 -14
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/__version__.py +2 -2
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/authsrv.py +102 -9
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/broker_mpw.py +3 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/cfg.py +4 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/fsutil.py +66 -23
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/ftpd.py +3 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/httpcli.py +23 -11
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/mdns.py +3 -1
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/mtag.py +2 -4
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/svchub.py +14 -2
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/u2idx.py +29 -4
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/up2k.py +20 -9
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/util.py +48 -22
- copyparty-1.19.10/copyparty/web/baguettebox.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/browser.css.gz +0 -0
- copyparty-1.19.10/copyparty/web/browser.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/dbg-audio.js.gz +0 -0
- copyparty-1.19.10/copyparty/web/deps/busy.mp3.gz +0 -0
- copyparty-1.19.10/copyparty/web/deps/easymde.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/easymde.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/marked.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/mini-fa.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/prism.css.gz +0 -0
- copyparty-1.19.10/copyparty/web/deps/prism.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/prismd.css.gz +0 -0
- copyparty-1.19.10/copyparty/web/deps/scp.woff2 +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/sha512.ac.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/md.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/md.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/md2.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/md2.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/mde.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/mde.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/msg.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/rups.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/rups.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/shares.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/shares.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/splash.css.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/splash.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/ui.css.gz +0 -0
- copyparty-1.19.10/copyparty/web/up2k.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty.egg-info/PKG-INFO +7 -3
- copyparty-1.19.8/copyparty/web/baguettebox.js.gz +0 -0
- copyparty-1.19.8/copyparty/web/browser.js.gz +0 -0
- copyparty-1.19.8/copyparty/web/deps/busy.mp3.gz +0 -0
- copyparty-1.19.8/copyparty/web/deps/easymde.css.gz +0 -0
- copyparty-1.19.8/copyparty/web/deps/prism.js.gz +0 -0
- copyparty-1.19.8/copyparty/web/deps/scp.woff2 +0 -0
- copyparty-1.19.8/copyparty/web/up2k.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/LICENSE +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/bos/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/bos/bos.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/bos/path.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/broker_mp.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/broker_thr.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/broker_util.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/cert.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/dxml.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/httpconn.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/httpsrv.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/ico.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/metrics.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/multicast.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/pwhash.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/res/COPYING.txt +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/res/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/res/insecure.pem +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/smbd.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/ssdp.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/star.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/bimap.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/bit.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/buffer.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/dns.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/label.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/lex.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/dnslib/ranges.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/ifaddr/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/ifaddr/_posix.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/ifaddr/_shared.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/ifaddr/_win32.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/qrcodegen.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/stolen/surrogateescape.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/sutil.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/szip.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/tcpsrv.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/tftpd.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/th_cli.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/th_srv.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/a/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/a/partyfuse.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/a/u2c.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/a/webdav-cfg.bat +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/browser.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/browser2.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/cf.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/__init__.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/fuse.py +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/mini-fa.woff +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/deps/sha512.hw.js.gz +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/idp.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/md.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/mde.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/msg.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/rups.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/shares.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/splash.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty/web/svcs.html +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty.egg-info/SOURCES.txt +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty.egg-info/dependency_links.txt +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty.egg-info/entry_points.txt +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty.egg-info/requires.txt +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/copyparty.egg-info/top_level.txt +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/pyproject.toml +0 -0
- {copyparty-1.19.8 → copyparty-1.19.10}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: copyparty
|
|
3
|
-
Version: 1.19.
|
|
3
|
+
Version: 1.19.10
|
|
4
4
|
Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
|
|
5
5
|
Author-email: ed <copyparty@ocv.me>
|
|
6
6
|
License: MIT
|
|
@@ -1418,7 +1418,7 @@ general usage:
|
|
|
1418
1418
|
on macos, connect from finder:
|
|
1419
1419
|
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/
|
|
1420
1420
|
|
|
1421
|
-
|
|
1421
|
+
to upload or edit files with WebDAV clients, enable the `daw` volflag (because most WebDAV clients expect this) and give your account the delete-permission. This avoids getting several copies of the same file on the server. HOWEVER: This will also make all PUT-uploads overwrite existing files if the user has delete-access, so use with caution.
|
|
1422
1422
|
|
|
1423
1423
|
> note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients)
|
|
1424
1424
|
|
|
@@ -2770,6 +2770,10 @@ below are some tweaks roughly ordered by usefulness:
|
|
|
2770
2770
|
* using [pypy](https://www.pypy.org/) instead of [cpython](https://www.python.org/) *can* be 70% faster for some workloads, but slower for many others
|
|
2771
2771
|
* and pypy can sometimes crash on startup with `-j0` (TODO make issue)
|
|
2772
2772
|
|
|
2773
|
+
* if you are running the copyparty server **on Windows or Macos:**
|
|
2774
|
+
* `--casechk=n` makes it much faster, but also awakens [the usual surprises](https://github.com/9001/copyparty/issues/781) you expect from a case-insensitive filesystem
|
|
2775
|
+
* this is the same as `casechk: n` in a config-file
|
|
2776
|
+
|
|
2773
2777
|
|
|
2774
2778
|
## client-side
|
|
2775
2779
|
|
|
@@ -3084,7 +3088,7 @@ first install one of the following:
|
|
|
3084
3088
|
and then copypaste the following command into `a-Shell`:
|
|
3085
3089
|
|
|
3086
3090
|
```sh
|
|
3087
|
-
curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
|
3091
|
+
curl -L https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
|
3088
3092
|
```
|
|
3089
3093
|
|
|
3090
3094
|
what this does:
|
|
@@ -1353,7 +1353,7 @@ general usage:
|
|
|
1353
1353
|
on macos, connect from finder:
|
|
1354
1354
|
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/
|
|
1355
1355
|
|
|
1356
|
-
|
|
1356
|
+
to upload or edit files with WebDAV clients, enable the `daw` volflag (because most WebDAV clients expect this) and give your account the delete-permission. This avoids getting several copies of the same file on the server. HOWEVER: This will also make all PUT-uploads overwrite existing files if the user has delete-access, so use with caution.
|
|
1357
1357
|
|
|
1358
1358
|
> note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients)
|
|
1359
1359
|
|
|
@@ -2705,6 +2705,10 @@ below are some tweaks roughly ordered by usefulness:
|
|
|
2705
2705
|
* using [pypy](https://www.pypy.org/) instead of [cpython](https://www.python.org/) *can* be 70% faster for some workloads, but slower for many others
|
|
2706
2706
|
* and pypy can sometimes crash on startup with `-j0` (TODO make issue)
|
|
2707
2707
|
|
|
2708
|
+
* if you are running the copyparty server **on Windows or Macos:**
|
|
2709
|
+
* `--casechk=n` makes it much faster, but also awakens [the usual surprises](https://github.com/9001/copyparty/issues/781) you expect from a case-insensitive filesystem
|
|
2710
|
+
* this is the same as `casechk: n` in a config-file
|
|
2711
|
+
|
|
2708
2712
|
|
|
2709
2713
|
## client-side
|
|
2710
2714
|
|
|
@@ -3019,7 +3023,7 @@ first install one of the following:
|
|
|
3019
3023
|
and then copypaste the following command into `a-Shell`:
|
|
3020
3024
|
|
|
3021
3025
|
```sh
|
|
3022
|
-
curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
|
3026
|
+
curl -L https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
|
3023
3027
|
```
|
|
3024
3028
|
|
|
3025
3029
|
what this does:
|
|
@@ -1169,11 +1169,14 @@ def add_qr(ap, tty):
|
|
|
1169
1169
|
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)")
|
|
1170
1170
|
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")
|
|
1171
1171
|
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")
|
|
1172
|
+
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")
|
|
1173
|
+
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")
|
|
1172
1174
|
|
|
1173
1175
|
|
|
1174
1176
|
def add_fs(ap):
|
|
1175
1177
|
ap2 = ap.add_argument_group("filesystem options")
|
|
1176
1178
|
rm_re_def = "15/0.1" if ANYWIN else "0/0"
|
|
1179
|
+
ap2.add_argument("--casechk", metavar="N", type=u, default="auto", help="detect and prevent CI (case-insensitive) behavior if the underlying filesystem is CI? [\033[32my\033[0m] = detect and prevent, [\033[32mn\033[0m] = ignore and allow, [\033[32mauto\033[0m] = \033[32my\033[0m if CI fs detected. NOTE: \033[32my\033[0m is very slow but necessary for correct WebDAV behavior on Windows/Macos (volflag=casechk)")
|
|
1177
1180
|
ap2.add_argument("--rm-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be deleted because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=rm_retry)")
|
|
1178
1181
|
ap2.add_argument("--mv-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be renamed because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=mv_retry)")
|
|
1179
1182
|
ap2.add_argument("--iobuf", metavar="BYTES", type=int, default=256*1024, help="file I/O buffer-size; if your volumes are on a network drive, try increasing to \033[32m524288\033[0m or even \033[32m4194304\033[0m (and let me know if that improves your performance)")
|
|
@@ -1209,6 +1212,7 @@ def add_upload(ap):
|
|
|
1209
1212
|
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[32m777\033[0m] = full-yolo (volflag=chmod_d)")
|
|
1210
1213
|
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)")
|
|
1211
1214
|
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)")
|
|
1215
|
+
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)")
|
|
1212
1216
|
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
|
|
1213
1217
|
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)")
|
|
1214
1218
|
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
|
|
@@ -1566,6 +1570,9 @@ def add_logging(ap):
|
|
|
1566
1570
|
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
|
1567
1571
|
ap2.add_argument("--ohead", metavar="HEADER", type=u, action='append', help="print response \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
|
1568
1572
|
ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|[?&]th=[wjp]|/\.(_|ql_|DS_Store$|localized$)", help="dont log URLs matching regex \033[33mRE\033[0m")
|
|
1573
|
+
ap2.add_argument("--scan-st-r", metavar="SEC", type=float, default=0.1, help="fs-indexing: wait \033[33mSEC\033[0m between each status-message")
|
|
1574
|
+
ap2.add_argument("--scan-pr-r", metavar="SEC", type=float, default=10, help="fs-indexing: wait \033[33mSEC\033[0m between each 'progress:' message")
|
|
1575
|
+
ap2.add_argument("--scan-pr-s", metavar="MiB", type=float, default=1, help="fs-indexing: say 'file: <name>' when a file larger than \033[33mMiB\033[0m is about to be hashed")
|
|
1569
1576
|
|
|
1570
1577
|
|
|
1571
1578
|
def add_admin(ap):
|
|
@@ -1673,6 +1680,7 @@ def add_db_general(ap, hcores):
|
|
|
1673
1680
|
ap2.add_argument("--hash-mt", metavar="CORES", type=int, default=hcores, help="num cpu cores to use for file hashing; set 0 or 1 for single-core hashing")
|
|
1674
1681
|
ap2.add_argument("--re-maxage", metavar="SEC", type=int, default=0, help="rescan filesystem for changes every \033[33mSEC\033[0m seconds; 0=off (volflag=scan)")
|
|
1675
1682
|
ap2.add_argument("--db-act", metavar="SEC", type=float, default=10.0, help="defer any scheduled volume reindexing until \033[33mSEC\033[0m seconds after last db write (uploads, renames, ...)")
|
|
1683
|
+
ap2.add_argument("--srch-icase", action="store_true", help="case-insensitive search for all unicode characters (the default is icase for just ascii). NOTE: will make searches much slower (around 4x), and NOTE: only applies to filenames/paths, not tags")
|
|
1676
1684
|
ap2.add_argument("--srch-time", metavar="SEC", type=int, default=45, help="search deadline -- terminate searches running for more than \033[33mSEC\033[0m seconds")
|
|
1677
1685
|
ap2.add_argument("--srch-hits", metavar="N", type=int, default=7999, help="max search results to allow clients to fetch; 125 results will be shown initially")
|
|
1678
1686
|
ap2.add_argument("--srch-excl", metavar="PTN", type=u, default="", help="regex: exclude files from search results if the file-URL matches \033[33mPTN\033[0m (case-sensitive). Example: [\033[32mpassword|logs/[0-9]\033[0m] any URL containing 'password' or 'logs/DIGIT' (volflag=srch_excl)")
|
|
@@ -1727,7 +1735,7 @@ def add_og(ap):
|
|
|
1727
1735
|
ap2.add_argument("--uqe", action="store_true", help="query-string parceling; translate a request for \033[33m/foo/.uqe/BASE64\033[0m into \033[33m/foo?TEXT\033[0m, or \033[33m/foo/?TEXT\033[0m if the first character in \033[33mTEXT\033[0m is a slash. Automatically enabled for \033[33m--og\033[0m")
|
|
1728
1736
|
|
|
1729
1737
|
|
|
1730
|
-
def add_ui(ap, retry):
|
|
1738
|
+
def add_ui(ap, retry ):
|
|
1731
1739
|
THEMES = 10
|
|
1732
1740
|
ap2 = ap.add_argument_group("ui options")
|
|
1733
1741
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
|
@@ -1869,18 +1877,21 @@ def run_argparse(
|
|
|
1869
1877
|
for k, h, _ in sects:
|
|
1870
1878
|
ap2.add_argument("--help-" + k, action="store_true", help=h)
|
|
1871
1879
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
raise Exception()
|
|
1875
|
-
|
|
1880
|
+
if retry:
|
|
1881
|
+
a = ["ascii", "replace"]
|
|
1876
1882
|
for x in ap._actions:
|
|
1877
|
-
|
|
1878
|
-
|
|
1883
|
+
try:
|
|
1884
|
+
x.default = x.default.encode(*a).decode(*a)
|
|
1885
|
+
except:
|
|
1886
|
+
pass
|
|
1879
1887
|
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1888
|
+
try:
|
|
1889
|
+
if x.help and x.help is not argparse.SUPPRESS:
|
|
1890
|
+
x.help = x.help.replace("└─", "`-").encode(*a).decode(*a)
|
|
1891
|
+
if retry > 2:
|
|
1892
|
+
x.help = RE_ANSI.sub("", x.help)
|
|
1893
|
+
except:
|
|
1894
|
+
pass
|
|
1884
1895
|
|
|
1885
1896
|
ret = ap.parse_args(args=argv[1:])
|
|
1886
1897
|
for k, h, t in sects:
|
|
@@ -1990,7 +2001,7 @@ def main(argv = None) :
|
|
|
1990
2001
|
except:
|
|
1991
2002
|
nc = 486 # mdns/ssdp restart headroom; select() maxfd is 512 on windows
|
|
1992
2003
|
|
|
1993
|
-
retry =
|
|
2004
|
+
retry = 0
|
|
1994
2005
|
for fmtr in [RiceFormatter, RiceFormatter, Dodge11874, BasicDodge11874]:
|
|
1995
2006
|
try:
|
|
1996
2007
|
al = run_argparse(argv, fmtr, retry, nc)
|
|
@@ -1999,8 +2010,9 @@ def main(argv = None) :
|
|
|
1999
2010
|
except SystemExit:
|
|
2000
2011
|
raise
|
|
2001
2012
|
except:
|
|
2002
|
-
retry
|
|
2003
|
-
|
|
2013
|
+
retry += 1
|
|
2014
|
+
t = "WARNING: due to limitations in your terminal and/or OS, the helptext cannot be displayed correctly. Will show a simplified version due to the following error:\n[ %s ]:\n%s\n"
|
|
2015
|
+
lprint(t % (fmtr, min_ex()))
|
|
2004
2016
|
|
|
2005
2017
|
try:
|
|
2006
2018
|
assert al # type: ignore
|
|
@@ -13,7 +13,7 @@ import threading
|
|
|
13
13
|
import time
|
|
14
14
|
from datetime import datetime
|
|
15
15
|
|
|
16
|
-
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, WINDOWS, E
|
|
16
|
+
from .__init__ import ANYWIN, MACOS, PY2, TYPE_CHECKING, WINDOWS, E
|
|
17
17
|
from .bos import bos
|
|
18
18
|
from .cfg import flagdescs, permdescs, vf_bmap, vf_cmap, vf_vmap
|
|
19
19
|
from .pwhash import PWHash
|
|
@@ -92,6 +92,8 @@ SBADCFG = " ({})".format(BAD_CFG)
|
|
|
92
92
|
|
|
93
93
|
PTN_U_GRP = re.compile(r"\$\{u(%[+-][^}]+)\}")
|
|
94
94
|
PTN_G_GRP = re.compile(r"\$\{g(%[+-][^}]+)\}")
|
|
95
|
+
PTN_U_ANY = re.compile(r"(\${[u][}%])")
|
|
96
|
+
PTN_G_ANY = re.compile(r"(\${[g][}%])")
|
|
95
97
|
PTN_SIGIL = re.compile(r"(\${[ug][}%])")
|
|
96
98
|
|
|
97
99
|
|
|
@@ -417,15 +419,17 @@ class VFS(object):
|
|
|
417
419
|
self.all_nodes[vpath] = self
|
|
418
420
|
self.all_aps = [(rp, [self])]
|
|
419
421
|
self.all_vps = [(vp, self)]
|
|
422
|
+
self.canonical = self._canonical
|
|
423
|
+
self.dcanonical = self._dcanonical
|
|
420
424
|
else:
|
|
421
425
|
self.histpath = self.dbpath = ""
|
|
422
426
|
self.all_aps = []
|
|
423
427
|
self.all_vps = []
|
|
428
|
+
self.canonical = self._canonical_null
|
|
429
|
+
self.dcanonical = self._dcanonical_null
|
|
424
430
|
|
|
425
431
|
self.get_dbv = self._get_dbv
|
|
426
432
|
self.ls = self._ls
|
|
427
|
-
self.canonical = self._canonical
|
|
428
|
-
self.dcanonical = self._dcanonical
|
|
429
433
|
|
|
430
434
|
def __repr__(self) :
|
|
431
435
|
return "VFS(%s)" % (
|
|
@@ -619,6 +623,34 @@ class VFS(object):
|
|
|
619
623
|
vrem = vjoin(self.vpath[len(dbv.vpath) :].lstrip("/"), vrem)
|
|
620
624
|
return dbv, vrem
|
|
621
625
|
|
|
626
|
+
def casechk(self, rem , do_stat ) :
|
|
627
|
+
ap = self.canonical(rem, False)
|
|
628
|
+
if do_stat and not bos.path.exists(ap):
|
|
629
|
+
return True # doesn't exist at all; good to go
|
|
630
|
+
dp, fn = os.path.split(ap)
|
|
631
|
+
try:
|
|
632
|
+
fns = os.listdir(dp)
|
|
633
|
+
except:
|
|
634
|
+
return True # maybe chmod 111; assume ok
|
|
635
|
+
if fn in fns:
|
|
636
|
+
return True
|
|
637
|
+
hit = "<?>"
|
|
638
|
+
lfn = fn.lower()
|
|
639
|
+
for zs in fns:
|
|
640
|
+
if lfn == zs.lower():
|
|
641
|
+
hit = zs
|
|
642
|
+
break
|
|
643
|
+
if self.log:
|
|
644
|
+
t = "returning 404 due to underlying case-insensitive filesystem:\n http-req: %r\n local-fs: %r"
|
|
645
|
+
self.log("vfs", t % (fn, hit))
|
|
646
|
+
return False
|
|
647
|
+
|
|
648
|
+
def _canonical_null(self, rem , resolve = True) :
|
|
649
|
+
return ""
|
|
650
|
+
|
|
651
|
+
def _dcanonical_null(self, rem ) :
|
|
652
|
+
return ""
|
|
653
|
+
|
|
622
654
|
def _canonical(self, rem , resolve = True) :
|
|
623
655
|
"""returns the canonical path (fully-resolved absolute fs path)"""
|
|
624
656
|
ap = self.realpath
|
|
@@ -704,8 +736,12 @@ class VFS(object):
|
|
|
704
736
|
"""return user-readable [fsdir,real,virt] items at vpath"""
|
|
705
737
|
virt_vis = {} # nodes readable by user
|
|
706
738
|
abspath = self.canonical(rem)
|
|
707
|
-
|
|
708
|
-
|
|
739
|
+
if abspath:
|
|
740
|
+
real = list(statdir(self.log, scandir, lstat, abspath, throw))
|
|
741
|
+
real.sort()
|
|
742
|
+
else:
|
|
743
|
+
real = []
|
|
744
|
+
|
|
709
745
|
if not rem:
|
|
710
746
|
# no vfs nodes in the list of real inodes
|
|
711
747
|
real = [x for x in real if x[0] not in self.nodes]
|
|
@@ -1121,6 +1157,16 @@ class AuthSrv(object):
|
|
|
1121
1157
|
src0 = src # abspath
|
|
1122
1158
|
dst0 = dst # vpath
|
|
1123
1159
|
|
|
1160
|
+
zsl = []
|
|
1161
|
+
for ptn, sigil in ((PTN_U_ANY, "${u}"), (PTN_G_ANY, "${g}")):
|
|
1162
|
+
if bool(ptn.search(src)) != bool(ptn.search(dst)):
|
|
1163
|
+
zsl.append(sigil)
|
|
1164
|
+
if zsl:
|
|
1165
|
+
t = "ERROR: if %s is mentioned in a volume definition, it must be included in both the filesystem-path [%s] and the volume-url [/%s]"
|
|
1166
|
+
t = "\n".join([t % (x, src, dst) for x in zsl])
|
|
1167
|
+
self.log(t, 1)
|
|
1168
|
+
raise Exception(t)
|
|
1169
|
+
|
|
1124
1170
|
un_gn = [(un, gn) for un, gns in un_gns.items() for gn in gns]
|
|
1125
1171
|
if not un_gn:
|
|
1126
1172
|
# ensure volume creation if there's no users
|
|
@@ -1213,8 +1259,8 @@ class AuthSrv(object):
|
|
|
1213
1259
|
self.log(t, c=3)
|
|
1214
1260
|
raise Exception(BAD_CFG)
|
|
1215
1261
|
|
|
1216
|
-
if not bos.path.
|
|
1217
|
-
self.log("warning: filesystem-path
|
|
1262
|
+
if not bos.path.exists(src):
|
|
1263
|
+
self.log("warning: filesystem-path did not exist: %r" % (src,), 3)
|
|
1218
1264
|
|
|
1219
1265
|
mount[dst] = (src, dst0)
|
|
1220
1266
|
daxs[dst] = AXS()
|
|
@@ -1836,7 +1882,7 @@ class AuthSrv(object):
|
|
|
1836
1882
|
vol.all_vps.sort(key=lambda x: len(x[0]), reverse=True)
|
|
1837
1883
|
vol.root = vfs
|
|
1838
1884
|
|
|
1839
|
-
zs = "neversymlink"
|
|
1885
|
+
zs = "neversymlink du_iwho"
|
|
1840
1886
|
k_ign = set(zs.split())
|
|
1841
1887
|
for vol in vfs.all_vols.values():
|
|
1842
1888
|
unknown_flags = set()
|
|
@@ -1987,6 +2033,8 @@ class AuthSrv(object):
|
|
|
1987
2033
|
promote = []
|
|
1988
2034
|
demote = []
|
|
1989
2035
|
for vol in vfs.all_vols.values():
|
|
2036
|
+
if not vol.realpath:
|
|
2037
|
+
continue
|
|
1990
2038
|
hid = self.hid_cache.get(vol.realpath)
|
|
1991
2039
|
if not hid:
|
|
1992
2040
|
zb = hashlib.sha512(afsenc(vol.realpath)).digest()
|
|
@@ -2025,6 +2073,8 @@ class AuthSrv(object):
|
|
|
2025
2073
|
vol.histpath = absreal(vol.histpath)
|
|
2026
2074
|
|
|
2027
2075
|
for vol in vfs.all_vols.values():
|
|
2076
|
+
if not vol.realpath:
|
|
2077
|
+
continue
|
|
2028
2078
|
hid = self.hid_cache[vol.realpath]
|
|
2029
2079
|
vflag = vol.flags.get("dbpath")
|
|
2030
2080
|
if vflag == "-":
|
|
@@ -2336,7 +2386,7 @@ class AuthSrv(object):
|
|
|
2336
2386
|
vol.flags["du_iwho"] = n_du_who(vol.flags["du_who"])
|
|
2337
2387
|
|
|
2338
2388
|
if not enshare:
|
|
2339
|
-
vol.flags["shr_who"] = "no"
|
|
2389
|
+
vol.flags["shr_who"] = self.args.shr_who = "no"
|
|
2340
2390
|
|
|
2341
2391
|
if vol.flags.get("og"):
|
|
2342
2392
|
self.args.uqe = True
|
|
@@ -2513,6 +2563,47 @@ class AuthSrv(object):
|
|
|
2513
2563
|
self.log(t.format(vol.vpath, mtp), 1)
|
|
2514
2564
|
errors = True
|
|
2515
2565
|
|
|
2566
|
+
for vol in vfs.all_nodes.values():
|
|
2567
|
+
if not vol.realpath or os.path.isfile(vol.realpath):
|
|
2568
|
+
continue
|
|
2569
|
+
ccs = vol.flags["casechk"][:1].lower()
|
|
2570
|
+
if ccs in ("y", "n"):
|
|
2571
|
+
if ccs == "y":
|
|
2572
|
+
vol.flags["bcasechk"] = True
|
|
2573
|
+
continue
|
|
2574
|
+
try:
|
|
2575
|
+
bos.makedirs(vol.realpath, vf=vol.flags)
|
|
2576
|
+
files = os.listdir(vol.realpath)
|
|
2577
|
+
for fn in files:
|
|
2578
|
+
fn2 = fn.lower()
|
|
2579
|
+
if fn == fn2:
|
|
2580
|
+
fn2 = fn.upper()
|
|
2581
|
+
if fn == fn2 or fn2 in files:
|
|
2582
|
+
continue
|
|
2583
|
+
is_ci = os.path.exists(os.path.join(vol.realpath, fn2))
|
|
2584
|
+
ccs = "y" if is_ci else "n"
|
|
2585
|
+
break
|
|
2586
|
+
if ccs not in ("y", "n"):
|
|
2587
|
+
ap = os.path.join(vol.realpath, "casechk")
|
|
2588
|
+
open(ap, "wb").close()
|
|
2589
|
+
ccs = "y" if os.path.exists(ap[:-1] + "K") else "n"
|
|
2590
|
+
os.unlink(ap)
|
|
2591
|
+
except Exception as ex:
|
|
2592
|
+
if ANYWIN:
|
|
2593
|
+
zs = "Windows"
|
|
2594
|
+
ccs = "y"
|
|
2595
|
+
elif MACOS:
|
|
2596
|
+
zs = "Macos"
|
|
2597
|
+
ccs = "y"
|
|
2598
|
+
else:
|
|
2599
|
+
zs = "Linux"
|
|
2600
|
+
ccs = "n"
|
|
2601
|
+
t = "unable to determine if filesystem at %r is case-insensitive due to %r; assuming casechk=%s due to %s"
|
|
2602
|
+
self.log(t % (vol.realpath, ex, ccs, zs), 3)
|
|
2603
|
+
vol.flags["casechk"] = ccs
|
|
2604
|
+
if ccs == "y":
|
|
2605
|
+
vol.flags["bcasechk"] = True
|
|
2606
|
+
|
|
2516
2607
|
tags = self.args.mtp or []
|
|
2517
2608
|
tags = [x.split("=")[0] for x in tags]
|
|
2518
2609
|
tags = [y for x in tags for y in x.split(",")]
|
|
@@ -2784,6 +2875,8 @@ class AuthSrv(object):
|
|
|
2784
2875
|
shn.dcanonical = shn._dcanonical_shr
|
|
2785
2876
|
else:
|
|
2786
2877
|
shn.ls = shn._ls
|
|
2878
|
+
shn.canonical = shn._canonical
|
|
2879
|
+
shn.dcanonical = shn._dcanonical
|
|
2787
2880
|
|
|
2788
2881
|
shn.shr_owner = s_un
|
|
2789
2882
|
shn.shr_src = (s_vfs, s_rem)
|
|
@@ -12,6 +12,7 @@ import queue
|
|
|
12
12
|
from .__init__ import ANYWIN
|
|
13
13
|
from .authsrv import AuthSrv
|
|
14
14
|
from .broker_util import BrokerCli, ExceptionalQueue, NotExQueue
|
|
15
|
+
from .fsutil import ramdisk_chk
|
|
15
16
|
from .httpsrv import HttpSrv
|
|
16
17
|
from .util import FAKE_MP, Daemon, HMaccas
|
|
17
18
|
|
|
@@ -50,6 +51,7 @@ class MpWorker(BrokerCli):
|
|
|
50
51
|
|
|
51
52
|
# starting to look like a good idea
|
|
52
53
|
self.asrv = AuthSrv(args, None, False)
|
|
54
|
+
ramdisk_chk(self.asrv)
|
|
53
55
|
|
|
54
56
|
# instantiate all services here (TODO: inheritance?)
|
|
55
57
|
self.iphash = HMaccas(os.path.join(self.args.E.cfg, "iphash"), 8)
|
|
@@ -93,6 +95,7 @@ class MpWorker(BrokerCli):
|
|
|
93
95
|
if dest == "reload":
|
|
94
96
|
self.logw("mpw.asrv reloading")
|
|
95
97
|
self.asrv.reload()
|
|
98
|
+
ramdisk_chk(self.asrv)
|
|
96
99
|
self.logw("mpw.asrv reloaded")
|
|
97
100
|
continue
|
|
98
101
|
|
|
@@ -57,6 +57,7 @@ def vf_bmap() :
|
|
|
57
57
|
"rmagic",
|
|
58
58
|
"rss",
|
|
59
59
|
"wo_up_readme",
|
|
60
|
+
"wram",
|
|
60
61
|
"xdev",
|
|
61
62
|
"xlink",
|
|
62
63
|
"xvol",
|
|
@@ -81,6 +82,7 @@ def vf_vmap() :
|
|
|
81
82
|
}
|
|
82
83
|
for k in (
|
|
83
84
|
"bup_ck",
|
|
85
|
+
"casechk",
|
|
84
86
|
"chmod_d",
|
|
85
87
|
"chmod_f",
|
|
86
88
|
"dbd",
|
|
@@ -186,6 +188,7 @@ flagcats = {
|
|
|
186
188
|
"chmod_f=644": "unix-permission for new files",
|
|
187
189
|
"uid=573": "change owner of new files/folders to unix-user 573",
|
|
188
190
|
"gid=999": "change owner of new files/folders to unix-group 999",
|
|
191
|
+
"wram": "allow uploading into ramdisks",
|
|
189
192
|
"sparse": "force use of sparse files, mainly for s3-backed storage",
|
|
190
193
|
"nosparse": "deny use of sparse files, mainly for slow storage",
|
|
191
194
|
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
|
|
@@ -244,6 +247,7 @@ flagcats = {
|
|
|
244
247
|
"no_db_ip": "never store uploader-IP in the db; disables unpost",
|
|
245
248
|
"fat32": "avoid excessive reindexing on android sdcardfs",
|
|
246
249
|
"dbd=[acid|swal|wal|yolo]": "database speed-durability tradeoff",
|
|
250
|
+
"casechk=auto": "actively prevent case-insensitive filesystem? y/n",
|
|
247
251
|
"xlink": "cross-volume dupe detection / linking (dangerous)",
|
|
248
252
|
"xdev": "do not descend into other filesystems",
|
|
249
253
|
"xvol": "do not follow symlinks leaving the volume root",
|
|
@@ -7,27 +7,30 @@ import re
|
|
|
7
7
|
import time
|
|
8
8
|
|
|
9
9
|
from .__init__ import ANYWIN, MACOS
|
|
10
|
-
from .authsrv import AXS, VFS
|
|
10
|
+
from .authsrv import AXS, VFS, AuthSrv
|
|
11
11
|
from .bos import bos
|
|
12
12
|
from .util import chkcmd, min_ex, undot
|
|
13
13
|
|
|
14
14
|
class Fstab(object):
|
|
15
|
-
def __init__(self, log , args ):
|
|
15
|
+
def __init__(self, log , args , verbose ):
|
|
16
16
|
self.log_func = log
|
|
17
|
+
self.verbose = verbose
|
|
17
18
|
|
|
18
19
|
self.warned = False
|
|
19
20
|
self.trusted = False
|
|
20
21
|
self.tab = None
|
|
21
22
|
self.oldtab = None
|
|
22
23
|
self.srctab = "a"
|
|
23
|
-
self.cache
|
|
24
|
+
self.cache = {}
|
|
24
25
|
self.age = 0.0
|
|
25
26
|
self.maxage = args.mtab_age
|
|
26
27
|
|
|
27
28
|
def log(self, msg , c = 0) :
|
|
29
|
+
if not c or self.verbose:
|
|
30
|
+
return
|
|
28
31
|
self.log_func("fstab", msg, c)
|
|
29
32
|
|
|
30
|
-
def get(self, path )
|
|
33
|
+
def get(self, path ) :
|
|
31
34
|
now = time.time()
|
|
32
35
|
if now - self.age > self.maxage or len(self.cache) > 9000:
|
|
33
36
|
self.age = now
|
|
@@ -35,6 +38,7 @@ class Fstab(object):
|
|
|
35
38
|
self.tab = None
|
|
36
39
|
self.cache = {}
|
|
37
40
|
|
|
41
|
+
mp = ""
|
|
38
42
|
fs = "ext4"
|
|
39
43
|
msg = "failed to determine filesystem at %r; assuming %s\n%s"
|
|
40
44
|
|
|
@@ -44,7 +48,7 @@ class Fstab(object):
|
|
|
44
48
|
path = self._winpath(path)
|
|
45
49
|
except:
|
|
46
50
|
self.log(msg % (path, fs, min_ex()), 3)
|
|
47
|
-
return fs
|
|
51
|
+
return fs, ""
|
|
48
52
|
|
|
49
53
|
path = undot(path)
|
|
50
54
|
try:
|
|
@@ -53,14 +57,14 @@ class Fstab(object):
|
|
|
53
57
|
pass
|
|
54
58
|
|
|
55
59
|
try:
|
|
56
|
-
fs = self.get_w32(path) if ANYWIN else self.get_unix(path)
|
|
60
|
+
fs, mp = self.get_w32(path) if ANYWIN else self.get_unix(path)
|
|
57
61
|
except:
|
|
58
62
|
self.log(msg % (path, fs, min_ex()), 3)
|
|
59
63
|
|
|
60
64
|
fs = fs.lower()
|
|
61
|
-
self.cache[path] = fs
|
|
62
|
-
self.log("found %s at %r" % (fs, path))
|
|
63
|
-
return fs
|
|
65
|
+
self.cache[path] = (fs, mp)
|
|
66
|
+
self.log("found %s at %r, %r" % (fs, mp, path))
|
|
67
|
+
return fs, mp
|
|
64
68
|
|
|
65
69
|
def _winpath(self, path ) :
|
|
66
70
|
# try to combine volume-label + st_dev (vsn)
|
|
@@ -75,34 +79,49 @@ class Fstab(object):
|
|
|
75
79
|
self.tab = VFS(self.log_func, "idk", "/", "/", AXS(), {})
|
|
76
80
|
self.trusted = False
|
|
77
81
|
|
|
78
|
-
def
|
|
79
|
-
self.log("inspecting mtab for changes")
|
|
80
|
-
|
|
82
|
+
def _from_sp_mount(self) :
|
|
81
83
|
sptn = r"^.*? on (.*) type ([^ ]+) \(.*"
|
|
82
84
|
if MACOS:
|
|
83
85
|
sptn = r"^.*? on (.*) \(([^ ]+), .*"
|
|
84
86
|
|
|
85
87
|
ptn = re.compile(sptn)
|
|
86
88
|
so, _ = chkcmd(["mount"])
|
|
87
|
-
|
|
88
|
-
atab = []
|
|
89
|
+
dtab = {}
|
|
89
90
|
for ln in so.split("\n"):
|
|
90
91
|
m = ptn.match(ln)
|
|
91
92
|
if not m:
|
|
92
93
|
continue
|
|
93
94
|
|
|
94
95
|
zs1, zs2 = m.groups()
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
dtab[str(zs1)] = str(zs2)
|
|
97
|
+
|
|
98
|
+
return dtab
|
|
99
|
+
|
|
100
|
+
def _from_proc(self) :
|
|
101
|
+
ret = {}
|
|
102
|
+
with open("/proc/self/mounts", "rb", 262144) as f:
|
|
103
|
+
src = f.read(262144).decode("utf-8", "replace").split("\n")
|
|
104
|
+
for zsl in [x.split(" ") for x in src]:
|
|
105
|
+
if len(zsl) < 3:
|
|
106
|
+
continue
|
|
107
|
+
zs = zsl[1]
|
|
108
|
+
zs = zs.replace("\\011", "\t").replace("\\040", " ").replace("\\134", "\\")
|
|
109
|
+
ret[zs] = zsl[2]
|
|
110
|
+
return ret
|
|
111
|
+
|
|
112
|
+
def build_tab(self) :
|
|
113
|
+
self.log("inspecting mtab for changes")
|
|
114
|
+
dtab = self._from_sp_mount() if MACOS else self._from_proc()
|
|
97
115
|
|
|
98
116
|
# keep empirically-correct values if mounttab unchanged
|
|
99
|
-
srctab =
|
|
117
|
+
srctab = str(sorted(dtab.items()))
|
|
100
118
|
if srctab == self.srctab:
|
|
101
119
|
self.tab = self.oldtab
|
|
102
120
|
return
|
|
103
121
|
|
|
104
122
|
self.log("mtab has changed; reevaluating support for sparse files")
|
|
105
123
|
|
|
124
|
+
tab1 = list(dtab.items())
|
|
106
125
|
tab1.sort(key=lambda x: (len(x[0]), x[0]))
|
|
107
126
|
path1, fs1 = tab1[0]
|
|
108
127
|
tab = VFS(self.log_func, fs1, path1, path1, AXS(), {})
|
|
@@ -139,7 +158,7 @@ class Fstab(object):
|
|
|
139
158
|
vn.realpath = ptn.sub(nval, vn.realpath)
|
|
140
159
|
visit.extend(list(vn.nodes.values()))
|
|
141
160
|
|
|
142
|
-
def get_unix(self, path )
|
|
161
|
+
def get_unix(self, path ) :
|
|
143
162
|
if not self.tab:
|
|
144
163
|
try:
|
|
145
164
|
self.build_tab()
|
|
@@ -148,18 +167,42 @@ class Fstab(object):
|
|
|
148
167
|
# prisonparty or other restrictive environment
|
|
149
168
|
if not self.warned:
|
|
150
169
|
self.warned = True
|
|
151
|
-
|
|
170
|
+
t = "failed to associate fs-mounts with the VFS (this is fine):\n%s"
|
|
171
|
+
self.log(t % (min_ex(),), 6)
|
|
152
172
|
self.build_fallback()
|
|
153
173
|
|
|
154
174
|
ret = self.tab._find(path)[0]
|
|
155
175
|
if self.trusted or path == ret.vpath:
|
|
156
|
-
return ret.realpath.split("/")[0]
|
|
176
|
+
return ret.realpath.split("/")[0], ret.vpath
|
|
157
177
|
else:
|
|
158
|
-
return "idk"
|
|
178
|
+
return "idk", ""
|
|
159
179
|
|
|
160
|
-
def get_w32(self, path )
|
|
180
|
+
def get_w32(self, path ) :
|
|
161
181
|
if not self.tab:
|
|
162
182
|
self.build_fallback()
|
|
163
183
|
|
|
164
184
|
ret = self.tab._find(path)[0]
|
|
165
|
-
return ret.realpath
|
|
185
|
+
return ret.realpath, ""
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def ramdisk_chk(asrv ) :
|
|
189
|
+
# should have been in authsrv but that's a circular import
|
|
190
|
+
mods = []
|
|
191
|
+
ramfs = ("tmpfs", "overlay")
|
|
192
|
+
log = asrv.log_func or print
|
|
193
|
+
fstab = Fstab(log, asrv.args, False)
|
|
194
|
+
for vn in asrv.vfs.all_nodes.values():
|
|
195
|
+
if not vn.axs.uwrite or "wram" in vn.flags:
|
|
196
|
+
continue
|
|
197
|
+
ap = vn.realpath
|
|
198
|
+
if not ap or os.path.isfile(ap):
|
|
199
|
+
continue
|
|
200
|
+
fs, mp = fstab.get(ap)
|
|
201
|
+
mp = "/" + mp.strip("/")
|
|
202
|
+
if fs == "tmpfs" or (mp == "/" and fs in ramfs):
|
|
203
|
+
mods.append((vn.vpath, ap, fs, mp))
|
|
204
|
+
vn.axs.uwrite.clear()
|
|
205
|
+
if mods:
|
|
206
|
+
t = "WARNING: write-access was removed from the following volumes because they are not mapped to an actual HDD for storage! All uploaded data would live in RAM only, and all uploaded files would be LOST on next reboot. To allow uploading and ignore this hazard, enable the 'wram' option (global/volflag). List of affected volumes:"
|
|
207
|
+
t2 = ["\n volume=[/%s], abspath=%r, type=%s, root=%r" % x for x in mods]
|
|
208
|
+
log("vfs", t + "".join(t2) + "\n", 1)
|
|
@@ -198,6 +198,9 @@ class FtpFs(AbstractedFS):
|
|
|
198
198
|
if r and not cr or w and not cw or m and not cm or d and not cd:
|
|
199
199
|
raise FSE(t.format(vpath), 1)
|
|
200
200
|
|
|
201
|
+
if "bcasechk" in vfs.flags and not vfs.casechk(rem, True):
|
|
202
|
+
raise FSE("No such file or directory", 1)
|
|
203
|
+
|
|
201
204
|
return os.path.join(vfs.realpath, rem), vfs, rem
|
|
202
205
|
except Pebkac as ex:
|
|
203
206
|
raise FSE(str(ex))
|