Files
nvidia-installer/precompiled.c
Aaron Plattner 04c3e7bf6b 510.39.01
2022-01-11 10:01:21 -08:00

1019 lines
28 KiB
C

/*
* nvidia-installer: A tool for installing NVIDIA software packages on
* Unix and Linux systems.
*
* Copyright (C) 2003 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>.
*
*
* precompiled.c - this source file contains functions for dealing
* with precompiled kernel interfaces.
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdlib.h>
#include "nvidia-installer.h"
#include "user-interface.h"
#include "precompiled.h"
#include "misc.h"
#include "crc.h"
static int precompiled_read_fileinfo(Options *op, PrecompiledFileInfo *fileInfos,
int index, char *buf, int offset, int size);
/*
* 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 read_uint32(const char *buf, int *offset)
{
uint32 ret = 0;
ret += (((uint32) buf[*offset + 3]) & 0xff);
ret <<= 8;
ret += (((uint32) buf[*offset + 2]) & 0xff);
ret <<= 8;
ret += (((uint32) buf[*offset + 1]) & 0xff);
ret <<= 8;
ret += (((uint32) buf[*offset]) & 0xff);
ret <<= 0;
*offset += sizeof(uint32);
return ret;
}
/*
* read_proc_version() - return the string contained in /proc/version
*
* XXX could use getmntent() to determine where the proc filesystem is
* mounted.
*/
char *read_proc_version(Options *op, const char *proc_mount_point)
{
int fd, len, version_len;
char *version, *c = NULL;
char *proc_verson_filename;
len = strlen(proc_mount_point) + 9; /* strlen("/version") + 1 */
proc_verson_filename = (char *) nvalloc(len);
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).",
proc_verson_filename, strerror(errno));
return NULL;
}
/*
* it would be more convenient if we could just mmap(2)
* /proc/version, but proc files do not support mmap(2), so read
* the file instead
*/
len = version_len = 0;
version = NULL;
while (1) {
int ret;
if (version_len == len) {
version_len += NV_LINE_LEN;
version = nvrealloc(version, version_len);
c = version + len;
}
ret = read(fd, c, version_len - len);
if (ret == -1) {
ui_warn(op, "Error reading %s (%s).",
proc_verson_filename, strerror(errno));
nvfree(version);
version = NULL;
goto done;
}
if (ret == 0) {
*c = '\0';
break;
}
len += ret;
c += ret;
}
/* replace a newline with a null-terminator */
c = version;
while ((*c != '\0') && (*c != '\n')) c++;
*c = '\0';
done:
nvfree(proc_verson_filename);
close(fd);
return version;
}
/*
* 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 *get_precompiled_info(Options *op,
const char *filename,
const char *real_proc_version_string,
const char *package_version,
char *const *search_filelist)
{
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 = size = 0;
buf = description = proc_version_string = version = NULL;
/* open the file to be unpacked */
if ((fd = open(filename, O_RDONLY)) == -1) {
ui_expert(op, "Unable to open precompiled kernel interface file "
"'%s' (%s)", filename, strerror(errno));
goto done;
}
/* get the file length */
if (fstat(fd, &stat_buf) == -1) {
ui_expert(op, "Unable to determine '%s' file length (%s).",
filename, strerror(errno));
goto done;
}
size = stat_buf.st_size;
/* check for a minimum length */
if (size < PRECOMPILED_PKG_CONSTANT_LENGTH) {
ui_expert(op, "File '%s' appears to be too short.", filename);
goto done;
}
/* mmap(2) the input file */
buf = mmap(0, size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0);
if (buf == (void *) -1) {
ui_expert(op, "Unable to mmap file %s (%s).",
filename, strerror(errno));
goto done;
}
offset = 0;
/* check for the header */
if (strncmp(buf + offset, PRECOMPILED_PKG_HEADER, 8) != 0) {
ui_expert(op, "File '%s': unrecognized file format.", filename);
goto done;
}
offset += 8;
/* check the package format version */
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 = 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;
}
if (val > 0) {
version = nvalloc(val+1);
memcpy(version, buf + offset, val);
version[val] = '\0';
} else {
version = NULL;
}
offset += val;
/* fail if the version could not be read, or if a package version was
specified and the read version does not match it */
if (!version ||
(package_version && strcmp(version, package_version) != 0)) {
goto done;
}
/* read the description */
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;
}
description = nvalloc(val+1);
memcpy(description, buf + offset, val);
description[val] = '\0';
offset += val;
/* read the proc version string */
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;
}
proc_version_string = nvalloc(val+1);
memcpy(proc_version_string, buf + offset, val);
offset += val;
proc_version_string[val] = '\0';
/* check if the running kernel matches */
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);
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 (ret > 0) {
offset += ret;
} else {
ui_log(op, "An error occurred while trying to parse '%s'.",
filename);
goto done;
}
}
/*
* Check for the package validity.
*
* The package is valid if all of the files specified in
* search_filelist are present in the package.
*/
if (search_filelist != NULL) {
int index;
for (index = 0; search_filelist[index]; index++) {
int found = FALSE;
for (i = 0; i < num_files; i++) {
if (!strcmp(search_filelist[index], fileInfos[i].name)) {
found = TRUE;
break;
}
}
if (!found) {
ui_log(op, "Required file '%s' not found in package '%s'",
search_filelist[index], filename);
goto done;
}
}
}
/*
* now that we can no longer fail, allocate and initialize the
* PrecompiledInfo structure
*/
info = (PrecompiledInfo *) nvalloc(sizeof(PrecompiledInfo));
info->package_size = size;
info->version = version;
info->proc_version_string = proc_version_string;
info->description = description;
info->num_files = num_files;
info->files = fileInfos;
/*
* XXX so that the proc version, description, and version strings, and the
* PrecompiledFileInfo array aren't freed below
*/
proc_version_string = description = version = NULL;
fileInfos = NULL;
done:
/* cleanup whatever needs cleaning up */
if (buf) munmap(buf, size);
if (fd >= 0) close(fd);
nvfree(description);
nvfree(proc_version_string);
nvfree(fileInfos);
nvfree(version);
return info;
}
/*
* 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->target_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) +
strlen(info->files[i].target_directory) +
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);
uint32 target_directory_len = strlen(file->target_directory);
/* 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;
/* target directory name */
encode_uint32(target_directory_len, out, &offset);
memcpy(&(out[offset]), file->target_directory, target_directory_len);
offset += target_directory_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->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);
nvfree(fileInfo.target_directory);
}
/*
* 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,
const char *target_directory,
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->target_directory = nvstrdup(target_directory);
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,
const char *target_directory)
{
return precompiled_read_file(fileInfo, filename, linked_module_name,
core_object_name, target_directory,
PRECOMPILED_FILE_TYPE_INTERFACE);
}
int precompiled_read_module(PrecompiledFileInfo *fileInfo, const char *filename,
const char *target_directory)
{
return precompiled_read_file(fileInfo, filename, "", "", target_directory,
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;
val = read_uint32(buf, &offset);
if (offset + val > size) {
ui_log(op, "Bad target directory name length.");
return -1;
}
fileInfo->target_directory = nvalloc(val + 1);
memcpy(fileInfo->target_directory, 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",
};
static const char *unknown_attribute = "unknown attribute";
const int max_file_attribute_names = sizeof(attribute_mask) * 8;
/* leave room for a NULL terminator */
ret = nvalloc((max_file_attribute_names + 1) * sizeof(char *));
for (i = 0; i < max_file_attribute_names; i++) {
if (attribute_mask & (1 << i)) {
if (i >= ARRAY_LEN(file_attribute_names)) {
ret[attr++] = unknown_attribute;
} else {
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:
if (in) {
fclose(in);
}
return size;
}