]> git.sesse.net Git - vlc/blob - modules/access/file.c
Don't include config.h from the headers - refs #297.
[vlc] / modules / access / file.c
1 /*****************************************************************************
2  * file.c: file input (file: access plug-in)
3  *****************************************************************************
4  * Copyright (C) 2001-2006 the VideoLAN team
5  * Copyright © 2006-2007 Rémi Denis-Courmont
6  * $Id$
7  *
8  * Authors: Christophe Massiot <massiot@via.ecp.fr>
9  *          Rémi Denis-Courmont <rem # videolan # org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc/vlc.h>
34 #include <vlc_input.h>
35 #include <vlc_access.h>
36 #include <vlc_interface.h>
37
38 #include <assert.h>
39 #include <errno.h>
40 #ifdef HAVE_SYS_TYPES_H
41 #   include <sys/types.h>
42 #endif
43 #ifdef HAVE_SYS_STAT_H
44 #   include <sys/stat.h>
45 #endif
46 #ifdef HAVE_FCNTL_H
47 #   include <fcntl.h>
48 #endif
49
50 #if defined( WIN32 ) && !defined( UNDER_CE )
51 #   include <io.h>
52 #else
53 #   include <unistd.h>
54 #   include <poll.h>
55 #endif
56 #ifdef HAVE_MMAP
57 #   include <sys/mman.h>
58 #endif
59
60 #if defined( WIN32 ) && !defined( UNDER_CE )
61 #   ifdef lseek
62 #      undef lseek
63 #   endif
64 #   define lseek _lseeki64
65 #elif defined( UNDER_CE )
66 #   ifdef read
67 #      undef read
68 #   endif
69 #   define read(a,b,c) fread(b,1,c,a)
70 #   define close(a) fclose(a)
71 #   ifdef lseek
72 #      undef lseek
73 #   endif
74 #   define lseek fseek
75 #endif
76
77 #include <vlc_charset.h>
78
79 /*****************************************************************************
80  * Module descriptor
81  *****************************************************************************/
82 static int  Open ( vlc_object_t * );
83 static void Close( vlc_object_t * );
84
85 #define CACHING_TEXT N_("Caching value in ms")
86 #define CACHING_LONGTEXT N_( \
87     "Caching value for files. This " \
88     "value should be set in milliseconds." )
89 #define CAT_TEXT N_("Concatenate with additional files")
90 #define CAT_LONGTEXT N_( \
91     "Play split files as if they were part of a unique file. " \
92     "You need to specify a comma-separated list of files." )
93
94 vlc_module_begin();
95     set_description( _("File input") );
96     set_shortname( _("File") );
97     set_category( CAT_INPUT );
98     set_subcategory( SUBCAT_INPUT_ACCESS );
99     add_integer( "file-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
100     add_obsolete_string( "file-cat" );
101     set_capability( "access2", 50 );
102     add_shortcut( "file" );
103     add_shortcut( "stream" );
104     add_shortcut( "kfir" );
105     set_callbacks( Open, Close );
106 vlc_module_end();
107
108
109 /*****************************************************************************
110  * Exported prototypes
111  *****************************************************************************/
112 static int  Seek( access_t *, int64_t );
113 static ssize_t Read( access_t *, uint8_t *, size_t );
114 static int  Control( access_t *, int, va_list );
115 #ifdef HAVE_MMAP
116 static block_t *mmapBlock( access_t * );
117 #endif
118
119 static int  open_file( access_t *, const char * );
120
121 struct access_sys_t
122 {
123     uint64_t     pagemask;
124     unsigned int i_nb_reads;
125     vlc_bool_t   b_kfir;
126
127     int fd;
128
129     /* */
130     vlc_bool_t b_seekable;
131     vlc_bool_t b_pace_control;
132 };
133
134 /*****************************************************************************
135  * Open: open the file
136  *****************************************************************************/
137 static int Open( vlc_object_t *p_this )
138 {
139     access_t     *p_access = (access_t*)p_this;
140     access_sys_t *p_sys;
141
142     vlc_bool_t    b_stdin = !strcmp (p_access->psz_path, "-");
143
144     /* Update default_pts to a suitable value for file access */
145     var_Create( p_access, "file-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
146
147     STANDARD_READ_ACCESS_INIT;
148     p_sys->i_nb_reads = 0;
149     p_sys->b_kfir = VLC_FALSE;
150     int fd = p_sys->fd = -1;
151
152     if (!strcasecmp (p_access->psz_access, "stream"))
153     {
154         p_sys->b_seekable = VLC_FALSE;
155         p_sys->b_pace_control = VLC_FALSE;
156     }
157     else if (!strcasecmp (p_access->psz_access, "kfir"))
158     {
159         p_sys->b_seekable = VLC_FALSE;
160         p_sys->b_pace_control = VLC_FALSE;
161         p_sys->b_kfir = VLC_TRUE;
162     }
163     else
164     {
165         p_sys->b_seekable = VLC_TRUE;
166         p_sys->b_pace_control = VLC_TRUE;
167     }
168
169     /* Open file */
170     msg_Dbg (p_access, "opening file `%s'", p_access->psz_path);
171
172     if (b_stdin)
173         fd = dup (0);
174     else
175         fd = open_file (p_access, p_access->psz_path);
176
177 #ifdef HAVE_SYS_STAT_H
178     struct stat st;
179
180     while (fd != -1)
181     {
182         if (fstat (fd, &st))
183             msg_Err (p_access, "fstat(%d): %m", fd);
184         else
185         if (S_ISDIR (st.st_mode))
186             /* The directory plugin takes care of that */
187             msg_Dbg (p_access, "file is a directory, aborting");
188         else
189             break; // success
190
191         close (fd);
192         fd = -1;
193     }
194 #endif
195
196     if (fd == -1)
197     {
198         free (p_sys);
199         return VLC_EGENERIC;
200     }
201     p_sys->fd = fd;
202
203 #ifdef HAVE_SYS_STAT_H
204     p_access->info.i_size = st.st_size;
205     if (!S_ISREG (st.st_mode) && !S_ISBLK (st.st_mode)
206      && (!S_ISCHR (st.st_mode) || (st.st_size == 0)))
207         p_sys->b_seekable = VLC_FALSE;
208
209 # ifdef HAVE_MMAP
210     p_sys->pagemask = sysconf (_SC_PAGE_SIZE) - 1;
211
212     /* Autodetect mmap() support */
213     if (p_sys->b_pace_control && S_ISREG (st.st_mode) && (st.st_size > 0))
214     {
215         /* TODO: Do not allow PROT_WRITE, we should not need it.
216          * However, this far, "block" ownership seems such that whoever
217          * "receives" a block can freely modify its content. Hence we _may_
218          * need PROT_WRITE not to default memory protection.
219          * NOTE: With MAP_PRIVATE, changes are not committed to the underlying
220          * file, write open permission is not required.
221          */
222         void *addr = mmap (NULL, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
223         if (addr != MAP_FAILED)
224         {
225             /* Does the file system support mmap? */
226             munmap (addr, 1);
227             p_access->pf_read = NULL;
228             p_access->pf_block = mmapBlock;
229             msg_Dbg (p_this, "mmap enabled");
230         }
231         else
232             msg_Dbg (p_this, "mmap disabled (%m)");
233     }
234     else
235         msg_Dbg (p_this, "mmap disabled (non regular file)");
236 # endif
237 #else
238     p_sys->b_seekable = !b_stdin;
239 # warning File size not known!
240 #endif
241
242     if (p_sys->b_seekable && (p_access->info.i_size == 0))
243     {
244         /* FIXME that's bad because all others access will be probed */
245         msg_Err (p_access, "file is empty, aborting");
246         Close (p_this);
247         return VLC_EGENERIC;
248     }
249
250     return VLC_SUCCESS;
251 }
252
253 /*****************************************************************************
254  * Close: close the target
255  *****************************************************************************/
256 static void Close (vlc_object_t * p_this)
257 {
258     access_t     *p_access = (access_t*)p_this;
259     access_sys_t *p_sys = p_access->p_sys;
260
261     close (p_sys->fd);
262     free (p_sys);
263 }
264
265 /*****************************************************************************
266  * Read: standard read on a file descriptor.
267  *****************************************************************************/
268 static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
269 {
270     access_sys_t *p_sys = p_access->p_sys;
271     ssize_t i_ret;
272     int fd = p_sys->fd;
273
274 #if !defined(WIN32) && !defined(UNDER_CE)
275     if( !p_sys->b_pace_control )
276     {
277         if( !p_sys->b_kfir )
278         {
279             /* Find if some data is available. This won't work under Windows. */
280             do
281             {
282                 struct pollfd ufd;
283
284                 if( p_access->b_die )
285                     return 0;
286
287                 memset (&ufd, 0, sizeof (ufd));
288                 ufd.fd = fd;
289                 ufd.events = POLLIN;
290
291                 i_ret = poll (&ufd, 1, 500);
292             }
293             while (i_ret <= 0);
294
295             i_ret = read (fd, p_buffer, i_len);
296         }
297         else
298         {
299             /* b_kfir ; work around a buggy poll() driver implementation */
300             while (((i_ret = read (fd, p_buffer, i_len)) == 0)
301                 && !p_access->b_die)
302             {
303                 msleep( INPUT_ERROR_SLEEP );
304             }
305         }
306     }
307     else
308 #endif /* WIN32 || UNDER_CE */
309         /* b_pace_control || WIN32 */
310         i_ret = read( fd, p_buffer, i_len );
311
312     if( i_ret < 0 )
313     {
314         switch (errno)
315         {
316             case EINTR:
317             case EAGAIN:
318                 break;
319
320             default:
321                 msg_Err (p_access, "read failed (%m)");
322                 intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"),
323                                 _("VLC could not read the file."));
324         }
325
326         /* Delay a bit to avoid consuming all the CPU. This is particularly
327          * useful when reading from an unconnected FIFO. */
328         msleep( INPUT_ERROR_SLEEP );
329     }
330
331     p_sys->i_nb_reads++;
332
333 #ifdef HAVE_SYS_STAT_H
334     if( p_access->info.i_size != 0 &&
335         (p_sys->i_nb_reads % INPUT_FSTAT_NB_READS) == 0 )
336     {
337         struct stat st;
338
339         if ((fstat (fd, &st) == 0)
340          && (p_access->info.i_size != st.st_size))
341         {
342             p_access->info.i_size = st.st_size;
343             p_access->info.i_update |= INPUT_UPDATE_SIZE;
344         }
345     }
346 #endif
347
348     if( i_ret > 0 )
349         p_access->info.i_pos += i_ret;
350     else if( i_ret == 0 )
351         p_access->info.b_eof = VLC_TRUE;
352
353     return i_ret;
354 }
355
356 #ifdef HAVE_MMAP
357 # define MMAP_SIZE (1 << 20)
358
359 static block_t *mmapBlock (access_t *p_access)
360 {
361     access_sys_t *p_sys = p_access->p_sys;
362
363     const int flags = MAP_SHARED;
364     off_t offset = p_access->info.i_pos & ~p_sys->pagemask;
365     size_t align = p_access->info.i_pos & p_sys->pagemask;
366     size_t length = (MMAP_SIZE > p_sys->pagemask) ? MMAP_SIZE : (p_sys->pagemask + 1);
367     void *addr;
368
369 #ifndef NDEBUG
370     int64_t dbgpos = lseek (p_sys->fd, 0, SEEK_CUR);
371     if (dbgpos != p_access->info.i_pos)
372         msg_Err (p_access, "position: 0x%08llx instead of 0x%08llx",
373                  p_access->info.i_pos, dbgpos);
374 #endif
375
376     if (p_access->info.i_pos >= p_access->info.i_size)
377     {
378         /* End of file - check if file size changed... */
379         struct stat st;
380
381         if ((fstat (p_sys->fd, &st) == 0)
382          && (st.st_size != p_access->info.i_size))
383         {
384             p_access->info.i_size = st.st_size;
385             p_access->info.i_update |= INPUT_UPDATE_SIZE;
386         }
387
388         /* Really at end of file then */
389         if (p_access->info.i_pos >= p_access->info.i_size)
390         {
391             p_access->info.b_eof = VLC_TRUE;
392             msg_Dbg (p_access, "at end of memory mapped file");
393             return NULL;
394         }
395     }
396
397     if (offset + length > p_access->info.i_size)
398         /* Don't mmap beyond end of file */
399         length = p_access->info.i_size - offset;
400
401     assert (offset <= p_access->info.i_pos);               /* and */
402     assert (p_access->info.i_pos < p_access->info.i_size); /* imply */
403     assert (offset < p_access->info.i_size);               /* imply */
404     assert (length > 0);
405
406     addr = mmap (NULL, length, PROT_READ, flags, p_sys->fd, offset);
407     if (addr == MAP_FAILED)
408     {
409         msg_Err (p_access, "memory mapping failed (%m)");
410         intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"),
411                         _("VLC could not read the file."));
412         msleep( INPUT_ERROR_SLEEP );
413         return NULL;
414     }
415
416     p_access->info.i_pos = offset + length;
417
418     block_t *block = block_mmap_Alloc (addr, length);
419     if (block == NULL)
420         return NULL;
421
422     block->p_buffer += align;
423     block->i_buffer -= align;
424
425 #ifndef NDEBUG
426     msg_Dbg (p_access, "mapped 0x%lx bytes at %p from offset 0x%lx",
427              (unsigned long)length, addr, (unsigned long)offset);
428
429     /* Compare normal I/O with memory mapping */
430     char *buf = malloc (block->i_buffer);
431     ssize_t i_read = read (p_sys->fd, buf, block->i_buffer);
432
433     if (i_read != (ssize_t)block->i_buffer)
434         msg_Err (p_access, "read %u instead of %u bytes", (unsigned)i_read,
435                  (unsigned)block->i_buffer);
436     if (memcmp (buf, block->p_buffer, block->i_buffer))
437         msg_Err (p_access, "inconsistent data buffer");
438     free (buf);
439 #endif
440
441     return block;
442 }
443 #endif
444
445 /*****************************************************************************
446  * Seek: seek to a specific location in a file
447  *****************************************************************************/
448 static int Seek (access_t *p_access, int64_t i_pos)
449 {
450     /* FIXME: i_size should really be unsigned */
451     if ((uint64_t)i_pos > (uint64_t)p_access->info.i_size)
452     {
453         /* This should only happen with corrupted files.
454          * But it also seems to happen with buggy demuxes (ASF) */
455         msg_Err (p_access, "seeking too far (0x"I64Fx" / 0x"I64Fx")",
456                  i_pos, p_access->info.i_size);
457         i_pos = p_access->info.i_size;
458     }
459
460     p_access->info.i_pos = i_pos;
461     p_access->info.b_eof = VLC_FALSE;
462
463 #if defined (HAVE_MMAP) && defined (NDEBUG)
464     if (p_access->pf_block == NULL)
465 #endif
466         lseek (p_access->p_sys->fd, i_pos, SEEK_SET);
467     return VLC_SUCCESS;
468 }
469
470 /*****************************************************************************
471  * Control:
472  *****************************************************************************/
473 static int Control( access_t *p_access, int i_query, va_list args )
474 {
475     access_sys_t *p_sys = p_access->p_sys;
476     vlc_bool_t   *pb_bool;
477     int          *pi_int;
478     int64_t      *pi_64;
479
480     switch( i_query )
481     {
482         /* */
483         case ACCESS_CAN_SEEK:
484         case ACCESS_CAN_FASTSEEK:
485             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
486             *pb_bool = p_sys->b_seekable;
487             break;
488
489         case ACCESS_CAN_PAUSE:
490         case ACCESS_CAN_CONTROL_PACE:
491             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
492             *pb_bool = p_sys->b_pace_control;
493             break;
494
495         /* */
496         case ACCESS_GET_MTU:
497             pi_int = (int*)va_arg( args, int * );
498             *pi_int = 0;
499             break;
500
501         case ACCESS_GET_PTS_DELAY:
502             pi_64 = (int64_t*)va_arg( args, int64_t * );
503             *pi_64 = var_GetInteger( p_access, "file-caching" ) * I64C(1000);
504             break;
505
506         /* */
507         case ACCESS_SET_PAUSE_STATE:
508             /* Nothing to do */
509             break;
510
511         case ACCESS_GET_TITLE_INFO:
512         case ACCESS_SET_TITLE:
513         case ACCESS_SET_SEEKPOINT:
514         case ACCESS_SET_PRIVATE_ID_STATE:
515         case ACCESS_GET_META:
516         case ACCESS_GET_CONTENT_TYPE:
517             return VLC_EGENERIC;
518
519         default:
520             msg_Warn( p_access, "unimplemented query in control" );
521             return VLC_EGENERIC;
522
523     }
524     return VLC_SUCCESS;
525 }
526
527
528 static char *expand_path (const access_t *p_access, const char *path)
529 {
530     if (strncmp (path, "~/", 2) == 0)
531     {
532         char *res;
533
534          // TODO: we should also support the ~cmassiot/ syntax
535          if (asprintf (&res, "%s/%s", p_access->p_libvlc->psz_homedir, path + 2) == -1)
536              return NULL;
537          return res;
538     }
539
540 #if defined(WIN32)
541     if (!strcasecmp (p_access->psz_access, "file")
542       && ('/' == path[0]) && path[1] && (':' == path[2]) && ('/' == path[3]))
543         // Explorer can open path such as file:/C:/ or file:///C:/
544         // hence remove leading / if found
545         return strdup (path + 1);
546 #endif
547
548     return strdup (path);
549 }
550
551
552 /*****************************************************************************
553  * open_file: Opens a specific file
554  *****************************************************************************/
555 static int open_file (access_t *p_access, const char *psz_name)
556 {
557     char *path = expand_path (p_access, psz_name);
558
559 #ifdef UNDER_CE
560     p_sys->fd = utf8_fopen( path, "rb" );
561     if ( !p_sys->fd )
562     {
563         msg_Err( p_access, "cannot open file %s", psz_name );
564         intf_UserFatal( p_access, VLC_FALSE, _("File reading failed"),
565                         _("VLC could not open the file \"%s\"."), psz_name );
566         free (path);
567         return VLC_EGENERIC;
568     }
569
570     fseek( p_sys->fd, 0, SEEK_END );
571     p_access->info.i_size = ftell( p_sys->fd );
572     p_access->info.i_update |= INPUT_UPDATE_SIZE;
573     fseek( p_sys->fd, 0, SEEK_SET );
574 #else
575     int fd = utf8_open (path, O_RDONLY | O_NONBLOCK /* O_LARGEFILE*/, 0666);
576     free (path);
577     if (fd == -1)
578     {
579         msg_Err (p_access, "cannot open file %s (%m)", psz_name);
580         intf_UserFatal (p_access, VLC_FALSE, _("File reading failed"),
581                         _("VLC could not open the file \"%s\"."), psz_name);
582         return -1;
583     }
584
585 # if defined(HAVE_FCNTL_H) && defined(F_FDAHEAD) && defined(F_NOCACHE)
586     /* We'd rather use any available memory for reading ahead
587      * than for caching what we've already seen/heard */
588     fcntl (fd, F_RDAHEAD, 1);
589     fcntl (fd, F_NOCACHE, 1);
590 # endif
591 #endif
592
593     return fd;
594 }