-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathset-permissions
executable file
·100 lines (82 loc) · 3.13 KB
/
set-permissions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#!/usr/bin/env python3
# Copyright 2018 Joseph Lorimer <joseph@lorimer.me>
#
# Permission to use, copy, modify, and distribute this software for any purpose
# with or without fee is hereby granted, provided that the above copyright
# notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
from os import W_OK, access, chmod, stat, walk
from os.path import isfile, islink, join
from shutil import chown
from click import argument, command, option, secho
from magic import from_file as magic
@command()
@argument('perms')
@option('-d', 'start', default='.', help='Root directory to descend into.')
@option('-e', is_flag=True, default=False,
help='If file is executable, keep owner execute bit.')
def main(perms, start, e):
"""Sets user, group and read-write-execute permissions recursively.
Permission format is user:group:dirmode:filemode.
"""
user, group, dmode, fmode = perms.split(':')
for root, dirs, files in walk(start):
for d in dirs:
p = join(root, d)
if no_access(p):
continue
change_owner(p, user, group)
change_mode(p, dmode)
for f in files:
p = join(root, f)
if no_access(p):
continue
if islink(p) and not isfile(p):
error('broken symbolic link', p)
continue
is_exec = magic(p).find('executable') != -1 if e else False
change_owner(p, user, group)
change_mode(p, fmode, is_exec)
def no_access(path):
if not access(path, W_OK):
warn('no write access', path)
return True
return False
def change_owner(path, user, group):
if user or group:
try:
chown(path, user=user, group=group)
except LookupError:
error('user or group does not exist')
except OSError as e:
warn(e.strerror.lower(), e.filename)
def change_mode(path, new_mode, is_exec=False):
cur_mode = int(str(oct(stat(path).st_mode))[-3:], 8)
new_mode = int(new_mode, 8)
if is_exec:
exec_set = new_mode & 0o111
if not exec_set:
new_mode |= 0o100
if cur_mode != new_mode:
secho(
'{}: {:o} => {:o}'.format(path, cur_mode, new_mode),
err=True,
fg='green'
)
chmod(path, new_mode)
def error(msg, path=None):
prefix = '{}: error'.format(path) if path else 'fatal error'
secho('{}: {}'.format(prefix, msg), err=True, fg='red')
exit(1)
def warn(msg, path):
prefix = '{}: warning'.format(path)
secho('{}: {}'.format(prefix, msg), err=True, fg='yellow')
if __name__ == '__main__':
main()