diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2013-04-09 09:03:00 -0700 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2013-04-09 09:03:00 -0700 |
commit | 8391c1d912302dc9577ee09afc257ba71d95a07b (patch) | |
tree | 3e176b66e4f0f859c63927280bcd643f934f25e8 | |
parent | 89a64f3d6ab1097f819ea4925a58f3aea50f6fcd (diff) |
319.12319.12
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | backup.c | 181 | ||||
-rw-r--r-- | command-list.c | 43 | ||||
-rw-r--r-- | common-utils/common-utils.c | 47 | ||||
-rw-r--r-- | common-utils/common-utils.h | 33 | ||||
-rw-r--r-- | dist-files.mk | 2 | ||||
-rw-r--r-- | files.c | 220 | ||||
-rw-r--r-- | files.h | 3 | ||||
-rw-r--r-- | install-from-cwd.c | 405 | ||||
-rw-r--r-- | kernel.c | 792 | ||||
-rw-r--r-- | kernel.h | 40 | ||||
-rw-r--r-- | log.c | 21 | ||||
-rw-r--r-- | manifest.c | 214 | ||||
-rw-r--r-- | manifest.h | 39 | ||||
-rw-r--r-- | misc.c | 195 | ||||
-rw-r--r-- | misc.h | 7 | ||||
-rw-r--r-- | nvidia-installer.c | 32 | ||||
-rw-r--r-- | nvidia-installer.h | 274 | ||||
-rw-r--r-- | option_table.h | 57 | ||||
-rw-r--r-- | precompiled.c | 65 | ||||
-rw-r--r-- | precompiled.h | 3 | ||||
-rw-r--r-- | user-interface.c | 46 | ||||
-rw-r--r-- | user-interface.h | 26 | ||||
-rw-r--r-- | utils.mk | 6 | ||||
-rw-r--r-- | version.mk | 2 |
25 files changed, 2093 insertions, 668 deletions
@@ -17,6 +17,14 @@ implementation of nvidia-installer, but the source code is fairly well commented. +Build dependencies of nvidia-installer include: + + libkmod ncurses pciutils + +Please ensure that the appropriate development packages for these +dependencies have been installed before building nvidia-installer. + + One interesting thing to note is that user interface shared libraries are built into nvidia-installer to avoid potential problems with the installer not being able to find the user interface libraries (or @@ -121,6 +121,7 @@ static int sanity_check_backup_log_entries(Options *op, BackupInfo *b); static char *create_backwards_compatible_version_string(const char *str); +static int reverse_strlen_compare(const void *a, const void *b); @@ -148,7 +149,7 @@ int init_backup(Options *op, Package *p) /* create the backup directory, with perms only for owner */ - if (!mkdir_recursive(op, BACKUP_DIRECTORY, BACKUP_DIRECTORY_PERMS)) { + if (!mkdir_with_log(op, BACKUP_DIRECTORY, BACKUP_DIRECTORY_PERMS, FALSE)) { return FALSE; } @@ -494,9 +495,7 @@ static int parse_crc(const char *buf, uint32 *crc) /* * log_mkdir() - takes a newline-delimited list of directories and appends - * them to the log of directories created during installation. The caller - * should ensure that directories are listed in decreasing order of depth, - * so that removing them in that order will work. + * them to the log of directories created during installation. */ int log_mkdir(Options *op, const char *dirs) { @@ -534,18 +533,32 @@ int log_mkdir(Options *op, const char *dirs) return TRUE; } + + +/* + * reverse_strlen_compare() - Compare two strings by length, for sorting + * in order of decreasing length. + */ +static int reverse_strlen_compare(const void *a, const void *b) +{ + return strlen((const char *)b) - strlen((const char *)a); +} + + + /* * rmdir_recursive() - Use BACKUP_MKDIR_LOG to find directories that were * created by a previous nvidia-installer, and delete any such directories. * Returns TRUE if the log is found and all directories are successfully * deleted; returns FALSE if any directories failed to be deleted or the - * log isn't found. + * log isn't found. The log enries are processed in reverse order, so that + * child directories get properly deleted before their parents. */ static int rmdir_recursive(Options *op) { FILE *log; - char *dir; - int eof = FALSE, ret = TRUE; + char *dir, **dirs; + int eof = FALSE, ret = TRUE, lines, i; /* open the log file */ @@ -556,20 +569,43 @@ static int rmdir_recursive(Options *op) return FALSE; } - while (!eof) { + /* Count the number of lines */ + + for (lines = 0; !eof; lines++) { dir = fget_next_line(log, &eof); - if (dir) { + nvfree(dir); + } + + rewind(log); + + dirs = nvalloc(lines * sizeof(char*)); + + for (i = 0; i < lines; i++) { + dirs[i] = fget_next_line(log, &eof); + } + + qsort(dirs, lines, sizeof(char*), reverse_strlen_compare); + + for (i = lines; i < lines; i++) { + if (dirs[i]) { /* Ignore empty lines and the backup directory itself, since it is * never empty as long as the dirs file is still around. */ - if (strlen(dir) && strcmp(dir, BACKUP_DIRECTORY) != 0) { - if (rmdir(dir) != 0) { - ui_warn(op, "Failed to delete the directory '%s' (%s).", - dir, strerror(errno)); + if (strlen(dirs[i]) && strcmp(dir, BACKUP_DIRECTORY) != 0) { + if (rmdir(dirs[i]) != 0) { + ui_log(op, "Failed to delete the directory '%s' (%s).", + dirs[i], strerror(errno)); ret = FALSE; } } } - free(dir); + nvfree(dirs[i]); + } + + nvfree(dirs); + + if (!ret) { + ui_warn(op, "Failed to delete some directories. See %s for details.", + op->log_file_name); } /* close the log file */ @@ -595,6 +631,7 @@ static int do_uninstall(Options *op, const char *version) int i, len, ok; char *tmpstr, *cmd; float percent; + int removal_failed = FALSE, restore_failed = FALSE; static const char existing_installation_is_borked[] = "Your driver installation has been " @@ -667,16 +704,18 @@ static int do_uninstall(Options *op, const char *version) case INSTALLED_FILE: if (unlink(e->filename) == -1) { - ui_warn(op, "Unable to remove installed file '%s' (%s).", - e->filename, strerror(errno)); + ui_log(op, "Unable to remove installed file '%s' (%s).", + e->filename, strerror(errno)); + removal_failed = TRUE; } ui_status_update(op, percent, e->filename); break; case INSTALLED_SYMLINK: if (unlink(e->filename) == -1) { - ui_warn(op, "Unable to remove installed symlink '%s' (%s).", - e->filename, strerror(errno)); + ui_log(op, "Unable to remove installed symlink '%s' (%s).", + e->filename, strerror(errno)); + removal_failed = TRUE; } ui_status_update(op, percent, e->filename); break; @@ -707,22 +746,21 @@ static int do_uninstall(Options *op, const char *version) */ if (ok) { - ui_warn(op, "Unable to restore symbolic link " - "%s -> %s (%s).", e->filename, e->target, - strerror(errno)); - } else { - ui_log(op, "Unable to restore symbolic link " - "%s -> %s (%s).", e->filename, e->target, - strerror(errno)); + restore_failed = TRUE; } + + ui_log(op, "Unable to restore symbolic link " + "%s -> %s (%s).", e->filename, e->target, + strerror(errno)); } else { /* XXX do we need to chmod the symlink? */ if (lchown(e->filename, e->uid, e->gid)) { - ui_warn(op, "Unable to restore owner (%d) and group " - "(%d) for symbolic link '%s' (%s).", - e->uid, e->gid, e->filename, strerror(errno)); + ui_log(op, "Unable to restore owner (%d) and group " + "(%d) for symbolic link '%s' (%s).", + e->uid, e->gid, e->filename, strerror(errno)); + restore_failed = TRUE; } } ui_status_update(op, percent, e->filename); @@ -733,16 +771,19 @@ static int do_uninstall(Options *op, const char *version) tmpstr = nvalloc(len + 1); snprintf(tmpstr, len, "%s/%d", BACKUP_DIRECTORY, e->num); if (!nvrename(op, tmpstr, e->filename)) { - ui_warn(op, "Unable to restore file '%s'.", e->filename); + ui_log(op, "Unable to restore file '%s'.", e->filename); + restore_failed = TRUE; } else { if (chown(e->filename, e->uid, e->gid)) { - ui_warn(op, "Unable to restore owner (%d) and group " - "(%d) for file '%s' (%s).", - e->uid, e->gid, e->filename, strerror(errno)); + ui_log(op, "Unable to restore owner (%d) and group " + "(%d) for file '%s' (%s).", + e->uid, e->gid, e->filename, strerror(errno)); + restore_failed = TRUE; } else { if (chmod(e->filename, e->mode) == -1) { - ui_warn(op, "Unable to restore permissions %04o for " - "file '%s'.", e->mode, e->filename); + ui_log(op, "Unable to restore permissions %04o for " + "file '%s'.", e->mode, e->filename); + restore_failed = TRUE; } } } @@ -752,6 +793,16 @@ static int do_uninstall(Options *op, const char *version) } } + if (removal_failed) { + ui_warn(op, "Failed to remove some installed files/symlinks. See %s " + "for details", op->log_file_name); + } + + if (restore_failed) { + ui_warn(op, "Failed to restore some backed up files/symlinks, and/or " + "their attributes. See %s for details", op->log_file_name); + } + if (!rmdir_recursive(op)) { ui_log(op, "Unable to delete directories created by previous " "installation."); @@ -837,7 +888,8 @@ static BackupInfo *read_backup_log_file(Options *op) buf = mmap(0, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); if (!buf) { - ui_error(op, "Unable to mmap file '%s' (%s).", strerror(errno)); + ui_error(op, "Unable to mmap file '%s' (%s).", BACKUP_LOG, + strerror(errno)); return NULL; } @@ -1015,22 +1067,43 @@ static int check_backup_log_entries(Options *op, BackupInfo *b) case INSTALLED_FILE: - /* check if the file is still there, and has the same crc */ + /* check if the file is still there */ if (access(e->filename, F_OK) == -1) { ui_log(op, "Unable to access previously installed file " "'%s' (%s).", e->filename, strerror(errno)); ret = e->ok = FALSE; } else { - crc = compute_crc(op, e->filename); - - if (crc != e->crc) { - ui_log(op, "The previously installed file '%s' has a " - "different checksum (%lu) than " - "when it was installed (%lu). %s will not be " - "uninstalled.", - e->filename, crc, e->crc, e->filename); - ret = e->ok = FALSE; + /* check if the file still has the same crc; if not, try + * un-prelinking it and check the crc again */ + if (!verify_crc(op, e->filename, e->crc, &crc)) { + ui_expert(op, "The previously installed file '%s' has a " + "different checksum (%ul) than when it was " + "installed (%ul). This may be due to prelinking; " + "attempting `prelink -u %s` to restore the file.", + e->filename, crc, e->crc, e->filename); + + if (unprelink(op, e->filename) != 0) { + ui_log(op, "The previously installed file '%s' seems " + "to have changed, but `prelink -u` failed; " + "unable to restore '%s' to an un-prelinked " + "state.", e->filename, e->filename); + ret = e->ok = FALSE; + } else if (!verify_crc(op, e->filename, e->crc, &crc)) { + ui_log(op, "The previously installed file '%s' has a " + "different checksum (%ul) after running `pre" + "link -u` than when it was installed (%ul).", + e->filename, crc, e->crc); + ret = e->ok = FALSE; + } + + if (ret) { + ui_expert(op, "Un-prelinking successful: '%s' will " + "be uninstalled.", e->filename); + } else { + ui_log(op, "Un-prelinking unsuccessful: '%s' will " + "not be uninstalled.", e->filename); + } } } ui_status_update(op, percent, e->filename); @@ -1110,10 +1183,9 @@ static int check_backup_log_entries(Options *op, BackupInfo *b) if (crc != e->crc) { ui_log(op, "Backed up file '%s' (saved as '%s) has " - "different checksum (%lu) than" - "when it was backed up (%lu). %s will not be " - "restored.", e->filename, tmpstr, - crc, e->crc, e->filename); + "different checksum (%" PRIu32 ") than when it was " + "backed up (%" PRIu32"). %s will not be restored.", + e->filename, tmpstr, crc, e->crc, e->filename); ret = e->ok = FALSE; } } @@ -1463,8 +1535,9 @@ static int sanity_check_backup_log_entries(Options *op, BackupInfo *b) if (crc != e->crc) { ui_error(op, "The installed file '%s' has a different " - "checksum (%lu) than when it was installed " - "(%lu).", e->filename, crc, e->crc); + "checksum (%" PRIu32 ") than when it was " + "installed (%" PRIu32 ").", e->filename, crc, + e->crc); ret = FALSE; } } @@ -1523,9 +1596,9 @@ static int sanity_check_backup_log_entries(Options *op, BackupInfo *b) if (crc != e->crc) { ui_error(op, "Backed up file '%s' (saved as '%s) has a " - "different checksum (%lu) than" - "when it was backed up (%lu).", - e->filename, tmpstr, crc, e->crc); + "different checksum (%" PRIu32 ") than when it " + "was backed up (%" PRIu32 ").", e->filename, + tmpstr, crc, e->crc); ret = FALSE; } } diff --git a/command-list.c b/command-list.c index 98c9723..48506d0 100644 --- a/command-list.c +++ b/command-list.c @@ -42,6 +42,7 @@ #include "misc.h" #include "files.h" #include "kernel.h" +#include "manifest.h" static void free_file_list(FileList* l); @@ -62,7 +63,8 @@ static void find_conflicting_kernel_modules(Options *op, Package *p, FileList *l); -static void find_existing_files(Package *p, FileList *l, uint64_t); +static void find_existing_files(Package *p, FileList *l, + PackageEntryFileTypeList *file_type_list); static void condense_file_list(Package *p, FileList *l); @@ -92,11 +94,12 @@ CommandList *build_command_list(Options *op, Package *p) FileList *l; CommandList *c; int i, cmd; - uint64_t installable_files; + PackageEntryFileTypeList installable_files; + PackageEntryFileTypeList tmp_installable_files; char *tmp; - installable_files = get_installable_file_mask(op); - + get_installable_file_type_list(op, &installable_files); + l = (FileList *) nvalloc(sizeof(FileList)); c = (CommandList *) nvalloc(sizeof(CommandList)); @@ -205,10 +208,12 @@ CommandList *build_command_list(Options *op, Package *p) /* * find any existing files that clash with what we're going to * install - * DON'T include files of type FILE_TYPE_NEWSYM */ - - find_existing_files(p, l, installable_files | FILE_TYPE_SYMLINK); + + tmp_installable_files = installable_files; + add_symlinks_to_file_type_list(&tmp_installable_files); + + find_existing_files(p, l, &tmp_installable_files); /* condense the file list */ @@ -244,14 +249,14 @@ CommandList *build_command_list(Options *op, Package *p) */ if (op->selinux_enabled && (op->utils[EXECSTACK] != NULL) && - (p->entries[i].flags & FILE_TYPE_SHARED_LIB)) { + (p->entries[i].caps.is_shared_lib)) { tmp = nvstrcat(op->utils[EXECSTACK], " -c ", p->entries[i].dst, NULL); } else { tmp = NULL; } - if (p->entries[i].flags & installable_files) { + if (installable_files.types[p->entries[i].type]) { add_command(c, INSTALL_CMD, p->entries[i].file, p->entries[i].dst, @@ -266,14 +271,14 @@ CommandList *build_command_list(Options *op, Package *p) * based on templates earlier. */ - if ((p->entries[i].flags & FILE_TYPE_LIBGL_LA) || - (p->entries[i].flags & FILE_TYPE_DOT_DESKTOP)) { + if ((p->entries[i].type == FILE_TYPE_LIBGL_LA) || + (p->entries[i].type == FILE_TYPE_DOT_DESKTOP)) { add_command(c, DELETE_CMD, p->entries[i].file); } if (op->selinux_enabled && - (p->entries[i].flags & FILE_TYPE_SHARED_LIB)) { + (p->entries[i].caps.is_shared_lib)) { tmp = nvstrcat(op->utils[CHCON], " -t ", op->selinux_chcon_type, " ", p->entries[i].dst, NULL); add_command(c, RUN_CMD, tmp); @@ -285,11 +290,10 @@ CommandList *build_command_list(Options *op, Package *p) /* create any needed symbolic links */ for (i = 0; i < p->num_entries; i++) { - if ((p->entries[i].flags & FILE_TYPE_SYMLINK) || - (p->entries[i].flags & FILE_TYPE_NEWSYM)) { + if (p->entries[i].caps.is_symlink) { /* if it's a NEWSYM and the file already exists, don't add a command * for it */ - if (p->entries[i].flags & FILE_TYPE_NEWSYM) { + if (p->entries[i].type == FILE_TYPE_XMODULE_NEWSYM) { struct stat buf; if(!stat(p->entries[i].dst, &buf) || errno != ENOENT) { ui_expert(op, "Not creating a symlink from %s to %s " @@ -308,7 +312,7 @@ CommandList *build_command_list(Options *op, Package *p) /* find any commands we should run */ for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_TYPE_KERNEL_MODULE_CMD) { + if (p->entries[i].type == FILE_TYPE_KERNEL_MODULE_CMD) { add_command(c, RUN_CMD, p->entries[i].file, NULL, 0); } } @@ -322,7 +326,7 @@ CommandList *build_command_list(Options *op, Package *p) if (op->no_abi_note) { for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_TYPE_OPENGL_LIB) { + if (p->entries[i].type == FILE_TYPE_OPENGL_LIB) { tmp = nvstrcat(op->utils[OBJCOPY], " --remove-section=.note.ABI-tag ", p->entries[i].dst, @@ -728,13 +732,14 @@ static void find_conflicting_kernel_modules(Options *op, * FileList. */ -static void find_existing_files(Package *p, FileList *l, uint64_t flag) +static void find_existing_files(Package *p, FileList *l, + PackageEntryFileTypeList *file_type_list) { int i; struct stat stat_buf; for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & flag) { + if (file_type_list->types[p->entries[i].type]) { if (lstat(p->entries[i].dst, &stat_buf) == 0) { add_file_to_list(NULL, p->entries[i].dst, l); } diff --git a/common-utils/common-utils.c b/common-utils/common-utils.c index a8ee75a..a11d996 100644 --- a/common-utils/common-utils.c +++ b/common-utils/common-utils.c @@ -200,6 +200,44 @@ char *nvstrtolower(char *s) /* + * nvstrtoupper() - convert the given string to uppercase. + */ + +char *nvstrtoupper(char *s) +{ + char *start = s; + + if (s == NULL) return NULL; + + while (*s) { + *s = toupper(*s); + s++; + } + + return start; + +} /* nvstrtoupper() */ + + + +/* + * nvasprintf() - implementation of asprintf() that checks return values; if an + * error occurs, an error is printed to stderr and exit is called. + * -- this function will only return on success. + */ +char *nvasprintf(const char *fmt, ...) +{ + char *str; + + NV_VSNPRINTF(str, fmt); + + return str; + +} /* nvasprintf() */ + + + +/* * nvfree() - frees memory allocated with nvalloc(), provided * a non-NULL pointer is provided. */ @@ -646,3 +684,12 @@ char *fget_next_line(FILE *fp, int *eof) return NULL; /* should never get here */ } + +char *nvstrchrnul(char *s, int c) +{ + char *result = strchr(s, c); + if (!result) { + return (s + strlen(s)); + } + return result; +} diff --git a/common-utils/common-utils.h b/common-utils/common-utils.h index 626fd1b..7e2838f 100644 --- a/common-utils/common-utils.h +++ b/common-utils/common-utils.h @@ -40,6 +40,18 @@ #define VERBOSITY_DEFAULT VERBOSITY_ERROR +/* + * Define a printf format attribute macro. This definition is based on the one + * from Xfuncproto.h, available in the 'xproto' package at + * http://xorg.freedesktop.org/releases/individual/proto/ + */ + +#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 203) +# define NV_ATTRIBUTE_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) +#else /* not gcc >= 2.3 */ +# define NV_ATTRIBUTE_PRINTF(x,y) +#endif + typedef struct { char **t; /* the text rows */ int n; /* number of rows */ @@ -52,6 +64,9 @@ void *nvrealloc(void *ptr, size_t size); char *nvstrdup(const char *s); char *nvstrndup(const char *s, size_t n); char *nvstrtolower(char *s); +char *nvstrtoupper(char *s); +char *nvstrchrnul(char *s, int c); +char *nvasprintf(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); void nvfree(void *s); char *tilde_expansion(const char *str); @@ -66,11 +81,11 @@ void nv_free_text_rows(TextRows *t); void reset_current_terminal_width(unsigned short new_val); void silence_fmt(int val); -void fmtout(const char *fmt, ...); -void fmtoutp(const char *prefix, const char *fmt, ...); -void fmterr(const char *fmt, ...); -void fmtwarn(const char *fmt, ...); -void fmt(FILE *stream, const char *prefix, const char *fmt, ...); +void fmtout(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void fmtoutp(const char *prefix, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void fmterr(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void fmtwarn(const char *fmt, ...) NV_ATTRIBUTE_PRINTF(1, 2); +void fmt(FILE *stream, const char *prefix, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(3, 4); char *fget_next_line(FILE *fp, int *eof); @@ -99,9 +114,9 @@ do { \ va_list ap; \ int len, current_len = NV_FMT_BUF_LEN; \ \ - (buf) = malloc(current_len); \ - \ while (1) { \ + (buf) = nvalloc(current_len); \ + \ va_start(ap, fmt); \ len = vsnprintf((buf), current_len, (fmt), ap); \ va_end(ap); \ @@ -113,8 +128,8 @@ do { \ } else { \ current_len += NV_FMT_BUF_LEN; \ } \ - free(buf); \ - (buf) = malloc(current_len); \ + \ + nvfree(buf); \ } \ } \ } while (0) diff --git a/dist-files.mk b/dist-files.mk index d50c576..b08a951 100644 --- a/dist-files.mk +++ b/dist-files.mk @@ -43,6 +43,7 @@ SRC += stream-ui.c SRC += update.c SRC += user-interface.c SRC += sanity.c +SRC += manifest.c DIST_FILES := $(SRC) @@ -61,6 +62,7 @@ DIST_FILES += snarf-internal.h DIST_FILES += snarf.h DIST_FILES += update.h DIST_FILES += user-interface.h +DIST_FILES += manifest.h DIST_FILES += COPYING DIST_FILES += README @@ -387,14 +387,14 @@ void select_tls_class(Options *op, Package *p) /* * tls libraries will not run on this system; just install the * classic OpenGL libraries: clear the FILE_TYPE of any - * FILE_CLASS_NEW_TLS package entries. + * FILE_TLS_CLASS_NEW package entries. */ ui_log(op, "Installing classic TLS OpenGL libraries."); for (i = 0; i < p->num_entries; i++) { - if ((p->entries[i].flags & FILE_CLASS_NEW_TLS) && - (p->entries[i].flags & FILE_CLASS_NATIVE)) { + if ((p->entries[i].tls_class == FILE_TLS_CLASS_NEW) && + (p->entries[i].compat_arch == FILE_COMPAT_ARCH_NATIVE)) { /* * XXX don't try to free the destination string for * these invalidated TLS libraries; this prevents @@ -402,7 +402,7 @@ void select_tls_class(Options *op, Package *p) * I've been unable to reproduce/root cause. */ /* nvfree(p->entries[i].dst); */ - p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].type = FILE_TYPE_NONE; p->entries[i].dst = NULL; } } @@ -432,14 +432,14 @@ void select_tls_class(Options *op, Package *p) /* * 32bit tls libraries will not run on this system; just * install the classic OpenGL libraries: clear the FILE_TYPE - * of any FILE_CLASS_NEW_TLS_32 package entries. + * of any tls_class==NEW && compat_arch==COMPAT32 package entries. */ ui_log(op, "Installing classic TLS 32bit OpenGL libraries."); for (i = 0; i < p->num_entries; i++) { - if ((p->entries[i].flags & FILE_CLASS_NEW_TLS) && - (p->entries[i].flags & FILE_CLASS_COMPAT32)) { + if ((p->entries[i].tls_class == FILE_TLS_CLASS_NEW) && + (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32)) { /* * XXX don't try to free the destination string for * these invalidated TLS libraries; this prevents @@ -447,7 +447,7 @@ void select_tls_class(Options *op, Package *p) * I've been unable to reproduce/root cause. */ /* nvfree(p->entries[i].dst); */ - p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].type = FILE_TYPE_NONE; p->entries[i].dst = NULL; } } @@ -489,7 +489,7 @@ int set_destinations(Options *op, Package *p) for (i = 0; i < p->num_entries; i++) { - switch (p->entries[i].flags & FILE_TYPE_MASK) { + switch (p->entries[i].type) { case FILE_TYPE_KERNEL_MODULE_CMD: /* we don't install kernel module commands */ @@ -509,7 +509,7 @@ int set_destinations(Options *op, Package *p) case FILE_TYPE_OPENGL_LIB: case FILE_TYPE_OPENGL_SYMLINK: - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; } else { @@ -521,7 +521,7 @@ int set_destinations(Options *op, Package *p) case FILE_TYPE_VDPAU_LIB: case FILE_TYPE_VDPAU_SYMLINK: - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; } else { @@ -533,7 +533,7 @@ int set_destinations(Options *op, Package *p) case FILE_TYPE_CUDA_LIB: case FILE_TYPE_CUDA_SYMLINK: - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; } else { @@ -568,7 +568,7 @@ int set_destinations(Options *op, Package *p) case FILE_TYPE_TLS_LIB: case FILE_TYPE_TLS_SYMLINK: - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; } else { @@ -586,7 +586,7 @@ int set_destinations(Options *op, Package *p) break; case FILE_TYPE_LIBGL_LA: - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; } else { @@ -597,8 +597,8 @@ int set_destinations(Options *op, Package *p) break; case FILE_TYPE_NVCUVID_LIB: - case FILE_TYPE_NVCUVID_SYMLINK: - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + case FILE_TYPE_NVCUVID_LIB_SYMLINK: + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; } else { @@ -609,8 +609,8 @@ int set_destinations(Options *op, Package *p) break; case FILE_TYPE_ENCODEAPI_LIB: - case FILE_TYPE_ENCODEAPI_SYMLINK: - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + case FILE_TYPE_ENCODEAPI_LIB_SYMLINK: + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { prefix = op->compat32_prefix; dir = op->compat32_libdir; } else { @@ -621,7 +621,7 @@ int set_destinations(Options *op, Package *p) break; case FILE_TYPE_VGX_LIB: - case FILE_TYPE_VGX_SYMLINK: + case FILE_TYPE_VGX_LIB_SYMLINK: prefix = op->opengl_prefix; dir = op->opengl_libdir; path = ""; @@ -646,11 +646,17 @@ int set_destinations(Options *op, Package *p) break; case FILE_TYPE_MANPAGE: + case FILE_TYPE_NVIDIA_MODPROBE_MANPAGE: prefix = op->documentation_prefix; dir = op->documentation_mandir; path = p->entries[i].path; break; + case FILE_TYPE_APPLICATION_PROFILE: + prefix = op->application_profile_path; + dir = path = ""; + break; + case FILE_TYPE_UTILITY_BINARY: case FILE_TYPE_UTILITY_BIN_SYMLINK: prefix = op->utility_prefix; @@ -679,7 +685,13 @@ int set_destinations(Options *op, Package *p) continue; + case FILE_TYPE_MODULE_SIGNING_KEY: + prefix = op->module_signing_key_path; + dir = path = ""; + break; + case FILE_TYPE_EXPLICIT_PATH: + case FILE_TYPE_NVIDIA_MODPROBE: prefix = p->entries[i].path; dir = path = ""; break; @@ -706,7 +718,7 @@ int set_destinations(Options *op, Package *p) collapse_multiple_slashes(p->entries[i].dst); #if defined(NV_X86_64) - if ((p->entries[i].flags & FILE_CLASS_COMPAT32) && + if ((p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) && (op->compat32_chroot != NULL)) { /* @@ -995,6 +1007,7 @@ int add_kernel_module_to_package(Options *op, Package *p) NULL, /* target */ dst, FILE_TYPE_KERNEL_MODULE, + FILE_TLS_CLASS_NONE, 0644); return TRUE; @@ -1004,9 +1017,8 @@ int add_kernel_module_to_package(Options *op, Package *p) /* - * remove_non_kernel_module_files_from_package() - clear the - * FILE_TYPE_MASK bits for each package entry that is not of type - * FILE_TYPE_KERNEL_MODULE or FILE_TYPE_KERNEL_MODULE_SRC + * Clear the file type for each package entry that is not type + * FILE_TYPE_KERNEL_MODULE or FILE_TYPE_KERNEL_MODULE_SRC. */ void remove_non_kernel_module_files_from_package(Options *op, Package *p) @@ -1014,28 +1026,26 @@ void remove_non_kernel_module_files_from_package(Options *op, Package *p) int i; for (i = 0; i < p->num_entries; i++) { - uint64_t flags = p->entries[i].flags & FILE_TYPE_MASK; - if ((flags != FILE_TYPE_KERNEL_MODULE) && - (flags != FILE_TYPE_KERNEL_MODULE_CMD) && - (flags != FILE_TYPE_KERNEL_MODULE_SRC)) - p->entries[i].flags &= ~FILE_TYPE_MASK; + if ((p->entries[i].type != FILE_TYPE_KERNEL_MODULE) && + (p->entries[i].type != FILE_TYPE_KERNEL_MODULE_CMD) && + (p->entries[i].type != FILE_TYPE_KERNEL_MODULE_SRC)) { + p->entries[i].type = FILE_TYPE_NONE; + } } - -} /* remove_non_kernel_module_files_from_package() */ +} /* - * clear the FILE_TYPE_MASK bits for each package entry that is not of - * type FILE_TYPE_OPENGL_FILE + * Clear the file type for each package entry that is not type + * FILE_TYPE_OPENGL_FILE. */ void remove_opengl_files_from_package(Options *op, Package *p) { int i; for (i = 0; i < p->num_entries; i++) { - uint64_t flags = p->entries[i].flags & FILE_TYPE_MASK; - if (flags & FILE_TYPE_OPENGL_FILE) { - p->entries[i].flags &= ~FILE_TYPE_MASK; + if (p->entries[i].caps.is_opengl) { + p->entries[i].type = FILE_TYPE_NONE; } } } @@ -1572,7 +1582,7 @@ int copy_directory_contents(Options *op, const char *src, const char *dst) int pack_precompiled_kernel_interface(Options *op, Package *p) { - char *cmd, time_str[256], *proc_version_string; + char *cmd, time_str[256], *proc_version_string, *suffix, *file, *newfile; char *result, *descr; time_t t; struct utsname buf; @@ -1603,12 +1613,13 @@ int pack_precompiled_kernel_interface(Options *op, Package *p) /* build the mkprecompiled command */ + suffix = nvstrcat("-", p->version, ".", time_str, NULL); + cmd = nvstrcat("./mkprecompiled --interface=", p->kernel_module_build_directory, "/", PRECOMPILED_KERNEL_INTERFACE_FILENAME, " --output=", p->precompiled_kernel_interface_directory, - "/", PRECOMPILED_KERNEL_INTERFACE_FILENAME, - "-", p->version, ".", time_str, + "/", PRECOMPILED_KERNEL_INTERFACE_FILENAME, suffix, " --description=\"", descr, "\"", " --proc-version=\"", proc_version_string, "\"", " --version=", p->version, NULL); @@ -1620,15 +1631,41 @@ int pack_precompiled_kernel_interface(Options *op, Package *p) nvfree(cmd); nvfree(proc_version_string); nvfree(descr); + + /* pack the detached signature and checksum, if they exist */ + + file = nvstrcat(p->kernel_module_build_directory, "/", + DETACHED_SIGNATURE_FILENAME, NULL); + + if (access(file, R_OK) == 0) { + newfile = nvstrcat(p->precompiled_kernel_interface_directory, "/", + DETACHED_SIGNATURE_FILENAME, suffix, NULL); + rename(file, newfile); + nvfree(newfile); + } + + nvfree(file); + + file = nvstrcat(p->kernel_module_build_directory, "/", + KERNEL_MODULE_CHECKSUM_FILENAME, NULL); + + if (access(file, R_OK) == 0) { + newfile = nvstrcat(p->precompiled_kernel_interface_directory, "/", + KERNEL_MODULE_CHECKSUM_FILENAME, suffix, NULL); + rename(file, newfile); + nvfree(newfile); + } + + nvfree(file); /* remove the old kernel interface file */ - cmd = nvstrcat(p->kernel_module_build_directory, "/", - PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); + file = nvstrcat(p->kernel_module_build_directory, "/", + PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); - unlink(cmd); /* XXX what to do if this fails? */ + unlink(file); /* XXX what to do if this fails? */ - nvfree(cmd); + nvfree(file); if (ret != 0) { ui_error(op, "Unable to package precompiled kernel interface: %s", @@ -1652,7 +1689,7 @@ int pack_precompiled_kernel_interface(Options *op, Package *p) * 'replace'. */ -static char *nv_strreplace(char *src, char *orig, char *replace) +char *nv_strreplace(char *src, char *orig, char *replace) { char *prev_s, *end_s, *s; char *d, *dst; @@ -1886,9 +1923,9 @@ void process_libGL_la_files(Options *op, Package *p) NVIDIA_INSTALLER_VERSION, NULL); for (i = 0; i < package_num_entries; i++) { - if ((p->entries[i].flags & FILE_TYPE_LIBGL_LA)) { + if ((p->entries[i].type == FILE_TYPE_LIBGL_LA)) { - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { replacements[0] = nvstrcat(op->compat32_prefix, "/", op->compat32_libdir, NULL); } else { @@ -1898,7 +1935,7 @@ void process_libGL_la_files(Options *op, Package *p) /* invalidate the template file */ - p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].type = FILE_TYPE_NONE; p->entries[i].dst = NULL; tmpfile = process_template_file(op, &p->entries[i], tokens, @@ -1922,8 +1959,8 @@ void process_libGL_la_files(Options *op, Package *p) nvstrdup(p->entries[i].name), NULL, /* target */ NULL, /* dst */ - ((p->entries[i].flags & FILE_CLASS_MASK) | - FILE_TYPE_LIBGL_LA), + FILE_TYPE_LIBGL_LA, + p->entries[i].tls_class, p->entries[i].mode); } @@ -1961,11 +1998,11 @@ void process_dot_desktop_files(Options *op, Package *p) collapse_multiple_slashes(replacements[0]); for (i = 0; i < package_num_entries; i++) { - if ((p->entries[i].flags & FILE_TYPE_DOT_DESKTOP)) { + if ((p->entries[i].type == FILE_TYPE_DOT_DESKTOP)) { /* invalidate the template file */ - p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].type = FILE_TYPE_NONE; p->entries[i].dst = NULL; nvfree(replacements[1]); @@ -1998,8 +2035,8 @@ void process_dot_desktop_files(Options *op, Package *p) nvstrdup(p->entries[i].name), NULL, /* target */ NULL, /* dst */ - ((p->entries[i].flags & FILE_CLASS_MASK) | - FILE_TYPE_DOT_DESKTOP), + FILE_TYPE_DOT_DESKTOP, + p->entries[i].tls_class, p->entries[i].mode); } } @@ -2048,7 +2085,8 @@ void get_default_prefixes_and_paths(Options *op) #if defined(NV_X86_64) if ((op->distro == DEBIAN) || - (op->distro == UBUNTU)) { + (op->distro == UBUNTU) || + (op->distro == ARCH)) { default_libdir = DEBIAN_DEFAULT_64BIT_LIBDIR; } else { default_libdir = DEFAULT_64BIT_LIBDIR; @@ -2087,7 +2125,8 @@ void get_default_prefixes_and_paths(Options *op) if (!op->compat32_libdir) { if ((op->distro == UBUNTU) || - (op->distro == GENTOO)) { + (op->distro == GENTOO) || + (op->distro == ARCH)) { op->compat32_libdir = UBUNTU_DEFAULT_COMPAT32_LIBDIR; } else { op->compat32_libdir = DEFAULT_LIBDIR; @@ -2134,8 +2173,14 @@ void get_default_prefixes_and_paths(Options *op) if (!op->documentation_mandir) op->documentation_mandir = DEFAULT_MANDIR; + if (!op->application_profile_path) + op->application_profile_path = DEFAULT_APPLICATION_PROFILE_PATH; + if (!op->kernel_module_src_prefix) op->kernel_module_src_prefix = DEFAULT_KERNEL_MODULE_SRC_PREFIX; + + if (!op->module_signing_key_path) + op->module_signing_key_path = DEFAULT_MODULE_SIGNING_KEY_PATH; /* kernel_module_src_dir's default value is set in set_destinations() */ } /* get_default_prefixes_and_paths() */ @@ -2421,3 +2466,70 @@ static void get_x_library_and_module_paths(Options *op) } } /* get_x_library_and_module_paths() */ + + + +/* + * get_filename() - Prompt the user for the path to a file. If no file exists + * at the given path, keep reprompting until a valid path to a regular file or + * symbolic link is given. This is just a thin wrapper around ui_get_input(). + */ +char *get_filename(Options *op, const char *def, const char *fmt, ...) +{ + struct stat stat_buf; + char *file = NULL; + + /* XXX This function should never be called if op->no_questions is set, + * but just in case that happens by accident, do something besides looping + * infinitely if def is a filename that doesn't exist. */ + if (op->no_questions) { + return nvstrdup(def); + } + + file = ui_get_input(op, file ? file : def, fmt); + + while (stat(file, &stat_buf) == -1 || + !(S_ISREG(stat_buf.st_mode) || S_ISLNK(stat_buf.st_mode))) { + char *oldfile = file; + + ui_message(op, "File \"%s\" does not exist, or is not a regular " + "file. Please enter another filename.", file); + + file = ui_get_input(op, oldfile ? oldfile : def, fmt); + nvfree(oldfile); + } + + return file; +} /* get_filename() */ + + + +/* + * secure_delete() - Securely delete a file, using `shred -u`. If `shred` isn't + * found, fall back to just unlinking, but print a warning message. + */ +int secure_delete(Options *op, const char *file) +{ + char *cmd; + + cmd = find_system_util("shred"); + + if (cmd) { + int ret; + char *cmdline = nvstrcat(cmd, " -u \"", file, "\"", NULL); + + ret = run_command(op, cmdline, NULL, FALSE, 0, TRUE); + log_printf(op, NULL, "%s: %s", cmdline, ret == 0 ? "" : "failed!"); + + nvfree(cmd); + nvfree(cmdline); + + return ret == 0; + } else { + ui_warn(op, "`shred` was not found on the system. The file %s will " + "be deleted, but not securely. It may be possible to recover " + "the file after deletion.", file); + unlink(file); + return FALSE; + } +} /* secure_delete() */ @@ -61,5 +61,8 @@ void process_libGL_la_files(Options *op, Package *p); void process_dot_desktop_files(Options *op, Package *p); int set_security_context(Options *op, const char *filename); void get_default_prefixes_and_paths(Options *op); +char *nv_strreplace(char *src, char *orig, char *replace); +char *get_filename(Options *op, const char *def, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(3, 4); +int secure_delete(Options *op, const char *file); #endif /* __NVIDIA_INSTALLER_FILES_H__ */ diff --git a/install-from-cwd.c b/install-from-cwd.c index a7b040c..7fe719d 100644 --- a/install-from-cwd.c +++ b/install-from-cwd.c @@ -45,6 +45,12 @@ #include "files.h" #include "misc.h" #include "sanity.h" +#include "manifest.h" + +/* default names for generated signing keys */ + +#define SECKEY_NAME "tmp.key" +#define PUBKEY_NAME "tmp.der" /* local prototypes */ @@ -52,6 +58,7 @@ static Package *parse_manifest(Options *op); static int install_kernel_module(Options *op, Package *p); static void free_package(Package *p); +static int assisted_module_signing(Options *op, Package *p); /* @@ -140,9 +147,11 @@ int install_from_cwd(Options *op) if (!op->no_kernel_module) { /* Offer the DKMS option if DKMS exists and the kernel module sources - * will be installed somewhere */ + * will be installed somewhere. Don't offer DKMS as an option if module + * signing was requested. */ - if (find_system_util("dkms") && !op->no_kernel_module_source) { + if (find_system_util("dkms") && !op->no_kernel_module_source && + !(op->module_signing_secret_key && op->module_signing_public_key)) { op->dkms = ui_yes_no(op, op->dkms, "Would you like to register the kernel module " "sources with DKMS? This will allow DKMS to " @@ -378,6 +387,8 @@ int install_from_cwd(Options *op) static int install_kernel_module(Options *op, Package *p) { + PrecompiledInfo *precompiled_info; + /* determine where to install the kernel module */ if (!determine_kernel_module_installation_path(op)) return FALSE; @@ -399,10 +410,10 @@ static int install_kernel_module(Options *op, Package *p) * headers installed, so it's better to catch that earlier on. */ - if (find_precompiled_kernel_interface(op, p)) { + if ((precompiled_info = find_precompiled_kernel_interface(op, p))) { /* - * we have a prebuild kernel interface, so now link the kernel + * we have a prebuilt kernel interface, so now link the kernel * interface with the binary portion of the kernel module. * * XXX if linking fails, maybe we should fall through and @@ -411,7 +422,8 @@ static int install_kernel_module(Options *op, Package *p) * abort. */ - if (!link_kernel_module(op, p)) return FALSE; + if (!link_kernel_module(op, p, p->kernel_module_build_directory, + precompiled_info)) return FALSE; } else { /* @@ -441,6 +453,9 @@ static int install_kernel_module(Options *op, Package *p) if (!build_kernel_module(op, p)) return FALSE; } + /* Optionally sign the kernel module */ + if (!assisted_module_signing(op, p)) return FALSE; + /* * if we got this far, we have a complete kernel module; test it * to be sure it's OK @@ -692,84 +707,15 @@ static Package *parse_manifest (Options *op) /* every file has a type field */ - p->entries[n].flags = 0x0; + p->entries[n].type = FILE_TYPE_NONE; flag = read_next_word(c, &c); if (!flag) goto invalid_manifest_file; - if (strcmp(flag, "KERNEL_MODULE_SRC") == 0) - p->entries[n].flags |= FILE_TYPE_KERNEL_MODULE_SRC; - else if (strcmp(flag, "KERNEL_MODULE_CMD") == 0) - p->entries[n].flags |= FILE_TYPE_KERNEL_MODULE_CMD; - else if (strcmp(flag, "OPENGL_HEADER") == 0) - p->entries[n].flags |= FILE_TYPE_OPENGL_HEADER; - else if (strcmp(flag, "CUDA_ICD") == 0) - p->entries[n].flags |= FILE_TYPE_CUDA_ICD; - else if (strcmp(flag, "OPENGL_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_OPENGL_LIB; - else if (strcmp(flag, "CUDA_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_CUDA_LIB; - else if (strcmp(flag, "LIBGL_LA") == 0) - p->entries[n].flags |= FILE_TYPE_LIBGL_LA; - else if (strcmp(flag, "XLIB_STATIC_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_XLIB_STATIC_LIB; - else if (strcmp(flag, "XLIB_SHARED_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_XLIB_SHARED_LIB; - else if (strcmp(flag, "TLS_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_TLS_LIB; - else if (strcmp(flag, "UTILITY_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_UTILITY_LIB; - else if (strcmp(flag, "DOCUMENTATION") == 0) - p->entries[n].flags |= FILE_TYPE_DOCUMENTATION; - else if (strcmp(flag, "MANPAGE") == 0) - p->entries[n].flags |= FILE_TYPE_MANPAGE; - else if (strcmp(flag, "EXPLICIT_PATH") == 0) - p->entries[n].flags |= FILE_TYPE_EXPLICIT_PATH; - else if (strcmp(flag, "OPENGL_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_OPENGL_SYMLINK; - else if (strcmp(flag, "CUDA_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_CUDA_SYMLINK; - else if (strcmp(flag, "XLIB_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_XLIB_SYMLINK; - else if (strcmp(flag, "TLS_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_TLS_SYMLINK; - else if (strcmp(flag, "UTILITY_LIB_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_UTILITY_LIB_SYMLINK; - else if (strcmp(flag, "INSTALLER_BINARY") == 0) - p->entries[n].flags |= FILE_TYPE_INSTALLER_BINARY; - else if (strcmp(flag, "UTILITY_BINARY") == 0) - p->entries[n].flags |= FILE_TYPE_UTILITY_BINARY; - else if (strcmp(flag, "UTILITY_BIN_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_UTILITY_BIN_SYMLINK; - else if (strcmp(flag, "DOT_DESKTOP") == 0) - p->entries[n].flags |= FILE_TYPE_DOT_DESKTOP; - else if (strcmp(flag, "XMODULE_SHARED_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_XMODULE_SHARED_LIB; - else if (strcmp(flag, "XMODULE_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_XMODULE_SYMLINK; - else if (strcmp(flag, "GLX_MODULE_SHARED_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_GLX_MODULE_SHARED_LIB; - else if (strcmp(flag, "GLX_MODULE_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_GLX_MODULE_SYMLINK; - else if (strcmp(flag, "XMODULE_NEWSYM") == 0) - p->entries[n].flags |= FILE_TYPE_XMODULE_NEWSYM; - else if (strcmp(flag, "VDPAU_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_VDPAU_LIB; - else if (strcmp(flag, "VDPAU_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_VDPAU_SYMLINK; - else if (strcmp(flag, "NVCUVID_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_NVCUVID_LIB; - else if (strcmp(flag, "NVCUVID_LIB_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_NVCUVID_SYMLINK; - else if (strcmp(flag, "ENCODEAPI_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_ENCODEAPI_LIB; - else if (strcmp(flag, "ENCODEAPI_LIB_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_ENCODEAPI_SYMLINK; - else if (strcmp(flag, "VGX_LIB") == 0) - p->entries[n].flags |= FILE_TYPE_VGX_LIB; - else if (strcmp(flag, "VGX_LIB_SYMLINK") == 0) - p->entries[n].flags |= FILE_TYPE_VGX_SYMLINK; - else { + p->entries[n].type = parse_manifest_file_type(flag, + &p->entries[n].caps); + + if (p->entries[n].type == FILE_TYPE_NONE) { nvfree(flag); goto invalid_manifest_file; } @@ -778,14 +724,16 @@ static Package *parse_manifest (Options *op) /* some libs/symlinks have an arch field */ - if (p->entries[n].flags & FILE_TYPE_HAVE_ARCH) { + p->entries[n].compat_arch = FILE_COMPAT_ARCH_NONE; + + if (p->entries[n].caps.has_arch) { flag = read_next_word(c, &c); if (!flag) goto invalid_manifest_file; if (strcmp(flag, "COMPAT32") == 0) - p->entries[n].flags |= FILE_CLASS_COMPAT32; + p->entries[n].compat_arch = FILE_COMPAT_ARCH_COMPAT32; else if (strcmp(flag, "NATIVE") == 0) - p->entries[n].flags |= FILE_CLASS_NATIVE; + p->entries[n].compat_arch = FILE_COMPAT_ARCH_NATIVE; else { nvfree(flag); goto invalid_manifest_file; @@ -796,14 +744,16 @@ static Package *parse_manifest (Options *op) /* some libs/symlinks have a class field */ - if (p->entries[n].flags & FILE_TYPE_HAVE_CLASS) { + p->entries[n].tls_class = FILE_TLS_CLASS_NONE; + + if (p->entries[n].caps.has_tls_class) { flag = read_next_word(c, &c); if (!flag) goto invalid_manifest_file; if (strcmp(flag, "CLASSIC") == 0) - p->entries[n].flags |= FILE_CLASS_CLASSIC_TLS; + p->entries[n].tls_class = FILE_TLS_CLASS_CLASSIC; else if (strcmp(flag, "NEW") == 0) - p->entries[n].flags |= FILE_CLASS_NEW_TLS; + p->entries[n].tls_class = FILE_TLS_CLASS_NEW; else { nvfree(flag); goto invalid_manifest_file; @@ -814,16 +764,16 @@ static Package *parse_manifest (Options *op) /* libs and documentation have a path field */ - if (p->entries[n].flags & FILE_TYPE_HAVE_PATH) { + if (p->entries[n].caps.has_path) { p->entries[n].path = read_next_word(c, &c); if (!p->entries[n].path) goto invalid_manifest_file; } else { p->entries[n].path = NULL; } - /* symlinks and newsyms have a target */ + /* symlinks have a target */ - if (p->entries[n].flags & FILE_TYPE_HAVE_TARGET) { + if (p->entries[n].caps.is_symlink) { p->entries[n].target = read_next_word(c, &c); if (!p->entries[n].target) goto invalid_manifest_file; } else { @@ -900,7 +850,8 @@ void add_package_entry(Package *p, char *name, char *target, char *dst, - uint64_t flags, + PackageEntryFileType type, + PackageEntryFileTlsClass tls_class, mode_t mode) { int n; @@ -911,13 +862,17 @@ void add_package_entry(Package *p, p->entries = (PackageEntry *) nvrealloc(p->entries, (n + 1) * sizeof(PackageEntry)); - p->entries[n].file = file; - p->entries[n].path = path; - p->entries[n].name = name; - p->entries[n].target = target; - p->entries[n].dst = dst; - p->entries[n].flags = flags; - p->entries[n].mode = mode; + memset(&p->entries[n], 0, sizeof(PackageEntry)); + + p->entries[n].file = file; + p->entries[n].path = path; + p->entries[n].name = name; + p->entries[n].target = target; + p->entries[n].dst = dst; + p->entries[n].type = type; + p->entries[n].tls_class = tls_class; + p->entries[n].mode = mode; + p->entries[n].caps = get_file_type_capabilities(type); if (stat(p->entries[n].file, &stat_buf) != -1) { p->entries[n].inode = stat_buf.st_ino; @@ -984,3 +939,261 @@ static void free_package(Package *p) nvfree((char *) p); } /* free_package() */ + + + +/* + * assisted_module_signing() - Guide the user through the module signing process + */ + +static int assisted_module_signing(Options *op, Package *p) +{ + int generate_keys = FALSE, do_sign = FALSE, secureboot; + + secureboot = secure_boot_enabled(); + + if (secureboot < 0) { + ui_log(op, "Unable to determine if Secure Boot is enabled: %s", + strerror(-secureboot)); + } + + if (op->kernel_module_signed) { + /* The kernel module is already signed, e.g. from linking a precompiled + * interface + appending a detached signature */ + return TRUE; + } + + if (test_kernel_config_option(op, p, "CONFIG_DUMMY_OPTION") == + KERNEL_CONFIG_OPTION_UNKNOWN) { + /* Unable to test kernel configuration options, possibly due to + * missing kernel headers. Since we might be installing on a + * system that doesn't have the headers, bail out. */ + return TRUE; + } + + if (op->module_signing_secret_key && op->module_signing_public_key) { + /* If the user supplied signing keys, sign the module, regardless of + * whether or not we actually need to. */ + do_sign = TRUE; + } else if (test_kernel_config_option(op, p, "CONFIG_MODULE_SIG_FORCE") == + KERNEL_CONFIG_OPTION_DEFINED) { + /* If CONFIG_MODULE_SIG_FORCE is set, we must sign. */ + ui_message(op, "The target kernel has CONFIG_MODULE_SIG_FORCE set, " + "which means that it requires that kernel modules be " + "cryptographically signed by a trusted key."); + do_sign = TRUE; + } else if (secureboot != 1 && !op->expert) { + /* If this is a non-UEFI system, or a UEFI system with secure boot + * disabled, or we are unable to determine whether the system has + * secure boot enabled, bail out unless in expert mode. */ + return TRUE; + } else if (test_kernel_config_option(op, p, "CONFIG_MODULE_SIG") == + KERNEL_CONFIG_OPTION_DEFINED){ + /* The kernel may or may not enforce module signatures; ask the user + * whether to sign the module. */ + + const char* sb_message = (secureboot == 1) ? + "This system also has UEFI Secure Boot " + "enabled; many distributions enforce " + "module signature verification on UEFI " + "systems when Secure Boot is enabled. " : + ""; + + do_sign = ui_yes_no(op, FALSE, "The target kernel has " + "CONFIG_MODULE_SIG set, which means that it " + "supports cryptographic signatures on kernel " + "modules. On some systems, the kernel may " + "refuse to load modules without a valid " + "signature from a trusted key. %sWould you like " + "to sign the NVIDIA kernel module?", sb_message); + } + + if (!do_sign) { + /* The user explicitly opted out of module signing, or the kernel does + * not support module signatures, and no signing keys were provided; + * there is nothing for us to do here. */ + return TRUE; + } + + /* If we're missing either key, we need to get both from the user. */ + if (!op->module_signing_secret_key || !op->module_signing_public_key) { + generate_keys = !ui_yes_no(op, FALSE, "Do you already have a key pair " + "which can be used to sign the NVIDIA " + "kernel module? Answer 'Yes' to use an " + "existing key pair, or 'No' to generate a " + "new key pair."); + if (generate_keys) { + char *cmdline; + int ret; + + if (!op->utils[OPENSSL]) { + ui_error(op, "Unable to generate key pair: openssl not " + "found!"); + return FALSE; + } + + log_printf(op, NULL, "Generating key pair for module signing..."); + + /* Generate a key pair using openssl. + * XXX We assume that sign-file requires the X.509 certificate + * in DER format; if this changes in the future we will need + * to be able to accommodate the actual required format. */ + + cmdline = nvstrcat("cd ", p->kernel_module_build_directory, "; ", + op->utils[OPENSSL], " req -new -x509 -newkey " + "rsa:2048 -days 7300 -nodes -sha256 -subj " + "\"/CN=nvidia-installer generated signing key/\"" + " -keyout " SECKEY_NAME " -outform DER -out " + PUBKEY_NAME, NULL); + + ret = run_command(op, cmdline, NULL, TRUE, 8, TRUE); + + nvfree(cmdline); + + if (ret != 0) { + ui_error(op, "Failed to generate key pair!"); + return FALSE; + } + + log_printf(op, NULL, "Signing keys generated successfully."); + + /* Set the signing keys to the newly generated pair. The paths + * are relative to p->kernel_module_build_directory, since we + * cd to it before signing the module. */ + op->module_signing_secret_key = SECKEY_NAME; + op->module_signing_public_key = PUBKEY_NAME; + } else { + /* The user already has keys; prompt for their locations. */ + op->module_signing_secret_key = + get_filename(op, op->module_signing_secret_key, + "Please provide the path to the private key"); + op->module_signing_public_key = + get_filename(op, op->module_signing_public_key, + "Please provide the path to the public key"); + } + } + + /* Now that we have keys (user-supplied or installer-generated), + * sign the kernel module which we built earlier. */ + if (!sign_kernel_module(op, p->kernel_module_build_directory, TRUE)) { + return FALSE; + } + + if (generate_keys) { + + /* If keys were generated, we should install the verification cert + * so that the user can make the kernel trust it, and either delete + * or install the private signing key. */ + char *file, *name, *result = NULL, *fingerprint, *cmdline; + char short_fingerprint[9]; + int ret, delete_secret_key; + + delete_secret_key = ui_yes_no(op, TRUE, "The NVIDIA kernel module was " + "successfully signed with a newly " + "generated key pair. Would you like to " + "delete the private signing key?"); + + /* Get the fingerprint of the X.509 certificate. We already used + openssl to create a keypair at this point, so we know we have it; + otherwise, we would have already returned by now. */ + cmdline = nvstrcat(op->utils[OPENSSL], " x509 -noout -fingerprint ", + "-inform DER -in ", p->kernel_module_build_directory, + "/"PUBKEY_NAME, NULL); + ret = run_command(op, cmdline, &result, FALSE, 0, FALSE); + nvfree(cmdline); + + /* Format: "SHA1 Fingerprint=00:00:00:00:..." */ + fingerprint = strchr(result, '=') + 1; + + if (ret != 0 || !fingerprint || strlen(fingerprint) < 40) { + char *sha1sum = find_system_util("sha1sum"); + + if (sha1sum) { + /* the openssl command failed, or we parsed its output + * incorrectly; try to get a sha1sum of the DER certificate */ + cmdline = nvstrcat(sha1sum, p->kernel_module_build_directory, + "/"PUBKEY_NAME, NULL); + ret = run_command(op, cmdline, &result, FALSE, 0, FALSE); + nvfree(sha1sum); + nvfree(cmdline); + + fingerprint = result; + } + + if (!sha1sum || ret != 0 || !fingerprint || + strlen(fingerprint) < 40) { + /* Unable to determine fingerprint */ + fingerprint = "UNKNOWN"; + } else { + char *end = strchr(fingerprint, ' '); + *end = '\0'; + } + } else { + /* Remove any ':' characters from fingerprint and truncate */ + char *tmp = nv_strreplace(fingerprint, ":", ""); + strncpy(short_fingerprint, tmp, sizeof(fingerprint)); + nvfree(tmp); + } + short_fingerprint[sizeof(short_fingerprint) - 1] = '\0'; + + /* Add the public key to the package */ + file = nvstrcat(p->kernel_module_build_directory, "/"PUBKEY_NAME, NULL); + + /* XXX name will be leaked when freeing package */ + name = nvstrcat("nvidia-modsign-crt-", short_fingerprint, ".der", NULL); + + add_package_entry(p, + file, + NULL, /* path */ + name, + NULL, /* target */ + NULL, /* dst */ + FILE_TYPE_MODULE_SIGNING_KEY, + FILE_TLS_CLASS_NONE, + 0444); + + ui_message(op, "An X.509 certificate containing the public signing " + "key will be installed to %s/%s. The SHA1 fingerprint of " + "this certificate is: %s.\n\nThis certificate must be " + "added to a key database which is trusted by your kernel " + "in order for the kernel to be able to verify the module " + "signature.", op->module_signing_key_path, name, + fingerprint); + + nvfree(result); + + /* Delete or install the private key */ + file = nvstrcat(p->kernel_module_build_directory, "/"SECKEY_NAME, NULL); + + if (delete_secret_key) { + secure_delete(op, file); + } else { + + /* Add the private key to the package */ + + name = nvstrcat("nvidia-modsign-key-", short_fingerprint, ".key", + NULL); + + add_package_entry(p, + file, + NULL, /* path */ + name, + NULL, /* target */ + NULL, /* dst */ + FILE_TYPE_MODULE_SIGNING_KEY, + FILE_TLS_CLASS_NONE, + 0400); + + ui_message(op, "The private signing key will be installed to %s/%s. " + "After the public key is added to a key database which " + "is trusted by your kernel, you may reuse the saved " + "public/private key pair to sign additional kernel " + "modules, without needing to re-enroll the public key. " + "Please take some reasonable precautions to secure the " + "private key: see the README for suggestions.", + op->module_signing_key_path, name); + } + } /* if (generate_keys) */ + + return TRUE; +} /* assisted_module_signing() */ @@ -32,6 +32,8 @@ #include <string.h> #include <limits.h> #include <fts.h> +#include <dlfcn.h> +#include <libkmod.h> #include "nvidia-installer.h" #include "kernel.h" @@ -63,6 +65,20 @@ static char *build_distro_precompiled_kernel_interface_dir(Options *op); static char *convert_include_path_to_source_path(const char *inc); static char *guess_kernel_module_filename(Options *op); static char *get_machine_arch(Options *op); +static int init_libkmod(void); +static void close_libkmod(void); +static int run_conftest(Options *op, Package *p, const char *args, + char **result); + +/* libkmod handle and function pointers */ +static void *libkmod = NULL; +static struct kmod_ctx* (*lkmod_new)(const char*, const char* const*) = NULL; +static struct kmod_ctx* (*lkmod_unref)(struct kmod_ctx*) = NULL; +static struct kmod_module* (*lkmod_module_unref)(struct kmod_module *) = NULL; +static int (*lkmod_module_new_from_path)(struct kmod_ctx*, const char*, + struct kmod_module**) = NULL; +static int (*lkmod_module_insert_module)(struct kmod_module*, unsigned int, + const char*) = NULL; /* * Message text that is used by several error messages. @@ -145,6 +161,37 @@ int determine_kernel_module_installation_path(Options *op) /* + * run_conftest() - run conftest.sh with the given additional arguments; pass + * the result back to the caller. Returns TRUE on success, or FALSE on failure. + */ + +static int run_conftest(Options *op, Package *p, const char *args, char **result) +{ + char *CC, *cmd, *arch; + int ret; + + arch = get_machine_arch(op); + if (!arch) + return FALSE; + + CC = getenv("CC"); + if (!CC) CC = "cc"; + + cmd = nvstrcat("sh ", p->kernel_module_build_directory, + "/conftest.sh ", CC, " ", CC, " ", arch, " ", + op->kernel_source_path, " ", + op->kernel_output_path, " ", + args, NULL); + + ret = run_command(op, cmd, result, FALSE, 0, TRUE); + nvfree(cmd); + + return ret == 0; +} /* run_conftest() */ + + + +/* * determine_kernel_source_path() - find the qualified path to the * kernel source tree. This is called from install_from_cwd() if we * need to compile the kernel interface files. Assigns @@ -154,9 +201,8 @@ int determine_kernel_module_installation_path(Options *op) int determine_kernel_source_path(Options *op, Package *p) { - char *CC, *cmd, *result; + char *result; char *source_files[2], *source_path; - char *arch; int ret, count = 0; /* determine the kernel source path */ @@ -242,24 +288,11 @@ int determine_kernel_source_path(Options *op, Package *p) } free(result); - arch = get_machine_arch(op); - if (!arch) - return FALSE; if (!determine_kernel_output_path(op)) return FALSE; - CC = getenv("CC"); - if (!CC) CC = "cc"; - - cmd = nvstrcat("sh ", p->kernel_module_build_directory, - "/conftest.sh ", CC, " ", CC, " ", arch, " ", - op->kernel_source_path, " ", - op->kernel_output_path, " ", - "get_uname", NULL); - - ret = run_command(op, cmd, &result, FALSE, 0, TRUE); - nvfree(cmd); + ret = run_conftest(op, p, "get_uname", &result); - if (ret != 0) { + if (!ret) { ui_error(op, "Unable to determine the version of the kernel " "sources located in '%s'. %s", op->kernel_source_path, install_your_kernel_source); @@ -382,6 +415,87 @@ int determine_kernel_output_path(Options *op) /* + * attach_signature() - If we have a detached signature, verify the checksum of + * the linked module and append the signature. + */ +static int attach_signature(Options *op, Package *p, + const PrecompiledInfo *info) { + uint32 actual_crc; + char *module_filename; + int ret = FALSE, command_ret; + + ui_log(op, "Attaching module signature to linked kernel module."); + + module_filename = nvstrcat(p->kernel_module_build_directory, "/", + p->kernel_module_filename, NULL); + + command_ret = verify_crc(op, module_filename, info->linked_module_crc, + &actual_crc); + + if (command_ret) { + FILE *module_file, *signature_file; + + module_file = fopen(module_filename, "a+"); + signature_file = fopen(info->detached_signature, "r"); + + if (module_file && signature_file) { + char buf; + + while(fread(&buf, 1, 1, signature_file)) { + command_ret = fwrite(&buf, 1, 1, module_file); + if (command_ret != 1) { + goto attach_done; + } + } + + ret = feof(signature_file) && + !ferror(signature_file) && + !ferror(module_file); + + op->kernel_module_signed = ret; +attach_done: + fclose(module_file); + fclose(signature_file); + } else { + ret = ui_yes_no(op, FALSE, + "A detached signature was included with the " + "precompiled interface, but opening the linked " + "kernel module and/or the signature file failed." + "\n\nThe detached signature will not be added; " + "would you still like to install the unsigned " + "kernel module?"); + } + } else { + ret = ui_yes_no(op, FALSE, + "A detached signature was included with the " + "precompiled interface, but the checksum of the linked " + "kernel module (%d) did not match the checksum of the " + "the kernel module for which the detached signature " + "was generated (%d).\n\nThis can happen if the linker " + "on the installation target system is not the same as " + "the linker on the system that built the precompiled " + "interface.\n\nThe detached signature will not be " + "added; would you still like to install the unsigned " + "kernel module?", actual_crc, info->linked_module_crc); + } + + if (ret) { + if(op->kernel_module_signed) { + ui_log(op, "Signature attached successfully."); + } else { + ui_log(op, "Signature not attached."); + } + } else { + ui_error(op, "Failed to attach signature."); + } + + nvfree(module_filename); + return ret; +} /* attach_signature() */ + + + +/* * link_kernel_module() - link the prebuilt kernel interface against * the binary-only core of the kernel module. This results in a * complete kernel module, ready for installation. @@ -390,15 +504,15 @@ int determine_kernel_output_path(Options *op) * ld -r -o nvidia.o nv-linux.o nv-kernel.o */ -int link_kernel_module(Options *op, Package *p) +int link_kernel_module(Options *op, Package *p, const char *build_directory, + const PrecompiledInfo *info) { char *cmd, *result; int ret; p->kernel_module_filename = guess_kernel_module_filename(op); - cmd = nvstrcat("cd ", p->kernel_module_build_directory, - "; ", op->utils[LD], + cmd = nvstrcat("cd ", build_directory, "; ", op->utils[LD], " ", LD_OPTIONS, " -o ", p->kernel_module_filename, " ", PRECOMPILED_KERNEL_INTERFACE_FILENAME, @@ -415,6 +529,10 @@ int link_kernel_module(Options *op, Package *p) ui_log(op, "Kernel module linked successfully."); + if (info && info->detached_signature) { + return attach_signature(op, p, info); + } + return TRUE; } /* link_kernel_module() */ @@ -428,13 +546,8 @@ int link_kernel_module(Options *op, Package *p) int build_kernel_module(Options *op, Package *p) { - char *CC, *result, *cmd, *tmp; - char *arch; + char *result, *cmd, *tmp; int len, ret; - - arch = get_machine_arch(op); - if (!arch) - return FALSE; /* * touch all the files in the build directory to avoid make time @@ -443,23 +556,13 @@ int build_kernel_module(Options *op, Package *p) touch_directory(op, p->kernel_module_build_directory); - CC = getenv("CC"); - if (!CC) CC = "cc"; - /* * Check if conftest.sh can determine the Makefile, there's * no hope for the make rules if this fails. */ - cmd = nvstrcat("sh ", p->kernel_module_build_directory, - "/conftest.sh ", CC, " ", CC, " ", arch, " ", - op->kernel_source_path, " ", - op->kernel_output_path, " ", - "select_makefile just_msg", NULL); - - ret = run_command(op, cmd, &result, FALSE, 0, TRUE); - nvfree(cmd); + ret = run_conftest(op, p, "select_makefile just_msg", &result); - if (ret != 0) { + if (!ret) { ui_error(op, "%s", result); /* display conftest.sh's error message */ nvfree(result); return FALSE; @@ -535,6 +638,211 @@ int build_kernel_module(Options *op, Package *p) /* + * sign_kernel_module() - sign the kernel module. The caller is responsible + * for ensuring that the kernel module is already built successfully and that + * op->module_signing_{secret,public}_key are set. + */ +int sign_kernel_module(Options *op, const char *build_directory, int status) { + char *cmd, *mod_sign_cmd, *mod_sign_hash; + int ret, success; + + /* if module_signing_script isn't set, then set mod_sign_cmd below to end + * the nvstrcat() that builds cmd early. */ + mod_sign_cmd = op->module_signing_script ? + nvstrcat(" mod_sign_cmd=", op->module_signing_script, NULL) : + NULL; + + mod_sign_hash = op->module_signing_hash ? + nvstrcat(" CONFIG_MODULE_SIG_HASH=", + op->module_signing_hash, NULL) : + NULL; + + if (status) { + ui_status_begin(op, "Signing kernel module:", "Signing"); + } + + cmd = nvstrcat("cd ", build_directory, "; make module-sign" + " SYSSRC=", op->kernel_source_path, + " SYSOUT=", op->kernel_output_path, + " MODSECKEY=", op->module_signing_secret_key, + " MODPUBKEY=", op->module_signing_public_key, + mod_sign_cmd ? mod_sign_cmd : "", + mod_sign_hash ? mod_sign_hash : "", NULL); + + ret = run_command(op, cmd, NULL, TRUE, 20 /* XXX */, TRUE); + success = ret == 0; + + nvfree(mod_sign_hash); + nvfree(mod_sign_cmd); + nvfree(cmd); + + if (status) { + ui_status_end(op, success ? "done." : "Failed to sign kernel module."); + } else { + ui_log(op, success ? "Signed kernel module." : "Module signing failed"); + } + op->kernel_module_signed = success; + return success; +} /* sign_kernel_module */ + + + +/* + * byte_tail() - write to outfile from infile, starting at the specified byte + * offset, and going until the end of infile. This is needed because `tail -c` + * is unreliable in some implementations. + */ +static int byte_tail(const char *infile, const char *outfile, int start) +{ + FILE *in = NULL, *out = NULL; + int success = FALSE, ret; + char buf; + + in = fopen(infile, "r"); + out = fopen(outfile, "w"); + + if (!in || !out) { + goto done; + } + + ret = fseek(in, start, SEEK_SET); + if (ret != 0) { + goto done; + } + + while(fread(&buf, 1, 1, in)) { + ret = fwrite(&buf, 1, 1, out); + if (ret != 1) { + goto done; + } + } + + success = feof(in) && !ferror(in) && !ferror(out); + +done: + fclose(in); + fclose(out); + return success; +} + + + +/* + * create_detached_signature() - Link the precompiled interface with nv-kernel.o, + * sign the resulting linked nvidia.ko, and split the signature off into a separate + * file. Copy a checksum and detached signature for the linked module to the kernel + * module build directory on success. + */ +static int create_detached_signature(Options *op, Package *p, + const char *build_dir) +{ + int ret, command_ret; + struct stat st; + FILE *checksum_file; + char *module_path = NULL, *tmp_path = NULL, *error = NULL, *dstfile = NULL; + + ui_status_begin(op, "Creating a detached signature for the linked " + "kernel module:", "Linking module"); + + tmp_path = nvstrcat(build_dir, "/", PRECOMPILED_KERNEL_INTERFACE_FILENAME, + NULL); + symlink(p->kernel_interface_filename, tmp_path); + + ret = link_kernel_module(op, p, build_dir, NULL); + + if (!ret) { + ui_error(op, "Failed to link a kernel module for signing."); + goto done; + } + + module_path = nvstrcat(build_dir, "/", p->kernel_module_filename, NULL); + command_ret = stat(module_path, &st); + + if (command_ret != 0) { + ret = FALSE; + error = "Unable to determine size of linked module."; + goto done; + } + + ui_status_update(op, .25, "Generating module checksum"); + + nvfree(tmp_path); + tmp_path = nvstrcat(build_dir, "/", KERNEL_MODULE_CHECKSUM_FILENAME, NULL); + checksum_file = fopen(tmp_path, "w"); + + if(checksum_file) { + uint32 crc = compute_crc(op, module_path); + command_ret = fwrite(&crc, sizeof(crc), 1, checksum_file); + fclose(checksum_file); + if (command_ret != 1) { + ret = FALSE; + error = "Failed to write the module checksum."; + goto done; + } + } else { + ret = FALSE; + error = "Failed to open the checksum file for writing."; + goto done; + } + + dstfile = nvstrcat(p->kernel_module_build_directory, "/", + KERNEL_MODULE_CHECKSUM_FILENAME, NULL); + + if (!copy_file(op, tmp_path, dstfile, 0644)) { + ret = FALSE; + error = "Failed to copy the kernel module checksum file."; + goto done; + } + + ui_status_update(op, .50, "Signing linked module"); + + ret = sign_kernel_module(op, build_dir, FALSE); + + if (!ret) { + error = "Failed to sign the linked kernel module."; + goto done; + } + + ui_status_update(op, .75, "Detaching module signature"); + + nvfree(tmp_path); + tmp_path = nvstrcat(build_dir, "/", DETACHED_SIGNATURE_FILENAME, NULL); + ret = byte_tail(module_path, tmp_path, st.st_size); + + if (!ret) { + error = "Failed to detach the module signature"; + goto done; + } + + nvfree(dstfile); + dstfile = nvstrcat(p->kernel_module_build_directory, "/", + DETACHED_SIGNATURE_FILENAME, NULL); + + if (!copy_file(op, tmp_path, dstfile, 0644)) { + ret = FALSE; + error = "Failed to copy the detached signature file."; + goto done; + } + +done: + if (ret) { + ui_status_end(op, "done."); + } else { + ui_status_end(op, "Error."); + if (error) { + ui_error(op, error); + } + } + + nvfree(dstfile); + nvfree(tmp_path); + nvfree(module_path); + return ret; +} /* create_detached_signature() */ + + + +/* * build_kernel_interface() - build the kernel interface, and place it * here: * @@ -622,8 +930,12 @@ int build_kernel_interface(Options *op, Package *p) PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); if (!copy_file(op, kernel_interface, dstfile, 0644)) goto failed; - - ret = TRUE; + + if (op->module_signing_secret_key && op->module_signing_public_key) { + ret = create_detached_signature(op, p, tmpdir); + } else { + ret = TRUE; + } failed: @@ -681,16 +993,144 @@ void check_for_warning_messages(Options *op) /* + * init_libkmod() - Attempt to dlopen() libkmod and the function symbols we + * need from it. Set the global libkmod handle and function pointers on + * success. Return TRUE if loading all symbols succeeded; FALSE otherwise. + */ +static int init_libkmod(void) +{ + if (!libkmod) { + libkmod = dlopen("libkmod.so.2", RTLD_LAZY); + if(!libkmod) { + return FALSE; + } + + lkmod_new = dlsym(libkmod, "kmod_new"); + lkmod_unref = dlsym(libkmod, "kmod_unref"); + lkmod_module_unref = dlsym(libkmod, "kmod_module_unref"); + lkmod_module_new_from_path = dlsym(libkmod, "kmod_module_new_from_path"); + lkmod_module_insert_module = dlsym(libkmod, "kmod_module_insert_module"); + } + + if (libkmod) { + /* libkmod was already open, or was just successfully dlopen()ed: + * check to make sure all of the symbols are set */ + if (lkmod_new && lkmod_unref && lkmod_module_unref && + lkmod_module_new_from_path && lkmod_module_insert_module) { + return TRUE; + } else { + /* One or more symbols missing; abort */ + close_libkmod(); + return FALSE; + } + } + return FALSE; +} /* init_libkmod() */ + + + +/* + * close_libkmod() - clear all libkmod function pointers and dlclose() libkmod + */ +static void close_libkmod(void) +{ + if (libkmod) { + dlclose(libkmod); + } + + libkmod = NULL; + lkmod_new = NULL; + lkmod_unref = NULL; + lkmod_module_unref = NULL; + lkmod_module_new_from_path = NULL; + lkmod_module_insert_module = NULL; +} /* close_libkmod() */ + + + +/* + * do_insmod() - load the kernel module using libkmod if available; fall back + * to insmod otherwise. Returns the result of kmod_module_insert_module() if + * available, or of insmod otherwise. Pass the result of module loading up + * through the data argument, regardless of whether we used libkmod or insmod. + */ +static int do_insmod(Options *op, const char *module, char **data) +{ + int ret = 0, libkmod_failed = FALSE; + *data = NULL; + + if (init_libkmod()) { + struct kmod_ctx *ctx = NULL; + struct kmod_module *mod = NULL; + const char *config_paths = NULL; + + ctx = lkmod_new(NULL, &config_paths); + if (!ctx) { + libkmod_failed = TRUE; + goto kmod_done; + } + + ret = lkmod_module_new_from_path(ctx, module, &mod); + if (ret < 0) { + libkmod_failed = TRUE; + goto kmod_done; + } + + ret = lkmod_module_insert_module(mod, 0, ""); + if (ret < 0) { + /* insmod ignores > 0 return codes of kmod_module_insert_module(), + * so we should do it too. On failure, strdup() the error string to + * *data to ensure that it can be freed later. */ + *data = nvstrdup(strerror(-ret)); + } + +kmod_done: + if (mod) { + lkmod_module_unref(mod); + } + if (ctx) { + lkmod_unref(ctx); + } + } else { + if (op->expert) { + ui_log(op, "Unable to load module with libkmod; " + "falling back to insmod."); + } + libkmod_failed = TRUE; + } + + if (!libkmod || libkmod_failed) { + char *cmd; + + /* Fall back to insmod */ + + cmd = nvstrcat(op->utils[INSMOD], " ", module, NULL); + + /* only output the result of the test if in expert mode */ + + ret = run_command(op, cmd, data, op->expert, 0, TRUE); + nvfree(cmd); + } + + close_libkmod(); + + return ret; +} /* do_insmod() */ + + + +/* * test_kernel_module() - attempt to insmod the kernel module and then * rmmod it. Return TRUE if the insmod succeeded, or FALSE otherwise. */ int test_kernel_module(Options *op, Package *p) { - char *cmd = NULL, *data; + char *cmd = NULL, *data = NULL, *module_path; int old_loglevel = 0, new_loglevel = 0; - int fd, ret, name[] = { CTL_KERN, KERN_PRINTK }; + int fd, ret, name[] = { CTL_KERN, KERN_PRINTK }, i; size_t len = sizeof(int); + const char *depmods[] = { "i2c-core", "drm" }; /* * If we're building/installing for a different kernel, then we @@ -719,51 +1159,106 @@ int test_kernel_module(Options *op, Package *p) } /* - * On Linux 2.6+ we depend on the i2c-core.ko kernel module unless - * the kernel was configured without support for it. Preload it here to - * satisfy the dependency, which isn't resolved by `insmod`. + * Attempt to load modules that nvidia.ko might depend on. Silently ignore + * failures: if nvidia.ko doesn't depend on the module that failed, the test + * load below will succeed and it doesn't matter that the load here failed. */ - if (strncmp(get_kernel_name(op), "2.4", 3) != 0) { - cmd = nvstrcat(op->utils[MODPROBE], " -q i2c-core", NULL); + for (i = 0; i < ARRAY_LEN(depmods); i++) { + cmd = nvstrcat(op->utils[MODPROBE], " -q ", depmods[i], NULL); run_command(op, cmd, NULL, FALSE, 0, TRUE); nvfree(cmd); } - cmd = nvstrcat(op->utils[INSMOD], " ", - p->kernel_module_build_directory, "/", - p->kernel_module_filename, NULL); - - /* only output the result of the test if in expert mode */ - - ret = run_command(op, cmd, &data, op->expert, 0, TRUE); + /* Load nvidia.ko */ + module_path = nvstrcat(p->kernel_module_build_directory, "/", + p->kernel_module_filename, NULL); + ret = do_insmod(op, module_path, &data); + nvfree(module_path); if (ret != 0) { - ui_error(op, "Unable to load the kernel module '%s'. This " - "happens most frequently when this kernel module was " - "built against the wrong or improperly configured " - "kernel sources, with a version of gcc that differs " - "from the one used to build the target kernel, or " - "if a driver such as rivafb, nvidiafb, or nouveau is present " - "and prevents the NVIDIA kernel module from obtaining " - "ownership of the NVIDIA graphics device(s), or " - "NVIDIA GPU installed in this system is not supported " - "by this NVIDIA Linux graphics driver release.\n\n" - "Please see the log entries 'Kernel module load " - "error' and 'Kernel messages' at the end of the file " - "'%s' for more information.", - p->kernel_module_filename, op->log_file_name); + int ignore_error; + + /* Handle cases where we might want to allow the kernel module to be + * installed, even though the test load failed. */ + switch (-ret) { + case ENOKEY: + if (op->kernel_module_signed) { + ignore_error = ui_yes_no(op, TRUE, + "The signed kernel module failed to " + "load, because the kernel does not " + "trust any key which is capable of " + "verifying the module signature. " + "Would you like to install the signed " + "kernel module anyway?\n\nNote that " + "you will not be able to load the " + "installed module until after a key " + "that can verify the module signature " + "is added to a key database that is " + "trusted by the kernel. This will " + "likely require rebooting your " + "computer."); + } else { + char *secureboot_message, *dkms_message; + + secureboot_message = secure_boot_enabled() == 1 ? + "and sign the kernel module when " + "prompted to do so." : + "and set the --module-signing-secret-" + "key and --module-signing-public-key " + "options on the command line, or run " + "the installer in expert mode to " + "enable the interactive module " + "signing prompts."; + + dkms_message = op->dkms ? " Module signing is incompatible " + "with DKMS, so please select the " + "non-DKMS option when building the " + "kernel module to be signed." : ""; + + ui_error(op, "The kernel module failed to load, because it " + "was not signed by a key that is trusted by the " + "kernel. Please try installing the driver again, %s%s", + secureboot_message, dkms_message); + ignore_error = FALSE; + } + break; - /* - * if in expert mode, run_command() would have caused this to - * be written to the log file; so if not in expert mode, print - * the output now. - */ + default: + ignore_error = FALSE; + } - if (!op->expert) ui_log(op, "Kernel module load error: %s", data); - ret = FALSE; + if (ignore_error) { + ui_log(op, "An error was encountered when loading the kernel " + "module, but that error was ignored, and the kernel module " + "will be installed, anyway. The error was: %s", data); + ret = TRUE; + } else { + ui_error(op, "Unable to load the kernel module '%s'. This " + "happens most frequently when this kernel module was " + "built against the wrong or improperly configured " + "kernel sources, with a version of gcc that differs " + "from the one used to build the target kernel, or " + "if a driver such as rivafb, nvidiafb, or nouveau is " + "present and prevents the NVIDIA kernel module from " + "obtaining ownership of the NVIDIA graphics device(s), " + "or no NVIDIA GPU installed in this system is supported " + "by this NVIDIA Linux graphics driver release.\n\n" + "Please see the log entries 'Kernel module load " + "error' and 'Kernel messages' at the end of the file " + "'%s' for more information.", + p->kernel_module_filename, op->log_file_name); + + /* + * if in expert mode, run_command() would have caused this to + * be written to the log file; so if not in expert mode, print + * the output now. + */ + + if (!op->expert) ui_log(op, "Kernel module load error: %s", data); + ret = FALSE; + } } else { - nvfree(cmd); /* * check if the kernel module detected problems with this @@ -781,9 +1276,9 @@ int test_kernel_module(Options *op, Package *p) cmd = nvstrcat(op->utils[RMMOD], " ", p->kernel_module_name, NULL); run_command(op, cmd, NULL, FALSE, 0, TRUE); ret = TRUE; + nvfree(cmd); } - - nvfree(cmd); + nvfree(data); /* @@ -809,6 +1304,15 @@ int test_kernel_module(Options *op, Package *p) sysctl(name, 2, NULL, 0, &old_loglevel, len); } + /* + * Unload dependencies that might have been loaded earlier. + */ + for (i = 0; i < ARRAY_LEN(depmods); i++) { + cmd = nvstrcat(op->utils[MODPROBE], " -qr ", depmods[i], NULL); + run_command(op, cmd, NULL, FALSE, 0, TRUE); + nvfree(cmd); + } + return ret; } /* test_kernel_module() */ @@ -946,7 +1450,7 @@ int check_for_unloaded_kernel_module(Options *op, Package *p) * be installed for a kernel other than the currently running one. */ -int find_precompiled_kernel_interface(Options *op, Package *p) +PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) { char *proc_version_string, *output_filename, *tmp; PrecompiledInfo *info = NULL; @@ -955,7 +1459,7 @@ int find_precompiled_kernel_interface(Options *op, Package *p) if (op->no_precompiled_interface) { ui_log(op, "Not probing for precompiled kernel interfaces."); - return FALSE; + return NULL; } /* retrieve the proc version string for the running kernel */ @@ -1022,7 +1526,7 @@ int find_precompiled_kernel_interface(Options *op, Package *p) "found at '%s'; this means that the installer will need " "to compile a kernel interface for your kernel.", op->precompiled_kernel_interfaces_url); - return FALSE; + return NULL; } } @@ -1034,14 +1538,13 @@ int find_precompiled_kernel_interface(Options *op, Package *p) "use this? (answering 'no' will require the " "installer to compile the interface)", info->description)) { - /* XXX free info */ + free_precompiled(info); info = NULL; } } if (info) { - /* XXX free info */ - return TRUE; + return info; } failed: @@ -1052,7 +1555,7 @@ int find_precompiled_kernel_interface(Options *op, Package *p) "compile a new kernel interface."); } - return FALSE; + return NULL; } /* find_precompiled_kernel_interface() */ @@ -1086,6 +1589,31 @@ char *get_kernel_name(Options *op) /* + * test_kernel_config_option() - test to see if the given option is defined + * in the target kernel's configuration. + */ + +KernelConfigOptionStatus test_kernel_config_option(Options* op, Package *p, + const char *option) +{ + if (op->kernel_source_path && op->kernel_output_path) { + int ret; + char *conftest_cmd; + + conftest_cmd = nvstrcat("test_configuration_option ", option, NULL); + ret = run_conftest(op, p, conftest_cmd, NULL); + nvfree(conftest_cmd); + + return ret ? KERNEL_CONFIG_OPTION_DEFINED : + KERNEL_CONFIG_OPTION_NOT_DEFINED; + } + + return KERNEL_CONFIG_OPTION_UNKNOWN; +} + + + +/* *************************************************************************** * local static routines *************************************************************************** @@ -1434,10 +1962,11 @@ download_updated_kernel_interface(Options *op, Package *p, if (info && (info->crc != crc)) { ui_error(op, "The embedded checksum of the downloaded file " - "'%s' (%d) does not match the computed checksum ", - "(%d); not using.", buf, info->crc, crc); + "'%s' (%" PRIu32 ") does not match the computed " + "checksum (%" PRIu32 "); not using.", buf, info->crc, + crc); unlink(dstfile); - /* XXX free info */ + free_precompiled(info); info = NULL; } @@ -1474,9 +2003,9 @@ download_updated_kernel_interface(Options *op, Package *p, int check_cc_version(Options *op, Package *p) { - char *cmd, *CC, *result; - char *arch; + char *result; int ret; + Options dummyop; /* * If we're building/installing for a different kernel, then we @@ -1491,25 +2020,15 @@ int check_cc_version(Options *op, Package *p) return TRUE; } - arch = get_machine_arch(op); - if (!arch) - return FALSE; - - CC = getenv("CC"); - if (!CC) CC = "cc"; - - ui_log(op, "Performing CC version check with CC=\"%s\".", CC); - - cmd = nvstrcat("sh ", p->kernel_module_build_directory, - "/conftest.sh ", CC, " ", CC, " ", arch, " ", - "DUMMY_SOURCE DUMMY_OUTPUT ", - "cc_version_check just_msg", NULL); + /* Kernel source/output paths may not be set yet; we don't need them + * for this test, anyway. */ + dummyop = *op; + dummyop.kernel_source_path = "DUMMY_SOURCE_PATH"; + dummyop.kernel_output_path = "DUMMY_OUTPUT_PATH"; - ret = run_command(op, cmd, &result, FALSE, 0, TRUE); - - nvfree(cmd); + ret = run_conftest(&dummyop, p, "cc_version_check just_msg", &result); - if (ret == 0) return TRUE; + if (ret) return TRUE; ret = ui_yes_no(op, TRUE, "The CC version check failed:\n\n%s\n\n" "If you know what you are doing and want to " @@ -1536,30 +2055,14 @@ int check_cc_version(Options *op, Package *p) static int fbdev_check(Options *op, Package *p) { - char *CC, *cmd, *result; - char *arch; + char *result; int ret; - arch = get_machine_arch(op); - if (!arch) - return FALSE; - - CC = getenv("CC"); - if (!CC) CC = "cc"; - ui_log(op, "Performing rivafb check."); - cmd = nvstrcat("sh ", p->kernel_module_build_directory, - "/conftest.sh ", CC, " ", CC, " ", arch, " ", - op->kernel_source_path, " ", - op->kernel_output_path, " ", - "rivafb_sanity_check just_msg", NULL); - - ret = run_command(op, cmd, &result, FALSE, 0, TRUE); - - nvfree(cmd); + ret = run_conftest(op, p,"rivafb_sanity_check just_msg", &result); - if (ret != 0) { + if (!ret) { ui_error(op, "%s", result); nvfree(result); @@ -1568,17 +2071,9 @@ static int fbdev_check(Options *op, Package *p) ui_log(op, "Performing nvidiafb check."); - cmd = nvstrcat("sh ", p->kernel_module_build_directory, - "/conftest.sh ", CC, " ", CC, " ", arch, " ", - op->kernel_source_path, " ", - op->kernel_output_path, " ", - "nvidiafb_sanity_check just_msg", NULL); - - ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + ret = run_conftest(op, p,"nvidiafb_sanity_check just_msg", &result); - nvfree(cmd); - - if (ret != 0) { + if (!ret) { ui_error(op, "%s", result); nvfree(result); @@ -1598,30 +2093,14 @@ static int fbdev_check(Options *op, Package *p) static int xen_check(Options *op, Package *p) { - char *CC, *cmd, *result; - char *arch; + char *result; int ret; - arch = get_machine_arch(op); - if (!arch) - return FALSE; - - CC = getenv("CC"); - if (!CC) CC = "cc"; - ui_log(op, "Performing Xen check."); - cmd = nvstrcat("sh ", p->kernel_module_build_directory, - "/conftest.sh ", CC, " ", CC, " ", arch, " ", - op->kernel_source_path, " ", - op->kernel_output_path, " ", - "xen_sanity_check just_msg", NULL); - - ret = run_command(op, cmd, &result, FALSE, 0, TRUE); + ret = run_conftest(op, p,"xen_sanity_check just_msg", &result); - nvfree(cmd); - - if (ret != 0) { + if (!ret) { ui_error(op, "%s", result); nvfree(result); @@ -1662,7 +2141,8 @@ static PrecompiledInfo *scan_dir(Options *op, Package *p, while ((ent = readdir(dir)) != NULL) { if (((strcmp(ent->d_name, ".")) == 0) || - ((strcmp(ent->d_name, "..")) == 0)) continue; + ((strcmp(ent->d_name, "..")) == 0) || + strstr(ent->d_name, DETACHED_SIGNATURE_FILENAME)) continue; filename = nvstrcat(directory_name, "/", ent->d_name, NULL); @@ -21,18 +21,34 @@ #define __NVIDIA_INSTALLER_KERNEL_H__ #include "nvidia-installer.h" +#include "precompiled.h" -int determine_kernel_module_installation_path (Options*); -int determine_kernel_source_path (Options*, Package*); -int determine_kernel_output_path (Options*); -int link_kernel_module (Options*, Package*); -int check_cc_version (Options*, Package*); -int build_kernel_module (Options*, Package*); -int build_kernel_interface (Options*, Package*); -int test_kernel_module (Options*, Package*); -int load_kernel_module (Options*, Package*); -int check_for_unloaded_kernel_module (Options*, Package*); -int find_precompiled_kernel_interface (Options*, Package*); -char *get_kernel_name (Options*); +typedef enum { + KERNEL_CONFIG_OPTION_NOT_DEFINED = 0, + KERNEL_CONFIG_OPTION_DEFINED, + KERNEL_CONFIG_OPTION_UNKNOWN +} KernelConfigOptionStatus; + +int determine_kernel_module_installation_path (Options*); +int determine_kernel_source_path (Options*, Package*); +int determine_kernel_output_path (Options*); +int link_kernel_module (Options*, Package*, + const char*, + const PrecompiledInfo *); +int check_cc_version (Options*, Package*); +int build_kernel_module (Options*, Package*); +int build_kernel_interface (Options*, Package*); +int test_kernel_module (Options*, Package*); +int load_kernel_module (Options*, Package*); +int check_for_unloaded_kernel_module (Options*, Package*); +PrecompiledInfo *find_precompiled_kernel_interface (Options*, Package*); +char *get_kernel_name (Options*); +KernelConfigOptionStatus test_kernel_config_option (Options*, Package*, + const char*); +int sign_kernel_module (Options*, const char*, int); + +#ifndef ENOKEY +#define ENOKEY 126 /* Required key not available */ +#endif #endif /* __NVIDIA_INSTALLER_KERNEL_H__ */ @@ -88,25 +88,25 @@ void log_init(Options *op, int argc, char * const argv[]) return; } - log_printf(op, TRUE, NULL, "%s log file '%s'", + log_printf(op, NULL, "%s log file '%s'", PROGRAM_NAME, op->log_file_name); now = time(NULL); - log_printf(op, TRUE, NULL, "creation time: %s", ctime(&now)); - log_printf(op, TRUE, NULL, "installer version: %s", + log_printf(op, NULL, "creation time: %s", ctime(&now)); + log_printf(op, NULL, "installer version: %s", NVIDIA_INSTALLER_VERSION); - log_printf(op, TRUE, NULL, ""); + log_printf(op, NULL, ""); path = getenv("PATH"); - log_printf(op, TRUE, NULL, "PATH: %s", STRSTR(path)); - log_printf(op, TRUE, NULL, ""); + log_printf(op, NULL, "PATH: %s", STRSTR(path)); + log_printf(op, NULL, ""); - log_printf(op, TRUE, NULL, "nvidia-installer command line:"); + log_printf(op, NULL, "nvidia-installer command line:"); for (i = 0; i < argc; i++) { - log_printf(op, TRUE, " ", "%s", argv[i]); + log_printf(op, " ", "%s", argv[i]); } - log_printf(op, TRUE, NULL, ""); + log_printf(op, NULL, ""); } /* log_init() */ @@ -118,8 +118,7 @@ void log_init(Options *op, int argc, char * const argv[]) * option is not set, then nothing is done here. */ -void log_printf(Options *op, const int wb, - const char *prefix, const char *fmt, ...) +void log_printf(Options *op, const char *prefix, const char *fmt, ...) { char *buf; int append_newline = TRUE; diff --git a/manifest.c b/manifest.c new file mode 100644 index 0000000..5d8bcf0 --- /dev/null +++ b/manifest.c @@ -0,0 +1,214 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses>. + */ + +#include <string.h> + +#include "manifest.h" + +#define T TRUE +#define F FALSE + +#define ENTRY(_name, \ + _has_arch, \ + _has_tls_class, \ + _installable, \ + _has_path, \ + _is_symlink, \ + _is_shared_lib, \ + _is_opengl) \ + #_name , FILE_TYPE_ ## _name , \ + { \ + .has_arch = _has_arch, \ + .has_tls_class = _has_tls_class, \ + .installable = _installable, \ + .has_path = _has_path, \ + .is_symlink = _is_symlink, \ + .is_shared_lib = _is_shared_lib, \ + .is_opengl = _is_opengl \ + } + +/* + * Define properties of each FILE_TYPE. Keep in sync with the + * PackageEntryFileType definition in nvidia-installer.h. + */ +static const struct { + const char *name; + PackageEntryFileType type; + PackageEntryFileCapabilities caps; +} packageEntryFileTypeTable[] = { + + /* + * is_opengl ---------------------------------+ + * is_shared_lib ------------------------------+ | + * is_symlink ---------------------------+ | | + * has_path ------------------------+ | | | + * installable ---------------------+ | | | | + * has_tls_class ------------------+ | | | | | + * has_arch ---------------+ | | | | | | + * | | | | | | | + */ + { ENTRY(KERNEL_MODULE_SRC, F, F, T, F, F, F, F ) }, + { ENTRY(KERNEL_MODULE_CMD, F, F, F, F, F, F, F ) }, + { ENTRY(KERNEL_MODULE, F, F, T, F, F, F, F ) }, + { ENTRY(OPENGL_HEADER, F, F, T, T, F, F, T ) }, + { ENTRY(CUDA_ICD, F, F, T, F, F, F, F ) }, + { ENTRY(OPENGL_LIB, T, F, T, F, F, T, T ) }, + { ENTRY(CUDA_LIB, T, F, T, T, F, T, F ) }, + { ENTRY(LIBGL_LA, T, F, T, F, F, F, T ) }, + { ENTRY(XLIB_STATIC_LIB, F, F, T, F, F, F, F ) }, + { ENTRY(XLIB_SHARED_LIB, F, F, T, F, F, T, F ) }, + { ENTRY(TLS_LIB, T, T, T, T, F, T, T ) }, + { ENTRY(UTILITY_LIB, F, F, T, F, F, T, F ) }, + { ENTRY(DOCUMENTATION, F, F, T, T, F, F, F ) }, + { ENTRY(APPLICATION_PROFILE, F, F, T, T, F, F, F ) }, + { ENTRY(MANPAGE, F, F, T, T, F, F, F ) }, + { ENTRY(EXPLICIT_PATH, F, F, T, T, F, F, F ) }, + { ENTRY(OPENGL_SYMLINK, T, F, F, F, T, F, T ) }, + { ENTRY(CUDA_SYMLINK, T, F, F, T, T, F, F ) }, + { ENTRY(XLIB_SYMLINK, F, F, F, F, T, F, F ) }, + { ENTRY(TLS_SYMLINK, T, T, F, T, T, F, T ) }, + { ENTRY(UTILITY_LIB_SYMLINK, F, F, F, F, T, F, F ) }, + { ENTRY(INSTALLER_BINARY, F, F, T, F, F, F, F ) }, + { ENTRY(UTILITY_BINARY, F, F, T, F, F, F, F ) }, + { ENTRY(UTILITY_BIN_SYMLINK, F, F, F, F, T, F, F ) }, + { ENTRY(DOT_DESKTOP, F, F, T, T, F, F, F ) }, + { ENTRY(XMODULE_SHARED_LIB, F, F, T, T, F, T, F ) }, + { ENTRY(XMODULE_SYMLINK, F, F, F, T, T, F, F ) }, + { ENTRY(GLX_MODULE_SHARED_LIB, F, F, T, T, F, T, T ) }, + { ENTRY(GLX_MODULE_SYMLINK, F, F, F, T, T, F, T ) }, + { ENTRY(XMODULE_NEWSYM, F, F, F, T, T, F, F ) }, + { ENTRY(VDPAU_LIB, T, F, T, T, F, T, F ) }, + { ENTRY(VDPAU_SYMLINK, T, F, F, T, T, F, F ) }, + { ENTRY(NVCUVID_LIB, T, F, T, F, F, T, F ) }, + { ENTRY(NVCUVID_LIB_SYMLINK, T, F, F, F, T, F, F ) }, + { ENTRY(ENCODEAPI_LIB, T, F, T, F, F, T, F ) }, + { ENTRY(ENCODEAPI_LIB_SYMLINK, T, F, F, F, T, F, F ) }, + { ENTRY(VGX_LIB, F, F, T, F, F, T, F ) }, + { ENTRY(VGX_LIB_SYMLINK, F, F, F, F, T, F, F ) }, + { ENTRY(NVIDIA_MODPROBE, F, F, T, T, F, F, F ) }, + { ENTRY(NVIDIA_MODPROBE_MANPAGE,F, F, T, T, F, F, F ) }, + { ENTRY(MODULE_SIGNING_KEY, F, F, T, F, F, F, F ) }, +}; + +/* + * Scan packageEntryFileTypeTable[] for the given file type. If we find + * it, return the capabilities for the type. + */ +PackageEntryFileCapabilities get_file_type_capabilities( + PackageEntryFileType type +) +{ + int i; + PackageEntryFileCapabilities nullCaps = { F, F, F, F, F, F, F }; + + for (i = 0; i < ARRAY_LEN(packageEntryFileTypeTable); i++) { + if (type == packageEntryFileTypeTable[i].type) { + return packageEntryFileTypeTable[i].caps; + } + } + + return nullCaps; +} + +/* + * Scan packageEntryFileTypeTable[] for the given string. If we find + * it, return the corresponding type and assign the capabilities for + * the type. + */ +PackageEntryFileType parse_manifest_file_type( + const char *str, + PackageEntryFileCapabilities *caps +) +{ + int i; + + for (i = 0; i < ARRAY_LEN(packageEntryFileTypeTable); i++) { + if (strcmp(str, packageEntryFileTypeTable[i].name) == 0) { + *caps = packageEntryFileTypeTable[i].caps; + return packageEntryFileTypeTable[i].type; + } + } + + return FILE_TYPE_NONE; +} + +/* + * Return a list of what file types should be considered installable. + */ +void get_installable_file_type_list( + Options *op, + PackageEntryFileTypeList *installable_file_types +) +{ + int i; + + memset(installable_file_types, 0, sizeof(*installable_file_types)); + + for (i = 0; i < ARRAY_LEN(packageEntryFileTypeTable); i++) { + + PackageEntryFileType type = packageEntryFileTypeTable[i].type; + + if ((type == FILE_TYPE_OPENGL_HEADER) && !op->opengl_headers) { + continue; + } + + if ((type == FILE_TYPE_KERNEL_MODULE_SRC) && + op->no_kernel_module_source) { + continue; + } + + if (((type == FILE_TYPE_NVIDIA_MODPROBE) || + (type == FILE_TYPE_NVIDIA_MODPROBE_MANPAGE)) && + !op->nvidia_modprobe) { + continue; + } + + if (!packageEntryFileTypeTable[i].caps.installable) { + continue; + } + + installable_file_types->types[type] = 1; + } +} + +/* + * Add symlink file types to the given file list. This is used when + * building a list of existing files to remove. The NEWSYM type + * requires special handling: while it is a symlink, we do not remove + * it from the filesystem if it already exists. + */ +void add_symlinks_to_file_type_list(PackageEntryFileTypeList *file_type_list) +{ + int i; + + for (i = 0; i < ARRAY_LEN(packageEntryFileTypeTable); i++) { + + PackageEntryFileType type = packageEntryFileTypeTable[i].type; + + if (!packageEntryFileTypeTable[i].caps.is_symlink) { + continue; + } + + if (type == FILE_TYPE_XMODULE_NEWSYM) { + continue; + } + + file_type_list->types[type] = 1; + } +} diff --git a/manifest.h b/manifest.h new file mode 100644 index 0000000..0b6aa58 --- /dev/null +++ b/manifest.h @@ -0,0 +1,39 @@ +/* + * nvidia-installer: A tool for installing NVIDIA software packages on + * Unix and Linux systems. + * + * Copyright (C) 2013 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses>. + */ + +#ifndef __NVIDIA_INSTALLER_MANIFEST_H__ +#define __NVIDIA_INSTALLER_MANIFEST_H__ + +#include "nvidia-installer.h" + +PackageEntryFileCapabilities get_file_type_capabilities( + PackageEntryFileType type); + +PackageEntryFileType parse_manifest_file_type( + const char *str, + PackageEntryFileCapabilities *caps); + +void get_installable_file_type_list( + Options *op, + PackageEntryFileTypeList *installable_file_types); + +void add_symlinks_to_file_type_list( + PackageEntryFileTypeList *file_type_list); + +#endif /* __NVIDIA_INSTALLER_MANIFEST_H__ */ @@ -48,6 +48,7 @@ #include "misc.h" #include "crc.h" #include "nvLegacy.h" +#include "manifest.h" static int check_symlink(Options*, const char*, const char*, const char*); static int check_file(Options*, const char*, const mode_t, const uint32); @@ -146,7 +147,7 @@ int check_runlevel(Options *op) if (ret != 2) { ui_warn(op, "Skipping the runlevel check (unrecognized output from " - "the `runlevel` utility: '%d').", data); + "the `runlevel` utility: '%s').", data); nvfree(data); return TRUE; } @@ -200,7 +201,7 @@ int adjust_cwd(Options *op, const char *program_name) path = (char *) nvalloc(len + 1); strncpy(path, program_name, len); path[len] = '\0'; - if (op->expert) log_printf(op, TRUE, NULL, "chdir(\"%s\")", path); + if (op->expert) log_printf(op, NULL, "chdir(\"%s\")", path); if (chdir(path)) { fprintf(stderr, "Unable to chdir to %s (%s)", path, strerror(errno)); @@ -509,7 +510,8 @@ int find_system_utils(Options *op) { "getenforce", "selinux" }, { "execstack", "selinux" }, { "pkg-config", "pkg-config" }, - { "X", "xserver" } + { "X", "xserver" }, + { "openssl", "openssl" } }; int i, j; @@ -857,7 +859,7 @@ int do_install(Options *op, Package *p, CommandList *c) if (!ret) return FALSE; - ui_log(op, "Driver file installation is complete.", p->description); + ui_log(op, "Driver file installation is complete."); return TRUE; @@ -1051,7 +1053,7 @@ void should_install_opengl_headers(Options *op, Package *p) */ for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_TYPE_OPENGL_HEADER) { + if (p->entries[i].type == FILE_TYPE_OPENGL_HEADER) { have_headers = TRUE; break; } @@ -1099,7 +1101,7 @@ void should_install_compat32_files(Options *op, Package *p) */ for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { have_compat32_files = TRUE; break; } @@ -1133,9 +1135,9 @@ void should_install_compat32_files(Options *op, Package *p) if (!install_compat32_files) { for (i = 0; i < p->num_entries; i++) { - if (p->entries[i].flags & FILE_CLASS_COMPAT32) { + if (p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) { /* invalidate file */ - p->entries[i].flags &= ~FILE_TYPE_MASK; + p->entries[i].type = FILE_TYPE_NONE; p->entries[i].dst = NULL; } } @@ -1154,26 +1156,28 @@ void check_installed_files_from_package(Options *op, Package *p) { int i, ret = TRUE; float percent; - uint64_t installable_files; + PackageEntryFileTypeList installable_files; ui_status_begin(op, "Running post-install sanity check:", "Checking"); - installable_files = get_installable_file_mask(op); - + get_installable_file_type_list(op, &installable_files); + for (i = 0; i < p->num_entries; i++) { percent = (float) i / (float) p->num_entries; ui_status_update(op, percent, p->entries[i].dst); - if (p->entries[i].flags & FILE_TYPE_SYMLINK) { + if (p->entries[i].caps.is_symlink && /* Don't bother checking FILE_TYPE_NEWSYMs because we may not have * installed them. */ + p->entries[i].type != FILE_TYPE_XMODULE_NEWSYM) { + if (!check_symlink(op, p->entries[i].target, p->entries[i].dst, p->description)) { ret = FALSE; } - } else if (p->entries[i].flags & installable_files) { + } else if (installable_files.types[p->entries[i].type]) { if (!check_file(op, p->entries[i].dst, p->entries[i].mode, 0)) { ret = FALSE; } @@ -1241,6 +1245,46 @@ static int check_symlink(Options *op, const char *target, const char *link, /* + * unprelink() - attempt to run `prelink -u` on a file to restore it to + * its pre-prelinked state. + */ +int unprelink(Options *op, const char *filename) +{ + char *cmd; + int ret = ENOENT; + + cmd = find_system_util("prelink"); + if (cmd) { + char *cmdline; + cmdline = nvstrcat(cmd, " -u ", filename, NULL); + ret = run_command(op, cmdline, NULL, FALSE, 0, TRUE); + nvfree(cmd); + nvfree(cmdline); + } + return ret; +} /* unprelink() */ + + + +/* + * verify_crc() - Compute the CRC of a file and compare it against an + * expected value. Returns TRUE if the values match, FALSE otherwise. + * + */ +int verify_crc(Options *op, const char *filename, unsigned int crc, + unsigned int *actual_crc) +{ + /* only check the crc if we were handed a non-emtpy crc */ + if (crc == 0) { + return TRUE; + } + *actual_crc = compute_crc(op, filename); + return crc == *actual_crc; +} /* verify_crc() */ + + + +/* * check_file() - check that the specified installed file exists, has * the correct permissions, and has the correct crc. * @@ -1274,13 +1318,27 @@ static int check_file(Options *op, const char *filename, return FALSE; } - /* only check the crc if we were handed a non-emtpy crc */ - if (crc != 0) { - actual_crc = compute_crc(op, filename); - if (crc != actual_crc) { + if (!verify_crc(op, filename, crc, &actual_crc)) { + int ret; + + ui_expert(op, "The installed file '%s' has a different checksum (%ul) " + "than when it was installed (%ul). This may be due to " + "prelinking; attemping `prelink -u %s` to restore the file.", + filename, actual_crc, crc, filename); + + ret = unprelink(op, filename); + if (ret != 0) { + ui_warn(op, "The installed file '%s' seems to have changed, but " + "`prelink -u` failed; unable to restore '%s' to an " + "un-prelinked state.", filename, filename); + return FALSE; + } + + if (!verify_crc(op, filename, crc, &actual_crc)) { ui_warn(op, "The installed file '%s' has a different checksum " - "(%ul) than when it was installed (%ul).", + "(%ul) after running `prelink -u` than when it was " + "installed (%ul).", filename, actual_crc, crc); return FALSE; } @@ -1292,23 +1350,6 @@ static int check_file(Options *op, const char *filename, -/* - * get_installable_file_mask() - return the mask of what file types - * should be considered installable. - */ - -uint64_t get_installable_file_mask(Options *op) -{ - uint64_t installable_files = FILE_TYPE_INSTALLABLE_FILE; - if (!op->opengl_headers) installable_files &= ~FILE_TYPE_OPENGL_HEADER; - if (op->no_kernel_module_source) - installable_files &= ~FILE_TYPE_KERNEL_MODULE_SRC; - return installable_files; - -} /* get_installable_file_mask() */ - - - #if defined(NV_TLS_TEST) /* * tls_test() - Starting with glibc 2.3, there is a new thread local @@ -1655,25 +1696,27 @@ static int rtld_test_internal(Options *op, Package *p, /* perform the test(s) */ for (i = 0; i < p->num_entries; i++) { - if (!(p->entries[i].flags & FILE_TYPE_RTLD_CHECKED)) + if ((p->entries[i].type != FILE_TYPE_OPENGL_LIB) && + (p->entries[i].type != FILE_TYPE_TLS_LIB)) { continue; - else if ((which_tls & TLS_LIB_TYPE_FORCED) && - (p->entries[i].flags & FILE_TYPE_TLS_LIB)) + } else if ((which_tls & TLS_LIB_TYPE_FORCED) && + (p->entries[i].type == FILE_TYPE_TLS_LIB)) { continue; #if defined(NV_X86_64) - else if ((p->entries[i].flags & FILE_CLASS_NATIVE) - && compat_32_libs) + } else if ((p->entries[i].compat_arch == FILE_COMPAT_ARCH_NATIVE) + && compat_32_libs) { continue; - else if ((p->entries[i].flags & FILE_CLASS_COMPAT32) - && !compat_32_libs) + } else if ((p->entries[i].compat_arch == FILE_COMPAT_ARCH_COMPAT32) + && !compat_32_libs) { continue; #endif /* NV_X86_64 */ - else if ((which_tls == TLS_LIB_NEW_TLS) && - (p->entries[i].flags & FILE_CLASS_CLASSIC_TLS)) + } else if ((which_tls == TLS_LIB_NEW_TLS) && + (p->entries[i].tls_class == FILE_TLS_CLASS_CLASSIC)) { continue; - else if ((which_tls == TLS_LIB_CLASSIC_TLS) && - (p->entries[i].flags & FILE_CLASS_NEW_TLS)) + } else if ((which_tls == TLS_LIB_CLASSIC_TLS) && + (p->entries[i].tls_class == FILE_TLS_CLASS_NEW)) { continue; + } name = nvstrdup(p->entries[i].name); if (!name) continue; @@ -1830,6 +1873,7 @@ Distribution get_distribution(Options *op) if (access("/etc/SuSE-release", F_OK) == 0) return SUSE; if (access("/etc/UnitedLinux-release", F_OK) == 0) return UNITED_LINUX; if (access("/etc/gentoo-release", F_OK) == 0) return GENTOO; + if (access("/etc/arch-release", F_OK) == 0) return ARCH; /* * Attempt to determine if the host system is 'Ubuntu Linux' @@ -2711,3 +2755,62 @@ int dkms_remove_module(Options *op, const char *version) { return run_dkms(op, DKMS_REMOVE, version, NULL, NULL); } + +/* + * Test the last bit of the given file. Return 1 if the bit is set, 0 if it is + * not set, and < 0 on error. + * + */ +static int test_last_bit(const char *file) { + char buf; + int ret, data_read = FALSE; + FILE *fp = fopen(file, "r"); + + if (!fp) { + return -errno; + } + + /* XXX Using fseek(3) could make this more efficient for larger files, but + * trying to read after an fseek(stream, -1, SEEK_END) call on a UEFI + * variable file in sysfs hits a premature EOF. */ + + while(fread(&buf, 1, 1, fp)) { + data_read = TRUE; + } + + if (ferror(fp)) { + ret = -ferror(fp); + } else if (data_read) { + ret = buf & 1; + } else { + ret = -EIO; + } + + fclose(fp); + return ret; +} + +static const char* secure_boot_files[] = { + "/sys/firmware/efi/vars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c/data", + "/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c", +}; + +/* + * secure_boot_enabled() - Check the known paths where secure boot status is + * exposed. If secure boot is enabled, return 1. If secure boot is disabled, + * return 0. On failure to detect whether secure boot is enabled, return < 0. + */ +int secure_boot_enabled(void) { + int i, ret = -ENOENT; + + for (i = 0; i < ARRAY_LEN(secure_boot_files); i++) { + if (access(secure_boot_files[i], R_OK) == 0) { + ret = test_last_bit(secure_boot_files[i]); + if (ret >= 0) { + break; + } + } + } + + return ret; +} @@ -46,12 +46,11 @@ int check_selinux(Options *op); int check_proc_modprobe_path(Options *op); int check_development_tools(Options *op, Package *p); char *extract_version_string(const char *str); -int continue_after_error(Options *op, const char *fmt, ...); +int continue_after_error(Options *op, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(2, 3); int do_install(Options *op, Package *p, CommandList *c); void should_install_opengl_headers(Options *op, Package *p); void should_install_compat32_files(Options *op, Package *p); void check_installed_files_from_package(Options *op, Package *p); -uint64_t get_installable_file_mask(Options *op); int tls_test(Options *op, int compat_32_libs); int check_runtime_configuration(Options *op, Package *p); void collapse_multiple_slashes(char *s); @@ -66,5 +65,9 @@ int check_for_nouveau(Options *op); int dkms_module_installed(Options *op, const char *version); int dkms_install_module(Options *op, const char *version, const char *kernel); int dkms_remove_module(Options *op, const char *version); +int unprelink(Options *op, const char *filename); +int verify_crc(Options *op, const char *filename, unsigned int crc, + unsigned int *actual_crc); +int secure_boot_enabled(void); #endif /* __NVIDIA_INSTALLER_MISC_H__ */ diff --git a/nvidia-installer.c b/nvidia-installer.c index 75168ea..0117c5a 100644 --- a/nvidia-installer.c +++ b/nvidia-installer.c @@ -138,6 +138,7 @@ static Options *load_default_options(void) op->logging = TRUE; /* log by default */ op->opengl_headers = FALSE; /* do not install our GL headers by default */ + op->nvidia_modprobe = TRUE; op->run_nvidia_xconfig = FALSE; op->selinux_option = SELINUX_DEFAULT; @@ -253,6 +254,8 @@ static void parse_commandline(int argc, char *argv[], Options *op) #endif case DOCUMENTATION_PREFIX_OPTION: op->documentation_prefix = strval; break; + case APPLICATION_PROFILE_PATH_OPTION: + op->application_profile_path = strval; break; case INSTALLER_PREFIX_OPTION: op->installer_prefix = strval; break; case UTILITY_PREFIX_OPTION: @@ -282,6 +285,8 @@ static void parse_commandline(int argc, char *argv[], Options *op) op->tmpdir = strval; break; case OPENGL_HEADERS_OPTION: op->opengl_headers = TRUE; break; + case NO_NVIDIA_MODPROBE_OPTION: + op->nvidia_modprobe = FALSE; break; case FORCE_TLS_OPTION: if (strcasecmp(strval, "new") == 0) op->which_tls = FORCE_NEW_TLS; @@ -382,6 +387,21 @@ static void parse_commandline(int argc, char *argv[], Options *op) case DKMS_OPTION: op->dkms = TRUE; break; + case MODULE_SIGNING_SECRET_KEY_OPTION: + op->module_signing_secret_key = strval; + break; + case MODULE_SIGNING_PUBLIC_KEY_OPTION: + op->module_signing_public_key = strval; + break; + case MODULE_SIGNING_SCRIPT_OPTION: + op->module_signing_script = strval; + break; + case MODULE_SIGNING_KEY_PATH_OPTION: + op->module_signing_key_path = strval; + break; + case MODULE_SIGNING_HASH_OPTION: + op->module_signing_hash = strval; + break; default: goto fail; } @@ -420,6 +440,18 @@ static void parse_commandline(int argc, char *argv[], Options *op) exit(0); } + /* Disable DKMS if module signing was requested; the two options are + * mutually exclusive. */ + if (op->dkms && + op->module_signing_secret_key && op->module_signing_public_key) { + ui_warn(op, "You selected the DKMS kernel module option, and also " + "specified a pair of module signing keys. Keys cannot be " + "managed securely under the automated DKMS framework. The " + "DKMS option will be disabled."); + op->dkms = FALSE; + } + + /* * if the installer prefix was not specified, default it to the * utility prefix; this is done so that the installer prefix is diff --git a/nvidia-installer.h b/nvidia-installer.h index 580d69f..ea95051 100644 --- a/nvidia-installer.h +++ b/nvidia-installer.h @@ -24,7 +24,7 @@ #define __NVIDIA_INSTALLER_H__ #include <sys/types.h> -#include <stdint.h> +#include <inttypes.h> #include "common-utils.h" @@ -55,6 +55,7 @@ typedef enum { EXECSTACK, PKG_CONFIG, XSERVER, + OPENSSL, MAX_SYSTEM_OPTIONAL_UTILS } SystemOptionalUtils; @@ -85,6 +86,7 @@ typedef enum { DEBIAN, UBUNTU, GENTOO, + ARCH, OTHER } Distribution; @@ -116,6 +118,7 @@ typedef struct __options { int latest; int force_update; int opengl_headers; + int nvidia_modprobe; int no_questions; int silent; int which_tls; @@ -168,6 +171,8 @@ typedef struct __options { char *documentation_docdir; char *documentation_mandir; + char *application_profile_path; + int modular_xorg; char *kernel_source_path; @@ -192,6 +197,14 @@ typedef struct __options { char *precompiled_kernel_interfaces_url; const char *selinux_chcon_type; + char *module_signing_secret_key; + char *module_signing_public_key; + char *module_signing_script; + char *module_signing_key_path; + char *module_signing_hash; + + int kernel_module_signed; + Distribution distro; void *ui_priv; /* for use by the ui's */ @@ -200,6 +213,85 @@ typedef struct __options { } Options; +/* + * Types of installed files. Keep in sync with + * manifest.c:packageEntryFileTypeTable[] + */ +typedef enum { + FILE_TYPE_NONE, + FILE_TYPE_KERNEL_MODULE_SRC, + FILE_TYPE_KERNEL_MODULE_CMD, + FILE_TYPE_OPENGL_HEADER, + FILE_TYPE_OPENGL_LIB, + FILE_TYPE_XLIB_STATIC_LIB, + FILE_TYPE_XLIB_SHARED_LIB, + FILE_TYPE_DOCUMENTATION, + FILE_TYPE_OPENGL_SYMLINK, + FILE_TYPE_XLIB_SYMLINK, + FILE_TYPE_KERNEL_MODULE, + FILE_TYPE_INSTALLER_BINARY, + FILE_TYPE_UTILITY_BINARY, + FILE_TYPE_LIBGL_LA, + FILE_TYPE_TLS_LIB, + FILE_TYPE_TLS_SYMLINK, + FILE_TYPE_UTILITY_LIB, + FILE_TYPE_DOT_DESKTOP, + FILE_TYPE_UTILITY_LIB_SYMLINK, + FILE_TYPE_XMODULE_SHARED_LIB, + FILE_TYPE_XMODULE_SYMLINK, + FILE_TYPE_XMODULE_NEWSYM, /* Create a symlink if the file doesn't exist */ + FILE_TYPE_MANPAGE, + FILE_TYPE_EXPLICIT_PATH, + FILE_TYPE_CUDA_LIB, + FILE_TYPE_CUDA_SYMLINK, + FILE_TYPE_VDPAU_LIB, + FILE_TYPE_VDPAU_SYMLINK, + FILE_TYPE_UTILITY_BIN_SYMLINK, + FILE_TYPE_CUDA_ICD, + FILE_TYPE_NVCUVID_LIB, + FILE_TYPE_NVCUVID_LIB_SYMLINK, + FILE_TYPE_GLX_MODULE_SHARED_LIB, + FILE_TYPE_GLX_MODULE_SYMLINK, + FILE_TYPE_ENCODEAPI_LIB, + FILE_TYPE_ENCODEAPI_LIB_SYMLINK, + FILE_TYPE_VGX_LIB, + FILE_TYPE_VGX_LIB_SYMLINK, + FILE_TYPE_APPLICATION_PROFILE, + FILE_TYPE_NVIDIA_MODPROBE, + FILE_TYPE_NVIDIA_MODPROBE_MANPAGE, + FILE_TYPE_MODULE_SIGNING_KEY, + FILE_TYPE_MAX +} PackageEntryFileType; + +typedef enum { + FILE_TLS_CLASS_NONE, + FILE_TLS_CLASS_NEW, + FILE_TLS_CLASS_CLASSIC, +} PackageEntryFileTlsClass; + +typedef enum { + FILE_COMPAT_ARCH_NONE, + FILE_COMPAT_ARCH_NATIVE, + FILE_COMPAT_ARCH_COMPAT32, +} PackageEntryFileCompatArch; + +typedef struct { + unsigned int has_arch : 1; + unsigned int has_tls_class : 1; + unsigned int installable : 1; + unsigned int has_path : 1; + unsigned int is_symlink : 1; + unsigned int is_shared_lib : 1; + unsigned int is_opengl : 1; +} PackageEntryFileCapabilities; + +/* + * PackageEntryFileTypeList::types[] are booleans, indexed by + * PackageEntryFileType enum value. + */ +typedef struct { + uint8_t types[FILE_TYPE_MAX]; +} PackageEntryFileTypeList; typedef struct __package_entry { @@ -233,7 +325,11 @@ typedef struct __package_entry { * function. */ - uint64_t flags; + PackageEntryFileCapabilities caps; + PackageEntryFileType type; + PackageEntryFileTlsClass tls_class; + PackageEntryFileCompatArch compat_arch; + mode_t mode; ino_t inode; @@ -275,162 +371,6 @@ typedef struct __package { #define NV_LINE_LEN 1024 #define NV_MIN_LINE_LEN 256 -/* file types */ - -#define FILE_TYPE_MASK 0x0000003fffffffffULL - -#define FILE_TYPE_KERNEL_MODULE_SRC 0x0000000000000001ULL -#define FILE_TYPE_KERNEL_MODULE_CMD 0x0000000000000002ULL -#define FILE_TYPE_OPENGL_HEADER 0x0000000000000004ULL -#define FILE_TYPE_OPENGL_LIB 0x0000000000000008ULL -#define FILE_TYPE_XLIB_STATIC_LIB 0x0000000000000010ULL -#define FILE_TYPE_XLIB_SHARED_LIB 0x0000000000000020ULL -#define FILE_TYPE_DOCUMENTATION 0x0000000000000040ULL -#define FILE_TYPE_OPENGL_SYMLINK 0x0000000000000080ULL -#define FILE_TYPE_XLIB_SYMLINK 0x0000000000000100ULL -#define FILE_TYPE_KERNEL_MODULE 0x0000000000000200ULL -#define FILE_TYPE_INSTALLER_BINARY 0x0000000000000400ULL -#define FILE_TYPE_UTILITY_BINARY 0x0000000000000800ULL -#define FILE_TYPE_LIBGL_LA 0x0000000000001000ULL -#define FILE_TYPE_TLS_LIB 0x0000000000002000ULL -#define FILE_TYPE_TLS_SYMLINK 0x0000000000004000ULL -#define FILE_TYPE_UTILITY_LIB 0x0000000000008000ULL -#define FILE_TYPE_DOT_DESKTOP 0x0000000000010000ULL -#define FILE_TYPE_UTILITY_LIB_SYMLINK 0x0000000000020000ULL -#define FILE_TYPE_XMODULE_SHARED_LIB 0x0000000000040000ULL -#define FILE_TYPE_XMODULE_SYMLINK 0x0000000000080000ULL -/* Create a symlink only if the file doesn't exist */ -#define FILE_TYPE_XMODULE_NEWSYM 0x0000000000100000ULL -#define FILE_TYPE_MANPAGE 0x0000000000200000ULL -#define FILE_TYPE_EXPLICIT_PATH 0x0000000000400000ULL -#define FILE_TYPE_CUDA_LIB 0x0000000000800000ULL -#define FILE_TYPE_CUDA_SYMLINK 0x0000000001000000ULL -#define FILE_TYPE_VDPAU_LIB 0x0000000002000000ULL -#define FILE_TYPE_VDPAU_SYMLINK 0x0000000004000000ULL -/* unused 0x0000000008000000ULL */ -#define FILE_TYPE_UTILITY_BIN_SYMLINK 0x0000000010000000ULL -#define FILE_TYPE_CUDA_ICD 0x0000000020000000ULL -#define FILE_TYPE_NVCUVID_LIB 0x0000000040000000ULL -#define FILE_TYPE_NVCUVID_SYMLINK 0x0000000080000000ULL -#define FILE_TYPE_GLX_MODULE_SHARED_LIB 0x0000000100000000ULL -#define FILE_TYPE_GLX_MODULE_SYMLINK 0x0000000200000000ULL -#define FILE_TYPE_ENCODEAPI_LIB 0x0000000400000000ULL -#define FILE_TYPE_ENCODEAPI_SYMLINK 0x0000000800000000ULL -#define FILE_TYPE_VGX_LIB 0x0000001000000000ULL -#define FILE_TYPE_VGX_SYMLINK 0x0000002000000000ULL - -/* file class: this is used to distinguish OpenGL libraries */ - -#define FILE_CLASS_MASK 0xf000000000000000ULL - -#define FILE_CLASS_NEW_TLS 0x1000000000000000ULL -#define FILE_CLASS_CLASSIC_TLS 0x2000000000000000ULL -#define FILE_CLASS_NATIVE 0x4000000000000000ULL -#define FILE_CLASS_COMPAT32 0x8000000000000000ULL - -#define FILE_TYPE_XLIB_LIB (FILE_TYPE_XLIB_STATIC_LIB | \ - FILE_TYPE_XLIB_SHARED_LIB) - -#define FILE_TYPE_INSTALLABLE_FILE (FILE_TYPE_OPENGL_LIB | \ - FILE_TYPE_CUDA_LIB | \ - FILE_TYPE_XLIB_LIB | \ - FILE_TYPE_TLS_LIB | \ - FILE_TYPE_UTILITY_LIB | \ - FILE_TYPE_DOCUMENTATION | \ - FILE_TYPE_MANPAGE | \ - FILE_TYPE_EXPLICIT_PATH | \ - FILE_TYPE_OPENGL_HEADER | \ - FILE_TYPE_CUDA_ICD | \ - FILE_TYPE_KERNEL_MODULE | \ - FILE_TYPE_INSTALLER_BINARY | \ - FILE_TYPE_UTILITY_BINARY | \ - FILE_TYPE_LIBGL_LA | \ - FILE_TYPE_XMODULE_SHARED_LIB | \ - FILE_TYPE_GLX_MODULE_SHARED_LIB | \ - FILE_TYPE_DOT_DESKTOP | \ - FILE_TYPE_VDPAU_LIB | \ - FILE_TYPE_NVCUVID_LIB | \ - FILE_TYPE_KERNEL_MODULE_SRC | \ - FILE_TYPE_ENCODEAPI_LIB | \ - FILE_TYPE_VGX_LIB) - -#define FILE_TYPE_HAVE_PATH (FILE_TYPE_XMODULE_SHARED_LIB | \ - FILE_TYPE_XMODULE_SYMLINK | \ - FILE_TYPE_GLX_MODULE_SHARED_LIB | \ - FILE_TYPE_GLX_MODULE_SYMLINK | \ - FILE_TYPE_XMODULE_NEWSYM | \ - FILE_TYPE_MANPAGE | \ - FILE_TYPE_EXPLICIT_PATH | \ - FILE_TYPE_OPENGL_HEADER | \ - FILE_TYPE_CUDA_LIB | \ - FILE_TYPE_CUDA_SYMLINK | \ - FILE_TYPE_TLS_LIB | \ - FILE_TYPE_TLS_SYMLINK | \ - FILE_TYPE_DOT_DESKTOP | \ - FILE_TYPE_DOCUMENTATION | \ - FILE_TYPE_VDPAU_SYMLINK | \ - FILE_TYPE_VDPAU_LIB) - - -#define FILE_TYPE_HAVE_ARCH (FILE_TYPE_OPENGL_LIB | \ - FILE_TYPE_CUDA_LIB | \ - FILE_TYPE_OPENGL_SYMLINK | \ - FILE_TYPE_CUDA_SYMLINK | \ - FILE_TYPE_LIBGL_LA | \ - FILE_TYPE_TLS_LIB | \ - FILE_TYPE_TLS_SYMLINK | \ - FILE_TYPE_VDPAU_SYMLINK | \ - FILE_TYPE_VDPAU_LIB | \ - FILE_TYPE_NVCUVID_LIB | \ - FILE_TYPE_NVCUVID_SYMLINK | \ - FILE_TYPE_ENCODEAPI_LIB | \ - FILE_TYPE_ENCODEAPI_SYMLINK) - -#define FILE_TYPE_HAVE_CLASS (FILE_TYPE_TLS_LIB | \ - FILE_TYPE_TLS_SYMLINK) - -#define FILE_TYPE_SYMLINK (FILE_TYPE_OPENGL_SYMLINK | \ - FILE_TYPE_CUDA_SYMLINK | \ - FILE_TYPE_XLIB_SYMLINK | \ - FILE_TYPE_TLS_SYMLINK | \ - FILE_TYPE_XMODULE_SYMLINK | \ - FILE_TYPE_GLX_MODULE_SYMLINK | \ - FILE_TYPE_UTILITY_LIB_SYMLINK| \ - FILE_TYPE_UTILITY_BIN_SYMLINK| \ - FILE_TYPE_VDPAU_SYMLINK | \ - FILE_TYPE_NVCUVID_SYMLINK | \ - FILE_TYPE_ENCODEAPI_SYMLINK | \ - FILE_TYPE_VGX_SYMLINK) - -#define FILE_TYPE_NEWSYM (FILE_TYPE_XMODULE_NEWSYM) - -#define FILE_TYPE_HAVE_TARGET (FILE_TYPE_SYMLINK | \ - FILE_TYPE_NEWSYM) - -#define FILE_TYPE_RTLD_CHECKED (FILE_TYPE_OPENGL_LIB | \ - FILE_TYPE_TLS_LIB) - -#define FILE_TYPE_SHARED_LIB (FILE_TYPE_OPENGL_LIB | \ - FILE_TYPE_CUDA_LIB | \ - FILE_TYPE_XLIB_SHARED_LIB | \ - FILE_TYPE_TLS_LIB | \ - FILE_TYPE_XMODULE_SHARED_LIB | \ - FILE_TYPE_GLX_MODULE_SHARED_LIB | \ - FILE_TYPE_UTILITY_LIB | \ - FILE_TYPE_VDPAU_LIB | \ - FILE_TYPE_NVCUVID_LIB | \ - FILE_TYPE_ENCODEAPI_LIB | \ - FILE_TYPE_VGX_LIB) - -#define FILE_TYPE_OPENGL_FILE (FILE_TYPE_OPENGL_HEADER | \ - FILE_TYPE_OPENGL_LIB | \ - FILE_TYPE_OPENGL_SYMLINK | \ - FILE_TYPE_LIBGL_LA | \ - FILE_TYPE_TLS_LIB | \ - FILE_TYPE_TLS_SYMLINK | \ - FILE_TYPE_GLX_MODULE_SHARED_LIB | \ - FILE_TYPE_GLX_MODULE_SYMLINK) - #define TLS_LIB_TYPE_FORCED 0x0001 #define TLS_LIB_NEW_TLS 0x0002 #define TLS_LIB_CLASSIC_TLS 0x0004 @@ -445,6 +385,8 @@ typedef struct __package { #define PERM_MASK (S_IRWXU|S_IRWXG|S_IRWXO) #define PRECOMPILED_KERNEL_INTERFACE_FILENAME "precompiled-nv-linux.o" +#define KERNEL_MODULE_CHECKSUM_FILENAME "nvidia-module-checksum" +#define DETACHED_SIGNATURE_FILENAME "nvidia-detached-module-signature" /* @@ -454,10 +396,11 @@ typedef struct __package { * is detected, all prefixes/paths can be overriden from the * command line. */ -#define DEFAULT_OPENGL_PREFIX "/usr" -#define DEFAULT_X_PREFIX "/usr/X11R6" -#define DEFAULT_UTILITY_PREFIX "/usr" -#define DEFAULT_DOCUMENTATION_PREFIX "/usr" +#define DEFAULT_OPENGL_PREFIX "/usr" +#define DEFAULT_X_PREFIX "/usr/X11R6" +#define DEFAULT_UTILITY_PREFIX "/usr" +#define DEFAULT_DOCUMENTATION_PREFIX "/usr" +#define DEFAULT_APPLICATION_PROFILE_PATH "/usr/share/nvidia" #define DEFAULT_LIBDIR "lib" #define DEFAULT_64BIT_LIBDIR "lib64" @@ -468,6 +411,7 @@ typedef struct __package { #define DEFAULT_DOCDIR "share/doc" #define DEFAULT_MANDIR "share/man" +#define DEFAULT_MODULE_SIGNING_KEY_PATH "/usr/share/nvidia" #define DEFAULT_KERNEL_MODULE_SRC_PREFIX "/usr/src" /* @@ -537,8 +481,7 @@ typedef struct __package { /* prototypes of functions used throughout the installer */ void log_init(Options *op, int argc, char * const argv[]); -void log_printf(Options *op, const int wb, - const char *prefix, const char *fmt, ...); +void log_printf(Options *op, const char *prefix, const char *fmt, ...) NV_ATTRIBUTE_PRINTF(3, 4); int install_from_cwd(Options *op); int add_this_kernel(Options *op); @@ -549,7 +492,8 @@ void add_package_entry(Package *p, char *name, char *target, char *dst, - uint64_t flags, + PackageEntryFileType type, + PackageEntryFileTlsClass tls_class, mode_t mode); /* XXX */ diff --git a/option_table.h b/option_table.h index ab3b115..a5854ae 100644 --- a/option_table.h +++ b/option_table.h @@ -47,6 +47,7 @@ enum { HELP_ARGS_ONLY_OPTION, TMPDIR_OPTION, OPENGL_HEADERS_OPTION, + NO_NVIDIA_MODPROBE_OPTION, INSTALLER_PREFIX_OPTION, FORCE_TLS_OPTION, SANITY_OPTION, @@ -74,6 +75,7 @@ enum { NO_SIGWINCH_WORKAROUND_OPTION, X_MODULE_PATH_OPTION, DOCUMENTATION_PREFIX_OPTION, + APPLICATION_PROFILE_PATH_OPTION, X_LIBRARY_PATH_OPTION, NO_KERNEL_MODULE_OPTION, NO_X_CHECK_OPTION, @@ -84,6 +86,11 @@ enum { KERNEL_MODULE_SOURCE_DIR_OPTION, NO_KERNEL_MODULE_SOURCE_OPTION, DKMS_OPTION, + MODULE_SIGNING_SECRET_KEY_OPTION, + MODULE_SIGNING_PUBLIC_KEY_OPTION, + MODULE_SIGNING_SCRIPT_OPTION, + MODULE_SIGNING_KEY_PATH_OPTION, + MODULE_SIGNING_HASH_OPTION }; static const NVGetoptOption __options[] = { @@ -247,6 +254,12 @@ static const NVGetoptOption __options[] = { "driver will be installed. The default is: '" DEFAULT_DOCUMENTATION_PREFIX "'." }, + { "application-profile-path", APPLICATION_PROFILE_PATH_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, + "The directory under which default application profiles for the NVIDIA " + "driver will be installed. The default is: '" + DEFAULT_APPLICATION_PROFILE_PATH "'." }, + { "kernel-include-path", KERNEL_INCLUDE_PATH_OPTION, NVGETOPT_STRING_ARGUMENT, NULL, "The directory containing the kernel include files that " @@ -338,6 +351,16 @@ static const NVGetoptOption __options[] = { "a glx.h or gl.h. Until that is resolved, NVIDIA's OpenGL " "header files can still be chosen, through this installer option." }, + { "no-nvidia-modprobe", NO_NVIDIA_MODPROBE_OPTION, 0, NULL, + "Skip installation of 'nvidia-modprobe', a setuid root utility which " + "nvidia-installer installs by default. nvidia-modprobe can be used by " + "user-space NVIDIA driver components to load the NVIDIA kernel module, " + "and create the NVIDIA device files, when those components run without " + "sufficient privileges to do so on their own, e.g., the CUDA driver run " + "within the permissions of a non-privileged user. This utility is only " + "needed if other means of loading the NVIDIA kernel module and creating " + "the NVIDIA device files are unavailable." }, + { "force-tls", FORCE_TLS_OPTION, NVGETOPT_STRING_ARGUMENT, NULL, "NVIDIA's OpenGL libraries are compiled with one of two " "different thread local storage (TLS) mechanisms: 'classic tls' " @@ -539,6 +562,40 @@ static const NVGetoptOption __options[] = { "'--no-questions' or '--silent' options, which assume the default " "values for all questions." }, + { "module-signing-secret-key", MODULE_SIGNING_SECRET_KEY_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, + "Specify a path to a private key to use for signing the NVIDIA kernel " + "module. The corresponding public key must also be provided." }, + + { "module-signing-public-key", MODULE_SIGNING_PUBLIC_KEY_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, + "Specify a path to a public key to use for verifying the signature of " + "the NVIDIA kernel module. The corresponding private key must also be " + "provided." }, + + { "module-signing-script", MODULE_SIGNING_SCRIPT_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, + "Specify a path to a program to use for signing the NVIDIA kernel " + "module. The program will be called with the arguments: program-name " + "<HASH> <PRIVATEKEY> <PUBLICKEY> <MODULE>; if the program returns an " + "error status, it will be called again with the arguments: program-name " + "<PRIVATEKEY> <PUBLICKEY> <MODULE>. Default: use the \"sign-file\" " + "script in the kernel source directory." }, + + { "module-signing-key-path", MODULE_SIGNING_KEY_PATH_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, + "Specify a path where signing keys generated by nvidia-installer will " + "be installed. Default: install keys to '" DEFAULT_MODULE_SIGNING_KEY_PATH + "'." }, + + { "module-signing-hash", MODULE_SIGNING_HASH_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, + "Specify a cryptographic hash algorithm to use for signing kernel " + "modules. This requires a module signing tool that allows explicit " + "selection of the hash algorithm, and the hash algorithm name must " + "be recognizable by the module signing tool. Default: select a hash " + "algorithm automatically, based on the kernel's configuration." }, + /* Orphaned options: These options were in the long_options table in * nvidia-installer.c but not in the help. */ { "debug", 'd', 0, NULL,NULL }, diff --git a/precompiled.c b/precompiled.c index 647ae69..ae1ab91 100644 --- a/precompiled.c +++ b/precompiled.c @@ -152,8 +152,9 @@ PrecompiledInfo *precompiled_unpack(Options *op, { int dst_fd, fd, offset, len = 0; char *buf, *dst; - uint32 crc, val, size; - char *version, *description, *proc_version_string; + uint32 crc, val, size, linked_module_crc; + char *version, *description, *proc_version_string, + *detached_signature = NULL; struct stat stat_buf; PrecompiledInfo *info = NULL; @@ -265,6 +266,45 @@ PrecompiledInfo *precompiled_unpack(Options *op, offset += val; proc_version_string[val] = '\0'; + /* if this interface appears to have been packaged by nvidia-installer, + * get the linked module crc and path to the detached signature. */ + + if (strstr(filename, PRECOMPILED_KERNEL_INTERFACE_FILENAME) == + strrchr(filename, '/') + 1) { + char *suffix, *dirname, *tmp, *crc_file_path; + + dirname = nvstrdup(filename); + tmp = strrchr(dirname, '/'); + *tmp = '\0'; + + suffix = nvstrdup(strrchr(filename, '/') + 1 + + strlen(PRECOMPILED_KERNEL_INTERFACE_FILENAME)); + + detached_signature = nvstrcat(dirname, "/", DETACHED_SIGNATURE_FILENAME, + suffix, NULL); + crc_file_path = nvstrcat(dirname, "/", KERNEL_MODULE_CHECKSUM_FILENAME, + suffix, NULL); + + if (access(detached_signature, F_OK | R_OK) == 0 && + access(crc_file_path, F_OK | R_OK) == 0) { + FILE *crc_file; + + crc_file = fopen(crc_file_path, "r"); + if (crc_file) { + int items_read = fread(&linked_module_crc, + sizeof(linked_module_crc), 1, crc_file); + if (items_read != 1) { + ui_warn(op, "A checksum file for a linked kernel module " + "was found, but reading the checksum failed."); + detached_signature = NULL; + } + fclose(crc_file); + } + } else { + detached_signature = NULL; + } + } + /* check if the running kernel matches */ if (strcmp(real_proc_version_string, proc_version_string) != 0) { @@ -317,13 +357,15 @@ PrecompiledInfo *precompiled_unpack(Options *op, info->version = version; info->proc_version_string = proc_version_string; info->description = description; + info->detached_signature = detached_signature; + info->linked_module_crc = linked_module_crc; /* - * XXX so that the proc version and description strings aren't - * freed below + * XXX so that the proc version, description, and detached_signature strings + * aren't freed below */ - proc_version_string = description = NULL; + proc_version_string = description = detached_signature = NULL; done: @@ -335,7 +377,20 @@ PrecompiledInfo *precompiled_unpack(Options *op, if (dst_fd > 0) close(dst_fd); if (description) free(description); if (proc_version_string) free(proc_version_string); + if (detached_signature) free(detached_signature); return info; } /* mkprecompiled_unpack() */ + +/* + * free_precompiled() - free any malloced strings stored in a PrecompiledInfo, + * then free the PrecompiledInfo. + */ +void free_precompiled(PrecompiledInfo *info) +{ + nvfree(info->description); + nvfree(info->proc_version_string); + nvfree(info->detached_signature); + nvfree(info); +} diff --git a/precompiled.h b/precompiled.h index d760074..999208b 100644 --- a/precompiled.h +++ b/precompiled.h @@ -31,6 +31,8 @@ typedef struct { char *version; char *proc_version_string; char *description; + char *detached_signature; + uint32 linked_module_crc; } PrecompiledInfo; @@ -42,6 +44,7 @@ PrecompiledInfo *precompiled_unpack(Options *op, const char *output_filename, const char *real_proc_version_string, const char *package_version); +void free_precompiled(PrecompiledInfo *info); #endif /* __NVIDIA_INSTALLER_PRECOMPILED_H__ */ diff --git a/user-interface.c b/user-interface.c index aff75a0..ea21045 100644 --- a/user-interface.c +++ b/user-interface.c @@ -105,7 +105,7 @@ int ui_init(Options *op) } if (i == ARRAY_LEN(ui_list)) { - log_printf(op, TRUE, NULL, "Invalid \"ui\" option: %s", op->ui_str); + log_printf(op, NULL, "Invalid \"ui\" option: %s", op->ui_str); i = 0; } } else { @@ -125,20 +125,18 @@ int ui_init(Options *op) if (handle) { __ui = dlsym(handle, "ui_dispatch_table"); if (__ui && __ui->detect(op)) { - log_printf(op, TRUE, NULL, "Using: %s", - ui_list[i].descr); + log_printf(op, NULL, "Using: %s", ui_list[i].descr); __extracted_user_interface_filename = ui_list[i].filename; break; } else { - log_printf(op, TRUE, NULL, "Unable to initialize: %s", + log_printf(op, NULL, "Unable to initialize: %s", ui_list[i].descr); dlclose(handle); __ui = NULL; } } else { - log_printf(op, TRUE, NULL, "Unable to load: %s", - ui_list[i].descr); - log_printf(op, TRUE, NULL, ""); + log_printf(op, NULL, "Unable to load: %s", ui_list[i].descr); + log_printf(op, NULL, ""); } } @@ -146,7 +144,7 @@ int ui_init(Options *op) if (!__ui) { __ui = &stream_ui_dispatch_table; - log_printf(op, TRUE, NULL, "Using built-in stream user interface"); + log_printf(op, NULL, "Using built-in stream user interface"); } /* @@ -216,7 +214,7 @@ char *ui_get_input(Options *op, const char *def, const char *fmt, ...) ret = __ui->get_input(op, def, msg); tmp = nvstrcat(msg, " (Answer: '", ret, "')", NULL); } - log_printf(op, TRUE, NV_BULLET_STR, tmp); + log_printf(op, NV_BULLET_STR, tmp); nvfree(msg); nvfree(tmp); @@ -251,7 +249,7 @@ void ui_error(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); __ui->message(op, NV_MSG_LEVEL_ERROR, msg); - log_printf(op, TRUE, "ERROR: ", msg); + log_printf(op, "ERROR: ", msg); free(msg); @@ -266,7 +264,7 @@ void ui_warn(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); __ui->message(op, NV_MSG_LEVEL_WARNING, msg); - log_printf(op, TRUE, "WARNING: ", msg); + log_printf(op, "WARNING: ", msg); free(msg); @@ -282,7 +280,7 @@ void ui_message(Options *op, const char *fmt, ...) if (!op->silent) __ui->message(op, NV_MSG_LEVEL_MESSAGE, msg); - log_printf(op, TRUE, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, msg); free(msg); @@ -296,7 +294,7 @@ void ui_log(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); - log_printf(op, TRUE, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, msg); free(msg); @@ -317,7 +315,7 @@ void ui_expert(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->message(op, NV_MSG_LEVEL_LOG, msg); - log_printf(op, FALSE, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, msg); free (msg); @@ -333,7 +331,7 @@ void ui_command_output(Options *op, const char *fmt, ...) if (!op->silent) __ui->command_output(op, msg); - log_printf(op, FALSE, NV_CMD_OUT_PREFIX, "%s", msg); + log_printf(op, NV_CMD_OUT_PREFIX, "%s", msg); free(msg); @@ -387,7 +385,7 @@ int ui_yes_no (Options *op, const int def, const char *fmt, ...) tmp = nvstrcat(msg, " (Answer: ", (ret ? "Yes" : "No"), ")", NULL); } - log_printf(op, FALSE, NV_BULLET_STR, tmp); + log_printf(op, NV_BULLET_STR, tmp); nvfree(msg); nvfree(tmp); @@ -401,7 +399,7 @@ void ui_status_begin(Options *op, const char *title, const char *fmt, ...) { char *msg; - log_printf(op, TRUE, NV_BULLET_STR, title); + log_printf(op, NV_BULLET_STR, title); if (op->silent) return; @@ -434,7 +432,7 @@ void ui_status_end(Options *op, const char *fmt, ...) NV_VSNPRINTF(msg, fmt); if (!op->silent) __ui->status_end(op, msg); - log_printf(op, TRUE, NV_BULLET_STR, msg); + log_printf(op, NV_BULLET_STR, msg); free(msg); } @@ -480,7 +478,7 @@ static int extract_user_interface(Options *op, user_interface_attribute_t *ui) /* check that this ui is present in the binary */ if ((ui->data_array == NULL) || (ui->data_array_size == 0)) { - log_printf(op, TRUE, NULL, "%s: not present.", ui->descr); + log_printf(op, NULL, "%s: not present.", ui->descr); return FALSE; } @@ -490,7 +488,7 @@ static int extract_user_interface(Options *op, user_interface_attribute_t *ui) fd = mkstemp(ui->filename); if (fd == -1) { - log_printf(op, TRUE, NULL, "unable to create temporary file (%s)", + log_printf(op, NULL, "unable to create temporary file (%s)", strerror(errno)); goto failed; } @@ -498,12 +496,12 @@ static int extract_user_interface(Options *op, user_interface_attribute_t *ui) /* set the temporary file's size */ if (lseek(fd, ui->data_array_size - 1, SEEK_SET) == -1) { - log_printf(op, TRUE, NULL, "Unable to set file size for '%s' (%s)", + log_printf(op, NULL, "Unable to set file size for '%s' (%s)", ui->filename, strerror(errno)); goto failed; } if (write(fd, "", 1) != 1) { - log_printf(op, TRUE, NULL, "Unable to write file size for '%s' (%s)", + log_printf(op, NULL, "Unable to write file size for '%s' (%s)", ui->filename, strerror(errno)); goto failed; } @@ -512,7 +510,7 @@ static int extract_user_interface(Options *op, user_interface_attribute_t *ui) if ((dst = mmap(0, ui->data_array_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0)) == (void *) -1) { - log_printf(op, TRUE, NULL, "Unable to map destination file '%s' " + log_printf(op, NULL, "Unable to map destination file '%s' " "for copying (%s)", ui->filename, strerror(errno)); goto failed; } @@ -524,7 +522,7 @@ static int extract_user_interface(Options *op, user_interface_attribute_t *ui) /* unmap the temporary file */ if (munmap(dst, ui->data_array_size) == -1) { - log_printf(op, TRUE, NULL, "Unable to unmap destination file '%s' " + log_printf(op, NULL, "Unable to unmap destination file '%s' " "(%s)", ui->filename, strerror(errno)); goto failed; } diff --git a/user-interface.h b/user-interface.h index 88a68a2..bcf5dbb 100644 --- a/user-interface.h +++ b/user-interface.h @@ -27,20 +27,20 @@ #include "command-list.h" int ui_init (Options*); -void ui_set_title (Options*, const char*, ...); -char *ui_get_input (Options*, const char*, const char*, ...); +void ui_set_title (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +char *ui_get_input (Options*, const char*, const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); int ui_display_license (Options*, const char*); -void ui_error (Options*, const char*, ...); -void ui_warn (Options*, const char*, ...); -void ui_message (Options*, const char*, ...); -void ui_log (Options*, const char*, ...); -void ui_expert (Options*, const char*, ...); -void ui_command_output (Options*, const char*, ...); -int ui_approve_command_list(Options*, CommandList*,const char*, ...); -int ui_yes_no (Options*, const int, const char*, ...); -void ui_status_begin (Options*, const char*, const char*, ...); -void ui_status_update (Options*, const float, const char*, ...); -void ui_status_end (Options*, const char*, ...); +void ui_error (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void ui_warn (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void ui_message (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void ui_log (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void ui_expert (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +void ui_command_output (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); +int ui_approve_command_list(Options*, CommandList*,const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); +int ui_yes_no (Options*, const int, const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); +void ui_status_begin (Options*, const char*, const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); +void ui_status_update (Options*, const float, const char*, ...) NV_ATTRIBUTE_PRINTF(3, 4); +void ui_status_end (Options*, const char*, ...) NV_ATTRIBUTE_PRINTF(2, 3); void ui_close (Options*); #endif /* __NVIDIA_INSTALLER_USER_INTERFACE_H__ */ @@ -29,7 +29,7 @@ CC ?= gcc LD ?= ld # only set these warnings and optimizations if CFLAGS is unset -CFLAGS ?= -Wall -Wno-unused-parameter -O2 +CFLAGS ?= -Wall -O2 # always set these -f CFLAGS CFLAGS += -fno-strict-aliasing -fno-omit-frame-pointer CC_ONLY_CFLAGS ?= @@ -42,6 +42,10 @@ HOST_CFLAGS ?= $(CFLAGS) HOST_LDFLAGS ?= $(LDFLAGS) HOST_BIN_LDFLAGS ?= +# always disable warnings that will break the build +CFLAGS += -Wno-unused-parameter -Wno-format-zero-length +HOST_CFLAGS += -Wno-unused-parameter -Wno-format-zero-length + ifeq ($(DEBUG),1) STRIP_CMD ?= true CFLAGS += -O0 -g @@ -1 +1 @@ -NVIDIA_VERSION = 313.30 +NVIDIA_VERSION = 319.12 |