|
32 | 32 | import glob |
33 | 33 | import errno |
34 | 34 | import pwd |
| 35 | +import stat |
| 36 | +from typing import Tuple, Optional, Union |
35 | 37 |
|
36 | 38 | from enum import Enum |
37 | 39 | from pathlib import Path |
@@ -1168,14 +1170,46 @@ def _stat_info(self, path: str) -> dict: |
1168 | 1170 | except Exception as e: |
1169 | 1171 | return {"error": str(e)} |
1170 | 1172 |
|
1171 | | - def _try_open(self, path: str): |
| 1173 | + def _has_read_access(self, path: str) -> Tuple[bool, Optional[int], Optional[str]]: |
| 1174 | + """ |
| 1175 | + Check whether the current (real/effective) user can read the given path |
| 1176 | + without opening it. Returns (ok:bool, errno_or_None, message_or_None) |
| 1177 | + """ |
1172 | 1178 | try: |
1173 | | - fd = os.open(path, os.O_RDONLY) # Only read access is needed for permission check |
1174 | | - os.close(fd) |
1175 | | - return True, None, None |
| 1179 | + st = os.stat(path) |
1176 | 1180 | except OSError as e: |
1177 | 1181 | return False, e.errno, e.strerror |
1178 | 1182 |
|
| 1183 | + # root can always read |
| 1184 | + if os.geteuid() == 0: |
| 1185 | + return True, None, None |
| 1186 | + |
| 1187 | + mode = st.st_mode |
| 1188 | + uid = st.st_uid |
| 1189 | + gid = st.st_gid |
| 1190 | + |
| 1191 | + euid = os.geteuid() |
| 1192 | + egid = os.getegid() |
| 1193 | + groups = os.getgroups() |
| 1194 | + |
| 1195 | + # owner |
| 1196 | + if euid == uid: |
| 1197 | + if mode & stat.S_IRUSR: |
| 1198 | + return True, None, None |
| 1199 | + return False, errno.EACCES, "Permission denied (owner)" |
| 1200 | + |
| 1201 | + # group |
| 1202 | + if gid == egid or gid in groups: |
| 1203 | + if mode & stat.S_IRGRP: |
| 1204 | + return True, None, None |
| 1205 | + return False, errno.EACCES, "Permission denied (group)" |
| 1206 | + |
| 1207 | + # other |
| 1208 | + if mode & stat.S_IROTH: |
| 1209 | + return True, None, None |
| 1210 | + |
| 1211 | + return False, errno.EACCES, "Permission denied (other)" |
| 1212 | + |
1179 | 1213 | def check_required_groups(self, check_render=True, check_video=True): |
1180 | 1214 | """ |
1181 | 1215 | Check if the current user can access kfd and dri |
@@ -1210,7 +1244,23 @@ def check_required_groups(self, check_render=True, check_video=True): |
1210 | 1244 | denied = [] |
1211 | 1245 |
|
1212 | 1246 | for path in paths_to_check: |
1213 | | - ok, err, msg = self._try_open(path) |
| 1247 | + # Do not try to open all paths, may cause driver issues. |
| 1248 | + # Read access is sufficient to check permissions. |
| 1249 | + # |
| 1250 | + # Reason: GPUs which support partitioning (memory/compute), |
| 1251 | + # logical devices will not be valid until configured. |
| 1252 | + # See `sudo amd-smi set -h` or applicable APIs |
| 1253 | + # to configure on supported hardware. |
| 1254 | + # |
| 1255 | + # Example error dmesg output: |
| 1256 | + # [965358.883112] amdgpu 0000:15:00.0: amdgpu: renderD153 partition 1 not valid! |
| 1257 | + # [965358.883283] amdgpu 0000:15:00.0: amdgpu: renderD154 partition 2 not valid! |
| 1258 | + # [965358.883438] amdgpu 0000:15:00.0: amdgpu: renderD155 partition 3 not valid! |
| 1259 | + # [965358.883594] amdgpu 0000:15:00.0: amdgpu: renderD156 partition 4 not valid! |
| 1260 | + # [965358.883749] amdgpu 0000:15:00.0: amdgpu: renderD157 partition 5 not valid! |
| 1261 | + # [965358.883904] amdgpu 0000:15:00.0: amdgpu: renderD158 partition 6 not valid! |
| 1262 | + # [965358.884060] amdgpu 0000:15:00.0: amdgpu: renderD159 partition 7 not valid! |
| 1263 | + ok, err, msg = self._has_read_access(path) |
1214 | 1264 | if ok: |
1215 | 1265 | continue |
1216 | 1266 | # if permission denied or operation not permitted |
|
0 commit comments