Last active
January 5, 2023 06:28
-
-
Save 0xquad/c4a8428e55fbff0965e2fb4bf75f9eb9 to your computer and use it in GitHub Desktop.
Adjust uid/gid on all files and directories under a given root.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Adjust uid/gid on all files and directories under a given root. | |
# Adjustment can be made conditional on a minimum uid/gid. Adjustments | |
# can be either absolute or relative. | |
# | |
# Copyright (c) 2018-2023, Alexandre Hamelin <alexandre.hamelin gmail.com> | |
# Published under the MIT license. | |
import sys, os | |
from pathlib import Path | |
def adjown(file, stat, uid_adj, gid_adj): | |
if uid_adj is not None: | |
to_uid = stat.st_uid + uid_adj.value() if uid_adj.is_offset() else uid_adj.value() | |
if gid_adj is not None: | |
to_gid = stat.st_gid + gid_adj.value() if gid_adj.is_offset() else gid_adj.value() | |
if uid_adj is not None or gid_adj is not None: | |
if uid_adj is None: to_uid = stat.st_uid | |
if gid_adj is None: to_gid = stat.st_gid | |
os.chown(file, to_uid, to_gid, follow_symlinks=False) | |
#print(f'chown({file}, {stat.st_uid}->{to_uid}, {stat.st_gid}->{to_gid})') | |
#else: | |
# print(f'chown({file}) NOT CALLED') | |
class IdAdjustment: | |
def __init__(self, arg): | |
self._value = self._is_offset = None | |
self._parse(arg) | |
def is_offset(self): | |
return self._is_offset | |
def value(self): | |
return self._value | |
def _parse(self, arg): | |
self._value = int(arg) | |
self._is_offset = arg[0] in '+-' | |
if __name__ == '__main__': | |
if len(sys.argv) < 4 or len(sys.argv) == 5 or sys.argv[0] in ('-h', '--help', 'help'): | |
print(f'usage: {sys.argv[0]} rootpath [+|-]uid [+|-]gid [min-uid min-gid]') | |
raise SystemExit | |
rootpath = sys.argv[1] | |
uid_adj = IdAdjustment(sys.argv[2]) | |
gid_adj = IdAdjustment(sys.argv[3]) | |
uid_min = gid_min = None | |
if len(sys.argv) > 5: | |
uid_min = int(sys.argv[4]) | |
gid_min = int(sys.argv[5]) | |
for basedir, dirs, files in os.walk(rootpath): | |
stat = os.lstat(basedir) | |
eff_uid_adj = uid_adj if uid_min is None or stat.st_uid >= uid_min else None | |
eff_gid_adj = gid_adj if gid_min is None or stat.st_gid >= gid_min else None | |
adjown(basedir, stat, eff_uid_adj, eff_gid_adj) | |
# symlinks to directories are not reported as files, but rather as directories, | |
# and we don't handle directories here, so add them manually to files[] | |
files += [d for d in dirs if Path(basedir, d).is_symlink()] | |
for f in files: | |
fpath = Path(basedir, f) | |
stat = os.lstat(fpath) | |
eff_uid_adj = uid_adj if uid_min is None or stat.st_uid >= uid_min else None | |
eff_gid_adj = gid_adj if gid_min is None or stat.st_gid >= gid_min else None | |
adjown(fpath, stat, eff_uid_adj, eff_gid_adj) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment