copyparty 1.18.5__tar.gz → 1.18.7__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.
Files changed (127) hide show
  1. {copyparty-1.18.5 → copyparty-1.18.7}/PKG-INFO +32 -5
  2. {copyparty-1.18.5 → copyparty-1.18.7}/README.md +31 -4
  3. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/__main__.py +32 -27
  4. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/__version__.py +2 -2
  5. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/authsrv.py +48 -2
  6. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/bos/bos.py +14 -3
  7. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/cfg.py +6 -0
  8. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/ftpd.py +4 -4
  9. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/httpcli.py +75 -53
  10. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/httpsrv.py +1 -0
  11. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/mtag.py +3 -2
  12. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/smbd.py +1 -1
  13. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/svchub.py +4 -3
  14. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/tftpd.py +6 -3
  15. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/th_srv.py +2 -2
  16. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/up2k.py +14 -8
  17. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/util.py +62 -22
  18. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/browser.html +2 -2
  19. copyparty-1.18.7/copyparty/web/browser.js.gz +0 -0
  20. copyparty-1.18.7/copyparty/web/shares.js.gz +0 -0
  21. copyparty-1.18.7/copyparty/web/splash.js.gz +0 -0
  22. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/svcs.html +16 -4
  23. copyparty-1.18.7/copyparty/web/svcs.js.gz +0 -0
  24. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty.egg-info/PKG-INFO +32 -5
  25. copyparty-1.18.5/copyparty/web/browser.js.gz +0 -0
  26. copyparty-1.18.5/copyparty/web/shares.js.gz +0 -0
  27. copyparty-1.18.5/copyparty/web/splash.js.gz +0 -0
  28. copyparty-1.18.5/copyparty/web/svcs.js.gz +0 -0
  29. {copyparty-1.18.5 → copyparty-1.18.7}/LICENSE +0 -0
  30. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/__init__.py +0 -0
  31. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/bos/__init__.py +0 -0
  32. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/bos/path.py +0 -0
  33. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/broker_mp.py +0 -0
  34. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/broker_mpw.py +0 -0
  35. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/broker_thr.py +0 -0
  36. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/broker_util.py +0 -0
  37. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/cert.py +0 -0
  38. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/dxml.py +0 -0
  39. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/fsutil.py +0 -0
  40. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/httpconn.py +0 -0
  41. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/ico.py +0 -0
  42. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/mdns.py +0 -0
  43. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/metrics.py +0 -0
  44. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/multicast.py +0 -0
  45. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/pwhash.py +0 -0
  46. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/res/COPYING.txt +0 -0
  47. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/res/__init__.py +0 -0
  48. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/res/insecure.pem +0 -0
  49. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/ssdp.py +0 -0
  50. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/star.py +0 -0
  51. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/__init__.py +0 -0
  52. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/__init__.py +0 -0
  53. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/bimap.py +0 -0
  54. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/bit.py +0 -0
  55. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/buffer.py +0 -0
  56. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/dns.py +0 -0
  57. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/label.py +0 -0
  58. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/lex.py +0 -0
  59. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/dnslib/ranges.py +0 -0
  60. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/ifaddr/__init__.py +0 -0
  61. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/ifaddr/_posix.py +0 -0
  62. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/ifaddr/_shared.py +0 -0
  63. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/ifaddr/_win32.py +0 -0
  64. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/qrcodegen.py +0 -0
  65. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/stolen/surrogateescape.py +0 -0
  66. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/sutil.py +0 -0
  67. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/szip.py +0 -0
  68. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/tcpsrv.py +0 -0
  69. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/th_cli.py +0 -0
  70. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/u2idx.py +0 -0
  71. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/a/__init__.py +0 -0
  72. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/a/partyfuse.py +0 -0
  73. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/a/u2c.py +0 -0
  74. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/a/webdav-cfg.bat +0 -0
  75. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/baguettebox.js.gz +0 -0
  76. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/browser.css.gz +0 -0
  77. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/browser2.html +0 -0
  78. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/cf.html +0 -0
  79. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/dbg-audio.js.gz +0 -0
  80. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/dd/2.png +0 -0
  81. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/dd/3.png +0 -0
  82. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/dd/4.png +0 -0
  83. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/dd/5.png +0 -0
  84. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/dd/__init__.py +0 -0
  85. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/__init__.py +0 -0
  86. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/busy.mp3.gz +0 -0
  87. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/easymde.css.gz +0 -0
  88. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/easymde.js.gz +0 -0
  89. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/fuse.py +0 -0
  90. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/marked.js.gz +0 -0
  91. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/mini-fa.css.gz +0 -0
  92. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/mini-fa.woff +0 -0
  93. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/prism.css.gz +0 -0
  94. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/prism.js.gz +0 -0
  95. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/prismd.css.gz +0 -0
  96. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/scp.woff2 +0 -0
  97. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  98. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  99. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/idp.html +0 -0
  100. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/md.css.gz +0 -0
  101. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/md.html +0 -0
  102. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/md.js.gz +0 -0
  103. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/md2.css.gz +0 -0
  104. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/md2.js.gz +0 -0
  105. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/mde.css.gz +0 -0
  106. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/mde.html +0 -0
  107. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/mde.js.gz +0 -0
  108. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/msg.css.gz +0 -0
  109. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/msg.html +0 -0
  110. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/rups.css.gz +0 -0
  111. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/rups.html +0 -0
  112. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/rups.js.gz +0 -0
  113. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/shares.css.gz +0 -0
  114. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/shares.html +0 -0
  115. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/splash.css.gz +0 -0
  116. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/splash.html +0 -0
  117. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/ui.css.gz +0 -0
  118. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/up2k.js.gz +0 -0
  119. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/util.js.gz +0 -0
  120. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty/web/w.hash.js.gz +0 -0
  121. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty.egg-info/SOURCES.txt +0 -0
  122. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty.egg-info/dependency_links.txt +0 -0
  123. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty.egg-info/entry_points.txt +0 -0
  124. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty.egg-info/requires.txt +0 -0
  125. {copyparty-1.18.5 → copyparty-1.18.7}/copyparty.egg-info/top_level.txt +0 -0
  126. {copyparty-1.18.5 → copyparty-1.18.7}/pyproject.toml +0 -0
  127. {copyparty-1.18.5 → copyparty-1.18.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copyparty
3
- Version: 1.18.5
3
+ Version: 1.18.7
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
@@ -70,7 +70,7 @@ turn almost any device into a file server with resumable uploads/downloads using
70
70
 
71
71
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
72
72
 
73
- 🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm)
73
+ 🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm) // 👉 **[feature-showcase](https://a.ocv.me/pub/demo/showcase-hq.webm)** ([youtube](https://www.youtube.com/watch?v=15_-hgsX2V0))
74
74
 
