github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snap-confine/mount-support-nvidia.c (about) 1 /* 2 * Copyright (C) 2015 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18 #include "config.h" 19 #include "mount-support-nvidia.h" 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <glob.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <sys/mount.h> 27 #include <sys/stat.h> 28 #include <sys/types.h> 29 #include <stdint.h> 30 #include <unistd.h> 31 /* POSIX version of basename() and dirname() */ 32 #include <libgen.h> 33 34 #include "../libsnap-confine-private/classic.h" 35 #include "../libsnap-confine-private/cleanup-funcs.h" 36 #include "../libsnap-confine-private/string-utils.h" 37 #include "../libsnap-confine-private/utils.h" 38 39 #define SC_NVIDIA_DRIVER_VERSION_FILE "/sys/module/nvidia/version" 40 41 // note: if the parent dir changes to something other than 42 // the current /var/lib/snapd/lib then sc_mkdir_and_mount_and_bind 43 // and sc_mkdir_and_mount_and_bind need updating. 44 #define SC_LIB "/var/lib/snapd/lib" 45 #define SC_LIBGL_DIR SC_LIB "/gl" 46 #define SC_LIBGL32_DIR SC_LIB "/gl32" 47 #define SC_VULKAN_DIR SC_LIB "/vulkan" 48 #define SC_GLVND_DIR SC_LIB "/glvnd" 49 50 #define SC_VULKAN_SOURCE_DIR "/usr/share/vulkan" 51 #define SC_EGL_VENDOR_SOURCE_DIR "/usr/share/glvnd" 52 53 // Location for NVIDIA vulkan files (including _wayland) 54 static const char *vulkan_globs[] = { 55 "icd.d/*nvidia*.json", 56 }; 57 58 static const size_t vulkan_globs_len = 59 sizeof vulkan_globs / sizeof *vulkan_globs; 60 61 // Location of EGL vendor files 62 static const char *egl_vendor_globs[] = { 63 "egl_vendor.d/*nvidia*.json", 64 }; 65 66 static const size_t egl_vendor_globs_len = 67 sizeof egl_vendor_globs / sizeof *egl_vendor_globs; 68 69 #if defined(NVIDIA_BIARCH) || defined(NVIDIA_MULTIARCH) 70 71 // List of globs that describe nvidia userspace libraries. 72 // This list was compiled from the following packages. 73 // 74 // https://www.archlinux.org/packages/extra/x86_64/nvidia-304xx-libgl/files/ 75 // https://www.archlinux.org/packages/extra/x86_64/nvidia-304xx-utils/files/ 76 // https://www.archlinux.org/packages/extra/x86_64/nvidia-340xx-libgl/files/ 77 // https://www.archlinux.org/packages/extra/x86_64/nvidia-340xx-utils/files/ 78 // https://www.archlinux.org/packages/extra/x86_64/nvidia-libgl/files/ 79 // https://www.archlinux.org/packages/extra/x86_64/nvidia-utils/files/ 80 // 81 // FIXME: this doesn't yet work with libGLX and libglvnd redirector 82 // FIXME: this still doesn't work with the 361 driver 83 static const char *nvidia_globs[] = { 84 "libEGL.so*", 85 "libEGL_nvidia.so*", 86 "libGL.so*", 87 "libOpenGL.so*", 88 "libGLESv1_CM.so*", 89 "libGLESv1_CM_nvidia.so*", 90 "libGLESv2.so*", 91 "libGLESv2_nvidia.so*", 92 "libGLX_indirect.so*", 93 "libGLX_nvidia.so*", 94 "libGLX.so*", 95 "libGLdispatch.so*", 96 "libGLU.so*", 97 "libXvMCNVIDIA.so*", 98 "libXvMCNVIDIA_dynamic.so*", 99 "libcuda.so*", 100 "libcudart.so*", 101 "libnvcuvid.so*", 102 "libnvidia-cfg.so*", 103 "libnvidia-compiler.so*", 104 "libnvidia-eglcore.so*", 105 "libnvidia-egl-wayland*", 106 "libnvidia-encode.so*", 107 "libnvidia-fatbinaryloader.so*", 108 "libnvidia-fbc.so*", 109 "libnvidia-glcore.so*", 110 "libnvidia-glsi.so*", 111 "libnvidia-glvkspirv.so*", 112 "libnvidia-ifr.so*", 113 "libnvidia-ml.so*", 114 "libnvidia-opencl.so*", 115 "libnvidia-ptxjitcompiler.so*", 116 "libnvidia-tls.so*", 117 "tls/libnvidia-tls.so*", 118 "vdpau/libvdpau_nvidia.so*", 119 }; 120 121 static const size_t nvidia_globs_len = 122 sizeof nvidia_globs / sizeof *nvidia_globs; 123 124 #define LIBNVIDIA_GLCORE_SO_PATTERN "libnvidia-glcore.so.%d.%d" 125 126 #endif // defined(NVIDIA_BIARCH) || defined(NVIDIA_MULTIARCH) 127 128 // Populate libgl_dir with a symlink farm to files matching glob_list. 129 // 130 // The symbolic links are made in one of two ways. If the library found is a 131 // file a regular symlink "$libname" -> "/path/to/hostfs/$libname" is created. 132 // If the library is a symbolic link then relative links are kept as-is but 133 // absolute links are translated to have "/path/to/hostfs" up front so that 134 // they work after the pivot_root elsewhere. 135 // 136 // The glob list passed to us is produced with paths relative to source dir, 137 // to simplify the various tie-in points with this function. 138 static void sc_populate_libgl_with_hostfs_symlinks(const char *libgl_dir, 139 const char *source_dir, 140 const char *glob_list[], 141 size_t glob_list_len) 142 { 143 size_t source_dir_len = strlen(source_dir); 144 glob_t glob_res SC_CLEANUP(globfree) = { 145 .gl_pathv = NULL 146 }; 147 // Find all the entries matching the list of globs 148 for (size_t i = 0; i < glob_list_len; ++i) { 149 const char *glob_pattern = glob_list[i]; 150 char glob_pattern_full[512] = { 0 }; 151 sc_must_snprintf(glob_pattern_full, sizeof glob_pattern_full, 152 "%s/%s", source_dir, glob_pattern); 153 154 int err = glob(glob_pattern_full, i ? GLOB_APPEND : 0, NULL, 155 &glob_res); 156 // Not all of the files have to be there (they differ depending on the 157 // driver version used). Ignore all errors that are not GLOB_NOMATCH. 158 if (err != 0 && err != GLOB_NOMATCH) { 159 die("cannot search using glob pattern %s: %d", 160 glob_pattern_full, err); 161 } 162 } 163 // Symlink each file found 164 for (size_t i = 0; i < glob_res.gl_pathc; ++i) { 165 char symlink_name[512] = { 0 }; 166 char symlink_target[512] = { 0 }; 167 char prefix_dir[512] = { 0 }; 168 const char *pathname = glob_res.gl_pathv[i]; 169 char *pathname_copy1 170 SC_CLEANUP(sc_cleanup_string) = sc_strdup(pathname); 171 char *pathname_copy2 172 SC_CLEANUP(sc_cleanup_string) = sc_strdup(pathname); 173 // POSIX dirname() and basename() may modify their input arguments 174 char *filename = basename(pathname_copy1); 175 char *directory_name = dirname(pathname_copy2); 176 sc_must_snprintf(prefix_dir, sizeof prefix_dir, "%s", 177 libgl_dir); 178 179 if (strlen(directory_name) > source_dir_len) { 180 // Additional path elements between source_dir and dirname, meaning the 181 // actual file is not placed directly under source_dir but under one or 182 // more directories below source_dir. Make sure to recreate the whole 183 // prefix 184 sc_must_snprintf(prefix_dir, sizeof prefix_dir, 185 "%s%s", libgl_dir, 186 &directory_name[source_dir_len]); 187 if (sc_nonfatal_mkpath(prefix_dir, 0755) != 0) { 188 die("failed to create prefix path: %s", 189 prefix_dir); 190 } 191 } 192 193 struct stat stat_buf; 194 int err = lstat(pathname, &stat_buf); 195 if (err != 0) { 196 die("cannot stat file %s", pathname); 197 } 198 switch (stat_buf.st_mode & S_IFMT) { 199 case S_IFLNK:; 200 // Read the target of the symbolic link 201 char hostfs_symlink_target[512]; 202 ssize_t num_read; 203 hostfs_symlink_target[0] = 0; 204 num_read = 205 readlink(pathname, hostfs_symlink_target, 206 sizeof hostfs_symlink_target); 207 if (num_read == -1) { 208 die("cannot read symbolic link %s", pathname); 209 } 210 hostfs_symlink_target[num_read] = 0; 211 if (hostfs_symlink_target[0] == '/') { 212 sc_must_snprintf(symlink_target, 213 sizeof symlink_target, 214 "/var/lib/snapd/hostfs%s", 215 hostfs_symlink_target); 216 } else { 217 // Keep relative symlinks as-is, so that they point to -> libfoo.so.0.123 218 sc_must_snprintf(symlink_target, 219 sizeof symlink_target, "%s", 220 hostfs_symlink_target); 221 } 222 break; 223 case S_IFREG: 224 sc_must_snprintf(symlink_target, 225 sizeof symlink_target, 226 "/var/lib/snapd/hostfs%s", pathname); 227 break; 228 default: 229 debug("ignoring unsupported entry: %s", pathname); 230 continue; 231 } 232 sc_must_snprintf(symlink_name, sizeof symlink_name, 233 "%s/%s", prefix_dir, filename); 234 debug("creating symbolic link %s -> %s", symlink_name, 235 symlink_target); 236 237 // Make sure we don't have some link already (merged GLVND systems) 238 if (lstat(symlink_name, &stat_buf) == 0) { 239 if (unlink(symlink_name) != 0) { 240 die("cannot remove symbolic link target %s", 241 symlink_name); 242 } 243 } 244 245 if (symlink(symlink_target, symlink_name) != 0) { 246 die("cannot create symbolic link %s -> %s", 247 symlink_name, symlink_target); 248 } 249 } 250 } 251 252 static void sc_mkdir_and_mount_and_glob_files(const char *rootfs_dir, 253 const char *source_dir[], 254 size_t source_dir_len, 255 const char *tgt_dir, 256 const char *glob_list[], 257 size_t glob_list_len) 258 { 259 // Bind mount a tmpfs on $rootfs_dir/$tgt_dir (i.e. /var/lib/snapd/lib/gl) 260 char buf[512] = { 0 }; 261 sc_must_snprintf(buf, sizeof(buf), "%s%s", rootfs_dir, tgt_dir); 262 const char *libgl_dir = buf; 263 264 int res = mkdir(libgl_dir, 0755); 265 if (res != 0 && errno != EEXIST) { 266 die("cannot create tmpfs target %s", libgl_dir); 267 } 268 if (res == 0 && (chown(libgl_dir, 0, 0) < 0)) { 269 // Adjust the ownership only if we created the directory. 270 die("cannot change ownership of %s", libgl_dir); 271 } 272 273 debug("mounting tmpfs at %s", libgl_dir); 274 if (mount("none", libgl_dir, "tmpfs", MS_NODEV | MS_NOEXEC, NULL) != 0) { 275 die("cannot mount tmpfs at %s", libgl_dir); 276 }; 277 278 for (size_t i = 0; i < source_dir_len; i++) { 279 // Populate libgl_dir with symlinks to libraries from hostfs 280 sc_populate_libgl_with_hostfs_symlinks(libgl_dir, source_dir[i], 281 glob_list, 282 glob_list_len); 283 } 284 // Remount $tgt_dir (i.e. .../lib/gl) read only 285 debug("remounting tmpfs as read-only %s", libgl_dir); 286 if (mount(NULL, buf, NULL, MS_REMOUNT | MS_BIND | MS_RDONLY, NULL) != 0) { 287 die("cannot remount %s as read-only", buf); 288 } 289 } 290 291 #ifdef NVIDIA_BIARCH 292 293 // Expose host NVIDIA drivers to the snap on biarch systems. 294 // 295 // Order is absolutely imperative here. We'll attempt to find the 296 // primary files for the architecture in the main directory, and end 297 // up copying any files across. However it is possible we're using a 298 // GLVND enabled host, in which case we copied libGL* to the farm. 299 // The next step in the list is to look within the private nvidia 300 // directory, exposed using ld.so.conf tricks within the host OS. 301 // In some distros (i.e. Solus) only the private libGL/libEGL files 302 // may be found here, and they'll clobber the existing GLVND files from 303 // the previous run. 304 // In other distros (like Fedora) all NVIDIA libraries are contained 305 // within the private directory, so we clobber the GLVND files and we 306 // also grab all the private NVIDIA libraries. 307 // 308 // In non GLVND cases we just copy across the exposed libGLs and NVIDIA 309 // libraries from wherever we find, and clobbering is also harmless. 310 static void sc_mount_nvidia_driver_biarch(const char *rootfs_dir) 311 { 312 313 const char *native_sources[] = { 314 NATIVE_LIBDIR, 315 NATIVE_LIBDIR "/nvidia*", 316 }; 317 const size_t native_sources_len = 318 sizeof native_sources / sizeof *native_sources; 319 320 #if UINTPTR_MAX == 0xffffffffffffffff 321 // Alternative 32-bit support 322 const char *lib32_sources[] = { 323 LIB32_DIR, 324 LIB32_DIR "/nvidia*", 325 }; 326 const size_t lib32_sources_len = 327 sizeof lib32_sources / sizeof *lib32_sources; 328 #endif 329 330 // Primary arch 331 sc_mkdir_and_mount_and_glob_files(rootfs_dir, 332 native_sources, native_sources_len, 333 SC_LIBGL_DIR, nvidia_globs, 334 nvidia_globs_len); 335 336 #if UINTPTR_MAX == 0xffffffffffffffff 337 // Alternative 32-bit support 338 sc_mkdir_and_mount_and_glob_files(rootfs_dir, lib32_sources, 339 lib32_sources_len, SC_LIBGL32_DIR, 340 nvidia_globs, nvidia_globs_len); 341 #endif 342 } 343 344 #endif // ifdef NVIDIA_BIARCH 345 346 #ifdef NVIDIA_MULTIARCH 347 348 struct sc_nvidia_driver { 349 int major_version; 350 int minor_version; 351 }; 352 353 static void sc_probe_nvidia_driver(struct sc_nvidia_driver *driver) 354 { 355 FILE *file SC_CLEANUP(sc_cleanup_file) = NULL; 356 debug("opening file describing nvidia driver version"); 357 file = fopen(SC_NVIDIA_DRIVER_VERSION_FILE, "rt"); 358 if (file == NULL) { 359 if (errno == ENOENT) { 360 debug("nvidia driver version file doesn't exist"); 361 driver->major_version = 0; 362 driver->minor_version = 0; 363 return; 364 } 365 die("cannot open file describing nvidia driver version"); 366 } 367 // Driver version format is MAJOR.MINOR where both MAJOR and MINOR are 368 // integers. We can use sscanf to parse this data. 369 if (fscanf 370 (file, "%d.%d", &driver->major_version, 371 &driver->minor_version) != 2) { 372 die("cannot parse nvidia driver version string"); 373 } 374 debug("parsed nvidia driver version: %d.%d", driver->major_version, 375 driver->minor_version); 376 } 377 378 static void sc_mkdir_and_mount_and_bind(const char *rootfs_dir, 379 const char *src_dir, 380 const char *tgt_dir) 381 { 382 struct sc_nvidia_driver driver; 383 384 // Probe sysfs to get the version of the driver that is currently inserted. 385 sc_probe_nvidia_driver(&driver); 386 387 // If there's driver in the kernel then don't mount userspace. 388 if (driver.major_version == 0) { 389 return; 390 } 391 // Construct the paths for the driver userspace libraries 392 // and for the gl directory. 393 char src[PATH_MAX] = { 0 }; 394 char dst[PATH_MAX] = { 0 }; 395 sc_must_snprintf(src, sizeof src, "%s-%d", src_dir, 396 driver.major_version); 397 sc_must_snprintf(dst, sizeof dst, "%s%s", rootfs_dir, tgt_dir); 398 399 // If there is no userspace driver available then don't try to mount it. 400 // This can happen for any number of reasons but one interesting one is 401 // that that snapd runs in a lxd container on a host that uses nvidia. In 402 // that case the container may not have the userspace library installed but 403 // the kernel will still have the module around. 404 if (access(src, F_OK) != 0) { 405 return; 406 } 407 int res = mkdir(dst, 0755); 408 if (res != 0 && errno != EEXIST) { 409 die("cannot create directory %s", dst); 410 } 411 if (res == 0 && (chown(dst, 0, 0) < 0)) { 412 // Adjust the ownership only if we created the directory. 413 die("cannot change ownership of %s", dst); 414 } 415 // Bind mount the binary nvidia driver into $tgt_dir (i.e. /var/lib/snapd/lib/gl). 416 debug("bind mounting nvidia driver %s -> %s", src, dst); 417 if (mount(src, dst, NULL, MS_BIND, NULL) != 0) { 418 die("cannot bind mount nvidia driver %s -> %s", src, dst); 419 } 420 } 421 422 static int sc_mount_nvidia_is_driver_in_dir(const char *dir) 423 { 424 char driver_path[512] = { 0 }; 425 426 struct sc_nvidia_driver driver; 427 428 // Probe sysfs to get the version of the driver that is currently inserted. 429 sc_probe_nvidia_driver(&driver); 430 431 // If there's no driver then we should not bother ourselves with finding the 432 // matching library 433 if (driver.major_version == 0) { 434 return 0; 435 } 436 // Probe if a well known library is found in directory dir 437 sc_must_snprintf(driver_path, sizeof driver_path, 438 "%s/" LIBNVIDIA_GLCORE_SO_PATTERN, dir, 439 driver.major_version, driver.minor_version); 440 441 if (access(driver_path, F_OK) == 0) { 442 debug("nvidia library detected at path %s", driver_path); 443 return 1; 444 } 445 return 0; 446 } 447 448 static void sc_mount_nvidia_driver_multiarch(const char *rootfs_dir) 449 { 450 const char *native_libdir = NATIVE_LIBDIR "/" HOST_ARCH_TRIPLET; 451 const char *lib32_libdir = NATIVE_LIBDIR "/" HOST_ARCH32_TRIPLET; 452 453 if ((strlen(HOST_ARCH_TRIPLET) > 0) && 454 (sc_mount_nvidia_is_driver_in_dir(native_libdir) == 1)) { 455 456 // sc_mkdir_and_mount_and_glob_files() takes an array of strings, so 457 // initialize native_sources accordingly, but calculate the array length 458 // dynamically to make adjustments to native_sources easier. 459 const char *native_sources[] = { native_libdir }; 460 const size_t native_sources_len = 461 sizeof native_sources / sizeof *native_sources; 462 // Primary arch 463 sc_mkdir_and_mount_and_glob_files(rootfs_dir, 464 native_sources, 465 native_sources_len, 466 SC_LIBGL_DIR, nvidia_globs, 467 nvidia_globs_len); 468 469 // Alternative 32-bit support 470 if ((strlen(HOST_ARCH32_TRIPLET) > 0) && 471 (sc_mount_nvidia_is_driver_in_dir(lib32_libdir) == 1)) { 472 473 // sc_mkdir_and_mount_and_glob_files() takes an array of strings, so 474 // initialize lib32_sources accordingly, but calculate the array length 475 // dynamically to make adjustments to lib32_sources easier. 476 const char *lib32_sources[] = { lib32_libdir }; 477 const size_t lib32_sources_len = 478 sizeof lib32_sources / sizeof *lib32_sources; 479 sc_mkdir_and_mount_and_glob_files(rootfs_dir, 480 lib32_sources, 481 lib32_sources_len, 482 SC_LIBGL32_DIR, 483 nvidia_globs, 484 nvidia_globs_len); 485 } 486 } else { 487 // Attempt mount of both the native and 32-bit variants of the driver if they exist 488 sc_mkdir_and_mount_and_bind(rootfs_dir, "/usr/lib/nvidia", 489 SC_LIBGL_DIR); 490 // Alternative 32-bit support 491 sc_mkdir_and_mount_and_bind(rootfs_dir, "/usr/lib32/nvidia", 492 SC_LIBGL32_DIR); 493 } 494 } 495 496 #endif // ifdef NVIDIA_MULTIARCH 497 498 static void sc_mount_vulkan(const char *rootfs_dir) 499 { 500 const char *vulkan_sources[] = { 501 SC_VULKAN_SOURCE_DIR, 502 }; 503 const size_t vulkan_sources_len = 504 sizeof vulkan_sources / sizeof *vulkan_sources; 505 506 sc_mkdir_and_mount_and_glob_files(rootfs_dir, vulkan_sources, 507 vulkan_sources_len, SC_VULKAN_DIR, 508 vulkan_globs, vulkan_globs_len); 509 } 510 511 static void sc_mount_egl(const char *rootfs_dir) 512 { 513 const char *egl_vendor_sources[] = { SC_EGL_VENDOR_SOURCE_DIR }; 514 const size_t egl_vendor_sources_len = 515 sizeof egl_vendor_sources / sizeof *egl_vendor_sources; 516 517 sc_mkdir_and_mount_and_glob_files(rootfs_dir, egl_vendor_sources, 518 egl_vendor_sources_len, SC_GLVND_DIR, 519 egl_vendor_globs, 520 egl_vendor_globs_len); 521 } 522 523 void sc_mount_nvidia_driver(const char *rootfs_dir) 524 { 525 /* If NVIDIA module isn't loaded, don't attempt to mount the drivers */ 526 if (access(SC_NVIDIA_DRIVER_VERSION_FILE, F_OK) != 0) { 527 return; 528 } 529 530 int res = mkdir(SC_LIB, 0755); 531 if (res != 0 && errno != EEXIST) { 532 die("cannot create " SC_LIB); 533 } 534 if (res == 0 && (chown(SC_LIB, 0, 0) < 0)) { 535 // Adjust the ownership only if we created the directory. 536 die("cannot change ownership of " SC_LIB); 537 } 538 #ifdef NVIDIA_MULTIARCH 539 sc_mount_nvidia_driver_multiarch(rootfs_dir); 540 #endif // ifdef NVIDIA_MULTIARCH 541 #ifdef NVIDIA_BIARCH 542 sc_mount_nvidia_driver_biarch(rootfs_dir); 543 #endif // ifdef NVIDIA_BIARCH 544 545 // Common for both driver mechanisms 546 sc_mount_vulkan(rootfs_dir); 547 sc_mount_egl(rootfs_dir); 548 }