X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Ffile.c;h=9f5750d317f29589b0f74841bf3b04a87b7cfd00;hb=f49cbfc01af00fce8f94b6b8ba8d0f9bed20858b;hp=572f406a6cfada8c14984eac228d00e62ccc72e5;hpb=e131931fa3e4293a68388c79538543d2a747256c;p=vlc diff --git a/modules/access/file.c b/modules/access/file.c index 572f406a6c..9f5750d317 100644 --- a/modules/access/file.c +++ b/modules/access/file.c @@ -2,7 +2,7 @@ * file.c: file input (file: access plug-in) ***************************************************************************** * Copyright (C) 2001-2006 the VideoLAN team - * Copyright © 2006 Rémi Denis-Courmont + * Copyright © 2006-2007 Rémi Denis-Courmont * $Id$ * * Authors: Christophe Massiot @@ -27,11 +27,11 @@ * Preamble *****************************************************************************/ #include -#include -#include +#include +#include +#include -#include -#include +#include #include #ifdef HAVE_SYS_TYPES_H # include @@ -49,10 +49,11 @@ # include # include #endif +#ifdef HAVE_MMAP +# include +#endif #if defined( WIN32 ) && !defined( UNDER_CE ) -/* fstat() support for large files on win32 */ -# define fstat(a,b) _fstati64(a,b) # ifdef lseek # undef lseek # endif @@ -69,7 +70,7 @@ # define lseek fseek #endif -#include "charset.h" +#include /***************************************************************************** * Module descriptor @@ -92,7 +93,7 @@ vlc_module_begin(); set_category( CAT_INPUT ); set_subcategory( SUBCAT_INPUT_ACCESS ); add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); - add_deprecated( "file-cat", VLC_TRUE ); + add_obsolete_string( "file-cat" ); set_capability( "access2", 50 ); add_shortcut( "file" ); add_shortcut( "stream" ); @@ -105,13 +106,17 @@ vlc_module_end(); * Exported prototypes *****************************************************************************/ static int Seek( access_t *, int64_t ); -static int Read( access_t *, uint8_t *, int ); +static ssize_t Read( access_t *, uint8_t *, size_t ); static int Control( access_t *, int, va_list ); +#ifdef HAVE_MMAP +static block_t *mmapBlock( access_t * ); +#endif static int open_file( access_t *, const char * ); struct access_sys_t { + uint64_t pagemask; unsigned int i_nb_reads; vlc_bool_t b_kfir; @@ -171,7 +176,7 @@ static int Open( vlc_object_t *p_this ) while (fd != -1) { if (fstat (fd, &st)) - msg_Err (p_access, "fstat(%d): %s", fd, strerror (errno)); + msg_Err (p_access, "fstat(%d): %m", fd); else if (S_ISDIR (st.st_mode)) /* The directory plugin takes care of that */ @@ -196,6 +201,35 @@ static int Open( vlc_object_t *p_this ) if (!S_ISREG (st.st_mode) && !S_ISBLK (st.st_mode) && (!S_ISCHR (st.st_mode) || (st.st_size == 0))) p_sys->b_seekable = VLC_FALSE; + +# ifdef HAVE_MMAP + p_sys->pagemask = sysconf (_SC_PAGE_SIZE) - 1; + + /* Autodetect mmap() support */ + if (p_sys->b_pace_control && S_ISREG (st.st_mode) && (st.st_size > 0)) + { + /* TODO: Do not allow PROT_WRITE, we should not need it. + * However, this far, "block" ownership seems such that whoever + * "receives" a block can freely modify its content. Hence we _may_ + * need PROT_WRITE not to default memory protection. + * NOTE: With MAP_PRIVATE, changes are not committed to the underlying + * file, write open permission is not required. + */ + void *addr = mmap (NULL, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (addr != MAP_FAILED) + { + /* Does the file system support mmap? */ + munmap (addr, 1); + p_access->pf_read = NULL; + p_access->pf_block = mmapBlock; + msg_Dbg (p_this, "mmap enabled"); + } + else + msg_Dbg (p_this, "mmap disabled (%m)"); + } + else + msg_Dbg (p_this, "mmap disabled (non regular file)"); +# endif #else p_sys->b_seekable = !b_stdin; # warning File size not known! @@ -227,10 +261,10 @@ static void Close (vlc_object_t * p_this) /***************************************************************************** * Read: standard read on a file descriptor. *****************************************************************************/ -static int Read( access_t *p_access, uint8_t *p_buffer, int i_len ) +static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len ) { access_sys_t *p_sys = p_access->p_sys; - int i_ret; + ssize_t i_ret; int fd = p_sys->fd; #if !defined(WIN32) && !defined(UNDER_CE) @@ -280,10 +314,9 @@ static int Read( access_t *p_access, uint8_t *p_buffer, int i_len ) break; default: - msg_Err (p_access, "read failed (%s)", strerror (errno)); + msg_Err (p_access, "read failed (%m)"); intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"), - _("VLC could not read file \"%s\"."), - strerror (errno)); + _("VLC could not read the file.")); } /* Delay a bit to avoid consuming all the CPU. This is particularly @@ -316,27 +349,117 @@ static int Read( access_t *p_access, uint8_t *p_buffer, int i_len ) return i_ret; } +#ifdef HAVE_MMAP +# define MMAP_SIZE (1 << 20) + +static block_t *mmapBlock (access_t *p_access) +{ + access_sys_t *p_sys = p_access->p_sys; + + const int flags = MAP_SHARED; + off_t offset = p_access->info.i_pos & ~p_sys->pagemask; + size_t align = p_access->info.i_pos & p_sys->pagemask; + size_t length = (MMAP_SIZE > p_sys->pagemask) ? MMAP_SIZE : (p_sys->pagemask + 1); + void *addr; + +#ifndef NDEBUG + int64_t dbgpos = lseek (p_sys->fd, 0, SEEK_CUR); + if (dbgpos != p_access->info.i_pos) + msg_Err (p_access, "position: 0x%08llx instead of 0x%08llx", + p_access->info.i_pos, dbgpos); +#endif + + if (p_access->info.i_pos >= p_access->info.i_size) + { + /* End of file - check if file size changed... */ + struct stat st; + + if ((fstat (p_sys->fd, &st) == 0) + && (st.st_size != p_access->info.i_size)) + { + p_access->info.i_size = st.st_size; + p_access->info.i_update |= INPUT_UPDATE_SIZE; + } + + /* Really at end of file then */ + if (p_access->info.i_pos >= p_access->info.i_size) + { + p_access->info.b_eof = VLC_TRUE; + msg_Dbg (p_access, "at end of memory mapped file"); + return NULL; + } + } + + if (offset + length > p_access->info.i_size) + /* Don't mmap beyond end of file */ + length = p_access->info.i_size - offset; + + assert (offset <= p_access->info.i_pos); /* and */ + assert (p_access->info.i_pos < p_access->info.i_size); /* imply */ + assert (offset < p_access->info.i_size); /* imply */ + assert (length > 0); + + addr = mmap (NULL, length, PROT_READ, flags, p_sys->fd, offset); + if (addr == MAP_FAILED) + { + msg_Err (p_access, "memory mapping failed (%m)"); + intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"), + _("VLC could not read the file.")); + msleep( INPUT_ERROR_SLEEP ); + return NULL; + } + + p_access->info.i_pos = offset + length; + + block_t *block = block_mmap_Alloc (addr, length); + if (block == NULL) + return NULL; + + block->p_buffer += align; + block->i_buffer -= align; + +#ifndef NDEBUG + msg_Dbg (p_access, "mapped 0x%lx bytes at %p from offset 0x%lx", + (unsigned long)length, addr, (unsigned long)offset); + + /* Compare normal I/O with memory mapping */ + char *buf = malloc (block->i_buffer); + ssize_t i_read = read (p_sys->fd, buf, block->i_buffer); + + if (i_read != (ssize_t)block->i_buffer) + msg_Err (p_access, "read %u instead of %u bytes", (unsigned)i_read, + (unsigned)block->i_buffer); + if (memcmp (buf, block->p_buffer, block->i_buffer)) + msg_Err (p_access, "inconsistent data buffer"); + free (buf); +#endif + + return block; +} +#endif + /***************************************************************************** * Seek: seek to a specific location in a file *****************************************************************************/ static int Seek (access_t *p_access, int64_t i_pos) { - if (i_pos > p_access->info.i_size) + /* FIXME: i_size should really be unsigned */ + if ((uint64_t)i_pos > (uint64_t)p_access->info.i_size) { - msg_Err (p_access, "seeking too far"); + /* This should only happen with corrupted files. + * But it also seems to happen with buggy demuxes (ASF) */ + msg_Err (p_access, "seeking too far (0x"I64Fx" / 0x"I64Fx")", + i_pos, p_access->info.i_size); i_pos = p_access->info.i_size; } - else if (i_pos < 0) - { - msg_Err (p_access, "seeking too early"); - i_pos = 0; - } p_access->info.i_pos = i_pos; p_access->info.b_eof = VLC_FALSE; - /* Determine which file we need to access */ - lseek (p_access->p_sys->fd, i_pos, SEEK_SET); +#if defined (HAVE_MMAP) && defined (NDEBUG) + if (p_access->pf_block == NULL) +#endif + lseek (p_access->p_sys->fd, i_pos, SEEK_SET); return VLC_SUCCESS; } @@ -386,6 +509,7 @@ static int Control( access_t *p_access, int i_query, va_list args ) case ACCESS_SET_SEEKPOINT: case ACCESS_SET_PRIVATE_ID_STATE: case ACCESS_GET_META: + case ACCESS_GET_CONTENT_TYPE: return VLC_EGENERIC; default: @@ -433,8 +557,8 @@ static int open_file (access_t *p_access, const char *psz_name) if ( !p_sys->fd ) { msg_Err( p_access, "cannot open file %s", psz_name ); - intf_UserFatal( p_access, VLC_FALSE, _("File reading failed"), - _("VLC could not open file \"%s\"."), psz_name ); + intf_UserFatal( p_access, VLC_FALSE, _("File reading failed"), + _("VLC could not open the file \"%s\"."), psz_name ); free (path); return VLC_EGENERIC; } @@ -445,13 +569,12 @@ static int open_file (access_t *p_access, const char *psz_name) fseek( p_sys->fd, 0, SEEK_SET ); #else int fd = utf8_open (path, O_RDONLY | O_NONBLOCK /* O_LARGEFILE*/, 0666); + free (path); if (fd == -1) { - msg_Err (p_access, "cannot open file %s (%s)", psz_name, - strerror (errno)); - intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"), - _("VLC could not open file \"%s\" (%s)."), - psz_name, strerror (errno)); + msg_Err (p_access, "cannot open file %s (%m)", psz_name); + intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"), + _("VLC could not open the file \"%s\"."), psz_name); return -1; }