2021-09-01 21:30:13 +00:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < andreas @ ladybird . org >
2021-09-01 21:30:13 +00:00
* Copyright ( c ) 2021 , sin - ack < sin - ack @ protonmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-02-09 03:02:46 +01:00
# include <LibCore/File.h>
2022-01-13 21:21:16 +00:00
# include <LibCore/System.h>
2024-10-20 07:51:54 +05:00
# if !defined(AK_OS_WINDOWS)
# include <unistd.h>
# else
2025-02-15 18:37:38 +05:00
# include <AK / Windows.h>
2024-10-20 07:51:54 +05:00
# endif
2021-09-01 21:30:13 +00:00
2023-02-09 03:02:46 +01:00
namespace Core {
2021-09-01 21:30:13 +00:00
2022-02-05 20:18:07 +03:30
ErrorOr < NonnullOwnPtr < File > > File : : open ( StringView filename , OpenMode mode , mode_t permissions )
2021-09-01 21:30:13 +00:00
{
2021-12-29 22:31:45 +00:00
auto file = TRY ( adopt_nonnull_own_or_enomem ( new ( nothrow ) File ( mode ) ) ) ;
TRY ( file - > open_path ( filename , permissions ) ) ;
2021-09-01 21:30:13 +00:00
return file ;
}
2022-10-12 11:31:34 +02:00
ErrorOr < NonnullOwnPtr < File > > File : : adopt_fd ( int fd , OpenMode mode , ShouldCloseFileDescriptor should_close_file_descriptor )
2021-09-01 21:30:13 +00:00
{
if ( fd < 0 ) {
return Error : : from_errno ( EBADF ) ;
}
if ( ! has_any_flag ( mode , OpenMode : : ReadWrite ) ) {
2023-02-09 03:02:46 +01:00
dbgln ( " Core::File::adopt_fd: Attempting to adopt a file with neither Read nor Write specified in mode " ) ;
2021-09-01 21:30:13 +00:00
return Error : : from_errno ( EINVAL ) ;
}
2022-10-12 11:31:34 +02:00
auto file = TRY ( adopt_nonnull_own_or_enomem ( new ( nothrow ) File ( mode , should_close_file_descriptor ) ) ) ;
2021-12-29 22:31:45 +00:00
file - > m_fd = fd ;
2021-09-01 21:30:13 +00:00
return file ;
}
2022-09-13 23:52:57 +02:00
ErrorOr < NonnullOwnPtr < File > > File : : standard_input ( )
{
return File : : adopt_fd ( STDIN_FILENO , OpenMode : : Read , ShouldCloseFileDescriptor : : No ) ;
}
ErrorOr < NonnullOwnPtr < File > > File : : standard_output ( )
{
return File : : adopt_fd ( STDOUT_FILENO , OpenMode : : Write , ShouldCloseFileDescriptor : : No ) ;
}
ErrorOr < NonnullOwnPtr < File > > File : : standard_error ( )
{
return File : : adopt_fd ( STDERR_FILENO , OpenMode : : Write , ShouldCloseFileDescriptor : : No ) ;
}
2022-08-22 13:24:56 +02:00
ErrorOr < NonnullOwnPtr < File > > File : : open_file_or_standard_stream ( StringView filename , OpenMode mode )
{
if ( ! filename . is_empty ( ) & & filename ! = " - " sv )
return File : : open ( filename , mode ) ;
switch ( mode ) {
case OpenMode : : Read :
2022-09-13 23:52:57 +02:00
return standard_input ( ) ;
2022-08-22 13:24:56 +02:00
case OpenMode : : Write :
2022-09-13 23:52:57 +02:00
return standard_output ( ) ;
2022-08-22 13:24:56 +02:00
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2022-04-10 18:17:31 +02:00
int File : : open_mode_to_options ( OpenMode mode )
2021-09-01 21:30:13 +00:00
{
int flags = 0 ;
2022-04-10 18:17:31 +02:00
if ( has_flag ( mode , OpenMode : : ReadWrite ) ) {
2021-09-01 21:30:13 +00:00
flags | = O_RDWR | O_CREAT ;
2022-04-10 18:17:31 +02:00
} else if ( has_flag ( mode , OpenMode : : Read ) ) {
2021-09-01 21:30:13 +00:00
flags | = O_RDONLY ;
2022-04-10 18:17:31 +02:00
} else if ( has_flag ( mode , OpenMode : : Write ) ) {
2021-09-01 21:30:13 +00:00
flags | = O_WRONLY | O_CREAT ;
2022-04-10 18:17:31 +02:00
bool should_truncate = ! has_any_flag ( mode , OpenMode : : Append | OpenMode : : MustBeNew ) ;
2021-09-01 21:30:13 +00:00
if ( should_truncate )
flags | = O_TRUNC ;
}
2022-04-10 18:17:31 +02:00
if ( has_flag ( mode , OpenMode : : Append ) )
2021-09-01 21:30:13 +00:00
flags | = O_APPEND ;
2022-04-10 18:17:31 +02:00
if ( has_flag ( mode , OpenMode : : Truncate ) )
2021-09-01 21:30:13 +00:00
flags | = O_TRUNC ;
2022-04-10 18:17:31 +02:00
if ( has_flag ( mode , OpenMode : : MustBeNew ) )
2021-09-01 21:30:13 +00:00
flags | = O_EXCL ;
2022-04-10 18:17:31 +02:00
if ( ! has_flag ( mode , OpenMode : : KeepOnExec ) )
2021-09-01 21:30:13 +00:00
flags | = O_CLOEXEC ;
2024-10-20 07:51:54 +05:00
if ( has_flag ( mode , OpenMode : : Nonblocking ) ) {
# if !defined(AK_OS_WINDOWS)
2021-09-01 21:30:13 +00:00
flags | = O_NONBLOCK ;
2024-10-20 07:51:54 +05:00
# else
dbgln ( " Core::File::OpenMode::Nonblocking is not implemented " ) ;
VERIFY_NOT_REACHED ( ) ;
# endif
}
2023-06-02 23:30:32 +01:00
// Some open modes, like `ReadWrite` imply the ability to create the file if it doesn't exist.
2025-04-09 17:02:30 +00:00
// Certain applications may not want this privilege, and for compatibility reasons, this is
2023-06-02 23:30:32 +01:00
// the easiest way to add this option.
if ( has_flag ( mode , OpenMode : : DontCreate ) )
flags & = ~ O_CREAT ;
2022-04-10 18:17:31 +02:00
return flags ;
}
ErrorOr < void > File : : open_path ( StringView filename , mode_t permissions )
{
VERIFY ( m_fd = = - 1 ) ;
auto flags = open_mode_to_options ( m_mode ) ;
2021-09-01 21:30:13 +00:00
2022-07-08 23:12:31 +02:00
m_fd = TRY ( System : : open ( filename , flags , permissions ) ) ;
2021-09-01 21:30:13 +00:00
return { } ;
}
2023-02-24 22:38:01 +01:00
ErrorOr < Bytes > File : : read_some ( Bytes buffer )
2021-09-01 21:30:13 +00:00
{
if ( ! has_flag ( m_mode , OpenMode : : Read ) ) {
// NOTE: POSIX says that if the fd is not open for reading, the call
// will return EBADF. Since we already know whether we can or
// can't read the file, let's avoid a syscall.
2021-12-29 23:13:11 +00:00
return Error : : from_errno ( EBADF ) ;
2021-09-01 21:30:13 +00:00
}
2022-01-18 13:24:05 +00:00
ssize_t nread = TRY ( System : : read ( m_fd , buffer ) ) ;
m_last_read_was_eof = nread = = 0 ;
2023-07-06 13:41:38 +02:00
m_file_offset + = nread ;
2022-04-15 13:33:02 +01:00
return buffer . trim ( nread ) ;
2021-09-01 21:30:13 +00:00
}
2022-12-11 17:49:00 +01:00
ErrorOr < ByteBuffer > File : : read_until_eof ( size_t block_size )
2022-02-27 12:57:31 +01:00
{
// Note: This is used as a heuristic, it's not valid for devices or virtual files.
auto const potential_file_size = TRY ( System : : fstat ( m_fd ) ) . st_size ;
2022-12-11 17:49:00 +01:00
return read_until_eof_impl ( block_size , potential_file_size ) ;
2022-02-27 12:57:31 +01:00
}
2023-02-24 22:38:01 +01:00
ErrorOr < size_t > File : : write_some ( ReadonlyBytes buffer )
2021-09-01 21:30:13 +00:00
{
if ( ! has_flag ( m_mode , OpenMode : : Write ) ) {
// NOTE: Same deal as Read.
2021-12-29 23:13:11 +00:00
return Error : : from_errno ( EBADF ) ;
2021-09-01 21:30:13 +00:00
}
2023-07-06 13:41:38 +02:00
auto nwritten = TRY ( System : : write ( m_fd , buffer ) ) ;
m_file_offset + = nwritten ;
return nwritten ;
2021-09-01 21:30:13 +00:00
}
bool File : : is_eof ( ) const { return m_last_read_was_eof ; }
bool File : : is_open ( ) const { return m_fd > = 0 ; }
void File : : close ( )
{
if ( ! is_open ( ) ) {
return ;
}
// NOTE: The closing of the file can be interrupted by a signal, in which
// case EINTR will be returned by the close syscall. So let's try closing
// the file until we aren't interrupted by rude signals. :^)
2022-01-18 13:24:05 +00:00
ErrorOr < void > result ;
2021-09-01 21:30:13 +00:00
do {
2022-01-18 13:24:05 +00:00
result = System : : close ( m_fd ) ;
} while ( result . is_error ( ) & & result . error ( ) . code ( ) = = EINTR ) ;
2021-09-01 21:30:13 +00:00
2022-01-18 13:24:05 +00:00
VERIFY ( ! result . is_error ( ) ) ;
2021-09-01 21:30:13 +00:00
m_fd = - 1 ;
}
2023-01-17 14:52:46 +01:00
ErrorOr < size_t > File : : seek ( i64 offset , SeekMode mode )
2021-09-01 21:30:13 +00:00
{
int syscall_mode ;
switch ( mode ) {
case SeekMode : : SetPosition :
syscall_mode = SEEK_SET ;
break ;
case SeekMode : : FromCurrentPosition :
syscall_mode = SEEK_CUR ;
break ;
case SeekMode : : FromEndPosition :
syscall_mode = SEEK_END ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
2023-01-17 14:52:46 +01:00
size_t seek_result = TRY ( System : : lseek ( m_fd , offset , syscall_mode ) ) ;
2023-07-06 13:41:38 +02:00
m_file_offset = seek_result ;
2021-09-01 21:30:13 +00:00
m_last_read_was_eof = false ;
2022-01-18 13:24:05 +00:00
return seek_result ;
2021-09-01 21:30:13 +00:00
}
2023-07-06 13:41:38 +02:00
ErrorOr < size_t > File : : tell ( ) const
{
return m_file_offset ;
}
2023-01-29 13:49:42 +01:00
ErrorOr < void > File : : truncate ( size_t length )
2022-02-03 19:21:51 +00:00
{
2023-01-29 13:49:42 +01:00
if ( length > static_cast < size_t > ( NumericLimits < off_t > : : max ( ) ) )
return Error : : from_string_literal ( " Length is larger than the maximum supported length " ) ;
2023-07-06 13:41:38 +02:00
m_file_offset = min ( length , m_file_offset ) ;
2022-02-03 19:21:51 +00:00
return System : : ftruncate ( m_fd , length ) ;
}
2023-05-21 22:24:58 +02:00
ErrorOr < void > File : : set_blocking ( bool enabled )
{
// NOTE: This works fine on Serenity, but some systems out there don't support changing the blocking state of certain POSIX objects (message queues, pipes, etc) after their creation.
// Therefore, this method shouldn't be used in Lagom.
// https://github.com/SerenityOS/serenity/pull/18965#discussion_r1207951840
int value = enabled ? 0 : 1 ;
return System : : ioctl ( fd ( ) , FIONBIO , & value ) ;
}
2021-09-01 21:30:13 +00:00
}