/
githubmirror
/
libsmbios
Обзор
Документация
Войти
/
githubmirror
/
libsmbios
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/libsmbios_c/memory/memory_linux.c
331 строка
10 KB
Mario Limonciello
Fix bus errors with small memory files (Closes: #58)
17 июл 2018, 23:18
17 июл 2018, 23:18
dab5dfd
Код
Авторство
О чём код?
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=c:cindent:textwidth=0: * * Copyright (C) 2005 Dell Inc. * by Michael Brown <Michael_E_Brown@dell.com> * Licensed under the Open Software License version 2.1 * * Alternatively, you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * 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. */ #define LIBSMBIOS_C_SOURCE // Include compat.h first, then system headers, then public, then private #include "smbios_c/compat.h" #include <stdarg.h> // va_list #include <stdlib.h> #include <ctype.h> // isalnum #include <stdio.h> #include <string.h> // memcpy #include <errno.h> #include <sys/mman.h> // mmap #include <sys/stat.h> #include "smbios_c/obj/memory.h" #include "smbios_c/types.h" #include "memory_impl.h" #include "common_internal.h" // usually want to include this last #include "libsmbios_c_intlize.h" struct linux_data { char *filename; FILE *fd; int mem_errno; bool rw; void *lastMapping; off_t lastMappedOffset; size_t mappingSize; size_t fileSize; }; #define READ_MMAP 0 #define WRITE_MMAP 1 static void closefds(struct linux_data *private_data) { fnprintf("\n"); if (private_data->lastMapping) { fnprintf("\t\tmunmap(%p)\n", private_data->lastMapping); munmap(private_data->lastMapping, private_data->mappingSize); } private_data->lastMapping = 0; private_data->lastMappedOffset = -1; if (private_data->fd) fclose(private_data->fd); private_data->fd = 0; } static FILE * reopen(struct linux_data *private_data, int rw) { char *openMode = rw ? "r+b": "rb"; fnprintf(" file: %s, rw: %d\n", private_data->filename, rw ); if(private_data->fd) fclose(private_data->fd); private_data->rw = rw; private_data->lastMapping = 0; private_data->lastMappedOffset = -1; private_data->fd = fopen( private_data->filename, openMode ); // re-open for write return private_data->fd; } #ifdef DEBUG_MEMORY_C static void debug_dump_buffer(const char *fn, const char *s, const u8 *buffer, size_t start, size_t toCopy) { dbg_printf("%s %s: ", fn, s); for(int i=0;i<(toCopy>100?100:toCopy);++i) if (isalnum(buffer[start + i])) dbg_printf("%c", buffer[start + i]); else dbg_printf("*"); dbg_printf("'\n"); } #else #define debug_dump_buffer(...) do {} while(0) #endif static void remap(struct linux_data *private_data, off_t offset, bool rw) { int flags = rw ? PROT_WRITE : PROT_READ; off_t mmoff = offset % private_data->mappingSize; fnprintf("\n"); // no need to remap if we already have the correct area mapped. if (offset-mmoff == private_data->lastMappedOffset) goto out; private_data->lastMappedOffset = offset-mmoff; if (private_data->lastMapping) munmap(private_data->lastMapping, private_data->mappingSize); private_data->lastMapping = mmap ( 0, private_data->mappingSize, flags, MAP_SHARED, fileno(private_data->fd), private_data->lastMappedOffset); // last arg, offset, must be mod pagesize. out: return; } static size_t trycopy(struct linux_data *private_data, u8 *buffer, u64 offset, size_t length, bool rw) { off_t mmoff = offset % private_data->mappingSize; fnprintf("\t\tbuffer(%p), offset(%lld), length(%zd), mmoff(%lld)\n", buffer, offset, length, (u64)mmoff); if( length + mmoff > (private_data->mappingSize) ) length = (private_data->mappingSize) - mmoff; fnprintf("\t\tCOPYING(%zu)\n", length); if (rw) memcpy(((u8 *)(private_data->lastMapping) + mmoff), buffer, length); else memcpy(buffer, ((const u8 *)(private_data->lastMapping) + mmoff), length); debug_dump_buffer(__PRETTY_FUNCTION__, "BUFFER", buffer, 0, length); debug_dump_buffer(__PRETTY_FUNCTION__, "MEMORY", (const u8 *)(private_data->lastMapping), mmoff, length); return length; } static int copy_mmap(const struct memory_access_obj *this, u8 *buffer, u64 offset, size_t length, bool rw) { struct linux_data *private_data = (struct linux_data *)this->private_data; private_data->mem_errno = errno = 0; int retval = -1; const char *error = 0; size_t bytesCopied = 0; fnprintf("buffer(%p) offset(%lld) length(%zd) rw(%d)\n", buffer, offset, length, rw); fnprintf("->rw: %d fd: %p\n", private_data->rw, private_data->fd); if(private_data->fileSize > 0 && length + offset > private_data->fileSize) { error = _("File size is too small: File: "); errno = EINVAL; goto err_out; } error = _("Could not (re)open file. File: "); if( (rw && !private_data->rw) || !private_data->fd) if (!reopen(private_data, rw)) goto err_out; fnprintf("Start of copy loop\n"); while( bytesCopied < length ) { fnprintf("\tLOOP: bytesCopied(%zd) length(%zd)\n", bytesCopied, length); remap(private_data, offset + bytesCopied, rw); fnprintf("\tlastMapping(%p)\n", private_data->lastMapping); error = _("The mmap() call returned mapping of -1 (failure). File: "); if (private_data->lastMapping == (void *)-1) goto err_out; bytesCopied += trycopy( private_data, buffer + bytesCopied, offset + bytesCopied, length - bytesCopied, rw); } retval = 0; goto out; err_out: fnprintf("%s - ERR_OUT: %d \n", __PRETTY_FUNCTION__, errno); perror("ERR_OUT: "); fnprintf("%s\n", strerror(errno)); private_data->mem_errno = errno; strlcpy(this->errstring, error, ERROR_BUFSIZE); strlcat(this->errstring, private_data->filename, ERROR_BUFSIZE); strlcat(this->errstring, _("\nThe OS Error string was: "), ERROR_BUFSIZE); fixed_strerror(errno, this->errstring, ERROR_BUFSIZE); if (private_data->lastMapping == (void*)-1) private_data->lastMapping = 0; out: // close on error, or if close hint fnprintf("\t\t out: lastMapping(%p)\n", private_data->lastMapping); if (memory_obj_should_close(this) || retval) closefds(private_data); return retval; } static int linux_read_fn(const struct memory_access_obj *this, u8 *buffer, u64 offset, size_t length) { return copy_mmap(this, buffer, offset, length, READ_MMAP); } static int linux_write_fn(const struct memory_access_obj *this, u8 *buffer, u64 offset, size_t length) { return copy_mmap(this, buffer, offset, length, WRITE_MMAP); } static void linux_cleanup(struct memory_access_obj *this) { struct linux_data *private_data = (struct linux_data *)this->private_data; fnprintf(" memory\n"); closefds(private_data); private_data->mem_errno = 0; private_data->rw = 0; } static void linux_free(struct memory_access_obj *this) { struct linux_data *private_data = (struct linux_data *)this->private_data; fnprintf("\n"); if (this->errstring) free(this->errstring); this->errstring = NULL; if (private_data) { if (private_data->filename) free(private_data->filename); private_data->filename = NULL; free(private_data); this->private_data = NULL; } this->initialized=0; } __hidden int init_mem_struct_filename(struct memory_access_obj *m, const char *fn) { char *errbuf=0; int retval = 0; const char *error; struct linux_data *private_data; struct stat st_dev; fnprintf("\n"); // do allocations error = _("There was an allocation failure while trying to construct the memory object. Filename: "); m->private_data = NULL; m->private_data = private_data = calloc(1, sizeof(struct linux_data)); if (!private_data) goto out_fail; private_data->filename = calloc(1, strlen(fn) + 1); m->errstring = calloc(1, ERROR_BUFSIZE); if (!private_data->filename || !m->errstring) goto out_fail; strcat(private_data->filename, fn); private_data->lastMappedOffset = -1; private_data->rw = 0; private_data->mappingSize = getpagesize(); // must be power of 2, >= getpagesize() m->free = linux_free; m->read_fn = linux_read_fn; m->write_fn = linux_write_fn; m->cleanup = linux_cleanup; m->close = 1; error = _("File open error during memory object construction. The filename: "); if (!reopen(private_data, false)) goto out_fail; if (fstat (fileno(private_data->fd), &st_dev) == -1) goto out_fail; if (S_ISCHR (st_dev.st_mode)) private_data->fileSize = -1; else private_data->fileSize = st_dev.st_size; closefds(private_data); m->initialized = 1; goto out; out_fail: // if any allocations failed, roll everything back. This should be safe. fnprintf("out_fail:\n"); errbuf = memory_get_module_error_buf(); if (errbuf){ strlcpy(errbuf, error, ERROR_BUFSIZE); strlcat(errbuf, fn, ERROR_BUFSIZE); strlcat(errbuf, _("\nThe OS Error string was: "), ERROR_BUFSIZE); fixed_strerror(errno, errbuf, ERROR_BUFSIZE); fnprintf(" errbuf ->%p (%zd) '%s'\n", errbuf, strlen(errbuf), errbuf); } linux_free(m); retval = -1; out: fnprintf("out:\n"); return retval; } __hidden int init_mem_struct(struct memory_access_obj *m) { return init_mem_struct_filename(m, "/dev/mem"); }