/* * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "error.h" #include "file.h" #include "file_open.h" #include "internal.h" #include "log.h" #include "mem.h" #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_IO_H #include #endif #if HAVE_MMAP #include #elif HAVE_MAPVIEWOFFILE #include #endif #ifdef _WIN32 /* struct stat has a 32 bit st_size with the Windows SDK, use the 64 bit variants */ #undef stat #undef fstat #define stat _stat64 #define fstat _fstat64 #endif typedef struct FileLogContext { const AVClass *class; int log_offset; void *log_ctx; } FileLogContext; static const AVClass file_log_ctx_class = { .class_name = "FILE", .item_name = av_default_item_name, .option = NULL, .version = LIBAVUTIL_VERSION_INT, .log_level_offset_offset = offsetof(FileLogContext, log_offset), .parent_log_context_offset = offsetof(FileLogContext, log_ctx), }; #if HAVE_MMAP || HAVE_MAPVIEWOFFILE /* Map the first size bytes of the file, as a private copy or shared and * writable. The shared mapping extends the file to size, on Windows the * mapping object does it. */ static int map_file(int fd, size_t size, int shared, void **ptr) { #if HAVE_MMAP if (shared) { struct stat st; if (fstat(fd, &st) < 0) return AVERROR(errno); if (st.st_size < size && ftruncate(fd, size) < 0) return AVERROR(errno); } void *map = mmap(NULL, size, PROT_READ | PROT_WRITE, shared ? MAP_SHARED : MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) return AVERROR(errno); #else HANDLE fh = (HANDLE)_get_osfhandle(fd); if (fh == INVALID_HANDLE_VALUE) return AVERROR(EBADF); HANDLE mh = CreateFileMapping(fh, NULL, shared ? PAGE_READWRITE : PAGE_READONLY, (uint64_t)size >> 32, size, NULL); if (!mh) return AVERROR(EIO); void *map = MapViewOfFile(mh, shared ? FILE_MAP_ALL_ACCESS : FILE_MAP_COPY, 0, 0, size); CloseHandle(mh); if (!map) return AVERROR(EIO); #endif *ptr = map; return 0; } static void unmap_file(void *ptr, size_t size) { #if HAVE_MMAP munmap(ptr, size); #else UnmapViewOfFile(ptr); #endif } #endif int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, int log_offset, void *log_ctx) { FileLogContext file_log_ctx = { &file_log_ctx_class, log_offset, log_ctx }; int err, fd = avpriv_open(filename, O_RDONLY); struct stat st; av_unused void *ptr; *bufptr = NULL; *size = 0; if (fd < 0) { err = AVERROR(errno); av_log(&file_log_ctx, AV_LOG_ERROR, "Cannot read file '%s': %s\n", filename, av_err2str(err)); return err; } if (fstat(fd, &st) < 0) { err = AVERROR(errno); av_log(&file_log_ctx, AV_LOG_ERROR, "Error occurred in fstat(): %s\n", av_err2str(err)); close(fd); return err; } if (st.st_size > SIZE_MAX) { av_log(&file_log_ctx, AV_LOG_ERROR, "File size for file '%s' is too big\n", filename); close(fd); return AVERROR(EINVAL); } *size = st.st_size; if (!*size) { *bufptr = NULL; goto out; } #if HAVE_MMAP || HAVE_MAPVIEWOFFILE err = map_file(fd, *size, 0, &ptr); if (err < 0) { av_log(&file_log_ctx, AV_LOG_ERROR, "Cannot map file '%s': %s\n", filename, av_err2str(err)); close(fd); *size = 0; return err; } *bufptr = ptr; #else *bufptr = av_malloc(*size); if (!*bufptr) { av_log(&file_log_ctx, AV_LOG_ERROR, "Memory allocation error occurred\n"); close(fd); *size = 0; return AVERROR(ENOMEM); } read(fd, *bufptr, *size); #endif out: close(fd); return 0; } void av_file_unmap(uint8_t *bufptr, size_t size) { if (!size || !bufptr) return; #if HAVE_MMAP || HAVE_MAPVIEWOFFILE unmap_file(bufptr, size); #else av_free(bufptr); #endif } int av_file_map_shared(int fd, size_t size, void **bufptr) { *bufptr = NULL; if (!size) return AVERROR(EINVAL); #if HAVE_MMAP || HAVE_MAPVIEWOFFILE return map_file(fd, size, 1, bufptr); #else return AVERROR(ENOSYS); #endif } void av_file_unmap_shared(void *bufptr, size_t size) { #if HAVE_MMAP || HAVE_MAPVIEWOFFILE if (size && bufptr) unmap_file(bufptr, size); #endif }