2001-07-22 14:18:56 +00:00
/*
2011-10-30 16:56:57 +00:00
* buffered file I / O
2002-05-25 22:34:32 +00:00
* Copyright ( c ) 2001 Fabrice Bellard
2001-07-22 14:18:56 +00:00
*
2006-10-07 15:30:46 +00:00
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
2002-05-25 22:34:32 +00:00
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2006-10-07 15:30:46 +00:00
* version 2.1 of the License , or ( at your option ) any later version .
2001-07-22 14:18:56 +00:00
*
2006-10-07 15:30:46 +00:00
* FFmpeg is distributed in the hope that it will be useful ,
2001-07-22 14:18:56 +00:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2002-05-25 22:34:32 +00:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
2001-07-22 14:18:56 +00:00
*
2002-05-25 22:34:32 +00:00
* You should have received a copy of the GNU Lesser General Public
2006-10-07 15:30:46 +00:00
* License along with FFmpeg ; if not , write to the Free Software
2006-01-12 22:43:26 +00:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2001-07-22 14:18:56 +00:00
*/
2008-05-09 11:56:36 +00:00
2022-02-23 12:56:49 +00:00
# include "config_components.h"
2008-05-09 11:56:36 +00:00
# include "libavutil/avstring.h"
2022-08-27 21:33:06 +00:00
# include "libavutil/file_open.h"
2013-08-06 18:19:28 +00:00
# include "libavutil/internal.h"
2012-09-09 19:42:56 +00:00
# include "libavutil/opt.h"
2022-08-27 21:33:06 +00:00
# include "avio.h"
2015-06-28 22:13:43 +00:00
# if HAVE_DIRENT_H
2015-06-21 22:01:32 +00:00
# include <dirent.h>
2015-06-28 22:13:43 +00:00
# endif
2001-07-22 14:18:56 +00:00
# include <fcntl.h>
2012-06-24 21:42:27 +00:00
# if HAVE_IO_H
2009-01-11 22:05:43 +00:00
# include <io.h>
# endif
2012-06-24 21:42:27 +00:00
# if HAVE_UNISTD_H
2001-08-13 21:37:10 +00:00
# include <unistd.h>
2012-06-24 21:42:27 +00:00
# endif
2010-04-04 14:21:29 +00:00
# include <sys/stat.h>
2007-08-17 15:14:29 +00:00
# include <stdlib.h>
2007-11-22 02:27:39 +00:00
# include "os_support.h"
2011-04-07 18:25:52 +00:00
# include "url.h"
2001-07-22 14:18:56 +00:00
2012-09-07 13:45:09 +00: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 14:18:56 +00:00
2015-06-28 22:13:43 +00: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 14:18:56 +00:00
/* standard file protocol */
2012-09-09 19:35:50 +00:00
typedef struct FileContext {
2012-09-09 19:42:56 +00:00
const AVClass * class ;
2012-09-09 19:35:50 +00:00
int fd ;
2012-09-09 19:42:56 +00:00
int trunc ;
2013-07-31 12:22:01 +00:00
int blocksize ;
2015-02-05 13:06:42 +00:00
int follow ;
2019-04-02 23:13:55 +00:00
int seekable ;
2015-06-28 22:13:43 +00:00
# if HAVE_DIRENT_H
2015-06-21 22:01:32 +00:00
DIR * dir ;
2015-06-28 22:13:43 +00:00
# endif
2012-09-09 19:35:50 +00:00
} FileContext ;
2012-09-09 19:42:56 +00:00
static const AVOption file_options [ ] = {
2015-11-21 21:05:07 +00: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 12:22:01 +00: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 13:06:42 +00: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-02 23:13:55 +00: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 12:22:01 +00: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 19:42:56 +00:00
{ NULL }
} ;
static const AVClass file_class = {
. class_name = " file " ,
. item_name = av_default_item_name ,
. option = file_options ,
. version = L IBAVUTIL_VERSION_INT ,
} ;
2013-07-31 12:22:01 +00: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 14:28:32 +00:00
static int file_read ( URLContext * h , unsigned char * buf , int size )
{
2012-09-09 19:35:50 +00:00
FileContext * c = h - > priv_data ;
2015-09-07 14:11:57 +00:00
int ret ;
2013-07-31 12:22:01 +00:00
size = FFMIN ( size , c - > blocksize ) ;
2015-09-07 14:11:57 +00:00
ret = read ( c - > fd , buf , size ) ;
2015-02-05 13:06:42 +00:00
if ( ret = = 0 & & c - > follow )
return AVERROR ( EAGAIN ) ;
2017-06-05 18:30:16 +00:00
if ( ret = = 0 )
return AVERROR_EOF ;
2015-08-27 04:04:15 +00:00
return ( ret = = - 1 ) ? AVERROR ( errno ) : ret ;
2010-07-06 14:28:32 +00:00
}
static int file_write ( URLContext * h , const unsigned char * buf , int size )
{
2012-09-09 19:35:50 +00:00
FileContext * c = h - > priv_data ;
2015-09-07 14:11:57 +00:00
int ret ;
2013-07-31 12:22:01 +00:00
size = FFMIN ( size , c - > blocksize ) ;
2015-09-07 14:11:57 +00:00
ret = write ( c - > fd , buf , size ) ;
2015-08-27 04:04:15 +00:00
return ( ret = = - 1 ) ? AVERROR ( errno ) : ret ;
2010-07-06 14:28:32 +00:00
}
static int file_get_handle ( URLContext * h )
{
2012-09-09 19:35:50 +00:00
FileContext * c = h - > priv_data ;
return c - > fd ;
2010-07-06 14:28:32 +00:00
}
2011-05-04 17:20:03 +00:00
static int file_check ( URLContext * h , int mask )
{
2012-11-27 01:57:20 +00:00
int ret = 0 ;
2014-01-02 18:00:15 +00: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 17:20:03 +00:00
return AVERROR ( errno ) ;
2012-11-27 01:57:20 +00:00
if ( mask & AVIO_FLAG_READ )
2014-01-02 18:00:15 +00:00
if ( access ( filename , R_OK ) > = 0 )
2012-11-27 01:57:20 +00:00
ret | = AVIO_FLAG_READ ;
if ( mask & AVIO_FLAG_WRITE )
2014-01-02 18:00:15 +00:00
if ( access ( filename , W_OK ) > = 0 )
2012-11-27 01:57:20 +00:00
ret | = AVIO_FLAG_WRITE ;
2012-11-27 03:57:18 +00:00
# else
struct stat st ;
2014-01-02 18:00:15 +00:00
ret = stat ( filename , & st ) ;
2012-11-27 03:57:18 +00: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 18:00:15 +00:00
}
2011-05-04 17:20:03 +00:00
return ret ;
}
2021-08-10 16:25:00 +00:00
# if CONFIG_FILE_PROTOCOL
2015-06-20 12:52:49 +00: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 07:37:49 +00:00
if ( ret < 0 & & ( errno = = ENOTDIR
# ifdef _WIN32
| | errno = = EINVAL
# endif
) )
2015-06-20 12:52:49 +00: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 10:40:12 +00:00
av_strstart ( filename_dst , " file: " , & filename_dst ) ;
2015-06-20 12:52:49 +00:00
if ( rename ( filename_src , filename_dst ) < 0 )
return AVERROR ( errno ) ;
return 0 ;
}
2001-07-22 14:18:56 +00:00
static int file_open ( URLContext * h , const char * filename , int flags )
{
2012-09-09 19:35:50 +00:00
FileContext * c = h - > priv_data ;
2001-07-22 14:18:56 +00:00
int access ;
int fd ;
2012-06-28 17:31:04 +00:00
struct stat st ;
2001-07-22 14:18:56 +00:00
2007-06-24 11:27:12 +00:00
av_strstart ( filename , " file: " , & filename ) ;
2002-11-29 19:19:47 +00:00
2011-04-15 14:42:09 +00:00
if ( flags & AVIO_FLAG_WRITE & & flags & AVIO_FLAG_READ ) {
2012-09-09 19:42:56 +00:00
access = O_CREAT | O_RDWR ;
if ( c - > trunc )
access | = O_TRUNC ;
2011-04-15 14:42:09 +00:00
} else if ( flags & AVIO_FLAG_WRITE ) {
2012-09-09 19:42:56 +00:00
access = O_CREAT | O_WRONLY ;
if ( c - > trunc )
access | = O_TRUNC ;
2001-07-22 14:18:56 +00:00
} else {
access = O_RDONLY ;
}
2007-06-25 20:34:20 +00:00
# ifdef O_BINARY
2001-08-13 21:37:10 +00:00
access | = O_BINARY ;
# endif
2013-08-06 18:19:28 +00:00
fd = avpriv_open ( filename , access , 0666 ) ;
2009-07-27 12:36:22 +00:00
if ( fd = = - 1 )
2010-04-26 22:36:51 +00:00
return AVERROR ( errno ) ;
2012-09-09 19:35:50 +00:00
c - > fd = fd ;
2012-06-28 17:31:04 +00:00
2012-06-30 22:47:49 +00:00
h - > is_streamed = ! fstat ( fd , & st ) & & S_ISFIFO ( st . st_mode ) ;
2012-06-28 17:31:04 +00:00
2017-06-04 02:25:43 +00: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-02 23:13:55 +00:00
if ( c - > seekable > = 0 )
h - > is_streamed = ! c - > seekable ;
2001-07-22 14:18:56 +00:00
return 0 ;
}
/* XXX: use llseek */
2008-10-03 10:16:29 +00:00
static int64_t file_seek ( URLContext * h , int64_t pos , int whence )
2001-07-22 14:18:56 +00:00
{
2012-09-09 19:35:50 +00:00
FileContext * c = h - > priv_data ;
2012-10-09 07:00:28 +00:00
int64_t ret ;
2012-09-14 15:01:35 +00:00
2010-04-04 14:21:29 +00:00
if ( whence = = AVSEEK_SIZE ) {
struct stat st ;
2012-09-14 15:01:35 +00:00
ret = fstat ( c - > fd , & st ) ;
2012-06-15 13:05:33 +00:00
return ret < 0 ? AVERROR ( errno ) : ( S_ISFIFO ( st . st_mode ) ? 0 : st . st_size ) ;
2010-04-04 14:21:29 +00:00
}
2012-09-14 15:01:35 +00:00
ret = lseek ( c - > fd , pos , whence ) ;
return ret < 0 ? AVERROR ( errno ) : ret ;
2001-07-22 14:18:56 +00:00
}
static int file_close ( URLContext * h )
{
2012-09-09 19:35:50 +00:00
FileContext * c = h - > priv_data ;
2021-11-29 23:50:16 +00:00
int ret = close ( c - > fd ) ;
return ( ret = = - 1 ) ? AVERROR ( errno ) : 0 ;
2001-07-22 14:18:56 +00:00
}
2015-06-21 22:01:32 +00:00
static int file_open_dir ( URLContext * h )
{
2015-08-25 10:48:12 +00:00
# if HAVE_LSTAT
2015-06-21 22:01:32 +00:00
FileContext * c = h - > priv_data ;
c - > dir = opendir ( h - > filename ) ;
if ( ! c - > dir )
return AVERROR ( errno ) ;
return 0 ;
2015-06-28 22:13:43 +00:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 10:48:12 +00:00
# endif /* HAVE_LSTAT */
2015-06-21 22:01:32 +00:00
}
static int file_read_dir ( URLContext * h , AVIODirEntry * * next )
{
2015-08-25 10:48:12 +00:00
# if HAVE_LSTAT
2015-06-21 22:01:32 +00: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-28 22:13:43 +00: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-21 22:01:32 +00: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-28 22:13:43 +00:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 10:48:12 +00:00
# endif /* HAVE_LSTAT */
2015-06-21 22:01:32 +00:00
}
static int file_close_dir ( URLContext * h )
{
2015-08-25 10:48:12 +00:00
# if HAVE_LSTAT
2015-06-21 22:01:32 +00:00
FileContext * c = h - > priv_data ;
closedir ( c - > dir ) ;
return 0 ;
2015-06-28 22:13:43 +00:00
# else
return AVERROR ( ENOSYS ) ;
2015-08-25 10:48:12 +00:00
# endif /* HAVE_LSTAT */
2015-06-21 22:01:32 +00:00
}
2016-02-19 09:39:29 +00:00
const URLProtocol ff_file_protocol = {
2011-04-08 05:41:47 +00: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 17:04:51 +00:00
. url_get_file_handle = file_get_handle ,
2010-09-30 11:21:42 +00:00
. url_check = file_check ,
2015-06-20 12:52:49 +00:00
. url_delete = file_delete ,
. url_move = file_move ,
2012-09-09 19:35:50 +00:00
. priv_data_size = sizeof ( FileContext ) ,
2012-09-09 19:42:56 +00:00
. priv_data_class = & file_class ,
2015-06-21 22:01:32 +00:00
. url_open_dir = file_open_dir ,
. url_read_dir = file_read_dir ,
. url_close_dir = file_close_dir ,
2019-09-24 03:18:59 +00:00
. default_whitelist = " file,crypto,data "
2001-07-22 14:18:56 +00:00
} ;
2010-07-06 14:28:32 +00:00
# endif /* CONFIG_FILE_PROTOCOL */
# if CONFIG_PIPE_PROTOCOL
2001-07-22 14:18:56 +00:00
static int pipe_open ( URLContext * h , const char * filename , int flags )
{
2012-09-09 19:35:50 +00:00
FileContext * c = h - > priv_data ;
2001-07-22 14:18:56 +00:00
int fd ;
2008-05-27 01:19:19 +00:00
char * final ;
2007-08-17 15:14:29 +00:00
av_strstart ( filename , " pipe: " , & filename ) ;
2001-07-22 14:18:56 +00:00
2007-08-17 15:14:29 +00:00
fd = strtol ( filename , & final , 10 ) ;
if ( ( filename = = final ) | | * final ) { /* No digits found, or something like 10ab */
2011-04-15 14:42:09 +00:00
if ( flags & AVIO_FLAG_WRITE ) {
2007-08-17 15:12:30 +00:00
fd = 1 ;
} else {
fd = 0 ;
}
2007-08-17 15:14:29 +00:00
}
2009-01-13 23:44:16 +00:00
# if HAVE_SETMODE
2004-03-24 18:29:30 +00:00
setmode ( fd , O_BINARY ) ;
# endif
2012-09-09 19:35:50 +00:00
c - > fd = fd ;
2004-12-06 00:08:37 +00:00
h - > is_streamed = 1 ;
2001-07-22 14:18:56 +00:00
return 0 ;
}
2016-02-19 09:39:29 +00:00
const URLProtocol ff_pipe_protocol = {
2011-04-08 05:41:47 +00:00
. name = " pipe " ,
. url_open = pipe_open ,
. url_read = file_read ,
. url_write = file_write ,
2009-03-03 17:04:51 +00:00
. url_get_file_handle = file_get_handle ,
2010-09-30 11:21:42 +00:00
. url_check = file_check ,
2012-09-09 19:35:50 +00:00
. priv_data_size = sizeof ( FileContext ) ,
2013-07-31 12:22:01 +00:00
. priv_data_class = & pipe_class ,
2019-09-24 03:18:59 +00:00
. default_whitelist = " crypto,data "
2001-07-22 14:18:56 +00:00
} ;
2010-07-06 14:28:32 +00:00
# endif /* CONFIG_PIPE_PROTOCOL */