2001-07-22 17:18:56 +03:00
/*
2011-10-30 19:56:57 +03:00
* buffered file I / O
2002-05-26 01:34:32 +03:00
* Copyright ( c ) 2001 Fabrice Bellard
2001-07-22 17:18:56 +03:00
*
2006-10-07 18:30:46 +03:00
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
2002-05-26 01:34:32 +03:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 18:30:46 +03:00
* version 2.1 of the License , or ( at your option ) any later version .
2001-07-22 17:18:56 +03:00
*
2006-10-07 18:30:46 +03:00
* FFmpeg is distributed in the hope that it will be useful ,
2001-07-22 17:18:56 +03:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2002-05-26 01:34:32 +03:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2001-07-22 17:18:56 +03:00
*
2002-05-26 01:34:32 +03:00
* You should have received a copy of the GNU Lesser General Public
2006-10-07 18:30:46 +03:00
* License along with FFmpeg ; if not , write to the Free Software
2006-01-13 00:43:26 +02:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2001-07-22 17:18:56 +03:00
*/
2008-05-09 14:56:36 +03:00
# include "libavutil/avstring.h"
2013-08-06 21:19:28 +03:00
# include "libavutil/internal.h"
2012-09-09 22:42:56 +03:00
# include "libavutil/opt.h"
2001-08-14 00:37:10 +03:00
# include "avformat.h"
2015-06-29 00:13:43 +02:00
# if HAVE_DIRENT_H
2015-06-22 00:01:32 +02:00
# include <dirent.h>
2015-06-29 00:13:43 +02:00
# endif
2001-07-22 17:18:56 +03:00
# include <fcntl.h>
2012-06-25 00:42:27 +03:00
# if HAVE_IO_H
2009-01-12 00:05:43 +02:00
# include <io.h>
# endif
2012-06-25 00:42:27 +03:00
# if HAVE_UNISTD_H
2001-08-14 00:37:10 +03:00
# include <unistd.h>
2012-06-25 00:42:27 +03:00
# endif
2010-04-04 17:21:29 +03:00
# include <sys/stat.h>
2007-08-17 18:14:29 +03:00
# include <stdlib.h>
2007-11-22 04:27:39 +02:00
# include "os_support.h"
2011-04-07 21:25:52 +03:00
# include "url.h"
2001-07-22 17:18:56 +03:00
2012-09-07 16:45:09 +03:00
/* Some systems may not have S_ISFIFO */
# ifndef S_ISFIFO
# ifdef S_IFIFO
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
# else
# define S_ISFIFO(m) 0
# endif
# endif
2001-07-22 17:18:56 +03:00
2015-06-29 00:13:43 +02:00
/* Not available in POSIX.1-1996 */
# ifndef S_ISLNK
# ifdef S_IFLNK
# define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK)
# else
# define S_ISLNK(m) 0
# endif
# endif
/* Not available in POSIX.1-1996 */
# ifndef S_ISSOCK
# ifdef S_IFSOCK
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
# else
# define S_ISSOCK(m) 0
# endif
# endif
2001-07-22 17:18:56 +03:00
/* standard file protocol */
2012-09-09 22:35:50 +03:00
typedef struct FileContext {
2012-09-09 22:42:56 +03:00
const AVClass * class ;
2012-09-09 22:35:50 +03:00
int fd ;
2012-09-09 22:42:56 +03:00
int trunc ;
2013-07-31 15:22:01 +03:00
int blocksize ;
2015-02-05 15:06:42 +02:00
int follow ;
2019-04-03 01:13:55 +02:00
int seekable ;
2015-06-29 00:13:43 +02:00
# if HAVE_DIRENT_H
2015-06-22 00:01:32 +02:00
DIR * dir ;
2015-06-29 00:13:43 +02:00
# endif
2012-09-09 22:35:50 +03:00
} FileContext ;
2012-09-09 22:42:56 +03:00
static const AVOption file_options [ ] = {
2015-11-21 23:05:07 +02:00
{ " truncate " , " truncate existing files on write " , offsetof ( FileContext , trunc ) , AV_OPT_TYPE_BOOL , { . i64 = 1 } , 0 , 1 , AV_OPT_FLAG_ENCODING_PARAM } ,
2013-07-31 15:22:01 +03:00
{ " blocksize " , " set I/O operation maximum block size " , offsetof ( FileContext , blocksize ) , AV_OPT_TYPE_INT , { . i64 = INT_MAX } , 1 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
2015-02-05 15:06:42 +02:00
{ " follow " , " Follow a file as it is being written " , offsetof ( FileContext , follow ) , AV_OPT_TYPE_INT , { . i64 = 0 } , 0 , 1 , AV_OPT_FLAG_DECODING_PARAM } ,
2019-04-03 01:13:55 +02:00
{ " seekable " , " Sets if the file is seekable " , offsetof ( FileContext , seekable ) , AV_OPT_TYPE_INT , { . i64 = - 1 } , - 1 , 0 , AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM } ,
2013-07-31 15:22:01 +03:00
{ NULL }
} ;
static const AVOption pipe_options [ ] = {
{ " blocksize " , " set I/O operation maximum block size " , offsetof ( FileContext , blocksize ) , AV_OPT_TYPE_INT , { . i64 = INT_MAX } , 1 , INT_MAX , AV_OPT_FLAG_ENCODING_PARAM } ,
2012-09-09 22:42:56 +03:00
{ NULL }
} ;
static const AVClass file_class = {
. class_name = " file " ,
. item_name = av_default_item_name ,
. option = file_options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2013-07-31 15:22:01 +03:00
static const AVClass pipe_class = {
. class_name = " pipe " ,
. item_name = av_default_item_name ,
. option = pipe_options ,
. version = LIBAVUTIL_VERSION_INT ,
} ;
2010-07-06 17:28:32 +03:00
static int file_read ( URLContext * h , unsigned char * buf , int size )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2015-09-07 16:11:57 +02:00
int ret ;
2013-07-31 15:22:01 +03:00
size = FFMIN ( size , c - > blocksize ) ;
2015-09-07 16:11:57 +02:00
ret = read ( c - > fd , buf , size ) ;
2015-02-05 15:06:42 +02:00
if ( ret = = 0 & & c - > follow )
return AVERROR ( EAGAIN ) ;
2017-06-05 20:30:16 +02:00
if ( ret = = 0 )
return AVERROR_EOF ;
2015-08-27 06:04:15 +02:00
return ( ret = = - 1 ) ? AVERROR ( errno ) : ret ;
2010-07-06 17:28:32 +03:00
}
static int file_write ( URLContext * h , const unsigned char * buf , int size )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2015-09-07 16:11:57 +02:00
int ret ;
2013-07-31 15:22:01 +03:00
size = FFMIN ( size , c - > blocksize ) ;
2015-09-07 16:11:57 +02:00
ret = write ( c - > fd , buf , size ) ;
2015-08-27 06:04:15 +02:00
return ( ret = = - 1 ) ? AVERROR ( errno ) : ret ;
2010-07-06 17:28:32 +03:00
}
static int file_get_handle ( URLContext * h )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
return c - > fd ;
2010-07-06 17:28:32 +03:00
}
2011-05-04 20:20:03 +03:00
static int file_check ( URLContext * h , int mask )
{
2012-11-27 04:57:20 +03:00
int ret = 0 ;
2014-01-02 21:00:15 +03:00
const char * filename = h - > filename ;
av_strstart ( filename , " file: " , & filename ) ;
{
# if HAVE_ACCESS && defined(R_OK)
if ( access ( filename , F_OK ) < 0 )
2011-05-04 20:20:03 +03:00
return AVERROR ( errno ) ;
2012-11-27 04:57:20 +03:00
if ( mask & AVIO_FLAG_READ )
2014-01-02 21:00:15 +03:00
if ( access ( filename , R_OK ) > = 0 )
2012-11-27 04:57:20 +03:00
ret | = AVIO_FLAG_READ ;
if ( mask & AVIO_FLAG_WRITE )
2014-01-02 21:00:15 +03:00
if ( access ( filename , W_OK ) > = 0 )
2012-11-27 04:57:20 +03:00
ret | = AVIO_FLAG_WRITE ;
2012-11-27 06:57:18 +03:00
# else
struct stat st ;
2016-06-06 09:04:39 +02:00
# ifndef _WIN32
2014-01-02 21:00:15 +03:00
ret = stat ( filename , & st ) ;
2016-06-06 09:04:39 +02:00
# else
ret = win32_stat ( filename , & st ) ;
# endif
2012-11-27 06:57:18 +03:00
if ( ret < 0 )
return AVERROR ( errno ) ;
ret | = st . st_mode & S_IRUSR ? mask & AVIO_FLAG_READ : 0 ;
ret | = st . st_mode & S_IWUSR ? mask & AVIO_FLAG_WRITE : 0 ;
# endif
2014-01-02 21:00:15 +03:00
}
2011-05-04 20:20:03 +03:00
return ret ;
}
2021-08-10 18:25:00 +02:00
# if CONFIG_FILE_PROTOCOL
2015-06-20 14:52:49 +02:00
static int file_delete ( URLContext * h )
{
# if HAVE_UNISTD_H
int ret ;
const char * filename = h - > filename ;
av_strstart ( filename , " file: " , & filename ) ;
ret = rmdir ( filename ) ;
2018-12-31 09:37:49 +02:00
if ( ret < 0 & & ( errno = = ENOTDIR
# ifdef _WIN32
| | errno = = EINVAL
# endif
) )
2015-06-20 14:52:49 +02:00
ret = unlink ( filename ) ;
if ( ret < 0 )
return AVERROR ( errno ) ;
return ret ;
# else
return AVERROR ( ENOSYS ) ;
# endif /* HAVE_UNISTD_H */
}
static int file_move ( URLContext * h_src , URLContext * h_dst )
{
const char * filename_src = h_src - > filename ;
const char * filename_dst = h_dst - > filename ;
av_strstart ( filename_src , " file: " , & filename_src ) ;
2015-09-03 12:40:12 +02:00
av_strstart ( filename_dst , " file: " , & filename_dst ) ;
2015-06-20 14:52:49 +02:00
if ( rename ( filename_src , filename_dst ) < 0 )
return AVERROR ( errno ) ;
return 0 ;
}
2001-07-22 17:18:56 +03:00
static int file_open ( URLContext * h , const char * filename , int flags )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2001-07-22 17:18:56 +03:00
int access ;
int fd ;
2012-06-28 20:31:04 +03:00
struct stat st ;
2001-07-22 17:18:56 +03:00
2007-06-24 14:27:12 +03:00
av_strstart ( filename , " file: " , & filename ) ;
2002-11-29 21:19:47 +02:00
2011-04-15 17:42:09 +03:00
if ( flags & AVIO_FLAG_WRITE & & flags & AVIO_FLAG_READ ) {
2012-09-09 22:42:56 +03:00
access = O_CREAT | O_RDWR ;
if ( c - > trunc )
access | = O_TRUNC ;
2011-04-15 17:42:09 +03:00
} else if ( flags & AVIO_FLAG_WRITE ) {
2012-09-09 22:42:56 +03:00
access = O_CREAT | O_WRONLY ;
if ( c - > trunc )
access | = O_TRUNC ;
2001-07-22 17:18:56 +03:00
} else {
access = O_RDONLY ;
}
2007-06-25 23:34:20 +03:00
# ifdef O_BINARY
2001-08-14 00:37:10 +03:00
access | = O_BINARY ;
# endif
2013-08-06 21:19:28 +03:00
fd = avpriv_open ( filename , access , 0666 ) ;
2009-07-27 15:36:22 +03:00
if ( fd = = - 1 )
2010-04-27 01:36:51 +03:00
return AVERROR ( errno ) ;
2012-09-09 22:35:50 +03:00
c - > fd = fd ;
2012-06-28 20:31:04 +03:00
2012-07-01 01:47:49 +03:00
h - > is_streamed = ! fstat ( fd , & st ) & & S_ISFIFO ( st . st_mode ) ;
2012-06-28 20:31:04 +03:00
2017-06-04 04:25:43 +02:00
/* Buffer writes more than the default 32k to improve throughput especially
* with networked file systems */
if ( ! h - > is_streamed & & flags & AVIO_FLAG_WRITE )
h - > min_packet_size = h - > max_packet_size = 262144 ;
2019-04-03 01:13:55 +02:00
if ( c - > seekable > = 0 )
h - > is_streamed = ! c - > seekable ;
2001-07-22 17:18:56 +03:00
return 0 ;
}
/* XXX: use llseek */
2008-10-03 13:16:29 +03:00
static int64_t file_seek ( URLContext * h , int64_t pos , int whence )
2001-07-22 17:18:56 +03:00
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2012-10-09 10:00:28 +03:00
int64_t ret ;
2012-09-14 18:01:35 +03:00
2010-04-04 17:21:29 +03:00
if ( whence = = AVSEEK_SIZE ) {
struct stat st ;
2012-09-14 18:01:35 +03:00
ret = fstat ( c - > fd , & st ) ;
2012-06-15 16:05:33 +03:00
return ret < 0 ? AVERROR ( errno ) : ( S_ISFIFO ( st . st_mode ) ? 0 : st . st_size ) ;
2010-04-04 17:21:29 +03:00
}
2012-09-14 18:01:35 +03:00
ret = lseek ( c - > fd , pos , whence ) ;
return ret < 0 ? AVERROR ( errno ) : ret ;
2001-07-22 17:18:56 +03:00
}
static int file_close ( URLContext * h )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
return close ( c - > fd ) ;
2001-07-22 17:18:56 +03:00
}
2015-06-22 00:01:32 +02:00
static int file_open_dir ( URLContext * h )
{
2015-08-25 12:48:12 +02:00
# if HAVE_LSTAT
2015-06-22 00:01:32 +02:00
FileContext * c = h - > priv_data ;
c - > dir = opendir ( h - > filename ) ;
if ( ! c - > dir )
return AVERROR ( errno ) ;
return 0 ;
2015-06-29 00:13:43 +02:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 12:48:12 +02:00
# endif /* HAVE_LSTAT */
2015-06-22 00:01:32 +02:00
}
static int file_read_dir ( URLContext * h , AVIODirEntry * * next )
{
2015-08-25 12:48:12 +02:00
# if HAVE_LSTAT
2015-06-22 00:01:32 +02:00
FileContext * c = h - > priv_data ;
struct dirent * dir ;
char * fullpath = NULL ;
* next = ff_alloc_dir_entry ( ) ;
if ( ! * next )
return AVERROR ( ENOMEM ) ;
do {
errno = 0 ;
dir = readdir ( c - > dir ) ;
if ( ! dir ) {
av_freep ( next ) ;
return AVERROR ( errno ) ;
}
} while ( ! strcmp ( dir - > d_name , " . " ) | | ! strcmp ( dir - > d_name , " .. " ) ) ;
fullpath = av_append_path_component ( h - > filename , dir - > d_name ) ;
if ( fullpath ) {
struct stat st ;
2015-06-29 00:13:43 +02:00
if ( ! lstat ( fullpath , & st ) ) {
if ( S_ISDIR ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_DIRECTORY ;
else if ( S_ISFIFO ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_NAMED_PIPE ;
else if ( S_ISCHR ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_CHARACTER_DEVICE ;
else if ( S_ISBLK ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_BLOCK_DEVICE ;
else if ( S_ISLNK ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_SYMBOLIC_LINK ;
else if ( S_ISSOCK ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_SOCKET ;
else if ( S_ISREG ( st . st_mode ) )
( * next ) - > type = AVIO_ENTRY_FILE ;
else
( * next ) - > type = AVIO_ENTRY_UNKNOWN ;
2015-06-22 00:01:32 +02:00
( * next ) - > group_id = st . st_gid ;
( * next ) - > user_id = st . st_uid ;
( * next ) - > size = st . st_size ;
( * next ) - > filemode = st . st_mode & 0777 ;
( * next ) - > modification_timestamp = INT64_C ( 1000000 ) * st . st_mtime ;
( * next ) - > access_timestamp = INT64_C ( 1000000 ) * st . st_atime ;
( * next ) - > status_change_timestamp = INT64_C ( 1000000 ) * st . st_ctime ;
}
av_free ( fullpath ) ;
}
( * next ) - > name = av_strdup ( dir - > d_name ) ;
return 0 ;
2015-06-29 00:13:43 +02:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 12:48:12 +02:00
# endif /* HAVE_LSTAT */
2015-06-22 00:01:32 +02:00
}
static int file_close_dir ( URLContext * h )
{
2015-08-25 12:48:12 +02:00
# if HAVE_LSTAT
2015-06-22 00:01:32 +02:00
FileContext * c = h - > priv_data ;
closedir ( c - > dir ) ;
return 0 ;
2015-06-29 00:13:43 +02:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 12:48:12 +02:00
# endif /* HAVE_LSTAT */
2015-06-22 00:01:32 +02:00
}
2016-02-19 11:39:29 +02:00
const URLProtocol ff_file_protocol = {
2011-04-08 08:41:47 +03:00
. name = " file " ,
. url_open = file_open ,
. url_read = file_read ,
. url_write = file_write ,
. url_seek = file_seek ,
. url_close = file_close ,
2009-03-03 19:04:51 +02:00
. url_get_file_handle = file_get_handle ,
2010-09-30 14:21:42 +03:00
. url_check = file_check ,
2015-06-20 14:52:49 +02:00
. url_delete = file_delete ,
. url_move = file_move ,
2012-09-09 22:35:50 +03:00
. priv_data_size = sizeof ( FileContext ) ,
2012-09-09 22:42:56 +03:00
. priv_data_class = & file_class ,
2015-06-22 00:01:32 +02:00
. url_open_dir = file_open_dir ,
. url_read_dir = file_read_dir ,
. url_close_dir = file_close_dir ,
2019-09-24 05:18:59 +02:00
. default_whitelist = " file,crypto,data "
2001-07-22 17:18:56 +03:00
} ;
2010-07-06 17:28:32 +03:00
# endif /* CONFIG_FILE_PROTOCOL */
# if CONFIG_PIPE_PROTOCOL
2001-07-22 17:18:56 +03:00
static int pipe_open ( URLContext * h , const char * filename , int flags )
{
2012-09-09 22:35:50 +03:00
FileContext * c = h - > priv_data ;
2001-07-22 17:18:56 +03:00
int fd ;
2008-05-27 04:19:19 +03:00
char * final ;
2007-08-17 18:14:29 +03:00
av_strstart ( filename , " pipe: " , & filename ) ;
2001-07-22 17:18:56 +03:00
2007-08-17 18:14:29 +03:00
fd = strtol ( filename , & final , 10 ) ;
if ( ( filename = = final ) | | * final ) { /* No digits found, or something like 10ab */
2011-04-15 17:42:09 +03:00
if ( flags & AVIO_FLAG_WRITE ) {
2007-08-17 18:12:30 +03:00
fd = 1 ;
} else {
fd = 0 ;
}
2007-08-17 18:14:29 +03:00
}
2009-01-14 01:44:16 +02:00
# if HAVE_SETMODE
2004-03-24 20:29:30 +02:00
setmode ( fd , O_BINARY ) ;
# endif
2012-09-09 22:35:50 +03:00
c - > fd = fd ;
2004-12-06 02:08:37 +02:00
h - > is_streamed = 1 ;
2001-07-22 17:18:56 +03:00
return 0 ;
}
2016-02-19 11:39:29 +02:00
const URLProtocol ff_pipe_protocol = {
2011-04-08 08:41:47 +03:00
. name = " pipe " ,
. url_open = pipe_open ,
. url_read = file_read ,
. url_write = file_write ,
2009-03-03 19:04:51 +02:00
. url_get_file_handle = file_get_handle ,
2010-09-30 14:21:42 +03:00
. url_check = file_check ,
2012-09-09 22:35:50 +03:00
. priv_data_size = sizeof ( FileContext ) ,
2013-07-31 15:22:01 +03:00
. priv_data_class = & pipe_class ,
2019-09-24 05:18:59 +02:00
. default_whitelist = " crypto,data "
2001-07-22 17:18:56 +03:00
} ;
2010-07-06 17:28:32 +03:00
# endif /* CONFIG_PIPE_PROTOCOL */