github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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-opticalflow.so*", 116 "libnvidia-ptxjitcompiler.so*", 117 "libnvidia-rtcore.so*", 118 "libnvidia-tls.so*", 119 "libnvoptix.so*", 120 "tls/libnvidia-tls.so*", 121 "vdpau/libvdpau_nvidia.so*", 122 }; 123 124 static const size_t nvidia_globs_len = 125 sizeof nvidia_globs / sizeof *nvidia_globs; 126 127 #endif // defined(NVIDIA_BIARCH) || defined(NVIDIA_MULTIARCH) 128 129 // Populate libgl_dir with a symlink farm to files matching glob_list. 130 // 131 // The symbolic links are made in one of two ways. If the library found is a 132 // file a regular symlink "$libname" -> "/path/to/hostfs/$libname" is created. 133 // If the library is a symbolic link then relative links are kept as-is but 134 // absolute links are translated to have "/path/to/hostfs" up front so that 135 // they work after the pivot_root elsewhere. 136 // 137 // The glob list passed to us is produced with paths relative to source dir, 138 // to simplify the various tie-in points with this function. 139 static void sc_populate_libgl_with_hostfs_symlinks(const char *libgl_dir, 140 const char *source_dir, 141 const char *glob_list[], 142 size_t glob_list_len) 143 { 144 size_t source_dir_len = strlen(source_dir); 145 glob_t glob_res SC_CLEANUP(globfree) = { 146 .gl_pathv = NULL 147 }; 148 // Find all the entries matching the list of globs 149 for (size_t i = 0; i < glob_list_len; ++i) { 150 const char *glob_pattern = glob_list[i]; 151 char glob_pattern_full[512] = { 0 }; 152 sc_must_snprintf(glob_pattern_full, sizeof glob_pattern_full, 153 "%s/%s", source_dir, glob_pattern); 154 155 int err = glob(glob_pattern_full, i ? GLOB_APPEND : 0, NULL, 156 &glob_res); 157 // Not all of the files have to be there (they differ depending on the 158 // driver version used). Ignore all errors that are not GLOB_NOMATCH. 159 if (err != 0 && err != GLOB_NOMATCH) { 160 die("cannot search using glob pattern %s: %d", 161 glob_pattern_full, err); 162 } 163 } 164 // Symlink each file found 165 for (size_t i = 0; i < glob_res.gl_pathc; ++i) { 166 char symlink_name[512] = { 0 }; 167 char symlink_target[512] = { 0 }; 168 char prefix_dir[512] = { 0 }; 169 const char *pathname = glob_res.gl_pathv[i]; 170 char *pathname_copy1 171 SC_CLEANUP(sc_cleanup_string) = sc_strdup(pathname); 172 char *pathname_copy2 173 SC_CLEANUP(sc_cleanup_string) = sc_strdup(pathname); 174 // POSIX dirname() and basename() may modify their input arguments 175 char *filename = basename(pathname_copy1); 176 char *directory_name = dirname(pathname_copy2); 177 sc_must_snprintf(prefix_dir, sizeof prefix_dir, "%s", 178 libgl_dir); 179 180 if (strlen(directory_name) > source_dir_len) { 181 // Additional path elements between source_dir and dirname, meaning the 182 // actual file is not placed directly under source_dir but under one or 183 // more directories below source_dir. Make sure to recreate the whole 184 // prefix 185 sc_must_snprintf(prefix_dir, sizeof prefix_dir, 186 "%s%s", libgl_dir, 187 &directory_name[source_dir_len]); 188 sc_identity old = 189 sc_set_effective_identity(sc_root_group_identity()); 190 if (sc_nonfatal_mkpath(prefix_dir, 0755) != 0) { 191 die("failed to create prefix path: %s", 192 prefix_dir); 193 } 194 (void)sc_set_effective_identity(old); 195 } 196 197 struct stat stat_buf; 198 int err = lstat(pathname, &stat_buf); 199 if (err != 0) { 200 die("cannot stat file %s", pathname); 201 } 202 switch (stat_buf.st_mode & S_IFMT) { 203 case S_IFLNK:; 204 // Read the target of the symbolic link 205 char hostfs_symlink_target[512] = { 0 }; 206 ssize_t num_read; 207 hostfs_symlink_target[0] = 0; 208 num_read = 209 readlink(pathname, hostfs_symlink_target, 210 sizeof hostfs_symlink_target - 1); 211 if (num_read == -1) { 212 die("cannot read symbolic link %s", pathname); 213 } 214 hostfs_symlink_target[num_read] = 0; 215 if (hostfs_symlink_target[0] == '/') { 216 sc_must_snprintf(symlink_target, 217 sizeof symlink_target, 218 "/var/lib/snapd/hostfs%s", 219 hostfs_symlink_target); 220 } else { 221 // Keep relative symlinks as-is, so that they point to -> libfoo.so.0.123 222 sc_must_snprintf(symlink_target, 223 sizeof symlink_target, "%s", 224 hostfs_symlink_target); 225 } 226 break; 227 case S_IFREG: 228 sc_must_snprintf(symlink_target, 229 sizeof symlink_target, 230 "/var/lib/snapd/hostfs%s", pathname); 231 break; 232 default: 233 debug("ignoring unsupported entry: %s", pathname); 234 continue; 235 } 236 sc_must_snprintf(symlink_name, sizeof symlink_name, 237 "%s/%s", prefix_dir, filename); 238 debug("creating symbolic link %s -> %s", symlink_name, 239 symlink_target); 240 241 // Make sure we don't have some link already (merged GLVND systems) 242 if (lstat(symlink_name, &stat_buf) == 0) { 243 if (unlink(symlink_name) != 0) { 244 die("cannot remove symbolic link target %s", 245 symlink_name); 246 } 247 } 248 249 if (symlink(symlink_target, symlink_name) != 0) { 250 die("cannot create symbolic link %s -> %s", 251 symlink_name, symlink_target); 252 } 253 } 254 } 255 256 static void sc_mkdir_and_mount_and_glob_files(const char *rootfs_dir, 257 const char *source_dir[], 258 size_t source_dir_len, 259 const char *tgt_dir, 260 const char *glob_list[], 261 size_t glob_list_len) 262 { 263 // Bind mount a tmpfs on $rootfs_dir/$tgt_dir (i.e. /var/lib/snapd/lib/gl) 264 char buf[512] = { 0 }; 265 sc_must_snprintf(buf, sizeof(buf), "%s%s", rootfs_dir, tgt_dir); 266 const char *libgl_dir = buf; 267 268 sc_identity old = sc_set_effective_identity(sc_root_group_identity()); 269 int res = mkdir(libgl_dir, 0755); 270 if (res != 0 && errno != EEXIST) { 271 die("cannot create tmpfs target %s", libgl_dir); 272 } 273 if (res == 0 && (chown(libgl_dir, 0, 0) < 0)) { 274 // Adjust the ownership only if we created the directory. 275 die("cannot change ownership of %s", libgl_dir); 276 } 277 (void)sc_set_effective_identity(old); 278 279 debug("mounting tmpfs at %s", libgl_dir); 280 if (mount("none", libgl_dir, "tmpfs", MS_NODEV | MS_NOEXEC, NULL) != 0) { 281 die("cannot mount tmpfs at %s", libgl_dir); 282 }; 283 284 for (size_t i = 0; i < source_dir_len; i++) { 285 // Populate libgl_dir with symlinks to libraries from hostfs 286 sc_populate_libgl_with_hostfs_symlinks(libgl_dir, source_dir[i], 287 glob_list, 288 glob_list_len); 289 } 290 // Remount $tgt_dir (i.e. .../lib/gl) read only 291 debug("remounting tmpfs as read-only %s", libgl_dir); 292 if (mount(NULL, buf, NULL, MS_REMOUNT | MS_BIND | MS_RDONLY, NULL) != 0) { 293 die("cannot remount %s as read-only", buf); 294 } 295 } 296 297 #ifdef NVIDIA_BIARCH 298 299 // Expose host NVIDIA drivers to the snap on biarch systems. 300 // 301 // Order is absolutely imperative here. We'll attempt to find the 302 // primary files for the architecture in the main directory, and end 303 // up copying any files across. However it is possible we're using a 304 // GLVND enabled host, in which case we copied libGL* to the farm. 305 // The next step in the list is to look within the private nvidia 306 // directory, exposed using ld.so.conf tricks within the host OS. 307 // In some distros (i.e. Solus) only the private libGL/libEGL files 308 // may be found here, and they'll clobber the existing GLVND files from 309 // the previous run. 310 // In other distros (like Fedora) all NVIDIA libraries are contained 311 // within the private directory, so we clobber the GLVND files and we 312 // also grab all the private NVIDIA libraries. 313 // 314 // In non GLVND cases we just copy across the exposed libGLs and NVIDIA 315 // libraries from wherever we find, and clobbering is also harmless. 316 static void sc_mount_nvidia_driver_biarch(const char *rootfs_dir) 317 { 318 319 const char *native_sources[] = { 320 NATIVE_LIBDIR, 321 NATIVE_LIBDIR "/nvidia*", 322 }; 323 const size_t native_sources_len = 324 sizeof native_sources / sizeof *native_sources; 325 326 #if UINTPTR_MAX == 0xffffffffffffffff 327 // Alternative 32-bit support 328 const char *lib32_sources[] = { 329 LIB32_DIR, 330 LIB32_DIR "/nvidia*", 331 }; 332 const size_t lib32_sources_len = 333 sizeof lib32_sources / sizeof *lib32_sources; 334 #endif 335 336 // Primary arch 337 sc_mkdir_and_mount_and_glob_files(rootfs_dir, 338 native_sources, native_sources_len, 339 SC_LIBGL_DIR, nvidia_globs, 340 nvidia_globs_len); 341 342 #if UINTPTR_MAX == 0xffffffffffffffff 343 // Alternative 32-bit support 344 sc_mkdir_and_mount_and_glob_files(rootfs_dir, lib32_sources, 345 lib32_sources_len, SC_LIBGL32_DIR, 346 nvidia_globs, nvidia_globs_len); 347 #endif 348 } 349 350 #endif // ifdef NVIDIA_BIARCH 351 352 #ifdef NVIDIA_MULTIARCH 353 354 typedef struct { 355 int major; 356 // Driver version format is MAJOR.MINOR[.MICRO] but we only care about the 357 // major version and the full version string. The micro component has been 358 // seen with relevant leading zeros (e.g. "440.48.02"). 359 char raw[128]; // The size was picked as "big enough" for version strings. 360 } sc_nv_version; 361 362 static void sc_probe_nvidia_driver(sc_nv_version * version) 363 { 364 memset(version, 0, sizeof *version); 365 366 FILE *file SC_CLEANUP(sc_cleanup_file) = NULL; 367 debug("opening file describing nvidia driver version"); 368 file = fopen(SC_NVIDIA_DRIVER_VERSION_FILE, "rt"); 369 if (file == NULL) { 370 if (errno == ENOENT) { 371 debug("nvidia driver version file doesn't exist"); 372 return; 373 } 374 die("cannot open file describing nvidia driver version"); 375 } 376 int nread = fread(version->raw, 1, sizeof version->raw - 1, file); 377 if (nread < 0) { 378 die("cannot read nvidia driver version string"); 379 } 380 if (nread == sizeof version->raw - 1 && !feof(file)) { 381 die("cannot fit entire nvidia driver version string"); 382 } 383 version->raw[nread] = '\0'; 384 if (nread > 0 && version->raw[nread - 1] == '\n') { 385 version->raw[nread - 1] = '\0'; 386 } 387 if (sscanf(version->raw, "%d.", &version->major) != 1) { 388 die("cannot parse major version from nvidia driver version string"); 389 } 390 } 391 392 static void sc_mkdir_and_mount_and_bind(const char *rootfs_dir, 393 const char *src_dir, 394 const char *tgt_dir) 395 { 396 sc_nv_version version; 397 398 // Probe sysfs to get the version of the driver that is currently inserted. 399 sc_probe_nvidia_driver(&version); 400 401 // If there's driver in the kernel then don't mount userspace. 402 if (version.major == 0) { 403 return; 404 } 405 // Construct the paths for the driver userspace libraries 406 // and for the gl directory. 407 char src[PATH_MAX] = { 0 }; 408 char dst[PATH_MAX] = { 0 }; 409 sc_must_snprintf(src, sizeof src, "%s-%d", src_dir, version.major); 410 sc_must_snprintf(dst, sizeof dst, "%s%s", rootfs_dir, tgt_dir); 411 412 // If there is no userspace driver available then don't try to mount it. 413 // This can happen for any number of reasons but one interesting one is 414 // that that snapd runs in a lxd container on a host that uses nvidia. In 415 // that case the container may not have the userspace library installed but 416 // the kernel will still have the module around. 417 if (access(src, F_OK) != 0) { 418 return; 419 } 420 sc_identity old = sc_set_effective_identity(sc_root_group_identity()); 421 int res = mkdir(dst, 0755); 422 if (res != 0 && errno != EEXIST) { 423 die("cannot create directory %s", dst); 424 } 425 if (res == 0 && (chown(dst, 0, 0) < 0)) { 426 // Adjust the ownership only if we created the directory. 427 die("cannot change ownership of %s", dst); 428 } 429 (void)sc_set_effective_identity(old); 430 // Bind mount the binary nvidia driver into $tgt_dir (i.e. /var/lib/snapd/lib/gl). 431 debug("bind mounting nvidia driver %s -> %s", src, dst); 432 if (mount(src, dst, NULL, MS_BIND, NULL) != 0) { 433 die("cannot bind mount nvidia driver %s -> %s", src, dst); 434 } 435 } 436 437 static int sc_mount_nvidia_is_driver_in_dir(const char *dir) 438 { 439 char driver_path[512] = { 0 }; 440 441 sc_nv_version version; 442 443 // Probe sysfs to get the version of the driver that is currently inserted. 444 sc_probe_nvidia_driver(&version); 445 446 // If there's no driver then we should not bother ourselves with finding the 447 // matching library 448 if (version.major == 0) { 449 return 0; 450 } 451 452 // Probe if a well known library is found in directory dir. We must use the 453 // raw version because it may contain more than just major.minor. In 454 // practice the micro version may have leading zeros that are relevant. 455 sc_must_snprintf(driver_path, sizeof driver_path, 456 "%s/libnvidia-glcore.so.%s", dir, version.raw); 457 458 debug("looking for nvidia canary file %s", driver_path); 459 if (access(driver_path, F_OK) == 0) { 460 debug("nvidia library detected at path %s", driver_path); 461 return 1; 462 } 463 return 0; 464 } 465 466 static void sc_mount_nvidia_driver_multiarch(const char *rootfs_dir) 467 { 468 const char *native_libdir = NATIVE_LIBDIR "/" HOST_ARCH_TRIPLET; 469 const char *lib32_libdir = NATIVE_LIBDIR "/" HOST_ARCH32_TRIPLET; 470 471 if ((strlen(HOST_ARCH_TRIPLET) > 0) && 472 (sc_mount_nvidia_is_driver_in_dir(native_libdir) == 1)) { 473 474 // sc_mkdir_and_mount_and_glob_files() takes an array of strings, so 475 // initialize native_sources accordingly, but calculate the array length 476 // dynamically to make adjustments to native_sources easier. 477 const char *native_sources[] = { native_libdir }; 478 const size_t native_sources_len = 479 sizeof native_sources / sizeof *native_sources; 480 // Primary arch 481 sc_mkdir_and_mount_and_glob_files(rootfs_dir, 482 native_sources, 483 native_sources_len, 484 SC_LIBGL_DIR, nvidia_globs, 485 nvidia_globs_len); 486 487 // Alternative 32-bit support 488 if ((strlen(HOST_ARCH32_TRIPLET) > 0) && 489 (sc_mount_nvidia_is_driver_in_dir(lib32_libdir) == 1)) { 490 491 // sc_mkdir_and_mount_and_glob_files() takes an array of strings, so 492 // initialize lib32_sources accordingly, but calculate the array length 493 // dynamically to make adjustments to lib32_sources easier. 494 const char *lib32_sources[] = { lib32_libdir }; 495 const size_t lib32_sources_len = 496 sizeof lib32_sources / sizeof *lib32_sources; 497 sc_mkdir_and_mount_and_glob_files(rootfs_dir, 498 lib32_sources, 499 lib32_sources_len, 500 SC_LIBGL32_DIR, 501 nvidia_globs, 502 nvidia_globs_len); 503 } 504 } else { 505 // Attempt mount of both the native and 32-bit variants of the driver if they exist 506 sc_mkdir_and_mount_and_bind(rootfs_dir, "/usr/lib/nvidia", 507 SC_LIBGL_DIR); 508 // Alternative 32-bit support 509 sc_mkdir_and_mount_and_bind(rootfs_dir, "/usr/lib32/nvidia", 510 SC_LIBGL32_DIR); 511 } 512 } 513 514 #endif // ifdef NVIDIA_MULTIARCH 515 516 static void sc_mount_vulkan(const char *rootfs_dir) 517 { 518 const char *vulkan_sources[] = { 519 SC_VULKAN_SOURCE_DIR, 520 }; 521 const size_t vulkan_sources_len = 522 sizeof vulkan_sources / sizeof *vulkan_sources; 523 524 sc_mkdir_and_mount_and_glob_files(rootfs_dir, vulkan_sources, 525 vulkan_sources_len, SC_VULKAN_DIR, 526 vulkan_globs, vulkan_globs_len); 527 } 528 529 static void sc_mount_egl(const char *rootfs_dir) 530 { 531 const char *egl_vendor_sources[] = { SC_EGL_VENDOR_SOURCE_DIR }; 532 const size_t egl_vendor_sources_len = 533 sizeof egl_vendor_sources / sizeof *egl_vendor_sources; 534 535 sc_mkdir_and_mount_and_glob_files(rootfs_dir, egl_vendor_sources, 536 egl_vendor_sources_len, SC_GLVND_DIR, 537 egl_vendor_globs, 538 egl_vendor_globs_len); 539 } 540 541 void sc_mount_nvidia_driver(const char *rootfs_dir) 542 { 543 /* If NVIDIA module isn't loaded, don't attempt to mount the drivers */ 544 if (access(SC_NVIDIA_DRIVER_VERSION_FILE, F_OK) != 0) { 545 return; 546 } 547 548 sc_identity old = sc_set_effective_identity(sc_root_group_identity()); 549 int res = mkdir(SC_LIB, 0755); 550 if (res != 0 && errno != EEXIST) { 551 die("cannot create " SC_LIB); 552 } 553 if (res == 0 && (chown(SC_LIB, 0, 0) < 0)) { 554 // Adjust the ownership only if we created the directory. 555 die("cannot change ownership of " SC_LIB); 556 } 557 (void)sc_set_effective_identity(old); 558 #ifdef NVIDIA_MULTIARCH 559 sc_mount_nvidia_driver_multiarch(rootfs_dir); 560 #endif // ifdef NVIDIA_MULTIARCH 561 #ifdef NVIDIA_BIARCH 562 sc_mount_nvidia_driver_biarch(rootfs_dir); 563 #endif // ifdef NVIDIA_BIARCH 564 565 // Common for both driver mechanisms 566 sc_mount_vulkan(rootfs_dir); 567 sc_mount_egl(rootfs_dir); 568 }