75
75
  made in Norway 🇳🇴
76
76
 
@@ -138,6 +138,7 @@ made in Norway 🇳🇴
138
138
  * [periodic rescan](#periodic-rescan) - filesystem monitoring
139
139
  * [upload rules](#upload-rules) - set upload rules using volflags
140
140
  * [compress uploads](#compress-uploads) - files can be autocompressed on upload
141
+ * [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
141
142
  * [other flags](#other-flags)
142
143
  * [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
143
144
  * [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
@@ -1497,12 +1498,17 @@ if you enable deduplication with `--dedup` then it'll create a symlink instead o
1497
1498
  **warning:** when enabling dedup, you should also:
1498
1499
  * enable indexing with `-e2dsa` or volflag `e2dsa` (see [file indexing](#file-indexing) section below); strongly recommended
1499
1500
  * ...and/or `--hardlink-only` to use hardlink-based deduplication instead of symlinks; see explanation below
1501
+ * ...and/or `--reflink` to use CoW/reflink-based dedup (much safer than hardlink, but OS/FS-dependent)
1500
1502
 
1501
1503
  it will not be safe to rename/delete files if you only enable dedup and none of the above; if you enable indexing then it is not *necessary* to also do hardlinks (but you may still want to)
1502
1504
 
1503
1505
  by default, deduplication is done based on symlinks (symbolic links); these are tiny files which are pointers to the nearest full copy of the file
1504
1506
 
1505
- you can choose to use hardlinks instead of softlinks, globally with `--hardlink-only` or volflag `hardlinkonly`;
1507
+ you can choose to use hardlinks instead of softlinks, globally with `--hardlink-only` or volflag `hardlinkonly`, and you can choose to use reflinks with `--reflink` or volflag `reflink`
1508
+
1509
+ advantages of using reflinks (CoW, copy-on-write):
1510
+ * entirely safe (when your filesystem supports it correctly); either file can be edited or deleted without affecting other copies
1511
+ * only linux 5.3 or newer, only python 3.14 or newer, only some filesystems (btrfs probably ok, maybe xfs too, but zfs had bugs)
1506
1512
 
1507
1513
  advantages of using hardlinks:
1508
1514
  * hardlinks are more compatible with other software; they behave entirely like regular files
@@ -1702,6 +1708,26 @@ some examples,
1702
1708
  allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`
1703
1709
 
1704
1710
 
1711
+ ## chmod and chown
1712
+
1713
+ per-volume filesystem-permissions and ownership
1714
+
1715
+ by default:
1716
+ * all folders are chmod 755
1717
+ * files are usually chmod 644 (umask-defined)
1718
+ * user/group is whatever copyparty is running as
1719
+
1720
+ this can be configured per-volume:
1721
+ * volflag `chmod_f` sets file permissions; default=`644` (usually)
1722
+ * volflag `chmod_d` sets directory permissions; default=`755`
1723
+ * volflag `uid` sets the owner user-id
1724
+ * volflag `gid` sets the owner group-id
1725
+
1726
+ notes:
1727
+ * `gid` can only be set to one of the groups which the copyparty process is a member of
1728
+ * `uid` can only be set if copyparty is running as root (i appreciate your faith)
1729
+
1730
+
1705
1731
  ## other flags
1706
1732
 
1707
1733
  * `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
@@ -2080,7 +2106,7 @@ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatical
2080
2106
  * **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now
2081
2107
  * depending on server/client, HTTP/1.1 can also be 5x faster than HTTP/2
2082
2108
 
2083
- for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/tmp/party.sock` (permission `770` means only members of group `www` can access it)
2109
+ for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/dev/shm/party.sock` (permission `770` means only members of group `www` can access it)
2084
2110
 
2085
2111
  example webserver / reverse-proxy configs:
2086
2112
 
@@ -2279,6 +2305,7 @@ force-enable features with known issues on your OS/env by setting any of the fo
2279
2305
  | env-var | what it does |
2280
2306
  | ------------------------ | ------------ |
2281
2307
  | `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
2308
+ | `PRTY_FORCE_MAGIC` | use [magic](https://pypi.org/project/python-magic/) on Windows (you will segfault) |
2282
2309
 
2283
2310
 
2284
2311
  # packages
@@ -2301,7 +2328,7 @@ NOTE: there used to be an aur package; this evaporated when copyparty was adopte
2301
2328
 
2302
2329
  ## fedora package
2303
2330
 
2304
- does not exist yet; using the [copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/) builds is **NOT recommended** because updates can be delayed by [several months](https://github.com/fedora-copr/copr/issues/3056)
2331
+ does not exist yet; there are rumours that it is being packaged! keep an eye on this space...
2305
2332
 
2306
2333
 
2307
2334
  ## nix package
@@ -12,7 +12,7 @@ turn almost any device into a file server with resumable uploads/downloads using
12
12
 
13
13
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
14
14
 
15
- 🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm)
15
+ 🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm) // 👉 **[feature-showcase](https://a.ocv.me/pub/demo/showcase-hq.webm)** ([youtube](https://www.youtube.com/watch?v=15_-hgsX2V0))
16
16
 
17
17
  made in Norway 🇳🇴
18
18
 
@@ -80,6 +80,7 @@ made in Norway 🇳🇴
80
80
  * [periodic rescan](#periodic-rescan) - filesystem monitoring
81
81
  * [upload rules](#upload-rules) - set upload rules using volflags
82
82
  * [compress uploads](#compress-uploads) - files can be autocompressed on upload
83
+ * [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
83
84
  * [other flags](#other-flags)
84
85
  * [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
85
86
  * [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
@@ -1439,12 +1440,17 @@ if you enable deduplication with `--dedup` then it'll create a symlink instead o
1439
1440
  **warning:** when enabling dedup, you should also:
1440
1441
  * enable indexing with `-e2dsa` or volflag `e2dsa` (see [file indexing](#file-indexing) section below); strongly recommended
1441
1442
  * ...and/or `--hardlink-only` to use hardlink-based deduplication instead of symlinks; see explanation below
1443
+ * ...and/or `--reflink` to use CoW/reflink-based dedup (much safer than hardlink, but OS/FS-dependent)
1442
1444
 
1443
1445
  it will not be safe to rename/delete files if you only enable dedup and none of the above; if you enable indexing then it is not *necessary* to also do hardlinks (but you may still want to)
1444
1446
 
1445
1447
  by default, deduplication is done based on symlinks (symbolic links); these are tiny files which are pointers to the nearest full copy of the file
1446
1448
 
1447
- you can choose to use hardlinks instead of softlinks, globally with `--hardlink-only` or volflag `hardlinkonly`;
1449
+ you can choose to use hardlinks instead of softlinks, globally with `--hardlink-only` or volflag `hardlinkonly`, and you can choose to use reflinks with `--reflink` or volflag `reflink`
1450
+
1451
+ advantages of using reflinks (CoW, copy-on-write):
1452
+ * entirely safe (when your filesystem supports it correctly); either file can be edited or deleted without affecting other copies
1453
+ * only linux 5.3 or newer, only python 3.14 or newer, only some filesystems (btrfs probably ok, maybe xfs too, but zfs had bugs)
1448
1454
 
1449
1455
  advantages of using hardlinks:
1450
1456
  * hardlinks are more compatible with other software; they behave entirely like regular files
@@ -1644,6 +1650,26 @@ some examples,
1644
1650
  allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`
1645
1651
 
1646
1652
 
1653
+ ## chmod and chown
1654
+
1655
+ per-volume filesystem-permissions and ownership
1656
+
1657
+ by default:
1658
+ * all folders are chmod 755
1659
+ * files are usually chmod 644 (umask-defined)
1660
+ * user/group is whatever copyparty is running as
1661
+
1662
+ this can be configured per-volume:
1663
+ * volflag `chmod_f` sets file permissions; default=`644` (usually)
1664
+ * volflag `chmod_d` sets directory permissions; default=`755`
1665
+ * volflag `uid` sets the owner user-id
1666
+ * volflag `gid` sets the owner group-id
1667
+
1668
+ notes:
1669
+ * `gid` can only be set to one of the groups which the copyparty process is a member of
1670
+ * `uid` can only be set if copyparty is running as root (i appreciate your faith)
1671
+
1672
+
1647
1673
  ## other flags
1648
1674
 
1649
1675
  * `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
@@ -2022,7 +2048,7 @@ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatical
2022
2048
  * **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now
2023
2049
  * depending on server/client, HTTP/1.1 can also be 5x faster than HTTP/2
2024
2050
 
2025
- for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/tmp/party.sock` (permission `770` means only members of group `www` can access it)
2051
+ for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/dev/shm/party.sock` (permission `770` means only members of group `www` can access it)
2026
2052
 
2027
2053
  example webserver / reverse-proxy configs:
2028
2054
 
@@ -2221,6 +2247,7 @@ force-enable features with known issues on your OS/env by setting any of the fo
2221
2247
  | env-var | what it does |
2222
2248
  | ------------------------ | ------------ |
2223
2249
  | `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
2250
+ | `PRTY_FORCE_MAGIC` | use [magic](https://pypi.org/project/python-magic/) on Windows (you will segfault) |
2224
2251
 
2225
2252
 
2226
2253
  # packages
@@ -2243,7 +2270,7 @@ NOTE: there used to be an aur package; this evaporated when copyparty was adopte
2243
2270
 
2244
2271
  ## fedora package
2245
2272
 
2246
- does not exist yet; using the [copr-pypi](https://copr.fedorainfracloud.org/coprs/g/copr/PyPI/) builds is **NOT recommended** because updates can be delayed by [several months](https://github.com/fedora-copr/copr/issues/3056)
2273
+ does not exist yet; there are rumours that it is being packaged! keep an eye on this space...
2247
2274
 
2248
2275
 
2249
2276
  ## nix package
@@ -53,13 +53,13 @@ from .util import (
53
53
  PYFTPD_VER,
54
54
  RAM_AVAIL,
55
55
  RAM_TOTAL,
56
+ RE_ANSI,
56
57
  SQLITE_VER,
57
58
  UNPLICATIONS,
58
59
  URL_BUG,
59
60
  URL_PRJ,
60
61
  Daemon,
61
62
  align_tab,
62
- ansi_re,
63
63
  b64enc,
64
64
  dedent,
65
65
  has_resource,
@@ -161,7 +161,7 @@ def lprint(*a , **ka ) :
161
161
  txt = " ".join(unicode(x) for x in a) + eol
162
162
  printed.append(txt)
163
163
  if not VT100:
164
- txt = ansi_re.sub("", txt)
164
+ txt = RE_ANSI.sub("", txt)
165
165
 
166
166
  print(txt, end="", **ka)
167
167
 
@@ -539,14 +539,15 @@ def get_sects():
539
539
  when running behind a reverse-proxy, it's recommended to
540
540
  use unix-sockets for improved performance and security;
541
541
 
542
- \033[32m-i unix:770:www:\033[33m/tmp/a.sock\033[0m listens on \033[33m/tmp/a.sock\033[0m with
543
- permissions \033[33m0770\033[0m; only accessible to members of the \033[33mwww\033[0m
544
- group. This is the best approach. Alternatively,
542
+ \033[32m-i unix:770:www:\033[33m/dev/shm/party.sock\033[0m listens on
543
+ \033[33m/dev/shm/party.sock\033[0m with permissions \033[33m0770\033[0m;
544
+ only accessible to members of the \033[33mwww\033[0m group.
545
+ This is the best approach. Alternatively,
545
546
 
546
- \033[32m-i unix:777:\033[33m/tmp/a.sock\033[0m sets perms \033[33m0777\033[0m so anyone can
547
- access it; bad unless it's inside a restricted folder
547
+ \033[32m-i unix:777:\033[33m/dev/shm/party.sock\033[0m sets perms \033[33m0777\033[0m so anyone
548
+ can access it; bad unless it's inside a restricted folder
548
549
 
549
- \033[32m-i unix:\033[33m/tmp/a.sock\033[0m keeps umask-defined permissions
550
+ \033[32m-i unix:\033[33m/dev/shm/party.sock\033[0m keeps umask-defined permission
550
551
  (usually \033[33m0600\033[0m) and the same user/group as copyparty
551
552
 
552
553
  \033[33m-p\033[0m (tcp ports) is ignored for unix sockets
@@ -864,31 +865,31 @@ def get_sects():
864
865
 
865
866
  similarly, \033[33m--chmod-d\033[0m and \033[33mchmod_d\033[0m sets the directory/folder perm
866
867
 
867
- the value is a three-digit octal number such as 755, 750, 644, etc.
868
+ the value is a three-digit octal number such as \033[32m755\033[0m, \033[32m750\033[0m, \033[32m644\033[0m, etc.
868
869
 
869
870
  first digit = "User"; permission for the unix-user
870
871
  second digit = "Group"; permission for the unix-group
871
872
  third digit = "Other"; permission for all other users/groups
872
873
 
873
874
  for files:
874
- 0 = --- = no access
875
- 1 = --x = can execute the file as a program
876
- 2 = -w- = can write
877
- 3 = -wx = can write and execute
878
- 4 = r-- = can read
879
- 5 = r-x = can read and execute
880
- 6 = rw- = can read and write
881
- 7 = rwx = can read, write, execute
875
+ \033[32m0\033[0m = \033[35m---\033[0m = no access
876
+ \033[32m1\033[0m = \033[35m--x\033[0m = can execute the file as a program
877
+ \033[32m2\033[0m = \033[35m-w-\033[0m = can write
878
+ \033[32m3\033[0m = \033[35m-wx\033[0m = can write and execute
879
+ \033[32m4\033[0m = \033[35mr--\033[0m = can read
880
+ \033[32m5\033[0m = \033[35mr-x\033[0m = can read and execute
881
+ \033[32m6\033[0m = \033[35mrw-\033[0m = can read and write
882
+ \033[32m7\033[0m = \033[35mrwx\033[0m = can read, write, execute
882
883
 
883
884
  for directories/folders:
884
- 0 = --- = no access
885
- 1 = --x = can read files in folder but not list contents
886
- 2 = -w- = n/a
887
- 3 = -wx = can create files but not list
888
- 4 = r-- = can list, but not read/write
889
- 5 = r-x = can list and read files
890
- 6 = rw- = n/a
891
- 7 = rwx = can read, write, list
885
+ \033[32m0\033[0m = \033[35m---\033[0m = no access
886
+ \033[32m1\033[0m = \033[35m--x\033[0m = can read files in folder but not list contents
887
+ \033[32m2\033[0m = \033[35m-w-\033[0m = n/a
888
+ \033[32m3\033[0m = \033[35m-wx\033[0m = can create files but not list
889
+ \033[32m4\033[0m = \033[35mr--\033[0m = can list, but not read/write
890
+ \033[32m5\033[0m = \033[35mr-x\033[0m = can list and read files
891
+ \033[32m6\033[0m = \033[35mrw-\033[0m = n/a
892
+ \033[32m7\033[0m = \033[35mrwx\033[0m = can read, write, list
892
893
  """
893
894
  ),
894
895
  ],
@@ -1044,10 +1045,13 @@ def add_upload(ap):
1044
1045
  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, ...)")
1045
1046
  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)")
1046
1047
  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)")
1048
+ 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)")
1049
+ 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)")
1047
1050
  ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
1048
1051
  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)")
1049
1052
  ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
1050
1053
  ap2.add_argument("--hardlink-only", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made (volflag=hardlinkonly)")
1054
+ 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)")
1051
1055
  ap2.add_argument("--no-dupe", action="store_true", help="reject duplicate files during upload; only matches within the same volume (volflag=nodupe)")
1052
1056
  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)")
1053
1057
  ap2.add_argument("--no-snap", action="store_true", help="disable snapshots -- forget unfinished uploads on shutdown; don't create .hist/up2k.snap files -- abandoned/interrupted uploads must be cleaned up manually")
@@ -1328,6 +1332,7 @@ def add_safety(ap):
1328
1332
  ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything (volflag=norobots)")
1329
1333
  ap2.add_argument("--logout", metavar="H", type=float, default=8086.0, help="logout clients after \033[33mH\033[0m hours of inactivity; [\033[32m0.0028\033[0m]=10sec, [\033[32m0.1\033[0m]=6min, [\033[32m24\033[0m]=day, [\033[32m168\033[0m]=week, [\033[32m720\033[0m]=month, [\033[32m8760\033[0m]=year)")
1330
1334
  ap2.add_argument("--ban-pw", metavar="N,W,B", type=u, default="9,60,1440", help="more than \033[33mN\033[0m wrong passwords in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
1335
+ ap2.add_argument("--ban-pwc", metavar="N,W,B", type=u, default="5,60,1440", help="more than \033[33mN\033[0m password-changes in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
1331
1336
  ap2.add_argument("--ban-404", metavar="N,W,B", type=u, default="50,60,1440", help="hitting more than \033[33mN\033[0m 404's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; only affects users who cannot see directory listings because their access is either g/G/h")
1332
1337
  ap2.add_argument("--ban-403", metavar="N,W,B", type=u, default="9,2,1440", help="hitting more than \033[33mN\033[0m 403's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week, [\033[32m43200\033[0m]=month")
1333
1338
  ap2.add_argument("--ban-422", metavar="N,W,B", type=u, default="9,2,1440", help="hitting more than \033[33mN\033[0m 422's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes (invalid requests, attempted exploits ++)")
@@ -1544,7 +1549,7 @@ def add_ui(ap, retry):
1544
1549
  ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
1545
1550
  ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
1546
1551
  ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
1547
- ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1552
+ ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1548
1553
  ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
1549
1554
  ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
1550
1555
  ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
@@ -1558,7 +1563,7 @@ def add_ui(ap, retry):
1558
1563
  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)")
1559
1564
  ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty @ --name", help="title / service-name to show in html documents")
1560
1565
  ap2.add_argument("--bname", metavar="TXT", type=u, default="--name", help="server name (displayed in filebrowser document title)")
1561
- ap2.add_argument("--pb-url", metavar="URL", type=u, default=URL_PRJ, help="powered-by link; disable with \033[33m-np\033[0m")
1566
+ ap2.add_argument("--pb-url", metavar="URL", type=u, default=URL_PRJ, help="powered-by link; disable with \033[33m-nb\033[0m")
1562
1567
  ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)")
1563
1568
  ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
1564
1569
  ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 18, 5)
3
+ VERSION = (1, 18, 7)
4
4
  CODENAME = "logtail"
5
- BUILD_DT = (2025, 7, 28)
5
+ BUILD_DT = (2025, 7, 30)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -33,6 +33,7 @@ from .util import (
33
33
  afsenc,
34
34
  get_df,
35
35
  humansize,
36
+ json_hesc,
36
37
  min_ex,
37
38
  odfusion,
38
39
  read_utf8,
@@ -63,6 +64,25 @@ if PY2:
63
64
 
64
65
 
65
66
  LEELOO_DALLAS = "leeloo_dallas"
67
+ ##
68
+ ## you might be curious what Leeloo Dallas is doing here, so let me explain:
69
+ ##
70
+ ## certain daemonic tasks, namely:
71
+ ## * deletion of expired files, running on a timer
72
+ ## * deletion of sidecar files, initiated by plugins
73
+ ## need to skip the usual permission-checks to do their thing,
74
+ ## so we let Leeloo handle these
75
+ ##
76
+ ## and also, the smb-server has really shitty support for user-accounts
77
+ ## so one popular way to avoid issues is by running copyparty without users;
78
+ ## this makes all smb-clients identify as LD to gain unrestricted access
79
+ ##
80
+ ## Leeloo, being a fictional character from The Fifth Element,
81
+ ## obviously does not exist and will never be able to access any copyparty
82
+ ## instances from the outside (the username is rejected at every entrypoint)
83
+ ##
84
+ ## thanks for coming to my ted talk
85
+
66
86
 
67
87
  SEE_LOG = "see log for details"
68
88
  SEESLOG = " (see serverlog for details)"
@@ -114,6 +134,8 @@ class Lim(object):
114
134
  self.reg = None # up2k registry
115
135
 
116
136
  self.chmod_d = 0o755
137
+ self.uid = self.gid = -1
138
+ self.chown = False
117
139
 
118
140
  self.nups = {} # num tracker
119
141
  self.bups = {} # byte tracker list
@@ -276,6 +298,8 @@ class Lim(object):
276
298
  # no branches yet; make one
277
299
  sub = os.path.join(path, "0")
278
300
  bos.mkdir(sub, self.chmod_d)
301
+ if self.chown:
302
+ os.chown(sub, self.uid, self.gid)
279
303
  else:
280
304
  # try newest branch only
281
305
  sub = os.path.join(path, str(dirs[-1]))
@@ -291,6 +315,8 @@ class Lim(object):
291
315
  # make a branch
292
316
  sub = os.path.join(path, str(dirs[-1] + 1))
293
317
  bos.mkdir(sub, self.chmod_d)
318
+ if self.chown:
319
+ os.chown(sub, self.uid, self.gid)
294
320
  ret = self.dive(sub, lvs - 1)
295
321
  if ret is None:
296
322
  raise Pebkac(500, "rotation bug")
@@ -2115,6 +2141,7 @@ class AuthSrv(object):
2115
2141
  all_mte = {}
2116
2142
  errors = False
2117
2143
  free_umask = False
2144
+ have_reflink = False
2118
2145
  for vol in vfs.all_nodes.values():
2119
2146
  if (self.args.e2ds and vol.axs.uwrite) or self.args.e2dsa:
2120
2147
  vol.flags["e2ds"] = True
@@ -2152,7 +2179,7 @@ class AuthSrv(object):
2152
2179
  if vf not in vol.flags:
2153
2180
  vol.flags[vf] = getattr(self.args, ga)
2154
2181
 
2155
- zs = "forget_ip nrand tail_who u2abort u2ow ups_who zip_who"
2182
+ zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
2156
2183
  for k in zs.split():
2157
2184
  if k in vol.flags:
2158
2185
  vol.flags[k] = int(vol.flags[k])
@@ -2189,8 +2216,17 @@ class AuthSrv(object):
2189
2216
  if (is_d and zi != 0o755) or not is_d:
2190
2217
  free_umask = True
2191
2218
 
2219
+ vol.flags.pop("chown", None)
2220
+ if vol.flags["uid"] != -1 or vol.flags["gid"] != -1:
2221
+ vol.flags["chown"] = True
2222
+ vol.flags.pop("fperms", None)
2223
+ if "chown" in vol.flags or vol.flags.get("chmod_f"):
2224
+ vol.flags["fperms"] = True
2192
2225
  if vol.lim:
2193
2226
  vol.lim.chmod_d = vol.flags["chmod_d"]
2227
+ vol.lim.chown = "chown" in vol.flags
2228
+ vol.lim.uid = vol.flags["uid"]
2229
+ vol.lim.gid = vol.flags["gid"]
2194
2230
 
2195
2231
  if vol.flags.get("og"):
2196
2232
  self.args.uqe = True
@@ -2198,6 +2234,9 @@ class AuthSrv(object):
2198
2234
  if "unlistcr" in vol.flags or "unlistcw" in vol.flags:
2199
2235
  self.args.have_unlistc = True
2200
2236
 
2237
+ if "reflink" in vol.flags:
2238
+ have_reflink = True
2239
+
2201
2240
  zs = str(vol.flags.get("tcolor", "")).lstrip("#")
2202
2241
  if len(zs) == 3: # fc5 => ffcc55
2203
2242
  vol.flags["tcolor"] = "".join([x * 2 for x in zs])
@@ -2562,6 +2601,13 @@ class AuthSrv(object):
2562
2601
  t = "WARNING! The following IdP volumes are mounted below another volume where other users can read and/or write files. This is a SECURITY HAZARD!! When copyparty is restarted, it will not know about these IdP volumes yet. These volumes will then be accessible by an unexpected set of permissions UNTIL one of the users associated with their volume sends a request to the server. RECOMMENDATION: You should create a restricted volume where nobody can read/write files, and make sure that all IdP volumes are configured to appear somewhere below that volume."
2563
2602
  self.log(t + "".join(self.idp_err), 1)
2564
2603
 
2604
+ if have_reflink:
2605
+ t = "WARNING: Reflink-based dedup was requested, but %s. This will not work; files will be full copies instead."
2606
+ if sys.version_info < (3, 14):
2607
+ self.log(t % "your python version is not new enough", 1)
2608
+ if not sys.platform.startswith("linux"):
2609
+ self.log(t % "your OS is not Linux", 1)
2610
+
2565
2611
  self.vfs = vfs
2566
2612
  self.acct = acct
2567
2613
  self.defpw = defpw
@@ -2731,7 +2777,7 @@ class AuthSrv(object):
2731
2777
  "lifetime": vn.js_ls["lifetime"],
2732
2778
  "u2sort": self.args.u2sort,
2733
2779
  }
2734
- vn.js_htm = json.dumps(js_htm)
2780
+ vn.js_htm = json_hesc(json.dumps(js_htm))
2735
2781
 
2736
2782
  vols = list(vfs.all_nodes.values())
2737
2783
  if enshare:
@@ -6,8 +6,11 @@ import os
6
6
  from ..util import SYMTIME, fsdec, fsenc
7
7
  from . import path as path
8
8
 
9
- _ = (path,)
10
- __all__ = ["path"]
9
+ MKD_755 = {"chmod_d": 0o755}
10
+ MKD_700 = {"chmod_d": 0o700}
11
+
12
+ _ = (path, MKD_755, MKD_700)
13
+ __all__ = ["path", "MKD_755", "MKD_700"]
11
14
 
12
15
  # grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
13
16
  # printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
@@ -17,11 +20,15 @@ def chmod(p , mode ) :
17
20
  return os.chmod(fsenc(p), mode)
18
21
 
19
22
 
23
+ def chown(p , uid , gid ) :
24
+ return os.chown(fsenc(p), uid, gid)
25
+
26
+
20
27
  def listdir(p = ".") :
21
28
  return [fsdec(x) for x in os.listdir(fsenc(p))]
22
29
 
23
30
 
24
- def makedirs(name , mode = 0o755, exist_ok = True) :
31
+ def makedirs(name , vf = MKD_755, exist_ok = True) :
25
32
  # os.makedirs does 777 for all but leaf; this does mode on all
26
33
  todo = []
27
34
  bname = fsenc(name)
@@ -34,9 +41,13 @@ def makedirs(name , mode = 0o755, exist_ok = True) :
34
41
  if not exist_ok:
35
42
  os.mkdir(bname) # to throw
36
43
  return False
44
+ mode = vf["chmod_d"]
45
+ chown = "chown" in vf
37
46
  for zb in todo[::-1]:
38
47
  try:
39
48
  os.mkdir(zb, mode)
49
+ if chown:
50
+ os.chown(zb, vf["uid"], vf["gid"])
40
51
  except:
41
52
  if os.path.isdir(zb):
42
53
  continue
@@ -52,6 +52,7 @@ def vf_bmap() :
52
52
  "og_no_head",
53
53
  "og_s_title",
54
54
  "rand",
55
+ "reflink",
55
56
  "rmagic",
56
57
  "rss",
57
58
  "wo_up_readme",
@@ -113,6 +114,8 @@ def vf_vmap() :
113
114
  "unlist",
114
115
  "u2abort",
115
116
  "u2ts",
117
+ "uid",
118
+ "gid",
116
119
  "ups_who",
117
120
  "zip_who",
118
121
  "zipmaxn",
@@ -168,11 +171,14 @@ flagcats = {
168
171
  "dedup": "enable symlink-based file deduplication",
169
172
  "hardlink": "enable hardlink-based file deduplication,\nwith fallback on symlinks when that is impossible",
170
173
  "hardlinkonly": "dedup with hardlink only, never symlink;\nmake a full copy if hardlink is impossible",
174
+ "reflink": "enable reflink-based file deduplication,\nwith fallback on full copy when that is impossible",
171
175
  "safededup": "verify on-disk data before using it for dedup",
172
176
  "noclone": "take dupe data from clients, even if available on HDD",
173
177
  "nodupe": "rejects existing files (instead of linking/cloning them)",
174
178
  "chmod_d=755": "unix-permission for new dirs/folders",
175
179
  "chmod_f=644": "unix-permission for new files",
180
+ "uid=573": "change owner of new files/folders to unix-user 573",
181
+ "gid=999": "change owner of new files/folders to unix-group 999",
176
182
  "sparse": "force use of sparse files, mainly for s3-backed storage",
177
183
  "nosparse": "deny use of sparse files, mainly for slow storage",
178
184
  "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
@@ -31,6 +31,7 @@ from .util import (
31
31
  relchk,
32
32
  runhook,
33
33
  sanitize_fn,
34
+ set_fperms,
34
35
  vjoin,
35
36
  wunlink,
36
37
  )
@@ -258,8 +259,8 @@ class FtpFs(AbstractedFS):
258
259
  wunlink(self.log, ap, VF_CAREFUL)
259
260
 
260
261
  ret = open(fsenc(ap), mode, self.args.iobuf)
261
- if w and "chmod_f" in vfs.flags:
262
- os.fchmod(ret.fileno(), vfs.flags["chmod_f"])
262
+ if w and "fperms" in vfs.flags:
263
+ set_fperms(ret, vfs.flags)
263
264
 
264
265
  return ret
265
266
 
@@ -293,8 +294,7 @@ class FtpFs(AbstractedFS):
293
294
 
294
295
  def mkdir(self, path ) :
295
296
  ap, vfs, _ = self.rv2a(path, w=True)
296
- chmod = vfs.flags["chmod_d"]
297
- bos.makedirs(ap, chmod) # filezilla expects this
297
+ bos.makedirs(ap, vf=vfs.flags) # filezilla expects this
298
298
 
299
299
  def listdir(self, path ) :
300
300
  vpath = join(self.cwd, path)