copyparty 1.18.5__py3-none-any.whl → 1.18.7__py3-none-any.whl
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/__main__.py +32 -27
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +48 -2
- copyparty/bos/bos.py +14 -3
- copyparty/cfg.py +6 -0
- copyparty/ftpd.py +4 -4
- copyparty/httpcli.py +75 -53
- copyparty/httpsrv.py +1 -0
- copyparty/mtag.py +3 -2
- copyparty/smbd.py +1 -1
- copyparty/svchub.py +4 -3
- copyparty/tftpd.py +6 -3
- copyparty/th_srv.py +2 -2
- copyparty/up2k.py +14 -8
- copyparty/util.py +62 -22
- copyparty/web/browser.html +2 -2
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +16 -4
- copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.18.5.dist-info → copyparty-1.18.7.dist-info}/METADATA +32 -5
- {copyparty-1.18.5.dist-info → copyparty-1.18.7.dist-info}/RECORD +27 -27
- {copyparty-1.18.5.dist-info → copyparty-1.18.7.dist-info}/WHEEL +0 -0
- {copyparty-1.18.5.dist-info → copyparty-1.18.7.dist-info}/entry_points.txt +0 -0
- {copyparty-1.18.5.dist-info → copyparty-1.18.7.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.18.5.dist-info → copyparty-1.18.7.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -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 =
|
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/
|
543
|
-
|
544
|
-
|
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/
|
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/
|
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
|
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
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
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
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
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-
|
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")
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -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:
|
copyparty/bos/bos.py
CHANGED
@@ -6,8 +6,11 @@ import os
|
|
6
6
|
from ..util import SYMTIME, fsdec, fsenc
|
7
7
|
from . import path as path
|
8
8
|
|
9
|
-
|
10
|
-
|
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 ,
|
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
|
copyparty/cfg.py
CHANGED
@@ -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",
|
copyparty/ftpd.py
CHANGED
@@ -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 "
|
262
|
-
|
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
|
-
|
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)
|