diff options
author | Aaron Plattner <aplattner@nvidia.com> | 2013-07-01 08:54:54 -0700 |
---|---|---|
committer | Aaron Plattner <aplattner@nvidia.com> | 2013-07-01 08:54:54 -0700 |
commit | bae15338d0f3be4553a09bb6d9dd3c702a83c457 (patch) | |
tree | 02d30b43bf1bc2fa9d97cc5a3aa9869c5012ccfe | |
parent | 45f2c99ed857988e274557857edbf494702e037e (diff) |
325.08325.08
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | command-list.c | 1 | ||||
-rw-r--r-- | common-utils/common-utils.c | 114 | ||||
-rw-r--r-- | common-utils/common-utils.h | 45 | ||||
-rw-r--r-- | crc.c | 30 | ||||
-rw-r--r-- | crc.h | 1 | ||||
-rw-r--r-- | files.c | 93 | ||||
-rw-r--r-- | files.h | 4 | ||||
-rw-r--r-- | install-from-cwd.c | 34 | ||||
-rw-r--r-- | kernel.c | 523 | ||||
-rw-r--r-- | kernel.h | 7 | ||||
-rw-r--r-- | misc.c | 98 | ||||
-rw-r--r-- | misc.h | 1 | ||||
-rw-r--r-- | mkprecompiled.c | 997 | ||||
-rw-r--r-- | nvidia-installer.1.m4 | 10 | ||||
-rw-r--r-- | nvidia-installer.c | 1 | ||||
-rw-r--r-- | nvidia-installer.h | 6 | ||||
-rw-r--r-- | option_table.h | 8 | ||||
-rw-r--r-- | precompiled.c | 826 | ||||
-rw-r--r-- | precompiled.h | 169 | ||||
-rw-r--r-- | version.mk | 2 |
21 files changed, 1962 insertions, 1011 deletions
@@ -151,7 +151,8 @@ HOST_CFLAGS += $(common_cflags) LDFLAGS += -L. LIBS += -ldl -MKPRECOMPILED_SRC = crc.c mkprecompiled.c +MKPRECOMPILED_SRC = crc.c mkprecompiled.c $(COMMON_UTILS_DIR)/common-utils.c \ + precompiled.c $(COMMON_UTILS_DIR)/nvgetopt.c MKPRECOMPILED_OBJS = $(call BUILD_OBJECT_LIST,$(MKPRECOMPILED_SRC)) MAKESELF_HELP_SCRIPT_SRC = makeself-help-script.c diff --git a/command-list.c b/command-list.c index 48506d0..bfa22dc 100644 --- a/command-list.c +++ b/command-list.c @@ -599,6 +599,7 @@ static ConflictingFileInfo __xfree86_non_opengl_libs[] = { { "libnvidia-ml.", 13, /* strlen("libnvidia-ml.") */ NULL }, { "libnvidia-encode.", 17, /* strlen("libnvidia-encode.") */ NULL }, { "libnvidia-vgx.", 14, /* strlen("libnvidia-vgx.") */ NULL }, + { "libnvidia-vgxcfg.", 17, /* strlen("libnvidia-vgxcfg.") */ NULL }, { NULL, 0, NULL } }; diff --git a/common-utils/common-utils.c b/common-utils/common-utils.c index 9b734f3..b2958a7 100644 --- a/common-utils/common-utils.c +++ b/common-utils/common-utils.c @@ -492,8 +492,6 @@ void nv_text_rows_append(TextRows *t, const char *msg) * result in t0 */ -#define NV_MAX(x,y) ((x) > (y) ? (x) : (y)) - void nv_concat_text_rows(TextRows *t0, TextRows *t1) { int n, i; @@ -707,6 +705,107 @@ char *nvstrchrnul(char *s, int c) return result; } +/****************************************************************************/ +/* file helper functions */ +/****************************************************************************/ + +/* + * nv_open() - open(2) wrapper; prints an error message if open(2) + * fails and calls exit(). This function only returns on success. + */ + +int nv_open(const char *pathname, int flags, mode_t mode) +{ + int fd; + fd = open(pathname, flags, mode); + if (fd == -1) { + fprintf(stderr, "Failure opening %s (%s).\n", + pathname, strerror(errno)); + exit(1); + } + return fd; + +} /* nv_name() */ + + + +/* + * nv_get_file_length() - stat(2) wrapper; prints an error message if + * the system call fails and calls exit(). This function only returns + * on success. + */ + +int nv_get_file_length(const char *filename) +{ + struct stat stat_buf; + int ret; + + ret = stat(filename, &stat_buf); + if (ret == -1) { + fprintf(stderr, "Unable to determine '%s' file length (%s).\n", + filename, strerror(errno)); + exit(1); + } + return stat_buf.st_size; + +} /* nv_get_file_length() */ + + + +/* + * nv_set_file_length() - wrapper for lseek() and write(); prints an + * error message if the system calls fail and calls exit(). This + * function only returns on success. + */ + +void nv_set_file_length(const char *filename, int fd, int len) +{ + if ((lseek(fd, len - 1, SEEK_SET) == -1) || + (write(fd, "", 1) == -1)) { + fprintf(stderr, "Unable to set file '%s' length %d (%s).\n", + filename, fd, strerror(errno)); + exit(1); + } +} /* nv_set_file_length() */ + + + +/* + * nv_mmap() - mmap(2) wrapper; prints an error message if mmap(2) + * fails and calls exit(). This function only returns on success. + */ + +void *nv_mmap(const char *filename, size_t len, int prot, int flags, int fd) +{ + void *ret; + + ret = mmap(0, len, prot, flags, fd, 0); + if (ret == (void *) -1) { + fprintf(stderr, "Unable to mmap file %s (%s).\n", + filename, strerror(errno)); + exit(1); + } + return ret; + +} /* nv_mmap() */ + + +/* + * nv_basename() - alternative to basename(3) which avoids differences in + * behavior from different implementations: this implementation never modifies + * the original string, and the return value can always be passed to free(3). + */ + +char *nv_basename(const char *path) +{ + char *last_slash = strrchr(path, '/'); + if (last_slash) { + return strdup(last_slash+1); + } else { + return strdup(path); + } +} + /****************************************************************************/ /* string helper functions */ @@ -741,8 +840,12 @@ char *nv_trim_space(char *string) { static char *trim_char(char *string, char trim, int *count) { int len, replaced = 0; - if (!string || trim == '\0') { - return NULL; + if (count) { + *count = 0; + } + + if (string == NULL || trim == '\0') { + return string; } if (string[0] == trim) { @@ -781,7 +884,7 @@ char *nv_trim_char(char *string, char trim) { */ char *nv_trim_char_strict(char *string, char trim) { - int count = 0; + int count; char *trimmed; trimmed = trim_char(string, trim, &count); @@ -792,3 +895,4 @@ char *nv_trim_char_strict(char *string, char trim) { return NULL; } + diff --git a/common-utils/common-utils.h b/common-utils/common-utils.h index 6dced25..92c57dd 100644 --- a/common-utils/common-utils.h +++ b/common-utils/common-utils.h @@ -19,6 +19,8 @@ #include <stdio.h> #include <stdarg.h> +#include <sys/types.h> +#include <stdint.h> #if !defined(TRUE) #define TRUE 1 @@ -30,6 +32,9 @@ #define ARRAY_LEN(_arr) (sizeof(_arr) / sizeof(_arr[0])) +#define NV_MIN(x,y) ((x) < (y) ? (x) : (y)) +#define NV_MAX(x,y) ((x) > (y) ? (x) : (y)) + #define TAB " " #define BIGTAB " " @@ -90,6 +95,12 @@ void fmt(FILE *stream, const char *prefix, const char *fmt, ...) NV_ATTRIBUTE_PR char *fget_next_line(FILE *fp, int *eof); +int nv_open(const char *pathname, int flags, mode_t mode); +int nv_get_file_length(const char *filename); +void nv_set_file_length(const char *filename, int fd, int len); +void *nv_mmap(const char *filename, size_t len, int prot, int flags, int fd); +char *nv_basename(const char *path); + char *nv_trim_space(char *string); char *nv_trim_char(char *string, char trim); char *nv_trim_char_strict(char *string, char trim); @@ -139,4 +150,38 @@ do { \ } \ } while (0) +#if defined(__GNUC__) +# define NV_INLINE __inline__ +#else +# define NV_INLINE +#endif + +/* + * Simple function which encodes a version number, given as major, minor, micro, + * and nano, as a 64-bit unsigned integer. This is defined in an inline function + * rather than as a macro for convenience so it can be examined by the debugger. + * Encoded version numbers can be compared directly in version checks. + */ +static NV_INLINE uint64_t nv_encode_version(unsigned int major, + unsigned int minor, + unsigned int micro, + unsigned int nano) +{ + return (((uint64_t)(nano & 0xFFFF)) | + (((uint64_t)(micro & 0xFFFF)) << 16) | + (((uint64_t)(minor & 0xFFFF)) << 32) | + (((uint64_t)(major & 0xFFFF)) << 48)); +} + +/* + * Wrapper macros for nv_encode_version(). For K in {2,3,4}, NV_VERSIONK() takes + * a K-part version number. + */ +#define NV_VERSION2(major, minor) \ + nv_encode_version(major, minor, 0, 0) +#define NV_VERSION3(major, minor, micro) \ + nv_encode_version(major, minor, micro, 0) +#define NV_VERSION4(major, minor, micro, nano) \ + nv_encode_version(major, minor, micro, nano) + #endif /* __COMMON_UTILS_H__ */ @@ -69,15 +69,12 @@ static uint32 crc_init(uint32 crc) -uint32 compute_crc(Options *op, const char *filename) +uint32 compute_crc_from_buffer(const uint8 *buf, int len) { uint32 cword = ~0; static uint32 *crctab = NULL; - uint8 *buf; - int i, fd; - struct stat stat_buf; - size_t len; - + int i; + if (!crctab) { crctab = (uint32 *) nvalloc(sizeof(uint32) * 256); for (i=0; i < 256; i++) { @@ -85,6 +82,23 @@ uint32 compute_crc(Options *op, const char *filename) } } + for (i = 0; i < len; i++) { + cword = crctab[buf[i] ^ (cword >> 24)] ^ (cword << 8); + } + + return cword; +} + + + +uint32 compute_crc(Options *op, const char *filename) +{ + uint32 cword = ~0; + uint8 *buf; + int fd; + struct stat stat_buf; + size_t len; + if ((fd = open(filename, O_RDONLY)) == -1) goto fail; if (fstat(fd, &stat_buf) == -1) goto fail; @@ -94,9 +108,7 @@ uint32 compute_crc(Options *op, const char *filename) buf = mmap(0, len, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); if (buf == (void *) -1) goto fail; - for (i = 0; i < len; i++) { - cword = crctab[buf[i] ^ (cword >> 24)] ^ (cword << 8); - } + cword = compute_crc_from_buffer(buf, len); if (munmap(buf, len) == -1) goto fail; if (close(fd) == -1) goto fail; @@ -21,6 +21,7 @@ #ifndef __NVIDIA_INSTALLER_CRC_H__ #define __NVIDIA_INSTALLER_CRC_H__ +uint32 compute_crc_from_buffer(const uint8 *buf, int len); uint32 compute_crc(Options *op, const char *filename); #endif /* __NVIDIA_INSTALLER_CRC_H__ */ @@ -1592,16 +1592,19 @@ int copy_directory_contents(Options *op, const char *src, const char *dst) /* - * pack_precompiled_kernel_interface() - + * pack_precompiled_files() - Create a new precompiled files package for the + * given PrecompiledFileInfo array and save it to disk. */ -int pack_precompiled_kernel_interface(Options *op, Package *p) +int pack_precompiled_files(Options *op, Package *p, int num_files, + PrecompiledFileInfo *files) { - char *cmd, time_str[256], *proc_version_string, *suffix, *file, *newfile; - char *result, *descr; + char time_str[256], *proc_version_string; + char *outfile, *descr; time_t t; struct utsname buf; int ret; + PrecompiledInfo *info; ui_log(op, "Packaging precompiled kernel interface."); @@ -1616,7 +1619,7 @@ int pack_precompiled_kernel_interface(Options *op, Package *p) /* read the proc version string */ - proc_version_string = read_proc_version(op); + proc_version_string = read_proc_version(op, op->proc_mount_point); /* use the uname string as the description */ @@ -1625,74 +1628,36 @@ int pack_precompiled_kernel_interface(Options *op, Package *p) buf.release, " ", buf.version, " ", buf.machine, NULL); - - /* 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, suffix, - " --description=\"", descr, "\"", - " --proc-version=\"", proc_version_string, "\"", - " --version=", p->version, NULL); - - /* execute the command */ - - ret = run_command(op, cmd, &result, FALSE, 0, TRUE); - - nvfree(cmd); - nvfree(proc_version_string); - nvfree(descr); - /* pack the detached signature and checksum, if they exist */ + /* build the PrecompiledInfo struct */ - 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); - } + info = nvalloc(sizeof(PrecompiledInfo)); - nvfree(file); + outfile = nvstrcat(p->precompiled_kernel_interface_directory, "/", + PRECOMPILED_PACKAGE_FILENAME, "-", p->version, + ".", time_str, NULL); - file = nvstrcat(p->kernel_module_build_directory, "/", - KERNEL_MODULE_CHECKSUM_FILENAME, NULL); + info->version = nvstrdup(p->version); + info->proc_version_string = proc_version_string; + info->description = descr; + info->num_files = num_files; + info->files = files; - if (access(file, R_OK) == 0) { - newfile = nvstrcat(p->precompiled_kernel_interface_directory, "/", - KERNEL_MODULE_CHECKSUM_FILENAME, suffix, NULL); - rename(file, newfile); - nvfree(newfile); - } + ret = precompiled_pack(info, outfile); - nvfree(file); + nvfree(outfile); + free_precompiled(info); - /* remove the old kernel interface file */ - - file = nvstrcat(p->kernel_module_build_directory, "/", - PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); - - unlink(file); /* XXX what to do if this fails? */ - - nvfree(file); - - if (ret != 0) { - ui_error(op, "Unable to package precompiled kernel interface: %s", - result); + if (ret) { + return TRUE; + } + else { + /* XXX precompiled_pack() never fails */ + ui_error(op, "Unable to package precompiled kernel interface."); + return FALSE; } - nvfree(result); - - if (ret == 0) return TRUE; - else return FALSE; - -} /* pack_kernel_interface() */ +} @@ -21,6 +21,7 @@ #define __NVIDIA_INSTALLER_FILES_H__ #include "nvidia-installer.h" +#include "precompiled.h" int remove_directory(Options *op, const char *victim); int touch_directory(Options *op, const char *victim); @@ -54,7 +55,8 @@ char *make_tmpdir(Options *op); int nvrename(Options *op, const char *src, const char *dst); int check_for_existing_rpms(Options *op); int copy_directory_contents(Options *op, const char *src, const char *dst); -int pack_precompiled_kernel_interface(Options *op, Package *p); +int pack_precompiled_files(Options *op, Package *p, int num_files, + PrecompiledFileInfo *files); char *process_template_file(Options *op, PackageEntry *pe, char **tokens, char **replacements); diff --git a/install-from-cwd.c b/install-from-cwd.c index c2d9a0d..9803a0f 100644 --- a/install-from-cwd.c +++ b/install-from-cwd.c @@ -127,6 +127,14 @@ int install_from_cwd(Options *op) if (!check_for_existing_driver(op, p)) goto exit_install; + /* + * check to see if an alternate method of installation is already installed + * or is available, but not installed; ask the user if they really want to + * install anyway despite the presence/availability of an alternate install. + */ + + if (!check_for_alternate_install(op)) goto exit_install; + /* run the distro preinstall hook */ if (!run_distro_hook(op, "pre-install")) { @@ -421,9 +429,15 @@ static int install_kernel_module(Options *op, Package *p) * then there is something pretty seriously wrong... better to * abort. */ - - if (!link_kernel_module(op, p, p->kernel_module_build_directory, - precompiled_info)) return FALSE; + + int i; + + for (i = 0; i < precompiled_info->num_files; i++) { + if (!link_kernel_module(op, p, p->kernel_module_build_directory, + &(precompiled_info->files[i]))) { + return FALSE; + } + } } else { /* @@ -480,6 +494,8 @@ static int install_kernel_module(Options *op, Package *p) int add_this_kernel(Options *op) { Package *p; + PrecompiledFileInfo *fileInfos; + int num_files; /* parse the manifest */ @@ -489,13 +505,15 @@ int add_this_kernel(Options *op) if (!determine_kernel_source_path(op, p)) goto failed; - /* build the precompiled kernel interface */ + /* build the precompiled files */ - if (!build_kernel_interface(op, p)) goto failed; + num_files = build_kernel_interface(op, p, &fileInfos); + if (!num_files) goto failed; - /* pack the precompiled kernel interface */ + /* pack the precompiled files */ - if (!pack_precompiled_kernel_interface(op, p)) goto failed; + if (!pack_precompiled_files(op, p, num_files, fileInfos)) + goto failed; free_package(p); @@ -1096,7 +1114,7 @@ guess_fail: cmdline = nvstrcat("cd ", p->kernel_module_build_directory, "; ", op->utils[OPENSSL], " req -new -x509 -newkey " - "rsa:2048 -days 7300 -nodes -sha256 -subj " + "rsa:2048 -days 7300 -nodes -subj " "\"/CN=nvidia-installer generated signing key/\"" " -keyout " SECKEY_NAME " -outform DER -out " PUBKEY_NAME, " -", x509_hash, NULL); @@ -48,6 +48,7 @@ static char *default_kernel_module_installation_path(Options *op); static char *default_kernel_source_path(Options *op); +static char *find_module_substring(char *string, const char *substring); static int check_for_loaded_kernel_module(Options *op, const char *); static void check_for_warning_messages(Options *op); static int rmmod_kernel_module(Options *op, const char *); @@ -55,10 +56,10 @@ static PrecompiledInfo *download_updated_kernel_interface(Options*, Package*, const char*); static int fbdev_check(Options *op, Package *p); static int xen_check(Options *op, Package *p); +static int preempt_rt_check(Options *op, Package *p); static PrecompiledInfo *scan_dir(Options *op, Package *p, const char *directory_name, - const char *output_filename, const char *proc_version_string); static char *build_distro_precompiled_kernel_interface_dir(Options *op); @@ -170,6 +171,9 @@ static int run_conftest(Options *op, Package *p, const char *args, char **result char *CC, *cmd, *arch; int ret; + if (result) + *result = NULL; + arch = get_machine_arch(op); if (!arch) return FALSE; @@ -186,6 +190,7 @@ static int run_conftest(Options *op, Package *p, const char *args, char **result nvfree(cmd); return ret == 0; + } /* run_conftest() */ @@ -418,7 +423,7 @@ int determine_kernel_output_path(Options *op) * the linked module and append the signature. */ static int attach_signature(Options *op, Package *p, - const PrecompiledInfo *info) { + const PrecompiledFileInfo *fileInfo) { uint32 actual_crc; char *module_filename; int ret = FALSE, command_ret; @@ -428,33 +433,24 @@ static int attach_signature(Options *op, Package *p, 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); + command_ret = verify_crc(op, module_filename, fileInfo->linked_module_crc, + &actual_crc); if (command_ret) { - FILE *module_file, *signature_file; + FILE *module_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; - } + if (module_file && fileInfo->signature_size) { + command_ret = fwrite(fileInfo->signature, 1, + fileInfo->signature_size, module_file); + if (command_ret != fileInfo->signature_size) { + goto attach_done; } - ret = feof(signature_file) && - !ferror(signature_file) && - !ferror(module_file); - - op->kernel_module_signed = ret; + op->kernel_module_signed = ret = !ferror(module_file); attach_done: fclose(module_file); - fclose(signature_file); } else { ret = ui_yes_no(op, FALSE, "A detached signature was included with the " @@ -475,7 +471,8 @@ attach_done: "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); + "kernel module?", actual_crc, + fileInfo->linked_module_crc); } if (ret) { @@ -504,18 +501,29 @@ attach_done: */ int link_kernel_module(Options *op, Package *p, const char *build_directory, - const PrecompiledInfo *info) + const PrecompiledFileInfo *fileInfo) { char *cmd, *result; int ret; - + uint32 attrmask; + + if (fileInfo->type != PRECOMPILED_FILE_TYPE_INTERFACE) { + ui_error(op, "The file does not appear to be a valid precompiled " + "kernel interface."); + return FALSE; + } + + ret = precompiled_file_unpack(op, fileInfo, build_directory); + if (!ret) { + ui_error(op, "Failed to unpack the precompiled interface."); + return FALSE; + } + p->kernel_module_filename = guess_kernel_module_filename(op); - cmd = nvstrcat("cd ", build_directory, "; ", op->utils[LD], - " ", LD_OPTIONS, - " -o ", p->kernel_module_filename, - " ", PRECOMPILED_KERNEL_INTERFACE_FILENAME, - " nv-kernel.o", NULL); + cmd = nvstrcat("cd ", build_directory, "; ", op->utils[LD], " ", + LD_OPTIONS, " -o ", fileInfo->linked_module_name, " ", + fileInfo->name, " ", fileInfo->core_object_name, NULL); ret = run_command(op, cmd, &result, TRUE, 0, TRUE); @@ -528,8 +536,11 @@ int link_kernel_module(Options *op, Package *p, const char *build_directory, ui_log(op, "Kernel module linked successfully."); - if (info && info->detached_signature) { - return attach_signature(op, p, info); + attrmask = PRECOMPILED_ATTR(DETACHED_SIGNATURE) | + PRECOMPILED_ATTR(LINKED_MODULE_CRC); + + if ((fileInfo->attributes & attrmask) == attrmask) { + return attach_signature(op, p, fileInfo); } return TRUE; @@ -562,13 +573,15 @@ int build_kernel_module(Options *op, Package *p) ret = run_conftest(op, p, "select_makefile just_msg", &result); if (!ret) { - ui_error(op, "%s", result); /* display conftest.sh's error message */ + if (result) + ui_error(op, "%s", result); /* display conftest.sh's error message */ nvfree(result); return FALSE; } if (!fbdev_check(op, p)) return FALSE; if (!xen_check(op, p)) return FALSE; + if (!preempt_rt_check(op, p)) return FALSE; cmd = nvstrcat("cd ", p->kernel_module_build_directory, "; make print-module-filename", @@ -687,67 +700,22 @@ int sign_kernel_module(Options *op, const char *build_directory, int status) { /* - * 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. + * create_detached_signature() - Link a precompiled interface into a module, + * sign the resulting linked module, and store a CRC for the linked, unsigned + * module and the detached signature in the provided PrecompiledFileInfo record. */ static int create_detached_signature(Options *op, Package *p, - const char *build_dir) + const char *build_dir, + PrecompiledFileInfo *fileInfo) { int ret, command_ret; struct stat st; - FILE *checksum_file; - char *module_path = NULL, *tmp_path = NULL, *error = NULL, *dstfile = NULL; + char *module_path = NULL, *error = 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); + ret = link_kernel_module(op, p, build_dir, fileInfo); if (!ret) { ui_error(op, "Failed to link a kernel module for signing."); @@ -765,33 +733,8 @@ static int create_detached_signature(Options *op, Package *p, 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; - } + fileInfo->linked_module_crc = compute_crc(op, module_path); + fileInfo->attributes |= PRECOMPILED_ATTR(LINKED_MODULE_CRC); ui_status_update(op, .50, "Signing linked module"); @@ -804,24 +747,15 @@ static int create_detached_signature(Options *op, Package *p, 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); + fileInfo->signature_size = byte_tail(module_path, st.st_size, + &(fileInfo->signature)); - if (!ret) { + if (!(fileInfo->signature) || fileInfo->signature_size == 0) { 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; - } + fileInfo->attributes |= PRECOMPILED_ATTR(DETACHED_SIGNATURE); done: if (ret) { @@ -833,8 +767,6 @@ done: } } - nvfree(dstfile); - nvfree(tmp_path); nvfree(module_path); return ret; } /* create_detached_signature() */ @@ -842,28 +774,30 @@ done: /* - * build_kernel_interface() - build the kernel interface, and place it - * here: - * - * "%s/%s", p->kernel_module_build_directory, - * PRECOMPILED_KERNEL_INTERFACE_FILENAME + * build_kernel_interface() - build the kernel interface(s), and store any + * built interfaces in a newly allocated PrecompiledFileInfo array, a pointer + * to which is passed back to the caller. Return the number of packaged + * interface files, or 0 on error. * * This is done by copying the sources to a temporary working - * directory, building, and copying the kernel interface back to the - * kernel module source directory. The tmpdir is removed when - * complete. + * directory and building the kernel interface in that directory. + * The tmpdir is removed when complete. * * XXX this and build_kernel_module() should be merged. + * XXX for multi-RM the dispatch module should be compiled separately, then + * packaged whole with file type PRECOMPILED_FILE_TYPE_MODULE. */ -int build_kernel_interface(Options *op, Package *p) +int build_kernel_interface(Options *op, Package *p, + PrecompiledFileInfo ** fileInfos) { char *tmpdir = NULL; char *cmd = NULL; - char *kernel_interface = NULL; char *dstfile = NULL; - int ret = FALSE; - int command_ret; + int files_packaged = 0, command_ret, i; + const int num_files = 1; /* XXX multi-RM */ + + *fileInfos = NULL; /* create a temporary directory */ @@ -892,60 +826,81 @@ int build_kernel_interface(Options *op, Package *p) touch_directory(op, p->kernel_module_build_directory); - /* build the kernel interface */ + *fileInfos = nvalloc(sizeof(PrecompiledFileInfo) * num_files); - ui_status_begin(op, "Building kernel interface:", "Building"); - - cmd = nvstrcat("cd ", tmpdir, "; make ", p->kernel_interface_filename, - " SYSSRC=", op->kernel_source_path, NULL); - - command_ret = run_command(op, cmd, NULL, TRUE, 25 /* XXX */, TRUE); + for (i = 0; i < num_files; i++) { + char *kernel_interface, *kernel_module_filename; + PrecompiledFileInfo *fileInfo = *fileInfos + i; - if (command_ret != 0) { - ui_status_end(op, "Error."); - ui_error(op, "Unable to build the NVIDIA kernel module interface."); - /* XXX need more descriptive error message */ - goto failed; - } - - /* check that the file exists */ + /* build the kernel interface */ - kernel_interface = nvstrcat(tmpdir, "/", - p->kernel_interface_filename, NULL); - - if (access(kernel_interface, F_OK) == -1) { - ui_status_end(op, "Error."); - ui_error(op, "The NVIDIA kernel module interface was not created."); - goto failed; - } + ui_status_begin(op, "Building kernel interface:", "Building (%d/%d)", + i + 1, num_files); - ui_status_end(op, "done."); + cmd = nvstrcat("cd ", tmpdir, "; make ", p->kernel_interface_filename, + " SYSSRC=", op->kernel_source_path, NULL); - ui_log(op, "Kernel module interface compilation complete."); + command_ret = run_command(op, cmd, NULL, TRUE, 25 /* XXX */, TRUE); - /* copy the kernel interface from the tmpdir back to the srcdir */ - - dstfile = nvstrcat(p->kernel_module_build_directory, "/", - PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); + if (command_ret != 0) { + ui_status_end(op, "Error."); + ui_error(op, "Unable to build the NVIDIA kernel module interface."); + /* XXX need more descriptive error message */ + goto failed; + } - if (!copy_file(op, kernel_interface, dstfile, 0644)) goto failed; + /* check that the file exists */ - if (op->module_signing_secret_key && op->module_signing_public_key) { - ret = create_detached_signature(op, p, tmpdir); - } else { - ret = TRUE; + kernel_interface = nvstrcat(tmpdir, "/", + p->kernel_interface_filename, NULL); + + if (access(kernel_interface, F_OK) == -1) { + ui_status_end(op, "Error."); + ui_error(op, "The NVIDIA kernel module interface was not created."); + nvfree(kernel_interface); + goto failed; + } + + ui_status_end(op, "done."); + + ui_log(op, "Kernel module interface compilation complete."); + + /* add the kernel interface to the list of files to be packaged */ + + kernel_module_filename = guess_kernel_module_filename(op); + command_ret = precompiled_read_interface(fileInfo, kernel_interface, + kernel_module_filename, + "nv-kernel.o"); + nvfree(kernel_interface); + nvfree(kernel_module_filename); + + if (command_ret) { + if (op->module_signing_secret_key && op->module_signing_public_key) { + if (!create_detached_signature(op, p, tmpdir, fileInfo)) { + goto failed; + } + } + files_packaged++; + } else { + goto failed; + } } - - failed: - - remove_directory(op, tmpdir); - if (tmpdir) nvfree(tmpdir); +failed: + + if (files_packaged == 0) { + nvfree(*fileInfos); + *fileInfos = NULL; + } + + if (tmpdir) { + remove_directory(op, tmpdir); + nvfree(tmpdir); + } if (cmd) nvfree(cmd); - if (kernel_interface) nvfree(kernel_interface); if (dstfile) nvfree(dstfile); - return ret; + return files_packaged; } /* build_kernel_interface() */ @@ -1470,7 +1425,7 @@ int check_for_unloaded_kernel_module(Options *op, Package *p) PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) { - char *proc_version_string, *output_filename, *tmp; + char *proc_version_string, *tmp; PrecompiledInfo *info = NULL; /* allow the user to completely skip this search */ @@ -1482,7 +1437,7 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) /* retrieve the proc version string for the running kernel */ - proc_version_string = read_proc_version(op); + proc_version_string = read_proc_version(op, op->proc_mount_point); if (!proc_version_string) goto failed; @@ -1491,11 +1446,6 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (!mkdir_recursive(op, p->kernel_module_build_directory, 0755)) goto failed; - /* build the output filename */ - - output_filename = nvstrcat(p->kernel_module_build_directory, "/", - PRECOMPILED_KERNEL_INTERFACE_FILENAME, NULL); - /* * if the --precompiled-kernel-interfaces-path option was * specified, search that directory, first @@ -1503,7 +1453,7 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (op->precompiled_kernel_interfaces_path) { info = scan_dir(op, p, op->precompiled_kernel_interfaces_path, - output_filename, proc_version_string); + proc_version_string); } /* @@ -1514,7 +1464,7 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (!info) { tmp = build_distro_precompiled_kernel_interface_dir(op); if (tmp) { - info = scan_dir(op, p, tmp, output_filename, proc_version_string); + info = scan_dir(op, p, tmp, proc_version_string); nvfree(tmp); } } @@ -1528,7 +1478,7 @@ PrecompiledInfo *find_precompiled_kernel_interface(Options *op, Package *p) if (!info) { info = scan_dir(op, p, p->precompiled_kernel_interface_directory, - output_filename, proc_version_string); + proc_version_string); } /* @@ -1807,6 +1757,74 @@ static char *default_kernel_source_path(Options *op) /* + * find_module_substring() - find substring in a given string where differences + * between hyphens and underscores are ignored. Returns a pointer to the + * beginning of the substring, or NULL if the string/substring is NULL, or if + * length of substring is greater than length of string, or substring is not + * found. + */ + +static char *find_module_substring(char *string, const char *substring) +{ + int string_len, substring_len, len; + char *tstr; + const char *tsubstr; + + if ((string == NULL) || (substring == NULL)) + return NULL; + + string_len = strlen(string); + substring_len = strlen(substring); + + for (len = 0; len <= string_len - substring_len; len++, string++) { + if (*string != *substring) { + continue; + } + + for (tstr = string, tsubstr = substring; + *tsubstr != '\0'; + tstr++, tsubstr++) { + if (*tstr != *tsubstr) { + if (((*tstr == '-') || (*tstr == '_')) && + ((*tsubstr == '-') || (*tsubstr == '_'))) + continue; + break; + } + } + + if (*tsubstr == '\0') + return string; + } + + return NULL; +} /* find_module_substring */ + + +/* + * substring_is_isolated() - given the string 'substring' with length 'len', + * which points to a location inside the string 'string', check to see if + * 'substring' is surrounded by either whitespace or the start/end of 'string' + * on both ends. + */ + +static int substring_is_isolated(const char *substring, const char *string, + int len) +{ + if (substring != string) { + if (!isspace(substring[-1])) { + return FALSE; + } + } + + if (substring[len] && !isspace(substring[len])) { + return FALSE; + } + + return TRUE; +} + + +/* * check_for_loaded_kernel_module() - check if the specified kernel * module is currently loaded using `lsmod`. Returns TRUE if the * kernel module is loaded; FALSE if it is not. @@ -1818,24 +1836,28 @@ static char *default_kernel_source_path(Options *op) static int check_for_loaded_kernel_module(Options *op, const char *module_name) { - char *ptr, *result = NULL; - int ret; + char *result = NULL; + int ret, found = FALSE; ret = run_command(op, op->utils[LSMOD], &result, FALSE, 0, TRUE); if ((ret == 0) && (result) && (result[0] != '\0')) { - ptr = strstr(result, module_name); - if (ptr) { - ptr += strlen(module_name); - if(!isspace(*ptr)) ret = 1; - } else { - ret = 1; + char *ptr; + int len = strlen(module_name); + + for (ptr = result; + (ptr = find_module_substring(ptr, module_name)); + ptr += len) { + if (substring_is_isolated(ptr, result, len)) { + found = TRUE; + break; + } } } if (result) free(result); - return ret ? FALSE : TRUE; + return found; } /* check_for_loaded_kernel_module() */ @@ -1875,17 +1897,15 @@ download_updated_kernel_interface(Options *op, Package *p, { int fd = -1; int dst_fd = -1; - int length; + int length, i; char *url = NULL; char *tmpfile = NULL; char *dstfile = NULL; char *buf = NULL; - char *output_filename = NULL; char *str = (void *) -1; char *ptr, *s; struct stat stat_buf; PrecompiledInfo *info = NULL; - uint32 crc; /* initialize the tmpfile and url strings */ @@ -1939,7 +1959,7 @@ download_updated_kernel_interface(Options *op, Package *p, s += 3; /* skip past the ":::" separator */ if (strcmp(proc_version_string, s) == 0) { - + /* proc versions strings match */ /* @@ -1978,33 +1998,34 @@ download_updated_kernel_interface(Options *op, Package *p, /* XXX once we have gpg setup, should check the file here */ - /* build the output filename string */ - - output_filename = nvstrcat(p->kernel_module_build_directory, "/", - PRECOMPILED_KERNEL_INTERFACE_FILENAME, - NULL); - - /* unpack the downloaded file */ - - info = precompiled_unpack(op, dstfile, output_filename, - proc_version_string, - p->version); - - /* compare checksums */ - - crc = compute_crc(op, output_filename); - - if (info && (info->crc != crc)) { - ui_error(op, "The embedded checksum of the downloaded file " - "'%s' (%" PRIu32 ") does not match the computed " - "checksum (%" PRIu32 "); not using.", buf, info->crc, - crc); - unlink(dstfile); + info = get_precompiled_info(op, dstfile, proc_version_string, + p->version); + + if (!info) { + ui_error(op, "The format of the downloaded precompiled package " + "is invalid!"); free_precompiled(info); info = NULL; } + /* compare checksums */ + + for (i = 0; info && i < info->num_files; i++) { + uint32 crc = compute_crc_from_buffer(info->files[i].data, + info->files[i].size); + if (info->files[i].crc != crc) { + ui_error(op, "The embedded checksum of the file %s in the " + "downloaded precompiled pacakge '%s' (%" PRIu32 + ") does not match the computed checksum (%" + PRIu32 "); not using.", info->files[i].name, + buf, info->files[i].crc, crc); + free_precompiled(info); + info = NULL; + } + } + goto done; + } nvfree(buf); @@ -2097,7 +2118,8 @@ static int fbdev_check(Options *op, Package *p) ret = run_conftest(op, p,"rivafb_sanity_check just_msg", &result); if (!ret) { - ui_error(op, "%s", result); + if (result) + ui_error(op, "%s", result); nvfree(result); return FALSE; @@ -2108,7 +2130,8 @@ static int fbdev_check(Options *op, Package *p) ret = run_conftest(op, p,"nvidiafb_sanity_check just_msg", &result); if (!ret) { - ui_error(op, "%s", result); + if (result) + ui_error(op, "%s", result); nvfree(result); return FALSE; @@ -2135,7 +2158,8 @@ static int xen_check(Options *op, Package *p) ret = run_conftest(op, p,"xen_sanity_check just_msg", &result); if (!ret) { - ui_error(op, "%s", result); + if (result) + ui_error(op, "%s", result); nvfree(result); return FALSE; @@ -2148,13 +2172,41 @@ static int xen_check(Options *op, Package *p) /* + * preempt_rt_check() - run the preempt_rt_sanity_check conftest; if this + * test fails, print the test's error message and abort the driver + * installation. + */ + +static int preempt_rt_check(Options *op, Package *p) +{ + char *result; + int ret; + + ui_log(op, "Performing PREEMPT_RT check."); + + ret = run_conftest(op, p, "preempt_rt_sanity_check just_msg", &result); + + if (!ret) { + if (result) + ui_error(op, "%s", result); + nvfree(result); + + return FALSE; + } + + return TRUE; + +} /* preempt_rt_check() */ + + + +/* * scan_dir() - scan through the specified directory for a matching * precompiled kernel interface. */ static PrecompiledInfo *scan_dir(Options *op, Package *p, const char *directory_name, - const char *output_filename, const char *proc_version_string) { DIR *dir; @@ -2173,21 +2225,18 @@ static PrecompiledInfo *scan_dir(Options *op, Package *p, */ while ((ent = readdir(dir)) != NULL) { - + if (((strcmp(ent->d_name, ".")) == 0) || - ((strcmp(ent->d_name, "..")) == 0) || - strstr(ent->d_name, DETACHED_SIGNATURE_FILENAME)) continue; + ((strcmp(ent->d_name, "..")) == 0)) continue; filename = nvstrcat(directory_name, "/", ent->d_name, NULL); - info = precompiled_unpack(op, filename, output_filename, - proc_version_string, - p->version); - - if (info) break; - + info = get_precompiled_info(op, filename, proc_version_string, + p->version); + free(filename); - filename = NULL; + + if (info) break; } if (closedir(dir) != 0) { @@ -33,11 +33,12 @@ 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 *); + const char *, + const PrecompiledFileInfo *); int check_cc_version (Options*, Package*); int build_kernel_module (Options*, Package*); -int build_kernel_interface (Options*, Package*); +int build_kernel_interface (Options*, Package*, + PrecompiledFileInfo **); int test_kernel_module (Options*, Package*); int load_kernel_module (Options*, Package*); int check_for_unloaded_kernel_module (Options*, Package*); @@ -447,8 +447,7 @@ int read_text_file(const char *filename, char **buf) if (!fp) return FALSE; - while (((line = fget_next_line(fp, &eof)) - != NULL) && !eof) { + while (((line = fget_next_line(fp, &eof)) != NULL)) { if ((index + strlen(line) + 1) > buflen) { buflen += 2 * strlen(line); tmpbuf = (char *)nvalloc(buflen); @@ -466,6 +465,10 @@ int read_text_file(const char *filename, char **buf) index += sprintf(*buf + index, "%s\n", line); nvfree(line); + + if (eof) { + break; + } } fclose(fp); @@ -2391,6 +2394,9 @@ int run_nvidia_xconfig(Options *op, int restore) } /* run_nvidia_xconfig() */ + +#define DISTRO_HOOK_DIRECTORY "/usr/lib/nvidia/" + /* * run_distro_hook() - run a distribution-provided hook script */ @@ -2398,7 +2404,7 @@ int run_nvidia_xconfig(Options *op, int restore) int run_distro_hook(Options *op, const char *hook) { int ret, status, shouldrun = op->run_distro_scripts; - char *cmd = nvstrcat("/usr/lib/nvidia/", hook, NULL); + char *cmd = nvstrcat(DISTRO_HOOK_DIRECTORY, hook, NULL); if (op->kernel_module_only) { ui_expert(op, @@ -2443,6 +2449,92 @@ done: } +/* + * prompt_for_user_cancel() - print a caller-supplied message and ask the + * user whether to cancel the installation. If the file at the caller-supplied + * path is readable, include any text from that file as additional detail for + * the message. Returns TRUE if the user decides to cancel the installation; + * returns FALSE if the user decides not to cancel. + */ +static int prompt_for_user_cancel(Options *op, const char *file, + int default_cancel, const char *text) +{ + int ret, file_read; + char *message = NULL; + + file_read = read_text_file(file, &message); + + if (!file_read || !message) { + message = nvstrdup(""); + } + + ret = ui_yes_no(op, default_cancel, + "%s\n\n%s\nWould you like to cancel this installation?", + text, message); + nvfree(message); + + return ret; +} + +#define INSTALL_PRESENT_FILE "alternate-install-present" +#define INSTALL_AVAILABLE_FILE "alternate-install-available" + +/* + * check_for_alternate_install() - check to see if an alternate install is + * available or present. If present, recommend updating via the alternate + * mechanism or uninstalling first before proceeding with an nvidia-installer + * installation; if available, but not present, inform the user about it. + * Returns TRUE if no alternate installation is available or present, or if + * checking for alternate installs is skipped, or if the user decides not to + * cancel the installation. Returns FALSE if the user decides to cancel the + * installation. + */ + +int check_for_alternate_install(Options *op) +{ + int shouldcheck = op->check_for_alternate_installs; + const char *alt_inst_present = DISTRO_HOOK_DIRECTORY INSTALL_PRESENT_FILE; + const char *alt_inst_avail = DISTRO_HOOK_DIRECTORY INSTALL_AVAILABLE_FILE; + + if (op->expert) { + shouldcheck = ui_yes_no(op, shouldcheck, + "Check for the availability or presence of " + "alternate driver installs?"); + } + + if (!shouldcheck) { + return TRUE; + } + + if (access(alt_inst_present, F_OK) == 0) { + const char *msg; + + msg = "The NVIDIA driver appears to have been installed previously " + "using a different installer. To prevent potential conflicts, it " + "is recommended either to update the existing installation using " + "the same mechanism by which it was originally installed, or to " + "uninstall the existing installation before installing this " + "driver."; + + return !prompt_for_user_cancel(op, alt_inst_present, TRUE, msg); + } + + if (access(alt_inst_avail, F_OK) == 0) { + const char *msg; + + msg = "An alternate method of installing the NVIDIA driver was " + "detected. (This is usually a package provided by your " + "distributor.) A driver installed via that method may integrate " + "better with your system than a driver installed by " + "nvidia-installer."; + + return !prompt_for_user_cancel(op, alt_inst_avail, FALSE, msg); + } + + return TRUE; +} + + /* * Determine if the nouveau driver is currently in use. We do the @@ -61,6 +61,7 @@ int check_for_modular_xorg(Options *op); int check_for_nvidia_graphics_devices(Options *op, Package *p); int run_nvidia_xconfig(Options *op, int restore); int run_distro_hook(Options *op, const char *hook); +int check_for_alternate_install(Options *op); 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); diff --git a/mkprecompiled.c b/mkprecompiled.c index 0c75a9f..3a4d3ca 100644 --- a/mkprecompiled.c +++ b/mkprecompiled.c @@ -20,32 +20,12 @@ * There is nothing specific to the NVIDIA graphics driver in this * program, so it should be usable for the nforce drivers, for * example. - * - * The format of a precompiled kernel interface package is: - * - * the first 8 bytes are: "NVIDIA " - * - * the next 4 bytes (unsigned) are: CRC of the kernel interface module - * - * the next 4 bytes (unsigned) are: the length of the version string (v) - * - * the next v bytes are the version string - * - * the next 4 bytes (unsigned) are: the length of the description (n) - * - * the next n bytes are the description - * - * the next 4 bytes (unsigned) are: the length of the proc version string (m) - * - * the next m bytes are the proc version string - * - * the rest of the file is the kernel interface module */ #define BINNAME "mkprecompiled" #define NV_LINE_LEN 256 #define NV_VERSION_LEN 4096 -#define PROC_VERSION "/proc/version" +#define PROC_MOUNT_POINT "/proc" #include <stdio.h> #include <string.h> @@ -58,67 +38,67 @@ #include <sys/mman.h> #include <ctype.h> #include <stdarg.h> +#include <inttypes.h> -#define _GNU_SOURCE /* XXX not portable */ -#include <getopt.h> - -#define CONSTANT_LENGTH (8 + 4 + 4 + 4 + 4) +#include <nvgetopt.h> typedef unsigned int uint32; typedef unsigned char uint8; +enum { + PACK = 'p', + UNPACK = 'u', + INFO = 'i', + MATCH = 'm', +}; + /* * Options structure */ typedef struct { - char *interface; - char *output; - char *unpack; + int action; + char *package_file; + char *output_directory; char *description; char *proc_version_string; + char *proc_mount_point; char *version; - uint8 info; - uint8 match; + int num_files; + struct __precompiled_file_info *new_files; + struct __precompiled_info *package; } Options; +#include "common-utils.h" #include "crc.h" +#include "precompiled.h" + /* - * nv_alloc() - malloc wrapper that checks for errors, and zeros out - * the memory; if an error occurs, an error is printed to stderr and - * exit() is called -- this function will only return on success. + * XXX hack to resolve symbols used by crc.c and precompiled.c */ -static void *nv_alloc (size_t size) -{ - void *m = malloc (size); - - if (!m) { - fprintf (stderr, "%s: memory allocation failure\n", BINNAME); - exit (1); - } - memset (m, 0, size); - return (m); - -} /* nv_alloc() */ +void ui_warn(Options *op, const char *fmt, ...); +void ui_warn(Options *op, const char *fmt, ...) +{ + va_list ap; -/* - * XXX hack to resolve symbols used by crc.c - */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} -void *nvalloc(size_t size); +void ui_expert(Options *op, const char *fmt, ...); -void *nvalloc(size_t size) +void ui_expert(Options *op, const char *fmt, ...) { - return nv_alloc(size); } -void ui_warn(Options *op, const char *fmt, ...); +void ui_error(Options *op, const char *fmt, ...); -void ui_warn(Options *op, const char *fmt, ...) +void ui_error(Options *op, const char *fmt, ...) { va_list ap; @@ -127,610 +107,577 @@ void ui_warn(Options *op, const char *fmt, ...) va_end(ap); } +void ui_log(Options *op, const char *fmt, ...); - -/* - * nv_open() - open(2) wrapper; prints an error message if open(2) - * fails and calls exit(). This function only returns on success. - */ - -static int nv_open(const char *pathname, int flags, mode_t mode) +void ui_log(Options *op, const char *fmt, ...) { - int fd; - fd = open(pathname, flags, mode); - if (fd == -1) { - fprintf(stderr, "Failure opening %s (%s).\n", - pathname, strerror(errno)); - exit(1); - } - return fd; +} -} /* nv_name() */ /* - * nv_get_file_length() - stat(2) wrapper; prints an error message if - * the system call fails and calls exit(). This function only returns - * on success. + * print_help() */ -static int nv_get_file_length(const char *filename) +static void print_help(void) { - struct stat stat_buf; - int ret; - - ret = stat(filename, &stat_buf); - if (ret == -1) { - fprintf(stderr, "Unable to determine '%s' file length (%s).\n", - filename, strerror(errno)); - exit(1); - } - return stat_buf.st_size; + printf("\n%s: pack/unpack precompiled files, and get information about\n" + "existing precompiled file packages.\n\n" + "USAGE: <action> <package-file> [options] \n\n", BINNAME); + + printf("<action> may be one of:\n\n" + " -p | --pack add files to a package\n" + " -u | --unpack unpack files from a package\n" + " -i | --info display information about a package\n" + " -m | --match check if a package matches the running kernel\n" + " -h | --help print this help text and exit\n\n" + "<package-file> is the package file to pack/unpack/test. It must be\n" + "an existing, valid package file for the --unpack, --info, and\n" + "--match actions. For the --pack action, if <package-file> does not\n" + "exist, it is created; if it exists but is not a valid package file,\n" + "it is overwritten; and if it exists and is a valid package file,\n" + "files will be added to the existing package.\n\n" + "--pack options:\n" + " -v | --driver-version (REQUIRED for new packages)\n" + " The version of the packaged components.\n" + " -P | --proc-version-string (RECOMMENDED for new packages)\n" + " The kernel version, as reported by '/proc/version', for the\n" + " target kernel. Default: the contents of the '/proc/version'\n" + " file on the current system.\n" + " -d | --description (RECOMMENDED for new packages)\n" + " A human readable description of the package.\n" + " --kernel-interface <file> --linked-module-name <module-name>\\\n" + " --core-object-name <core-name>\\\n" + " [ --linked-module <linked-kmod-file> \\\n" + " --signed-module <signed-kmod-file> ]\n" + " Pack <file> as a precompiled kernel interface.\n" + " <module-name> specifies the name of the kernel module file\n" + " that is produced by linking the precompiled kernel interface\n" + " with a separate precompiled core object file. <core-name>\n" + " specifies the name of the core object file that is linked\n" + " together with the precompiled interface to produce the final\n" + " kernel module.\n" + " A detached module signature may be produced by specifying\n" + " both the --linked-module and --signed-module options.\n" + " <linked-kmod-file> is a linked .ko file that is the result\n" + " of linking the precompiled interface with the remaining\n" + " object file(s) required to produce the finished module, and\n" + " <signed-kmod-file> is a copy of <linked-kmod-file> which an\n" + " appended module signature. In order for the signature to be\n" + " correctly applied on the target system, the linking should\n" + " be performed with the same linker and flags that will be\n" + " used on the target system.\n" + " The --linked-module and --signed-module options must be\n" + " given after the --kernel-interface option for the kernel\n" + " interface file with which they are associated, and before\n" + " any additional --kernel-interface or --kernel-module files.\n" + " --kernel-module <file> [ --signed ]\n" + " Pack <file> as a precompiled kernel module. The --signed\n" + " option specifies that <file> includes a module signature.\n" + " The --signed option must be given after the --kernel-module\n" + " option for the kernel module with which it is associated,\n" + " and before any additional --kernel-interface or\n" + " --kernel-module files.\n\n" + " If --driver-version, --proc-version-string, or --description\n" + " are given with an existing package file, the values in that\n" + " package file will be updated with new ones. At least one file\n" + " must be given with either --kernel-interface or --kernel-module\n" + " when using the --pack option.\n\n" + "--unpack options:\n" + " -o | --output-directory\n" + " The target directory where files will be unpacked. Default:\n" + " unpack files in the current directory.\n\n" + "Additional options:\n" + " --proc-mount-point\n" + " The procfs mount point on the current system, where the \n" + " '/proc/version' file may be found. Used by the --match\n" + " action, as well as to supply the default value of the\n" + " --proc-version-string option of the --pack action.\n" + " Default value: '/proc'\n"); -} /* nv_get_file_length() */ +} /* print_help() */ +enum { + PROC_MOUNT_POINT_OPTION = 1024, + KERNEL_INTERFACE_OPTION, + KERNEL_MODULE_OPTION, + SIGNED_FILE_OPTION, + LINKED_MODULE_OPTION, + LINKED_AND_SIGNED_MODULE_OPTION, + LINKED_MODULE_NAME_OPTION, + CORE_OBJECT_NAME_OPTION, +}; -/* - * nv_set_file_length() - wrapper for lseek() and write(); prints an - * error message if the system calls fail and calls exit(). This - * function only returns on success. - */ -static void nv_set_file_length(const char *filename, int fd, int len) +static void grow_file_array(Options *op, int *array_size) { - if ((lseek(fd, len - 1, SEEK_SET) == -1) || - (write(fd, "", 1) == -1)) { - fprintf(stderr, "Unable to set file '%s' length %d (%s).\n", - filename, fd, strerror(errno)); - exit(1); + if (op->num_files < 0) { + op->num_files = 0; } -} /* nv_set_file_length() */ + if (op->num_files >= *array_size) { + *array_size *= 2; + op->new_files = nvrealloc(op->new_files, + sizeof(PrecompiledFileInfo) * *array_size); + } +} -/* - * nv_mmap() - mmap(2) wrapper; prints an error message if mmap(2) - * fails and calls exit(). This function only returns on success. - */ +static uint32 file_type_from_option(int option) { + switch (option) { + case KERNEL_INTERFACE_OPTION: + return PRECOMPILED_FILE_TYPE_INTERFACE; + case KERNEL_MODULE_OPTION: + return PRECOMPILED_FILE_TYPE_MODULE; + default: + fprintf(stderr, "Unrecognized file type!"); + exit(1); + } +} -static void *nv_mmap(const char *filename, size_t len, int prot, - int flags, int fd) -{ - void *ret; - ret = mmap(0, len, prot, flags, fd, 0); - if (ret == (void *) -1) { - fprintf(stderr, "Unable to mmap file %s (%s).\n", - filename, strerror(errno)); +static void check_file_option_validity(Options *op, const char *option_name) +{ + if (op->num_files < 0) { + fprintf(stderr, "The --%s option cannot be specified before a file " + "name.\n", option_name); exit(1); } - return ret; - -} /* nv_mmap() */ - - +} -/* - * print_help() - */ -static void print_help(void) +static int create_detached_signature(Options *op, PrecompiledFileInfo *file, + const char *linked_module, + const char *signed_module) { - printf("\n%s [options] \n\n", BINNAME); - - printf("-i, --interface=<interface name>\n"); - printf(" Name of kernel interface file.\n\n"); - - printf("-o, --output=<output name>\n"); - printf(" Name of output file.\n\n"); - - printf("-u, --unpack=<filename>\n"); - printf(" Name of file to be unpacked.\n\n"); - - printf("-d, --description=<kernel description>\n"); - printf(" Kernel description.\n\n"); + if (file->type != PRECOMPILED_FILE_TYPE_INTERFACE) { + return TRUE; + } else if (linked_module && signed_module) { + struct stat st; + + if (stat(linked_module, &st) != 0) { + fprintf(stderr, "Unable to stat the linked kernel module file '%s'." + "\n", linked_module); + return FALSE; + } - printf("-v, --proc-version=<string>\n"); - printf(" /proc/version string for target kernel.\n\n"); + file->linked_module_crc = compute_crc(op, linked_module); + file->attributes |= PRECOMPILED_ATTR(LINKED_MODULE_CRC); - printf("--version=<version string>\n\n"); - - printf("--info\n"); - printf(" Print the description and version number of the file\n"); - printf(" specified by the unpack option.\n\n"); + file->signature_size = byte_tail(signed_module, st.st_size, + &(file->signature)); - printf("-m, --match\n"); - printf(" Check if the precompiled package matches the running\n"); - printf(" kernel.\n\n"); + if (file->signature_size > 0 && file->signature != NULL) { + file->attributes |= PRECOMPILED_ATTR(DETACHED_SIGNATURE); + return TRUE; + } else { + fprintf(stderr, "Failed to create a detached signature from signed " + "kernel module '%s'.\n", signed_module); + } - printf("This program can be used to either pack a precompiled kernel\n"); - printf("module interface, or unpack it.\n\n"); + } else if (linked_module || signed_module) { + fprintf(stderr, "Both --linked-module and --signed-module must be " + "specified to create a detached signature for precompiled " + "kernel interface file '%s'.\n", file->name); + } else { + return TRUE; + } -} /* print_help() */ + return FALSE; +} +static void set_action(Options *op, int action) +{ + if (op->action) { + fprintf(stderr, "Invalid command line; multiple actions cannot be " + "specified at the same time.\n"); + exit(1); + } -/* - * parse_commandline() - parse the commandline arguments. do some - * trivial validation, and return an initialized malloc'ed Options - * structure. - */ + op->action = action; +} -static Options *parse_commandline(int argc, char *argv[]) +static void pack_a_file(Options *op, PrecompiledFileInfo *file, + char *name, uint32 type, char **linked_name, + char **core_name, char **linked_module_file, + char **signed_module_file) { - Options *op; - int c, option_index = 0; + switch(type) { + case PRECOMPILED_FILE_TYPE_INTERFACE: + if (*linked_name == NULL || *core_name == NULL) { + fprintf(stderr, "Each kernel interface file must have both the " + "--linked-module-name and --core-object-name options set " + "in order to be added to a package.\n"); + exit(1); + } -#define INFO_OPTION 4 - - static struct option long_options[] = { - { "interface", 1, 0, 'i' }, - { "output", 1, 0, 'o' }, - { "unpack", 1, 0, 'u' }, - { "description", 1, 0, 'd' }, - { "help", 0, 0, 'h' }, - { "proc-version", 1, 0, 'v' }, - { "version", 1, 0, 'V' }, - { "info", 0, 0, INFO_OPTION }, - { "match", 0, 0, 'm' }, - { 0, 0, 0, 0 } - }; - - op = (Options *) nv_alloc(sizeof(Options)); - - while (1) { - c = getopt_long (argc, argv, "i:b:o:u:d:hv:m", - long_options, &option_index); - if (c == -1) - break; + if (!precompiled_read_interface(file, name, *linked_name, *core_name)) { + fprintf(stderr, "Failed to read kernel interface '%s'.\n", name); + exit(1); + } + break; - switch(c) { - case 'i': op->interface = optarg; break; - case 'o': op->output = optarg; break; - case 'u': op->unpack = optarg; break; - case 'd': op->description = optarg; break; - case 'h': print_help(); exit(0); break; - case 'v': op->proc_version_string = optarg; break; - case 'V': op->version = optarg; break; - case INFO_OPTION: - op->info = 1; break; - case 'm': - op->match = 1; break; - default: - fprintf (stderr, "Invalid commandline, please run `%s --help` " - "for usage information.\n", argv[0]); - exit (0); + case PRECOMPILED_FILE_TYPE_MODULE: + if (!precompiled_read_module(file, name)) { + fprintf(stderr, "Failed to read kernel module '%s'.\n", name); } - } + break; - if (optind < argc) { - fprintf (stderr, "Unrecognized arguments: "); - while (optind < argc) - fprintf (stderr, "%s ", argv[optind++]); - fprintf (stderr, "\n"); - fprintf (stderr, "Invalid commandline, please run `%s --help` for " - "usage information.\n", argv[0]); - exit (0); - } - - /* validate options */ - - if (!op->unpack && !(op->interface && op->proc_version_string)) { - - fprintf (stderr, "Incorrect options specified; please run " - "`%s --help` for usage information.\n", argv[0]); - exit(1); - } - - if (!op->info && !op->match && !op->output) { - fprintf(stderr, "Output file not specified.\n"); + default: exit(1); } + nvfree(name); - if (!op->version) { - fprintf(stderr, "Driver version string not specified.\n"); + if (!create_detached_signature(op, file, *linked_module_file, + *signed_module_file)) { exit(1); } - return op; - -} /* parse_commandline() */ + op->num_files++; + nvfree(*linked_name); + nvfree(*core_name); + nvfree(*linked_module_file); + nvfree(*signed_module_file); + *linked_name = *core_name = *linked_module_file = *signed_module_file = NULL; +} +/* + * parse_commandline() - parse the commandline arguments. do some + * trivial validation, and return an initialized malloc'ed Options + * structure. + */ -static char *read_proc_version(void) +static Options *parse_commandline(int argc, char *argv[]) { - int fd, ret, len, version_len; - char *version, *c = NULL; - - fd = nv_open(PROC_VERSION, O_RDONLY, 0); - - /* - * it would be more convenient if we could just mmap(2) - * /proc/version, but that's not supported, so just read in the - * whole file - */ - - len = version_len = 0; - version = NULL; + Options *op; + int c, file_array_size = 16; + uint32 type = 0; + PrecompiledFileInfo *file = NULL; + char *strval, *signed_mod = NULL, *linked_mod = NULL, *filename = NULL, + *linked_name = NULL, *core_name = NULL; + char see_help[1024]; + + static const NVGetoptOption long_options[] = { + { "pack", PACK, NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "unpack", UNPACK, NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "info", INFO, NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "match", MATCH, NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "help", 'h', 0, NULL, NULL }, + { "description", 'd', NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "output-directory", 'o', NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "driver-version", 'v', NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "proc-version-string", 'P', NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "proc-mount-point", PROC_MOUNT_POINT_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "kernel-interface", KERNEL_INTERFACE_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "kernel-module", KERNEL_MODULE_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "signed", SIGNED_FILE_OPTION, + 0, NULL, NULL }, + { "linked-module", LINKED_MODULE_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "signed-module", LINKED_AND_SIGNED_MODULE_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "linked-module-name", LINKED_MODULE_NAME_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { "core-object-name", CORE_OBJECT_NAME_OPTION, + NVGETOPT_STRING_ARGUMENT, NULL, NULL }, + { NULL, 0, 0, NULL, NULL } + }; - while (1) { - if (version_len == len) { - version_len += NV_VERSION_LEN; - version = realloc(version, version_len); - c = version + len; - } - ret = read(fd, c, version_len - len); - if (ret == -1) { - fprintf(stderr, "Error reading %s (%s).\n", - PROC_VERSION, strerror(errno)); - free(version); - return NULL; - } - if (ret == 0) { - *c = '\0'; - break; - } - len += ret; - c += ret; - } + snprintf(see_help, sizeof(see_help), "Please run `%s --help` for usage " + "information.\n", argv[0]); + see_help[sizeof(see_help) - 1] = '\0'; - /* replace a newline with a null-terminator */ + op = (Options *) nvalloc(sizeof(Options)); + op->new_files = nvalloc(sizeof(PrecompiledFileInfo) * file_array_size); + op->num_files = -1; - c = version; - while ((*c != '\0') && (*c != '\n')) c++; - *c = '\0'; + op->proc_mount_point = PROC_MOUNT_POINT; - return version; + while (1) { + c = nvgetopt(argc, argv, long_options, &strval, + NULL, /* boolval */ + NULL, /* intval */ + NULL, /* doubleval */ + NULL /* disable_val */); -} /* read_proc_version() */ + if (c == -1) + break; + switch(c) { + case PACK: case UNPACK: case INFO: case MATCH: + set_action(op, c); + op->package_file = strval; + break; + case 'h': print_help(); exit(0); break; + case 'f': op->package_file = strval; break; + case 'd': op->description = strval; break; + case 'o': op->output_directory = strval; break; + case 'v': op->version = strval; break; + case 'P': op->proc_version_string = strval; break; + case PROC_MOUNT_POINT_OPTION: op->proc_mount_point = strval; break; -/* - * check_match() - read /proc/version, and do a strcmp with str. - * Returns 1 if the strings match, 0 if they don't match. - */ + case KERNEL_INTERFACE_OPTION: case KERNEL_MODULE_OPTION: -static int check_match(char *str) -{ - int ret = 0; - char *version = read_proc_version(); - - if (strcmp(version, str) == 0) { - ret = 1; - printf("kernel interface matches.\n"); - } else { - ret = 0; - printf("kernel interface doesn't match.\n"); - } + grow_file_array(op, &file_array_size); - free(version); - - return ret; - -} /* check_match() */ + if (file) { + pack_a_file(op, file, filename, type, &linked_name, &core_name, + &linked_mod, &signed_mod); + } + file = op->new_files + op->num_files; + filename = strval; + type = file_type_from_option(c); + break; -/* - * encode_uint32() - given a uint32, and a 4 byte data buffer, write - * the integer to the data buffer. - */ + case SIGNED_FILE_OPTION: -static void encode_uint32(uint32 val, uint8 data[4]) -{ - data[0] = ((val >> 0) & 0xff); - data[1] = ((val >> 8) & 0xff); - data[2] = ((val >> 16) & 0xff); - data[3] = ((val >> 24) & 0xff); + check_file_option_validity(op, "signed"); -} /* encode_uint32() */ + /* + * for interfaces, signedness is implied by the presence of a linked + * module crc and a detached signature, so only set the signed file + * attribute in the case of a precompiled kernel module. + */ + if (type == PRECOMPILED_FILE_TYPE_MODULE) { + file->attributes |= PRECOMPILED_ATTR(EMBEDDED_SIGNATURE); + } + break; -/* - * decode_uint32() - given an index into a buffer, read the next 4 - * bytes, and build a uint32. - */ + case LINKED_MODULE_OPTION: -static uint32 decode_uint32(char *buf) -{ - uint32 ret = 0; + check_file_option_validity(op, "linked-module"); + linked_mod = strval; - ret += (((uint32) buf[3]) & 0xff); - ret <<= 8; + break; - ret += (((uint32) buf[2]) & 0xff); - ret <<= 8; + case LINKED_AND_SIGNED_MODULE_OPTION: - ret += (((uint32) buf[1]) & 0xff); - ret <<= 8; + check_file_option_validity(op, "signed-module"); + signed_mod = strval; - ret += (((uint32) buf[0]) & 0xff); - ret <<= 0; + break; - return ret; + case LINKED_MODULE_NAME_OPTION: -} /* decode_uint32() */ + check_file_option_validity(op, "linked-module-name"); + linked_name = strval; + break; + case CORE_OBJECT_NAME_OPTION: -/* - * pack() - pack the specified precompiled kernel interface file, - * prepended with a header, the CRC the driver version, a description - * string, and the proc version string. - */ + check_file_option_validity(op, "core-object-name"); + core_name = strval; -static int pack(Options *op) -{ - int fd, offset, src_fd; - uint8 *out, *src, data[4]; - uint32 crc; - int version_len, description_len, proc_version_len; - int interface_len, total_len; - - /* - * get the lengths of the description, the proc version string, - * and the interface file. - */ - - version_len = strlen(op->version); - description_len = strlen(op->description); - proc_version_len = strlen(op->proc_version_string); - interface_len = nv_get_file_length(op->interface); - - total_len = CONSTANT_LENGTH + - version_len + description_len + proc_version_len + interface_len; - - /* compute the crc of the kernel interface */ + break; - crc = compute_crc(NULL, op->interface); + default: + fprintf (stderr, "Invalid commandline; %s", see_help); + exit(0); + } + } - /* open the output file for writing */ - - fd = nv_open(op->output, O_CREAT|O_RDWR|O_TRUNC, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); - /* set the output file length */ + /* validate options */ - nv_set_file_length(op->output, fd, total_len); - - /* map the input file */ + if (!op->action) { + fprintf(stderr, "No action specified; one of --pack, --unpack, --info, " + "or --match options must be given. %s", see_help); + exit(1); + } - out = nv_mmap(op->output, total_len, PROT_READ|PROT_WRITE, - MAP_FILE|MAP_SHARED, fd); - offset = 0; + switch (op->action) { + case PACK: + if (file) { + pack_a_file(op, file, filename, type, &linked_name, &core_name, + &linked_mod, &signed_mod); + } - /* write the header */ - - memcpy(&(out[0]), "NVIDIA ", 8); - offset += 8; + if (op->num_files < 1) { + fprintf(stderr, "At least one file to pack must be specified " + "when using the --pack option; %s", see_help); + exit(1); + } + break; - /* write the crc */ + case UNPACK: + if (!op->output_directory) { + op->output_directory = "."; + } + break; - encode_uint32(crc, data); - memcpy(&(out[offset]), data, 4); - offset += 4; + case INFO: case MATCH: default: /* XXX should never hit default case */ + break; + } - /* write the version */ + op->package = get_precompiled_info(op, op->package_file, NULL, NULL); - encode_uint32(version_len, data); - memcpy(&(out[offset]), data, 4); - offset += 4; - - if (version_len) { - memcpy(&(out[offset]), op->version, version_len); - offset += version_len; + if (!op->package && op->action != PACK) { + fprintf(stderr, "Unable to read package file '%s'.\n", + op->package_file); + exit(1); } - /* write the description */ + return op; - encode_uint32(description_len, data); - memcpy(&(out[offset]), data, 4); - offset += 4; +} /* parse_commandline() */ - if (description_len) { - memcpy(&(out[offset]), op->description, description_len); - offset += description_len; - } - /* write the proc version string */ - - encode_uint32(proc_version_len, data); - memcpy(&(out[offset]), data, 4); - offset += 4; - - memcpy(&(out[offset]), op->proc_version_string, proc_version_len); - offset += proc_version_len; - /* open the precompiled kernel module interface for reading */ - src_fd = nv_open(op->interface, O_RDONLY, 0); - - /* mmap() the kernel module interface */ +/* + * check_match() - read /proc/version, and do a strcmp with str. + * Returns 1 if the strings match, 0 if they don't match. + */ - src = nv_mmap(op->interface, interface_len, PROT_READ, - MAP_FILE|MAP_SHARED, src_fd); +static int check_match(Options *op, char *str) +{ + int ret = 0; + char *version = read_proc_version(op, op->proc_mount_point); - memcpy(out + offset, src, interface_len); - - /* unmap src and dst */ + if (strcmp(version, str) == 0) { + ret = 1; + printf("kernel interface matches.\n"); + } else { + ret = 0; + printf("kernel interface doesn't match.\n"); + } - munmap(src, interface_len); - munmap(out, total_len); + free(version); + + return ret; - close(src_fd); - close(fd); - - return 0; - -} /* pack() */ +} /* check_match() */ /* - * unpack() - unpack the specified package + * program entry point */ -static int unpack(Options *op) +int main(int argc, char *argv[]) { - int dst_fd, fd, ret, offset, len = 0; - char *buf, *dst; - uint32 crc, val, size; - char *version, *description, *proc_version_string; - - fd = dst_fd = 0; - buf = dst = NULL; - ret = 1; - - /* open the file to be unpacked */ - - fd = nv_open(op->unpack, O_RDONLY, 0); - - /* get the file length */ - - size = nv_get_file_length(op->unpack); + Options *op; + int ret = 1; - /* check for a minimum length */ + op = parse_commandline(argc, argv); - if (size < CONSTANT_LENGTH) { - fprintf(stderr, "File '%s' appears to be too short.\n", op->unpack); - goto done; - } - - /* mmap(2) the input file */ - buf = nv_mmap(op->unpack, size, PROT_READ, MAP_FILE|MAP_SHARED, fd); - offset = 0; + switch (op->action) { + int i; + + case PACK: + if (!op->package) { + if (!op->version) { + fprintf (stderr, "The --driver-version option must be specified " + "when using the --pack option to create a new package; " + "Please run `%s --help` for usage information.\n", + argv[0]); + exit (1); + } + + op->package = nvalloc(sizeof(PrecompiledInfo)); + + op->package->description = op->description ? op->description : + nvstrdup(""); + op->package->proc_version_string = + op->proc_version_string ? + op->proc_version_string : + read_proc_version(op, op->proc_mount_point); + op->package->version = op->version; + } - /* check for the header */ + precompiled_append_files(op->package, op->new_files, op->num_files); - if (strncmp(buf, "NVIDIA ", 8) != 0) { - fprintf(stderr, "File '%s': unrecognized file format.\n", op->unpack); - goto done; - } - offset += 8; + if (precompiled_pack(op->package, op->package_file)) { + ret = 0; + } else { + fprintf(stderr, "An error occurred while writing the package " + "file '%s'.\n", op->package_file); + } - /* read the crc */ + break; - crc = decode_uint32(buf + offset); - offset += 4; - - /* read the version */ + case UNPACK: + if (precompiled_unpack(op, op->package, op->output_directory)) { + ret = 0; + } else { + fprintf(stderr, "An error occurred while unpacking the package " + "file '%s' to '%s'.\n", op->package_file, + op->output_directory); + } + break; - val = decode_uint32(buf + offset); - offset += 4; + case INFO: - if (val > 0) { - version = nv_alloc(val+1); - memcpy(version, buf + offset, val); - version[val] = '\0'; - } else { - version = NULL; - } - offset += val; + printf("description: %s\n", op->package->description); + printf("version: %s\n", op->package->version); + printf("proc version: %s\n", op->package->proc_version_string); + printf("number of files: %d\n\n", op->package->num_files); - /* read the description */ + for (i = 0; i < op->package->num_files; i++) { + PrecompiledFileInfo *file = op->package->files + i; + const char **attrs, **attr; - val = decode_uint32(buf + offset); - offset += 4; - if ((val + CONSTANT_LENGTH) > size) { - fprintf(stderr, "Invalid file.\n"); - goto done; - } - if (val > 0) { - description = nv_alloc(val+1); - memcpy(description, buf + offset, val); - description[val] = '\0'; - } else { - description = NULL; - } - offset += val; - - /* read the proc version string */ + attrs = precompiled_file_attribute_names(file->attributes); - val = decode_uint32(buf + offset); - offset += 4; - if ((val + CONSTANT_LENGTH) > size) { - fprintf(stderr, "Invalid file.\n"); - goto done; - } - proc_version_string = nv_alloc(val+1); - memcpy(proc_version_string, buf + offset, val); - offset += val; - proc_version_string[val] = '\0'; - - /* - * if info was requested, print the description, driver version, - * crc, proc version, and exit - */ - - if (op->info) { - printf("description: %s\n", description); - printf("version: %s\n", version); - printf("crc: %u\n", crc); - printf("proc version: %s\n", proc_version_string); - return 0; - } - - /* check if the running kernel matches */ + printf("file %d:\n", i + 1); + printf(" name: '%s'\n", file->name); + printf(" type: %s\n", + precompiled_file_type_name(file->type)); + printf(" attributes: "); - if (op->match) { - return check_match(proc_version_string); - } - - /* extract kernel interface module */ - - len = size - offset; + for(attr = attrs; *attr; attr++) { + if (attr > attrs) { + printf(", "); + } + printf("%s", *attr); + } - dst_fd = nv_open(op->output, O_CREAT | O_RDWR | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - - /* set the output file length */ + printf("\n"); - nv_set_file_length(op->output, dst_fd, len); - - /* mmap the dst */ + printf(" size: %d bytes\n", file->size); + printf(" crc: %" PRIu32 "\n", file->crc); - dst = nv_mmap(op->output, len, PROT_READ|PROT_WRITE, - MAP_FILE|MAP_SHARED, dst_fd); - - /* copy */ - - memcpy(dst, buf + offset, len); - - ret = 0; + if (file->type == PRECOMPILED_FILE_TYPE_INTERFACE) { + printf(" core object name: %s\n", file->core_object_name); + printf(" linked module name: %s\n", file->linked_module_name); + if (file->signature_size) { + printf(" linked module crc: %" PRIu32 "\n", + file->linked_module_crc); + printf(" signature size: %d\n", file->signature_size); + } + } - done: - if (dst) munmap(dst, len); - if (buf) munmap(buf, size); - if (fd > 0) close(fd); - if (dst_fd > 0) close(dst_fd); - return ret; - -} /* unpack() */ + printf("\n"); + } + ret = 0; + break; -/* - * program entry point - */ + case MATCH: -int main(int argc, char *argv[]) -{ - Options *op; - int ret; + ret = check_match(op, op->package->proc_version_string); + break; - op = parse_commandline(argc, argv); + default: /* XXX should never get here */ break; - if (op->unpack) { - ret = unpack(op); - } else { /* pack */ - ret = pack(op); } + + free_precompiled(op->package); return ret; diff --git a/nvidia-installer.1.m4 b/nvidia-installer.1.m4 index 61c4ed6..07f1291 100644 --- a/nvidia-installer.1.m4 +++ b/nvidia-installer.1.m4 @@ -122,6 +122,16 @@ These scripts should not require user interaction. Use the .B \-\-no\-distro\-scripts option to disable execution of these scripts. +.PP +In addition to the distribution hook scripts, distributors or other producers of driver packages may report the presence an already installed driver, or the availability of driver packages. Installing a text file to +.B /usr/lib/nvidia/alternate-install-present +will alert nvidia-installer that an existing driver is already installed. Installing a text file to +.B /usr/lib/nvidia/alternate-install-available +will alert nvidia-installer that an alternate installation option is available. The contents of +.B /usr/lib/nvidia/alternate-install-present +or +.B /usr/lib/nvidia/alternate-install-available +will be printed in a message informing the user of the presence/availability of the alternate driver installation, and asking the user whether to continue with the installation. .SH EXAMPLES .TP .B nvidia\-installer \-\-latest diff --git a/nvidia-installer.c b/nvidia-installer.c index 8b2ddc1..cc3c752 100644 --- a/nvidia-installer.c +++ b/nvidia-installer.c @@ -146,6 +146,7 @@ static Options *load_default_options(void) op->run_distro_scripts = TRUE; op->no_kernel_module_source = FALSE; op->dkms = FALSE; + op->check_for_alternate_installs = TRUE; return op; diff --git a/nvidia-installer.h b/nvidia-installer.h index ee47562..afaea29 100644 --- a/nvidia-installer.h +++ b/nvidia-installer.h @@ -145,6 +145,7 @@ typedef struct __options { int no_opengl_files; int no_kernel_module_source; int dkms; + int check_for_alternate_installs; char *opengl_prefix; char *opengl_libdir; @@ -385,10 +386,7 @@ 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" - +#define PRECOMPILED_PACKAGE_FILENAME "nvidia-precompiled" /* * These are the default installation prefixes and the default diff --git a/option_table.h b/option_table.h index 8ada92c..897a054 100644 --- a/option_table.h +++ b/option_table.h @@ -92,6 +92,7 @@ enum { MODULE_SIGNING_KEY_PATH_OPTION, MODULE_SIGNING_HASH_OPTION, MODULE_SIGNING_X509_HASH_OPTION, + NO_CHECK_FOR_ALTERNATE_INSTALLS_OPTION, }; static const NVGetoptOption __options[] = { @@ -604,6 +605,13 @@ static const NVGetoptOption __options[] = { "name must be one of the message digest algorithms recognized by " "the x509(1) command." }, + { "no-check-for-alternate-installs", NO_CHECK_FOR_ALTERNATE_INSTALLS_OPTION, + 0, NULL, + "Maintainers of alternate driver installation methods can report the " + "presence and/or availability of an alternate driver installation to " + "nvidia-installer. Setting this option skips the check for alternate " + "driver installations." }, + /* 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 ae1ab91..a5f8bfc 100644 --- a/precompiled.c +++ b/precompiled.c @@ -19,10 +19,6 @@ * * precompiled.c - this source file contains functions for dealing * with precompiled kernel interfaces. - * - * XXX portions of these functions are lifted from mkprecompiled (it - * was much easier to duplicate them than to finesse the code to be - * shared between the installer and mkprecompiled). */ @@ -42,32 +38,37 @@ #include "misc.h" #include "crc.h" -#define PRECOMPILED_CONSTANT_LENGTH (8 + 4 + 4 + 4 + 4) + + +static int precompiled_read_fileinfo(Options *op, PrecompiledFileInfo *fileInfos, + int index, char *buf, int offset, int size); /* - * decode_uint32() - given an index into a buffer, read the next 4 - * bytes, and build a uint32. + * read_uint32() - given a buffer and an offset, read the next 4 bytes from + * the buffer at the given offset, build a uint32, and advance the offset. */ -static uint32 decode_uint32(char *buf) +static uint32 read_uint32(const char *buf, int *offset) { uint32 ret = 0; - ret += (((uint32) buf[3]) & 0xff); + ret += (((uint32) buf[*offset + 3]) & 0xff); ret <<= 8; - ret += (((uint32) buf[2]) & 0xff); + ret += (((uint32) buf[*offset + 2]) & 0xff); ret <<= 8; - ret += (((uint32) buf[1]) & 0xff); + ret += (((uint32) buf[*offset + 1]) & 0xff); ret <<= 8; - ret += (((uint32) buf[0]) & 0xff); + ret += (((uint32) buf[*offset]) & 0xff); ret <<= 0; + *offset += sizeof(uint32); + return ret; -} /* decode_uint32() */ +} @@ -78,15 +79,15 @@ static uint32 decode_uint32(char *buf) * mounted. */ -char *read_proc_version(Options *op) +char *read_proc_version(Options *op, const char *proc_mount_point) { int fd, ret, len, version_len; char *version, *c = NULL; char *proc_verson_filename; - len = strlen(op->proc_mount_point) + 9; /* strlen("/version") + 1 */ + len = strlen(proc_mount_point) + 9; /* strlen("/version") + 1 */ proc_verson_filename = (char *) nvalloc(len); - snprintf(proc_verson_filename, len, "%s/version", op->proc_mount_point); + snprintf(proc_verson_filename, len, "%s/version", proc_mount_point); if ((fd = open(proc_verson_filename, O_RDONLY)) == -1) { ui_warn(op, "Unable to open the file '%s' (%s).", @@ -139,27 +140,26 @@ char *read_proc_version(Options *op) /* - * precompiled_unpack() - unpack the specified package. It's not - * really an error if we can't open the file or if it's not the right - * format, so just throw an expert-only log message. + * get_precompiled_info() - load the the specified package into a + * PrecompiledInfo record. It's not really an error if we can't open the file + * or if it's not the right format, so just throw an expert-only log message. */ -PrecompiledInfo *precompiled_unpack(Options *op, - const char *filename, - const char *output_filename, - const char *real_proc_version_string, - const char *package_version) +PrecompiledInfo *get_precompiled_info(Options *op, + const char *filename, + const char *real_proc_version_string, + const char *package_version) { - int dst_fd, fd, offset, len = 0; - char *buf, *dst; - uint32 crc, val, size, linked_module_crc; - char *version, *description, *proc_version_string, - *detached_signature = NULL; + int fd, offset, num_files, i; + char *buf; + uint32 val, size; + char *version, *description, *proc_version_string; struct stat stat_buf; PrecompiledInfo *info = NULL; + PrecompiledFileInfo *fileInfos = NULL; - fd = dst_fd = size = 0; - buf = dst = description = proc_version_string = NULL; + fd = size = 0; + buf = description = proc_version_string = NULL; /* open the file to be unpacked */ @@ -180,7 +180,7 @@ PrecompiledInfo *precompiled_unpack(Options *op, /* check for a minimum length */ - if (size < PRECOMPILED_CONSTANT_LENGTH) { + if (size < PRECOMPILED_PKG_CONSTANT_LENGTH) { ui_expert(op, "File '%s' appears to be too short.", filename); goto done; } @@ -197,22 +197,25 @@ PrecompiledInfo *precompiled_unpack(Options *op, /* check for the header */ - if (strncmp(buf + offset, "NVIDIA ", 8) != 0) { + if (strncmp(buf + offset, PRECOMPILED_PKG_HEADER, 8) != 0) { ui_expert(op, "File '%s': unrecognized file format.", filename); goto done; } offset += 8; - /* read the crc */ + /* check the package format version */ - crc = decode_uint32(buf + offset); - offset += 4; + val = read_uint32(buf, &offset); + if (val != PRECOMPILED_PKG_VERSION) { + ui_expert(op, "Incompatible package format version %d: expected %d.", + val, PRECOMPILED_PKG_VERSION); + goto done; + } /* read the version */ - val = decode_uint32(buf + offset); - offset += 4; - if ((val + PRECOMPILED_CONSTANT_LENGTH) > size) { + val = read_uint32(buf, &offset); + if ((val + PRECOMPILED_PKG_CONSTANT_LENGTH) > size) { ui_expert(op, "Invalid file '%s' (bad version string length %d).", filename, val); goto done; @@ -229,34 +232,28 @@ PrecompiledInfo *precompiled_unpack(Options *op, /* check if this precompiled kernel interface is the right driver version */ - if (strcmp(version, package_version) != 0) { + if (package_version && (strcmp(version, package_version) != 0)) { goto done; } /* read the description */ - val = decode_uint32(buf + offset); - offset += 4; - if ((val + PRECOMPILED_CONSTANT_LENGTH) > size) { + val = read_uint32(buf, &offset); + if ((val + PRECOMPILED_PKG_CONSTANT_LENGTH) > size) { ui_expert(op, "Invalid file '%s' (bad description string length %d).", filename, val); goto done; } - if (val > 0) { - description = nvalloc(val+1); - memcpy(description, buf + offset, val); - description[val] = '\0'; - } else { - description = NULL; - } + description = nvalloc(val+1); + memcpy(description, buf + offset, val); + description[val] = '\0'; offset += val; /* read the proc version string */ - val = decode_uint32(buf + offset); - offset += 4; - if ((val + PRECOMPILED_CONSTANT_LENGTH) > size) { + val = read_uint32(buf, &offset); + if ((val + PRECOMPILED_PKG_CONSTANT_LENGTH) > size) { ui_expert(op, "Invalid file '%s' (bad version string length %d).", filename, val); goto done; @@ -266,131 +263,686 @@ 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) { + if (real_proc_version_string && + (strcmp(real_proc_version_string, proc_version_string) != 0)) { goto done; } ui_log(op, "A precompiled kernel interface for kernel '%s' has been " "found here: %s.", description, filename); - - /* extract kernel interface module */ - - len = size - offset; - if ((dst_fd = open(output_filename, O_CREAT | O_RDWR | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { - ui_error(op, "Unable to open output file '%s' (%s).", output_filename, - strerror(errno)); - goto done; - } - - /* set the output file length */ + num_files = read_uint32(buf, &offset); + fileInfos = nvalloc(num_files * sizeof(PrecompiledFileInfo)); + for (i = 0; i < num_files; i++) { + int ret; + ret = precompiled_read_fileinfo(op, fileInfos, i, buf, offset, size); - if ((lseek(dst_fd, len - 1, SEEK_SET) == -1) || - (write(dst_fd, "", 1) == -1)) { - ui_error(op, "Unable to set output file '%s' length %d (%s).\n", - output_filename, len, strerror(errno)); - goto done; + if (ret > 0) { + offset += ret; + } else { + ui_log(op, "An error occurred while trying to parse '%s'.", + filename); + goto done; + } } - /* mmap the dst */ - - dst = mmap(0, len, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, dst_fd, 0); - if (dst == (void *) -1) { - ui_error(op, "Unable to mmap output file %s (%s).\n", - output_filename, strerror(errno)); - goto done; - } - - /* copy */ - - memcpy(dst, buf + offset, len); - /* * now that we can no longer fail, allocate and initialize the * PrecompiledInfo structure */ info = (PrecompiledInfo *) nvalloc(sizeof(PrecompiledInfo)); - info->crc = crc; + info->package_size = size; 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; + info->num_files = num_files; + info->files = fileInfos; /* - * XXX so that the proc version, description, and detached_signature strings - * aren't freed below + * XXX so that the proc version and description strings, and the + * PrecompiledFileInfo array aren't freed below */ - - proc_version_string = description = detached_signature = NULL; + + proc_version_string = description = NULL; + fileInfos = NULL; done: - + /* cleanup whatever needs cleaning up */ - if (dst) munmap(dst, len); if (buf) munmap(buf, size); if (fd > 0) close(fd); - 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); + if (fileInfos) free(fileInfos); return info; - -} /* mkprecompiled_unpack() */ +} + + +/* + * precompiled_file_unpack() - Unpack an individual precompiled file to the + * specified output directory. + */ + +int precompiled_file_unpack(Options *op, const PrecompiledFileInfo *fileInfo, + const char *output_directory) +{ + int ret = FALSE, dst_fd = 0; + char *dst_path, *dst = NULL; + + dst_path = nvstrcat(output_directory, "/", fileInfo->name, NULL); + + /* extract file */ + + if ((dst_fd = open(dst_path, O_CREAT | O_RDWR | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { + ui_error(op, "Unable to open output file '%s' (%s).", dst_path, + strerror(errno)); + goto done; + } + + /* set the output file length */ + + if ((lseek(dst_fd, fileInfo->size - 1, SEEK_SET) == -1) || + (write(dst_fd, "", 1) == -1)) { + ui_error(op, "Unable to set output file '%s' length %d (%s).\n", + dst_path, fileInfo->size, strerror(errno)); + goto done; + } + + /* mmap the dst */ + + dst = mmap(0, fileInfo->size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, dst_fd, 0); + if (dst == (void *) -1) { + ui_error(op, "Unable to mmap output file %s (%s).\n", + dst_path, strerror(errno)); + goto done; + } + + /* copy */ + + memcpy(dst, fileInfo->data, fileInfo->size); + + ret = TRUE; + +done: + + /* cleanup whatever needs cleaning up */ + + nvfree(dst_path); + if (dst) munmap(dst, fileInfo->size); + if (dst_fd > 0) close(dst_fd); + + return ret; +} + + +/* precompiled_unpack() - unpack all of the files in a PrecompiledInfo into + * the given destination directory */ +int precompiled_unpack(Options *op, const PrecompiledInfo *info, + const char *output_directory) +{ + int i; + + if (!info) { + return FALSE; + } + + for (i = 0; i < info->num_files; i++) { + if (!precompiled_file_unpack(op, &(info->files[i]), output_directory)) { + return FALSE; + } + } + + return TRUE; +} + + + + +/* + * encode_uint32() - given a uint32, a data buffer, and an offset into the data + * buffer, write the integer to the data buffer and advance the offset by the + * number of bytes written. + */ + +static void encode_uint32(uint32 val, uint8 *data, int *offset) +{ + data[*offset + 0] = ((val >> 0) & 0xff); + data[*offset + 1] = ((val >> 8) & 0xff); + data[*offset + 2] = ((val >> 16) & 0xff); + data[*offset + 3] = ((val >> 24) & 0xff); + + *offset += sizeof(uint32); +} + + +/* + * precompiled_pack() - pack the specified precompiled kernel interface + * file, prepended with a header, the CRC the driver version, a description + * string, and the proc version string. + */ + +int precompiled_pack(const PrecompiledInfo *info, const char *package_filename) +{ + int fd, offset; + uint8 *out; + int version_len, description_len, proc_version_len; + int total_len, files_len, i; + + /* + * get the lengths of the description, the proc version string, + * and the files to be packaged along with the associated metadata. + */ + + version_len = strlen(info->version); + description_len = strlen(info->description); + proc_version_len = strlen(info->proc_version_string); + + for (files_len = i = 0; i < info->num_files; i++) { + files_len += PRECOMPILED_FILE_CONSTANT_LENGTH + + strlen(info->files[i].name) + + strlen(info->files[i].linked_module_name) + + strlen(info->files[i].core_object_name) + + info->files[i].size + + info->files[i].signature_size; + } + + total_len = PRECOMPILED_PKG_CONSTANT_LENGTH + + version_len + description_len + proc_version_len + files_len; + + /* open the output file for writing */ + + fd = nv_open(package_filename, O_CREAT|O_RDWR|O_TRUNC, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + + /* set the output file length */ + + nv_set_file_length(package_filename, fd, total_len); + + /* map the output file */ + + out = nv_mmap(package_filename, total_len, PROT_READ|PROT_WRITE, + MAP_FILE|MAP_SHARED, fd); + offset = 0; + + /* write the header */ + + memcpy(&(out[0]), PRECOMPILED_PKG_HEADER, 8); + offset += 8; + + /* write the package version */ + + encode_uint32(PRECOMPILED_PKG_VERSION, out, &offset); + + /* write the version */ + + encode_uint32(version_len, out, &offset); + + if (version_len) { + memcpy(&(out[offset]), info->version, version_len); + offset += version_len; + } + + /* write the description */ + + encode_uint32(description_len, out, &offset); + + if (description_len) { + memcpy(&(out[offset]), info->description, description_len); + offset += description_len; + } + + /* write the proc version string */ + + encode_uint32(proc_version_len, out, &offset); + + memcpy(&(out[offset]), info->proc_version_string, proc_version_len); + offset += proc_version_len; + + /* write the number of files */ + + encode_uint32(info->num_files, out, &offset); + + /* write the files */ + for (i = 0; i < info->num_files; i++) { + PrecompiledFileInfo *file = &(info->files[i]); + uint32 name_len = strlen(file->name); + uint32 linked_module_name_len = strlen(file->linked_module_name); + uint32 core_object_name_len = strlen(file->core_object_name); + + /* file header */ + memcpy(&(out[offset]), PRECOMPILED_FILE_HEADER, 4); + offset += 4; + + /* file sequence number */ + encode_uint32(i, out, &offset); + + /* file type and attributes*/ + encode_uint32(file->type, out, &offset); + encode_uint32(file->attributes, out, &offset); + + /* file name */ + encode_uint32(name_len, out, &offset); + memcpy(&(out[offset]), file->name, name_len); + offset += name_len; + + /* linked module name */ + encode_uint32(linked_module_name_len, out, &offset); + memcpy(&(out[offset]), file->linked_module_name, linked_module_name_len); + offset += linked_module_name_len; + + /* core object name */ + encode_uint32(core_object_name_len, out, &offset); + memcpy(&(out[offset]), file->core_object_name, core_object_name_len); + offset += core_object_name_len; + + /* crc */ + encode_uint32(file->crc, out, &offset); + + /* file */ + encode_uint32(file->size, out, &offset); + memcpy(&(out[offset]), file->data, file->size); + offset += file->size; + + /* redundant crc */ + encode_uint32(file->crc, out, &offset); + + /* linked module crc */ + encode_uint32(file->linked_module_crc, out, &offset); + + /* detached signature */ + encode_uint32(file->signature_size, out, &offset); + if (file->signature_size) { + memcpy(&(out[offset]), file->signature, file->signature_size); + offset += file->signature_size; + } + + /* redundant file sequence number */ + encode_uint32(i, out, &offset); + + /* file footer */ + memcpy(&(out[offset]), PRECOMPILED_FILE_FOOTER, 4); + offset += 4; + } + + /* unmap package */ + + munmap(out, total_len); + + close(fd); + + return TRUE; + +} + + + /* * free_precompiled() - free any malloced strings stored in a PrecompiledInfo, * then free the PrecompiledInfo. */ void free_precompiled(PrecompiledInfo *info) { + int i; + + if (!info) { + return; + } + nvfree(info->description); nvfree(info->proc_version_string); - nvfree(info->detached_signature); + nvfree(info->version); + + for (i = 0; i < info->num_files; i++) { + free_precompiled_file_data(info->files[i]); + } + nvfree(info->files); + nvfree(info); } + + + +void free_precompiled_file_data(PrecompiledFileInfo fileInfo) +{ + nvfree(fileInfo.name); + nvfree(fileInfo.linked_module_name); + nvfree(fileInfo.data); + nvfree(fileInfo.signature); +} + + + +/* + * precompiled_read_file() - attempt to open the file at the specified path and + * populate a PrecompiledFileInfo record with its contents and the appropriate + * metadata. Return a pointer to a newly allocated PrecompiledFile record on + * success, or NULL on failure. + */ + +static int precompiled_read_file(PrecompiledFileInfo *fileInfo, + const char *filename, + const char *linked_module_name, + const char *core_object_name, uint32 type) +{ + int fd; + struct stat st; + int success = FALSE, ret; + + fd = open(filename, O_RDONLY); + if (fd == -1) { + goto done; + } + + if (fstat(fd, &st) != 0) { + goto done; + } + + fileInfo->size = st.st_size; + fileInfo->data = nvalloc(fileInfo->size); + + ret = read(fd, fileInfo->data, fileInfo->size); + + if (ret != fileInfo->size) { + goto done; + } + + fileInfo->type = type; + fileInfo->name = nv_basename(filename); + fileInfo->linked_module_name = nvstrdup(linked_module_name); + fileInfo->core_object_name = nvstrdup(core_object_name); + fileInfo->crc = compute_crc(NULL, filename); + + success = TRUE; + +done: + close(fd); + return success; +} + + +int precompiled_read_interface(PrecompiledFileInfo *fileInfo, + const char *filename, + const char *linked_module_name, + const char *core_object_name) +{ + return precompiled_read_file(fileInfo, filename, linked_module_name, + core_object_name, + PRECOMPILED_FILE_TYPE_INTERFACE); +} + +int precompiled_read_module(PrecompiledFileInfo *fileInfo, const char *filename) +{ + return precompiled_read_file(fileInfo, filename, "", "", + PRECOMPILED_FILE_TYPE_MODULE); +} + + + +/* + * precompiled_read_fileinfo() - Read a PrecompiledFileInfo record from a binary + * precompiled file package. + * + * Parameters: + * op: A pointer to the options structure, used for ui_* functions + * fileInfos: A PrecompiledFileInfo array, where the decoded file data from + * the binary package will be stored + * index: The index into fileInfos where the decoded file data will be + * stored. Used to verify that the file number recorded in the + * package is valid. + * buf: The raw buffer containing the binary package data. This should + * point to the beginning of the package file. + * offset: The offset into buf where the file data to be read begins. + * size: The size of the package data buffer. This is used for bounds + * checking, to make sure that a reading a length specified in the + * won't go past the end of the package file. + * + * Return value: + * The number of bytes read from the package file on success, or -1 on error. + */ + +static int precompiled_read_fileinfo(Options *op, PrecompiledFileInfo *fileInfos, + int index, char *buf, int offset, int size) +{ + PrecompiledFileInfo *fileInfo = fileInfos + index; + uint32 val; + int oldoffset = offset; + + if (size - offset < PRECOMPILED_FILE_CONSTANT_LENGTH) { + return -1; + } + + if (strncmp(buf + offset, PRECOMPILED_FILE_HEADER, 4) != 0) { + ui_log(op, "Unrecognized header for packaged file."); + return -1; + } + offset += 4; + + val = read_uint32(buf, &offset); + if (val != index) { + ui_log(op, "Invalid file index %d; expected %d.", val, index); + return -1; + } + + fileInfo->type = read_uint32(buf, &offset); + fileInfo->attributes = read_uint32(buf, &offset); + + val = read_uint32(buf, &offset); + if (offset + val > size) { + ui_log(op, "Bad filename length."); + return -1; + } + + fileInfo->name = nvalloc(val + 1); + memcpy(fileInfo->name, buf + offset, val); + offset += val; + + val = read_uint32(buf, &offset); + if (offset + val > size) { + ui_log(op, "Bad linked module name length."); + return -1; + } + + fileInfo->linked_module_name = nvalloc(val + 1); + memcpy(fileInfo->linked_module_name, buf + offset, val); + offset += val; + + val = read_uint32(buf, &offset); + if (offset + val > size) { + ui_log(op, "Bad core object file name length."); + return -1; + } + + fileInfo->core_object_name = nvalloc(val + 1); + memcpy(fileInfo->core_object_name, buf + offset, val); + offset += val; + + fileInfo->crc = read_uint32(buf, &offset); + + fileInfo->size = read_uint32(buf, &offset); + if (offset + fileInfo->size > size) { + ui_log(op, "Bad file length."); + return -1; + } + + fileInfo->data = nvalloc(fileInfo->size); + memcpy(fileInfo->data, buf + offset, fileInfo->size); + offset += fileInfo->size; + + val = read_uint32(buf, &offset); + if (val != fileInfo->crc) { + ui_log(op, "The redundant stored CRC values %" PRIu32 " and %" PRIu32 + " disagree with each other; the file may be corrupted.", + fileInfo->crc, val); + return -1; + } + + val = compute_crc_from_buffer(fileInfo->data, fileInfo->size); + if (val != fileInfo->crc) { + ui_log(op, "The CRC for the file '%s' (%" PRIu32 ") does not match the " + "expected value (%" PRIu32 ").", fileInfo->name, val, + fileInfo->crc); + } + + fileInfo->linked_module_crc = read_uint32(buf, &offset); + + fileInfo->signature_size = read_uint32(buf, &offset); + if(fileInfo->signature_size) { + if (offset + fileInfo->signature_size > size) { + ui_log(op, "Bad signature size"); + return -1; + } + fileInfo->signature = nvalloc(fileInfo->signature_size); + memcpy(fileInfo->signature, buf + offset, fileInfo->signature_size); + offset += fileInfo->signature_size; + } + + val = read_uint32(buf, &offset); + if (val != index) { + ui_log(op, "Invalid file index %d; expected %d.", val, index); + return -1; + } + + if (strncmp(buf + offset, PRECOMPILED_FILE_FOOTER, 4) != 0) { + ui_log(op, "Unrecognized footer for packaged file."); + return -1; + } + offset += 4; + + return offset - oldoffset; +} + + +/* + * precompiled_find_file() - search for a file with the given name within the + * given PrecompiledInfo record, and return a pointer to it if found, or NULL + * if not found. + */ + +PrecompiledFileInfo *precompiled_find_file(const PrecompiledInfo *info, + const char *file) +{ + int i; + + for (i = 0; i < info->num_files; i++) { + if (strcmp(file, info->files[i].name) == 0) { + return info->files + i; + } + } + + return NULL; +} + +/* + * precompiled_append_files() - append the given PrecompiledFileInfo array to + * the already existing files in the given PrecompiledInfo. + */ + +void precompiled_append_files(PrecompiledInfo *info, PrecompiledFileInfo *files, + int num_files) +{ + info->files = nvrealloc(info->files, (info->num_files + num_files) * + sizeof(PrecompiledFileInfo)); + memcpy(info->files + info->num_files, files, + num_files * sizeof(PrecompiledFileInfo)); + info->num_files += num_files; +} + +/* + * precompiled_file_type_name() - return a pointer to a human-readable string + * naming a file type. The string should not be freed. + */ +const char *precompiled_file_type_name(uint32 file_type) +{ + static const char *file_type_names[] = { + "precompiled kernel interface", + "precompiled kernel module", + }; + + if (file_type >= ARRAY_LEN(file_type_names)) { + return "unknown file type"; + } + + return file_type_names[file_type]; +} + +/* + * precompiled_file_attribute_names() - return a NULL-terminated list of + * human-readable strings naming the attributes in the given file attribute + * mask. The list should be freed when no longer used, but the constituent + * strings should not be freed. + */ +const char **precompiled_file_attribute_names(uint32 attribute_mask) +{ + const char **ret; + int i, attr = 0; + + static const char *file_attribute_names[] = { + "detached signature", + "linked module crc", + "embedded signature", + }; + + ret = nvalloc((ARRAY_LEN(file_attribute_names) + 1) * sizeof(char *)); + + for (i = 0; i < 32; i++) { + if (attribute_mask & (1 << i)) { + ret[attr++] = file_attribute_names[i]; + } + } + ret[attr] = NULL; + + return ret; +} + + + +/* + * byte_tail() - copy from infile, starting at the specified byte offset, and + * going until the end of infile to a newly allocated buffer, a pointer to + * which is stored at location given by the caller. Returns the size of the new + * buffer on success; returns 0 and sets the caller pointer to NULL on failure. + * This is needed because `tail -c` is unreliable in some implementations. + */ +int byte_tail(const char *infile, int start, char **buf) +{ + FILE *in = NULL; + int ret, end, size = 0; + + in = fopen(infile, "r"); + + if (!in) { + goto done; + } + + ret = fseek(in, 0, SEEK_END); + if (ret != 0) { + goto done; + } + end = ftell(in); + + ret = fseek(in, start, SEEK_SET); + if (ret != 0) { + goto done; + } + + size = end - start; + *buf = nvalloc(size); + + ret = (fread(*buf, 1, size + 1, in)); + if (ret != size || ferror(in) || !feof(in)) { + nvfree(*buf); + *buf = NULL; + goto done; + } + +done: + fclose(in); + return size; +} diff --git a/precompiled.h b/precompiled.h index 999208b..7d727a6 100644 --- a/precompiled.h +++ b/precompiled.h @@ -2,7 +2,7 @@ * nvidia-installer: A tool for installing NVIDIA software packages on * Unix and Linux systems. * - * Copyright (C) 2003 NVIDIA Corporation + * Copyright (C) 2003-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, @@ -16,35 +16,178 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses>. * + * precompiled.h: common definitions for mkprecompiled and nvidia-installer's + * precompiled kernel interface/module package format * - * precompiled.h + * The format of a precompiled kernel interface package is: + * + * the first 8 bytes are: "\aNVIDIA\a" + * + * the next 4 bytes (unsigned) are: version number of the package format + * + * the next 4 bytes (unsigned) are: the length of the version string (v) + * + * the next v bytes are the version string + * + * the next 4 bytes (unsigned) are: the length of the description (d) + * + * the next d bytes are the description + * + * the next 4 bytes (unsigned) are: the length of the proc version string (p) + * + * the next p bytes are the proc version string + * + * the next 4 bytes (unsigned) are: the number of files in this package (f) + * + * for each of the f packaged files: + * + * the first 4 bytes are: "FILE" + * + * the next 4 bytes (unsigned) are: the 0-indexed sequence number of this file + * + * the next 4 bytes (unsigned) are the file type: + * 0: precompiled interface + * 1: precompiled kernel module + * + * the next 4 bytes are an attribute mask: + * 1: has detached signature + * 2: has linked module CRC + * 4: has embedded signature + * + * the next 4 bytes (unsigned) are: length of the file name (n) + * + * the next n bytes are the file name + * + * the next 4 bytes (unsigned) are: length of the linked module name (m) + * + * the next m bytes are the linked module name (for kernel interfaces only) + * + * the next 4 bytes (unsigned) are: length of the core object file name (o) + * + * the next o bytes are the core object file name (for kernel interfaces only) + * + * the next 4 bytes (unsigned) are: CRC of the packaged file + * + * the next 4 bytes (unsigned) are: size of the packaged file (l) + * + * the next l bytes is the packaged file + * + * the next 4 bytes (unsigned) are: CRC of the packaged file, again + * + * the next 4 bytes (unsigned) are: CRC of linked module, when appropriate; + * undefined if "has linked module CRC" attribute is not set + * + * the next 4 bytes (unsigned) are: length of detached signature (s), when + * appropriate; 0 if "has detached signature" attribute is not set + * + * the next (s) bytes are: detached signature + * + * the next 4 bytes (unsigned) are: the 0-indexed sequence number of this file + * + * the next 4 bytes are: "END." */ #ifndef __NVIDIA_INSTALLER_PRECOMPILED_H__ #define __NVIDIA_INSTALLER_PRECOMPILED_H__ -#define OUTPUT_FILENAME "nv-linux.o" +#define PRECOMPILED_PKG_CONSTANT_LENGTH (8 + /* precompiled package header */ \ + 4 + /* package format version */ \ + 4 + /* driver version string length */ \ + 4 + /* description string length */ \ + 4 + /* proc version string length */ \ + 4) /* number of files */ + +#define PRECOMPILED_PKG_HEADER "\aNVIDIA\a" + +#define PRECOMPILED_PKG_VERSION 1 + +#define PRECOMPILED_FILE_CONSTANT_LENGTH (4 + /* precompiled file header */ \ + 4 + /* file serial number */ \ + 4 + /* file type */ \ + 4 + /* attributes mask */ \ + 4 + /* file name length */ \ + 4 + /* linked module name length */ \ + 4 + /* core object name length */ \ + 4 + /* file crc */ \ + 4 + /* file size */ \ + 4 + /* redundant file crc */ \ + 4 + /* linked module crc */ \ + 4 + /* detached signature length */ \ + 4 + /* redundant file serial number */ \ + 4) /* precompiled file footer*/ + +#define PRECOMPILED_FILE_HEADER "FILE" +#define PRECOMPILED_FILE_FOOTER "END." + +enum { + PRECOMPILED_FILE_TYPE_INTERFACE = 0, + PRECOMPILED_FILE_TYPE_MODULE, +}; -typedef struct { - +enum { + PRECOMPILED_FILE_HAS_DETACHED_SIGNATURE = 0, + PRECOMPILED_FILE_HAS_LINKED_MODULE_CRC, + PRECOMPILED_FILE_HAS_EMBEDDED_SIGNATURE, +}; + +#define PRECOMPILED_ATTR(attr) (1 << PRECOMPILED_FILE_HAS_##attr) + +typedef struct __precompiled_file_info { + uint32 type; + uint32 attributes; + char *name; + char *linked_module_name; + char *core_object_name; uint32 crc; + uint32 size; + uint8 *data; + uint32 linked_module_crc; + uint32 signature_size; + char *signature; +} PrecompiledFileInfo; + +typedef struct __precompiled_info { + + uint32 package_size; char *version; char *proc_version_string; char *description; - char *detached_signature; - uint32 linked_module_crc; + int num_files; + PrecompiledFileInfo *files; } PrecompiledInfo; -char *read_proc_version(Options *op); +char *read_proc_version(Options *op, const char *proc_mount_point); + +PrecompiledInfo *get_precompiled_info(Options *op, + const char *filename, + const char *real_proc_version_string, + const char *package_version); + +PrecompiledFileInfo *precompiled_find_file(const PrecompiledInfo *info, + const char *file); + +int precompiled_file_unpack(Options *op, const PrecompiledFileInfo *fileInfo, + const char *output_directory); +int precompiled_unpack(Options *op, const PrecompiledInfo *info, + const char *output_filename); + +int precompiled_pack(const PrecompiledInfo *info, const char *package_filename); -PrecompiledInfo *precompiled_unpack(Options *op, - const char *filename, - const char *output_filename, - const char *real_proc_version_string, - const char *package_version); void free_precompiled(PrecompiledInfo *info); +void free_precompiled_file_data(PrecompiledFileInfo fileInfo); +int precompiled_read_interface(PrecompiledFileInfo *fileInfo, + const char *filename, + const char *linked_module_name, + const char *core_object_name); +int precompiled_read_module(PrecompiledFileInfo *fileInfo, const char *filename); +void precompiled_append_files(PrecompiledInfo *info, PrecompiledFileInfo *files, + int num_files); + +const char *precompiled_file_type_name(uint32 file_type); +const char **precompiled_file_attribute_names(uint32 attribute_mask); +int byte_tail(const char *infile, int start, char **buf); #endif /* __NVIDIA_INSTALLER_PRECOMPILED_H__ */ @@ -1 +1 @@ -NVIDIA_VERSION = 319.32 +NVIDIA_VERSION = 325.08 |