X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Ffile.c;h=58aecfb8e91f46690c18b6f51d7691771bc91c69;hb=e5afe49af364081da062133856a8c5adad35bbbf;hp=6f3b568829bcea87a010b4f87a4dbbd0b8d69eac;hpb=1e69605cad646473814562fab990548d279a8d84;p=vlc diff --git a/modules/access/file.c b/modules/access/file.c index 6f3b568829..7b34f3ef36 100644 --- a/modules/access/file.c +++ b/modules/access/file.c @@ -1,16 +1,18 @@ /***************************************************************************** * file.c: file input (file: access plug-in) ***************************************************************************** - * Copyright (C) 2001, 2002 VideoLAN - * $Id: file.c,v 1.21 2003/06/09 09:29:12 massiot Exp $ + * Copyright (C) 2001-2006 the VideoLAN team + * Copyright © 2006-2007 Rémi Denis-Courmont + * $Id$ * * Authors: Christophe Massiot + * Rémi Denis-Courmont * * This program is free software; 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 @@ -18,418 +20,385 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -/***************************************************************************** - * Preamble - *****************************************************************************/ -#include -#include - -#include -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_TIME_H -# include +#ifdef HAVE_CONFIG_H +# include "config.h" #endif + +#include +#include "fs.h" +#include +#include +#include + +#include +#include +#include #ifdef HAVE_SYS_STAT_H # include #endif -#ifdef HAVE_ERRNO_H -# include -#endif #ifdef HAVE_FCNTL_H # include #endif +#if defined (__linux__) +# include +#ifdef HAVE_LINUX_MAGIC_H +# include +#endif +#elif defined (HAVE_SYS_MOUNT_H) +# include +# include +#endif -#ifdef HAVE_UNISTD_H -# include -#elif defined( WIN32 ) && !defined( UNDER_CE ) +#if defined( WIN32 ) # include +# include +# include +#else +# include +# include #endif -/* stat() support for large files on win32 */ #if defined( WIN32 ) && !defined( UNDER_CE ) -#define stat(a,b) _stati64(a,b) -#define fstat(a,b) _fstati64(a,b) +# ifdef lseek +# undef lseek +# endif +# define lseek _lseeki64 +#elif defined( UNDER_CE ) +/* FIXME the commandline on wince is a mess */ +# define dup(a) -1 +# define PathIsNetworkPathW(wpath) (! wcsncmp(wpath, L"\\\\", 2)) #endif -/***************************************************************************** - * Exported prototypes - *****************************************************************************/ -static int Open ( vlc_object_t * ); -static void Close ( vlc_object_t * ); - -static void Seek ( input_thread_t *, off_t ); -static ssize_t Read ( input_thread_t *, byte_t *, size_t ); +#include +#include -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -#define CACHING_TEXT N_("Caching value in ms") -#define CACHING_LONGTEXT N_( \ - "Allows you to modify the default caching value for file streams. This " \ - "value should be set in miliseconds units." ) - -vlc_module_begin(); - set_description( _("Standard filesystem file input") ); - add_category_hint( N_("file"), NULL, VLC_TRUE ); - add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); - set_capability( "access", 50 ); - add_shortcut( "file" ); - add_shortcut( "stream" ); - add_shortcut( "kfir" ); - set_callbacks( Open, Close ); -vlc_module_end(); - -/***************************************************************************** - * _input_socket_t: private access plug-in data, modified to add private - * fields - *****************************************************************************/ -typedef struct _input_socket_s +struct access_sys_t { - input_socket_t _socket; + unsigned int i_nb_reads; - unsigned int i_nb_reads; - vlc_bool_t b_kfir; -} _input_socket_t; + int fd; -/***************************************************************************** - * Open: open the file - *****************************************************************************/ -static int Open( vlc_object_t *p_this ) + /* */ + unsigned caching; + bool b_pace_control; +}; + +#ifndef WIN32 +static bool IsRemote (int fd) { - input_thread_t * p_input = (input_thread_t *)p_this; - char * psz_name = p_input->psz_name; -#ifdef HAVE_SYS_STAT_H - int i_stat; -#if defined( WIN32 ) && !defined( UNDER_CE ) - struct _stati64 stat_info; -#else - struct stat stat_info; -#endif -#endif - _input_socket_t * p_access_data; - vlc_bool_t b_stdin, b_kfir = 0; +#ifdef HAVE_FSTATFS + struct statfs stf; - p_input->i_mtu = 0; + if (fstatfs (fd, &stf)) + return false; - b_stdin = psz_name[0] == '-' && psz_name[1] == '\0'; +#if defined(MNT_LOCAL) + return !(stf.f_flags & MNT_LOCAL); -#ifdef HAVE_SYS_STAT_H - if( !b_stdin && (i_stat = stat( psz_name, &stat_info )) == (-1) ) +#else +# ifdef HAVE_LINUX_MAGIC_H + switch (stf.f_type) { -# ifdef HAVE_ERRNO_H - msg_Warn( p_input, "cannot stat() file `%s' (%s)", - psz_name, strerror(errno)); -# else - msg_Warn( p_input, "cannot stat() file `%s'", psz_name ); -# endif - return VLC_EGENERIC; + case AFS_SUPER_MAGIC: + case CODA_SUPER_MAGIC: + case NCP_SUPER_MAGIC: + case NFS_SUPER_MAGIC: + case SMB_SUPER_MAGIC: + case 0xFF534D42 /*CIFS_MAGIC_NUMBER*/: + return true; } + return false; +# endif #endif +#else /* !HAVE_FSTATFS */ + (void)fd; + return false; - p_input->pf_read = Read; - p_input->pf_set_program = input_SetProgram; - p_input->pf_set_area = NULL; - p_input->pf_seek = Seek; +#endif +} +#endif - vlc_mutex_lock( &p_input->stream.stream_lock ); +#ifndef HAVE_POSIX_FADVISE +# define posix_fadvise(fd, off, len, adv) +#endif - if( *p_input->psz_access && !strncmp( p_input->psz_access, "stream", 7 ) ) - { - /* stream:%s */ - p_input->stream.b_pace_control = 0; - p_input->stream.b_seekable = 0; - p_input->stream.p_selected_area->i_size = 0; - } - else if( *p_input->psz_access && - !strncmp( p_input->psz_access, "kfir", 7 ) ) - { - /* stream:%s */ - p_input->stream.b_pace_control = 0; - p_input->stream.b_seekable = 0; - p_input->stream.p_selected_area->i_size = 0; - b_kfir = 1; - } - else +/***************************************************************************** + * Open: open the file + *****************************************************************************/ +int Open( vlc_object_t *p_this ) +{ + access_t *p_access = (access_t*)p_this; + const char *path = p_access->psz_path; +#ifdef WIN32 + bool is_remote = false; +#endif + + /* Open file */ + int fd = -1; + + if (!strcasecmp (p_access->psz_access, "fd")) { - /* file:%s or %s */ - p_input->stream.b_pace_control = 1; + char *end; + int oldfd = strtol (path, &end, 10); - if( b_stdin ) - { - p_input->stream.b_seekable = 0; - p_input->stream.p_selected_area->i_size = 0; - } -#ifdef UNDER_CE - else if( VLC_TRUE ) - { - /* We'll update i_size after it's been opened */ - p_input->stream.b_seekable = 1; - } -#elif defined( HAVE_SYS_STAT_H ) - else if( S_ISREG(stat_info.st_mode) || S_ISCHR(stat_info.st_mode) - || S_ISBLK(stat_info.st_mode) ) - { - p_input->stream.b_seekable = 1; - p_input->stream.p_selected_area->i_size = stat_info.st_size; - } - else if( S_ISFIFO(stat_info.st_mode) -# if !defined( SYS_BEOS ) && !defined( WIN32 ) - || S_ISSOCK(stat_info.st_mode) -# endif - ) + if (*end == '\0') + fd = dup (oldfd); +#ifdef HAVE_FDOPENDIR + else if (*end == '/' && end > path) { - p_input->stream.b_seekable = 0; - p_input->stream.p_selected_area->i_size = 0; + char *name = decode_URI_duplicate (end - 1); + if (name != NULL) /* TODO: ToLocale(), FD_CLOEXEC */ + { + name[0] = '.'; + fd = openat (oldfd, name, O_RDONLY | O_NONBLOCK); + free (name); + } } #endif - else - { - vlc_mutex_unlock( &p_input->stream.stream_lock ); - msg_Err( p_input, "unknown file type for `%s'", psz_name ); - return VLC_EGENERIC; - } } - - p_input->stream.p_selected_area->i_tell = 0; - p_input->stream.i_method = INPUT_METHOD_FILE; - vlc_mutex_unlock( &p_input->stream.stream_lock ); - - msg_Dbg( p_input, "opening file `%s'", psz_name ); - p_access_data = malloc( sizeof(_input_socket_t) ); - p_input->p_access_data = (void *)p_access_data; - if( p_access_data == NULL ) + else { - msg_Err( p_input, "out of memory" ); - return VLC_ENOMEM; + msg_Dbg (p_access, "opening file `%s'", path); + fd = utf8_open (path, O_RDONLY | O_NONBLOCK); + if (fd == -1) + { + msg_Err (p_access, "cannot open file %s (%m)", path); + dialog_Fatal (p_access, _("File reading failed"), + _("VLC could not open the file \"%s\"."), path); + } + +#ifdef WIN32 + wchar_t wpath[MAX_PATH+1]; + if (MultiByteToWideChar (CP_UTF8, 0, path, -1, + wpath, MAX_PATH) + && PathIsNetworkPathW (wpath)) + is_remote = true; +# define IsRemote( fd ) ((void)fd, is_remote) +#endif } + if (fd == -1) + return VLC_EGENERIC; - p_access_data->i_nb_reads = 0; - p_access_data->b_kfir = b_kfir; - if( b_stdin ) + struct stat st; + if (fstat (fd, &st)) { - p_access_data->_socket.i_handle = 0; + msg_Err (p_access, "failed to read (%m)"); + goto error; } - else + /* Directories can be opened and read from, but only readdir() knows + * how to parse the data. The directory plugin will do it. */ + if (S_ISDIR (st.st_mode)) { -#ifdef UNDER_CE - wchar_t psz_filename[MAX_PATH]; - MultiByteToWideChar( CP_ACP, 0, psz_name, -1, psz_filename, MAX_PATH ); - - p_access_data->_socket.i_handle = (int)CreateFile( psz_filename, - GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL ); - - if( (HANDLE)p_access_data->_socket.i_handle == INVALID_HANDLE_VALUE ) - { - msg_Err( p_input, "cannot open file %s", psz_name ); - free( p_access_data ); - return VLC_EGENERIC; - } - p_input->stream.p_selected_area->i_size = - GetFileSize( (HANDLE)p_access_data->_socket.i_handle, NULL ); + if (!strcasecmp (p_access->psz_access, "fd")) + goto error; /* fd:// not supported for directories yet */ +#ifdef HAVE_FDOPENDIR + DIR *handle = fdopendir (fd); + if (handle == NULL) + goto error; /* Uh? */ + return DirInit (p_access, handle); #else - p_access_data->_socket.i_handle = open( psz_name, - O_NONBLOCK /*| O_LARGEFILE*/ ); - if( p_access_data->_socket.i_handle == -1 ) - { -# ifdef HAVE_ERRNO_H - msg_Err( p_input, "cannot open file %s (%s)", psz_name, - strerror(errno) ); -# else - msg_Err( p_input, "cannot open file %s", psz_name ); -# endif - free( p_access_data ); - return VLC_EGENERIC; - } + msg_Dbg (p_access, "ignoring directory"); + goto error; #endif } - if ( p_input->stream.b_seekable - && !p_input->stream.p_selected_area->i_size ) + access_sys_t *p_sys = malloc (sizeof (*p_sys)); + if (unlikely(p_sys == NULL)) + goto error; + access_InitFields (p_access); + p_access->pf_read = FileRead; + p_access->pf_block = NULL; + p_access->pf_control = FileControl; + p_access->pf_seek = FileSeek; + p_access->p_sys = p_sys; + p_sys->i_nb_reads = 0; + p_sys->fd = fd; + p_sys->caching = var_CreateGetInteger (p_access, "file-caching"); + if (IsRemote(fd)) + p_sys->caching += var_CreateGetInteger (p_access, "network-caching"); + p_sys->b_pace_control = true; + + if (S_ISREG (st.st_mode)) + p_access->info.i_size = st.st_size; + else if (!S_ISBLK (st.st_mode)) { - msg_Err( p_input, "file %s is empty, aborting", psz_name ); - free( p_access_data ); - return VLC_EGENERIC; + p_access->pf_seek = NoSeek; + p_sys->b_pace_control = strcasecmp (p_access->psz_access, "stream"); } - /* Update default_pts to a suitable value for file access */ - p_input->i_pts_delay = config_GetInt( p_input, "file-caching" ) * 1000; - + if (p_access->pf_seek != NoSeek) + { + /* Demuxers will need the beginning of the file for probing. */ + posix_fadvise (fd, 0, 4096, POSIX_FADV_WILLNEED); + /* In most cases, we only read the file once. */ + posix_fadvise (fd, 0, 0, POSIX_FADV_NOREUSE); +#if defined(HAVE_FCNTL) + /* We'd rather use any available memory for reading ahead + * than for caching what we've already seen/heard */ +# if defined(F_RDAHEAD) + fcntl (fd, F_RDAHEAD, 1); +# endif +# if defined(F_NOCACHE) + fcntl (fd, F_NOCACHE, 1); +# endif +#endif + } return VLC_SUCCESS; + +error: + close (fd); + return VLC_EGENERIC; } /***************************************************************************** * Close: close the target *****************************************************************************/ -static void Close( vlc_object_t * p_this ) +void Close (vlc_object_t * p_this) { - input_thread_t * p_input = (input_thread_t *)p_this; - input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data; - - msg_Info( p_input, "closing `%s/%s://%s'", - p_input->psz_access, p_input->psz_demux, p_input->psz_name ); - -#ifdef UNDER_CE - CloseHandle( (HANDLE)p_access_data->i_handle ); -#else - close( p_access_data->i_handle ); -#endif + access_t *p_access = (access_t*)p_this; + + if (p_access->pf_read == NULL) + { + DirClose (p_this); + return; + } + + access_sys_t *p_sys = p_access->p_sys; - free( p_access_data ); + close (p_sys->fd); + free (p_sys); } + +#include + /***************************************************************************** * Read: standard read on a file descriptor. *****************************************************************************/ -static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len ) +ssize_t FileRead( access_t *p_access, uint8_t *p_buffer, size_t i_len ) { - _input_socket_t * p_access_data = (_input_socket_t *)p_input->p_access_data; + access_sys_t *p_sys = p_access->p_sys; + int fd = p_sys->fd; ssize_t i_ret; - -#ifdef UNDER_CE - if( !ReadFile( (HANDLE)p_access_data->_socket.i_handle, p_buffer, i_len, - (LPDWORD)&i_ret, NULL ) ) - { - i_ret = -1; - } -#else -#ifndef WIN32 - if ( !p_input->stream.b_pace_control ) - { - if ( !p_access_data->b_kfir ) - { - /* Find if some data is available. This won't work under Windows. */ - struct timeval timeout; - fd_set fds; - - /* Initialize file descriptor set */ - FD_ZERO( &fds ); - FD_SET( p_access_data->_socket.i_handle, &fds ); - - /* We'll wait 0.5 second if nothing happens */ - timeout.tv_sec = 0; - timeout.tv_usec = 500000; - - /* Find if some data is available */ - while( (i_ret = select( p_access_data->_socket.i_handle + 1, &fds, - NULL, NULL, &timeout )) == 0 - || (i_ret < 0 && errno == EINTR) ) - { - FD_ZERO( &fds ); - FD_SET( p_access_data->_socket.i_handle, &fds ); - timeout.tv_sec = 0; - timeout.tv_usec = 500000; - - if( p_input->b_die || p_input->b_error ) - { - return 0; - } - } - if( i_ret < 0 ) - { - msg_Err( p_input, "select error (%s)", strerror(errno) ); - return -1; - } +#ifndef WIN32 + if (p_access->pf_seek == NoSeek) + i_ret = net_Read (p_access, fd, NULL, p_buffer, i_len, false); + else +#endif + i_ret = read (fd, p_buffer, i_len); - i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len ); - } - else + if( i_ret < 0 ) + { + switch (errno) { - /* b_kfir ; work around a buggy poll() driver implementation */ - while ( (i_ret = read( p_access_data->_socket.i_handle, p_buffer, - i_len )) == 0 && - !p_input->b_die && !p_input->b_error ) - { - msleep(INPUT_ERROR_SLEEP); - } + case EINTR: + case EAGAIN: + break; + + default: + msg_Err (p_access, "failed to read (%m)"); + dialog_Fatal (p_access, _("File reading failed"), "%s", + _("VLC could not read the file.")); + p_access->info.b_eof = true; + return 0; } } + else if( i_ret > 0 ) + p_access->info.i_pos += i_ret; else -# endif - { - /* b_pace_control || WIN32 */ - i_ret = read( p_access_data->_socket.i_handle, p_buffer, i_len ); - } -#endif + p_access->info.b_eof = true; - if( i_ret < 0 ) - { -#ifdef HAVE_ERRNO_H - if ( errno != EINTR && errno != EAGAIN ) - msg_Err( p_input, "read failed (%s)", strerror(errno) ); -#else - msg_Err( p_input, "read failed" ); -#endif + p_sys->i_nb_reads++; - /* Delay a bit to avoid consuming all the CPU. This is particularly - * useful when reading from an unconnected FIFO. */ - msleep( INPUT_ERROR_SLEEP ); - } - - p_access_data->i_nb_reads++; -#ifdef HAVE_SYS_STAT_H - if ( p_input->stream.p_selected_area->i_size != 0 - && (p_access_data->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 ) + if ((p_access->info.i_size && !(p_sys->i_nb_reads % INPUT_FSTAT_NB_READS)) + || (p_access->info.i_pos > p_access->info.i_size)) { -#if defined( WIN32 ) && !defined( UNDER_CE ) - struct _stati64 stat_info; -#else - struct stat stat_info; -#endif - if ( fstat( p_access_data->_socket.i_handle, &stat_info ) == -1 ) - { -# ifdef HAVE_ERRNO_H - msg_Warn( p_input, "couldn't stat again the file (%s)", - strerror(errno) ); -# else - msg_Warn( p_input, "couldn't stat again the file" ); -# endif - } - else if ( p_input->stream.p_selected_area->i_size != stat_info.st_size ) +#ifdef HAVE_SYS_STAT_H + struct stat st; + + if ((fstat (fd, &st) == 0) + && (p_access->info.i_size != st.st_size)) { - p_input->stream.p_selected_area->i_size = stat_info.st_size; - p_input->stream.b_changed = 1; + p_access->info.i_size = st.st_size; + p_access->info.i_update |= INPUT_UPDATE_SIZE; } - } #endif - + } return i_ret; } + /***************************************************************************** * Seek: seek to a specific location in a file *****************************************************************************/ -static void Seek( input_thread_t * p_input, off_t i_pos ) +int FileSeek (access_t *p_access, int64_t i_pos) { -#define S p_input->stream - input_socket_t * p_access_data = (input_socket_t *)p_input->p_access_data; + p_access->info.i_pos = i_pos; + p_access->info.b_eof = false; -#if defined( WIN32 ) && !defined( UNDER_CE ) - _lseeki64( p_access_data->i_handle, i_pos, SEEK_SET ); -#else - lseek( p_access_data->i_handle, i_pos, SEEK_SET ); -#endif + lseek (p_access->p_sys->fd, i_pos, SEEK_SET); + return VLC_SUCCESS; +} - vlc_mutex_lock( &S.stream_lock ); - S.p_selected_area->i_tell = i_pos; - if( S.p_selected_area->i_tell > S.p_selected_area->i_size ) - { - msg_Err( p_input, "seeking too far" ); - S.p_selected_area->i_tell = S.p_selected_area->i_size; - } - else if( S.p_selected_area->i_tell < 0 ) +int NoSeek (access_t *p_access, int64_t i_pos) +{ + /* assert(0); ?? */ + (void) p_access; (void) i_pos; + return VLC_EGENERIC; +} + +/***************************************************************************** + * Control: + *****************************************************************************/ +int FileControl( access_t *p_access, int i_query, va_list args ) +{ + access_sys_t *p_sys = p_access->p_sys; + bool *pb_bool; + int64_t *pi_64; + + switch( i_query ) { - msg_Err( p_input, "seeking too early" ); - S.p_selected_area->i_tell = 0; + /* */ + case ACCESS_CAN_SEEK: + case ACCESS_CAN_FASTSEEK: + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = (p_access->pf_seek != NoSeek); + break; + + case ACCESS_CAN_PAUSE: + case ACCESS_CAN_CONTROL_PACE: + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = p_sys->b_pace_control; + break; + + /* */ + case ACCESS_GET_PTS_DELAY: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = p_sys->caching * INT64_C(1000); + break; + + /* */ + case ACCESS_SET_PAUSE_STATE: + /* Nothing to do */ + break; + + case ACCESS_GET_TITLE_INFO: + case ACCESS_SET_TITLE: + case ACCESS_SET_SEEKPOINT: + case ACCESS_SET_PRIVATE_ID_STATE: + case ACCESS_GET_META: + case ACCESS_GET_PRIVATE_ID_STATE: + case ACCESS_GET_CONTENT_TYPE: + return VLC_EGENERIC; + + default: + msg_Warn( p_access, "unimplemented query %d in control", i_query ); + return VLC_EGENERIC; + } - vlc_mutex_unlock( &S.stream_lock ); -#undef S + return VLC_SUCCESS; } -