Skip to content

Instantly share code, notes, and snippets.

@0xquad
Last active January 5, 2023 06:28
Show Gist options
  • Save 0xquad/c4a8428e55fbff0965e2fb4bf75f9eb9 to your computer and use it in GitHub Desktop.
Save 0xquad/c4a8428e55fbff0965e2fb4bf75f9eb9 to your computer and use it in GitHub Desktop.
Adjust uid/gid on all files and directories under a given root.
#!/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