Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 13d23b4

Browse files
committed
chown: Add chown.py
1 parent 6383f56 commit 13d23b4

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed

userland/utilities/chown.py

+225
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import re
2+
3+
import shutil
4+
import sys
5+
from pathlib import Path
6+
7+
from tqdm import tqdm
8+
9+
from .. import core
10+
11+
12+
CHOWN_PATTERN = re.compile("^([^:]+)?(:([^:]+))?$")
13+
14+
parser = core.create_parser(
15+
usage=(
16+
"%prog [OPTION]... [USER][:[GROUP]] FILE...",
17+
"%prog [OPTION]... --reference=RFILE FILE...",
18+
),
19+
description="Change the user and/or group ownership of each FILE.",
20+
)
21+
22+
parser.add_option(
23+
"-f",
24+
"--silent",
25+
"--quiet",
26+
dest="verbosity",
27+
action="store_const",
28+
const=0,
29+
default=1,
30+
help="suppress most error messages",
31+
)
32+
parser.add_option(
33+
"-c",
34+
"--changes",
35+
dest="verbosity",
36+
action="store_const",
37+
const=2,
38+
help="report only when changes are made",
39+
)
40+
parser.add_option(
41+
"-v",
42+
"--verbose",
43+
dest="verbosity",
44+
action="store_const",
45+
const=3,
46+
help="print a diagnostic for each file",
47+
)
48+
49+
parser.add_option(
50+
"--progress",
51+
dest="progress",
52+
action="store_true",
53+
help="show a progress bar when changing groups",
54+
)
55+
parser.add_option(
56+
"--no-progress",
57+
dest="progress",
58+
action="store_false",
59+
default=False,
60+
help="do not show a progress bar (default)",
61+
)
62+
63+
parser.add_option(
64+
"--dereference",
65+
action="store_true",
66+
default=True,
67+
help="affect symlink referents instead of the symlinks themselves (default)",
68+
)
69+
parser.add_option(
70+
"-h",
71+
"--no-dereference",
72+
dest="dereference",
73+
action="store_false",
74+
help="opposite of --dereference",
75+
)
76+
77+
parser.add_option(
78+
"--no-preserve-root",
79+
dest="preserve_root",
80+
action="store_false",
81+
default=False,
82+
help="do not treat '/' specially (default)",
83+
)
84+
parser.add_option(
85+
"--preserve-root",
86+
dest="preserve_root",
87+
action="store_true",
88+
help="fail to operate recursively on '/'",
89+
)
90+
91+
parser.add_option(
92+
"--from",
93+
dest="from_spec", # prevent name collision with the `from` keyword
94+
metavar="[CURRENT_OWNER][:[CURRENT_GROUP]]",
95+
help="only affect files with CURRENT_OWNER and CURRENT_GROUP"
96+
" (either is optional and only checked if given)",
97+
)
98+
99+
parser.add_option("--reference", metavar="RFILE")
100+
101+
parser.add_option(
102+
"-R", "--recursive", action="store_true", help="operate on directories recursively"
103+
)
104+
parser.add_option(
105+
"-H",
106+
dest="recurse_mode",
107+
action="store_const",
108+
const="H",
109+
help="traverse directory symlinks only if they were given as command line arguments",
110+
)
111+
parser.add_option(
112+
"-L",
113+
dest="recurse_mode",
114+
action="store_const",
115+
const="L",
116+
help="traverse all directory symlinks encountered",
117+
)
118+
parser.add_option(
119+
"-P",
120+
dest="recurse_mode",
121+
action="store_const",
122+
const="P",
123+
default="P",
124+
help="do not traverse any symlinks (default)",
125+
)
126+
127+
128+
@core.command(parser)
129+
def python_userland_chown(opts, args):
130+
if not args:
131+
parser.error("missing operand")
132+
133+
from_uid: int | None = None
134+
from_gid: int | None = None
135+
136+
if opts.from_spec:
137+
from_uid, from_gid = core.parse_onwer_spec(parser, opts.from_spec)
138+
139+
chown_args = {"follow_symlinks": opts.dereference}
140+
141+
if opts.reference:
142+
try:
143+
ref_stat = Path(opts.reference).stat(follow_symlinks=True)
144+
except OSError as e:
145+
print(e, file=sys.stderr)
146+
return 1
147+
148+
chown_args["user"] = ref_stat.st_uid
149+
chown_args["group"] = ref_stat.st_gid
150+
else:
151+
owner_spec = args.pop(0)
152+
153+
if not args:
154+
parser.error(f"missing operand after '{owner_spec}'")
155+
156+
if not (owner_match := CHOWN_PATTERN.match(owner_spec)):
157+
parser.error(f"invalid owner spec: {owner_spec}")
158+
159+
chown_args["user"] = (
160+
core.parse_user(parser, owner_match.group(1))
161+
if owner_match.group(1)
162+
else None
163+
)
164+
chown_args["group"] = (
165+
core.parse_group(parser, owner_match.group(3))
166+
if owner_match.group(3)
167+
else None
168+
)
169+
170+
failed = False
171+
172+
for file in core.traverse_files(
173+
(tqdm(args, ascii=True, desc="Changing ownership") if opts.progress else args),
174+
recurse_mode=opts.recurse_mode if opts.recursive else None,
175+
preserve_root=opts.preserve_root,
176+
):
177+
if not file:
178+
failed = True
179+
continue
180+
181+
try:
182+
stat = file.stat(follow_symlinks=opts.dereference)
183+
prev_uid = stat.st_uid
184+
prev_gid = stat.st_gid
185+
except OSError as e:
186+
failed = True
187+
print(e, file=sys.stderr)
188+
print(
189+
f"failed to change ownership of '{file}' to {owner_spec}",
190+
file=sys.stderr,
191+
)
192+
continue
193+
194+
prev_uname = core.user_display_name_from_id(prev_uid)
195+
prev_gname = core.group_display_name_from_id(prev_gid)
196+
197+
if (from_uid is not None and prev_uid != from_uid) or (
198+
from_gid is not None and prev_gid != from_gid
199+
):
200+
if opts.verbosity > 2:
201+
print(f"ownership of '{file}' retained as {prev_uname}:{prev_gname}")
202+
continue
203+
204+
try:
205+
shutil.chown(file, **chown_args)
206+
except OSError as e:
207+
failed = True
208+
if opts.verbosity:
209+
print(e, file=sys.stderr)
210+
print(
211+
f"failed to change ownership of '{file}' to {owner_spec}",
212+
file=sys.stderr,
213+
)
214+
continue
215+
216+
if prev_uid == chown_args["user"] or prev_gid == chown_args["group"]:
217+
if opts.verbosity > 2:
218+
print(f"ownership of '{file}' retained as {prev_uname}:{prev_gname}")
219+
elif opts.verbosity > 1:
220+
print(
221+
f"changed ownership of '{file}' from"
222+
f" {prev_uname}:{prev_gname} to {owner_spec}"
223+
)
224+
225+
return int(failed)

0 commit comments

Comments
 (0)