summaryrefslogtreecommitdiff
path: root/hosts/vidhar/borg/copy.py
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2022-02-18 11:31:28 +0100
committerGregor Kleen <gkleen@yggdrasil.li>2022-02-18 11:31:28 +0100
commit29a65ee80c474b57f4fce974ff5040996220f77e (patch)
tree11dcdc66fd15d27add320b7079b9b4addb563a09 /hosts/vidhar/borg/copy.py
parent906594e63bbc28d6c0c354ab8dafa4a7b6042faf (diff)
downloadnixos-29a65ee80c474b57f4fce974ff5040996220f77e.tar
nixos-29a65ee80c474b57f4fce974ff5040996220f77e.tar.gz
nixos-29a65ee80c474b57f4fce974ff5040996220f77e.tar.bz2
nixos-29a65ee80c474b57f4fce974ff5040996220f77e.tar.xz
nixos-29a65ee80c474b57f4fce974ff5040996220f77e.zip
vidhar: copy-borg: setuid
Diffstat (limited to 'hosts/vidhar/borg/copy.py')
-rwxr-xr-xhosts/vidhar/borg/copy.py25
1 files changed, 19 insertions, 6 deletions
diff --git a/hosts/vidhar/borg/copy.py b/hosts/vidhar/borg/copy.py
index 0f39b495..1efeaef4 100755
--- a/hosts/vidhar/borg/copy.py
+++ b/hosts/vidhar/borg/copy.py
@@ -21,6 +21,8 @@ from xdg import xdg_runtime_dir
21import pathlib 21import pathlib
22 22
23import unshare 23import unshare
24from pyprctl import cap_permitted, cap_inheritable, cap_effective, cap_ambient, Cap
25from pwd import getpwnam
24 26
25import signal 27import signal
26from time import sleep 28from time import sleep
@@ -41,10 +43,20 @@ halo_args = {
41 'spinner': 'arc' 43 'spinner': 'arc'
42} 44}
43 45
46borg_pwd = getpwnam('borg')
47
48def as_borg(caps=set()):
49 if caps:
50 for capset in [cap_permitted, cap_inheritable, cap_effective, cap_ambient]:
51 capset.add(*caps)
52
53 os.setuid(borg_pwd.pw_uid)
54 os.setgid(borg_pwd.pw_gid)
55
44def read_repo(path): 56def read_repo(path):
45 with Halo(text=f'Listing {path}', **halo_args) as sp: 57 with Halo(text=f'Listing {path}', **halo_args) as sp:
46 res = None 58 res = None
47 with subprocess.Popen(['borg', 'list', '--info', '--lock-wait=600', '--json', path], stdout=subprocess.PIPE) as proc: 59 with subprocess.Popen(['borg', 'list', '--info', '--lock-wait=600', '--json', path], stdout=subprocess.PIPE, preexec_fn=lambda: as_borg()) as proc:
48 res = json.load(proc.stdout)['archives'] 60 res = json.load(proc.stdout)['archives']
49 if sp.enabled: 61 if sp.enabled:
50 sp.succeed(f'{len(res)} archives in {path}') 62 sp.succeed(f'{len(res)} archives in {path}')
@@ -83,7 +95,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
83 match = re.compile('^(.*)-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.(checkpoint|recreate)(\.[0-9]+)?)?').fullmatch(entry['name']) 95 match = re.compile('^(.*)-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.(checkpoint|recreate)(\.[0-9]+)?)?').fullmatch(entry['name'])
84 if match: 96 if match:
85 repo_id = None 97 repo_id = None
86 with subprocess.Popen(['borg', 'info', '--info', '--lock-wait=600', '--json', src_repo_path], stdout=subprocess.PIPE) as proc: 98 with subprocess.Popen(['borg', 'info', '--info', '--lock-wait=600', '--json', src_repo_path], stdout=subprocess.PIPE, preexec_fn=lambda: as_borg()) as proc:
87 repo_id = json.load(proc.stdout)['repository']['id'] 99 repo_id = json.load(proc.stdout)['repository']['id']
88 if repo_id: 100 if repo_id:
89 cache_suffix = f'{repo_id}_{match.group(1)}' 101 cache_suffix = f'{repo_id}_{match.group(1)}'
@@ -119,11 +131,12 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
119 os.chroot(chroot) 131 os.chroot(chroot)
120 os.chdir('/') 132 os.chdir('/')
121 dir = pathlib.Path('/borg') 133 dir = pathlib.Path('/borg')
122 dir.mkdir(parents=True,exist_ok=True) 134 dir.mkdir(parents=True,exist_ok=True,mode=750)
135 os.chown(dir, borg_pwd.pw_uid, borg_pwd.pw_gid)
123 with Halo(text=f'Determine size', **halo_args) as sp: 136 with Halo(text=f'Determine size', **halo_args) as sp:
124 total_size = None 137 total_size = None
125 total_files = None 138 total_files = None
126 with subprocess.Popen(['borg', 'info', '--info', '--json', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}'], stdout=subprocess.PIPE, text=True) as proc: 139 with subprocess.Popen(['borg', 'info', '--info', '--json', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}'], stdout=subprocess.PIPE, text=True, preexec_fn=lambda: as_borg()) as proc:
127 stats = json.load(proc.stdout)['archives'][0]['stats'] 140 stats = json.load(proc.stdout)['archives'][0]['stats']
128 total_size = stats['original_size'] 141 total_size = stats['original_size']
129 total_files = stats['nfiles'] 142 total_files = stats['nfiles']
@@ -132,7 +145,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
132 else: 145 else:
133 print(f'{total_files} files, {naturalsize(total_size, binary=True)}', file=stderr) 146 print(f'{total_files} files, {naturalsize(total_size, binary=True)}', file=stderr)
134 # print(f'Mounting to {dir}', file=stderr) 147 # print(f'Mounting to {dir}', file=stderr)
135 with subprocess.Popen(['borg', 'mount', '--foreground', '--progress', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}', dir]) as mount_proc: 148 with subprocess.Popen(['borg', 'mount', '--foreground', '--progress', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}', dir], preexec_fn=lambda: as_borg()) as mount_proc:
136 with Halo(text='Waiting for mount', **halo_args) as sp: 149 with Halo(text='Waiting for mount', **halo_args) as sp:
137 wait_start = datetime.now() 150 wait_start = datetime.now()
138 while True: 151 while True:
@@ -173,7 +186,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
173 else: 186 else:
174 create_args += ['--files-cache=disabled'] 187 create_args += ['--files-cache=disabled']
175 create_args += [f'{dst_repo_path}::{entry["name"]}', '.'] 188 create_args += [f'{dst_repo_path}::{entry["name"]}', '.']
176 with subprocess.Popen(create_args, cwd=dir, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True, env=env) as proc: 189 with subprocess.Popen(create_args, cwd=dir, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True, env=env, preexec_fn=lambda: as_borg(caps={Cap.DAC_READ_SEARCH})) as proc:
177 last_list = None 190 last_list = None
178 last_list_time = None 191 last_list_time = None
179 for line in proc.stderr: 192 for line in proc.stderr: