diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 83000d5b099b7..dfc29fce8dadb 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -831,6 +831,12 @@ function ftell($stream): int|false {} /** @param resource $stream */ function fflush($stream): bool {} +/** @param resource $stream */ +function fsync($stream): bool {} + +/** @param resource $stream */ +function fdatasync($stream): bool {} + /** @param resource $stream */ function fwrite($stream, string $data, ?int $length = null): int|false {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index f44a32226736b..b737792f85aa9 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 97edf8c87780c892984099e52ad1c6c745b919f8 */ + * Stub hash: eefc5bf7bdb5d6ba0b9cb79cf380433d34214e1f */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -1272,6 +1272,10 @@ ZEND_END_ARG_INFO() #define arginfo_fflush arginfo_rewind +#define arginfo_fsync arginfo_rewind + +#define arginfo_fdatasync arginfo_rewind + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_fwrite, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_INFO(0, stream) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) @@ -2563,6 +2567,8 @@ ZEND_FUNCTION(fstat); ZEND_FUNCTION(fseek); ZEND_FUNCTION(ftell); ZEND_FUNCTION(fflush); +ZEND_FUNCTION(fsync); +ZEND_FUNCTION(fdatasync); ZEND_FUNCTION(fwrite); ZEND_FUNCTION(mkdir); ZEND_FUNCTION(rename); @@ -3200,6 +3206,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(fseek, arginfo_fseek) ZEND_FE(ftell, arginfo_ftell) ZEND_FE(fflush, arginfo_fflush) + ZEND_FE(fsync, arginfo_fsync) + ZEND_FE(fdatasync, arginfo_fdatasync) ZEND_FE(fwrite, arginfo_fwrite) ZEND_FALIAS(fputs, fwrite, arginfo_fputs) ZEND_FE(mkdir, arginfo_mkdir) diff --git a/ext/standard/file.c b/ext/standard/file.c index ff29bf1b1c0a0..00dcd5c7b675a 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -1463,6 +1463,50 @@ PHP_FUNCTION(unlink) } /* }}} */ +/* {{{ Sync file to storage. Similar to fflush() but blocks until OS buffers have flushed. */ +PHP_FUNCTION(fsync) +{ + zval *res; + php_stream *stream; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(res) + ZEND_PARSE_PARAMETERS_END(); + + PHP_STREAM_TO_ZVAL(stream, res); + + if (!php_stream_sync_supported(stream)) { + php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); + RETURN_FALSE; + } + + RETURN_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0); +} +/* }}} */ + +/* {{{ Sync file data only to storage. Similar to fsync but does not flush modified metadata. POSIX only, aliased to fsync on Win32. */ +PHP_FUNCTION(fdatasync) +{ + zval *res; + php_stream *stream; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(res) + ZEND_PARSE_PARAMETERS_END(); + + PHP_STREAM_TO_ZVAL(stream, res); + + if (!php_stream_sync_supported(stream)) { + php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); + RETURN_FALSE; + } + + RETURN_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0); +} +/* }}} */ + + + /* {{{ Truncate file to 'size' length */ PHP_FUNCTION(ftruncate) { diff --git a/ext/standard/tests/file/fdatasync.phpt b/ext/standard/tests/file/fdatasync.phpt new file mode 100644 index 0000000000000..235669a9943f5 --- /dev/null +++ b/ext/standard/tests/file/fdatasync.phpt @@ -0,0 +1,73 @@ +--TEST-- +Test fdatasync() function: basic functionality +--FILE-- + +--CLEAN-- + +--EXPECTF-- +*** Testing fdatasync(): writing to a file and reading the contents *** +int(63) +bool(true) +first line of string +second line of string +third line of stringint(63) + +*** Testing fdatasync(): for return type *** +bool(true) + +*** Testing fdatasync(): attempting to sync stdin *** +bool(false) + +*** Testing fdatasync(): for non-file stream *** + +Warning: fdatasync(): Can't fsync this stream! in %s on line %d +bool(false) + +*** Done *** diff --git a/ext/standard/tests/file/fsync.phpt b/ext/standard/tests/file/fsync.phpt new file mode 100644 index 0000000000000..7a036a55d4d0d --- /dev/null +++ b/ext/standard/tests/file/fsync.phpt @@ -0,0 +1,73 @@ +--TEST-- +Test fsync() function: basic functionality +--FILE-- + +--CLEAN-- + +--EXPECTF-- +*** Testing fsync(): writing to a file and reading the contents *** +int(63) +bool(true) +first line of string +second line of string +third line of stringint(63) + +*** Testing fsync(): for return type *** +bool(true) + +*** Testing fsync(): attempting to sync stdin *** +bool(false) + +*** Testing fsync(): for non-file stream *** + +Warning: fsync(): Can't fsync this stream! in %s on line %d +bool(false) + +*** Done *** diff --git a/main/php_streams.h b/main/php_streams.h index f7196b8437622..ba1addd221600 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -336,6 +336,9 @@ PHPAPI int _php_stream_putc(php_stream *stream, int c); PHPAPI int _php_stream_flush(php_stream *stream, int closing); #define php_stream_flush(stream) _php_stream_flush((stream), 0) +PHPAPI int _php_stream_sync(php_stream *stream, bool data_only); +#define php_stream_sync(stream, d) _php_stream_sync((stream), (d)) + PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, size_t *returned_len); #define php_stream_gets(stream, buf, maxlen) _php_stream_get_line((stream), (buf), (maxlen), NULL) @@ -444,6 +447,15 @@ END_EXTERN_C() /* Enable/disable blocking reads on anonymous pipes on Windows. */ #define PHP_STREAM_OPTION_PIPE_BLOCKING 13 +/* Stream can support fsync operation */ +#define PHP_STREAM_OPTION_SYNC_API 14 +#define PHP_STREAM_SYNC_SUPPORTED 0 +#define PHP_STREAM_SYNC_FSYNC 1 +#define PHP_STREAM_SYNC_FDSYNC 2 + +#define php_stream_sync_supported(stream) (_php_stream_set_option((stream), PHP_STREAM_OPTION_SYNC_API, PHP_STREAM_SYNC_SUPPORTED, NULL) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0) + + #define PHP_STREAM_OPTION_RETURN_OK 0 /* option set OK */ #define PHP_STREAM_OPTION_RETURN_ERR -1 /* problem setting option */ #define PHP_STREAM_OPTION_RETURN_NOTIMPL -2 /* underlying stream does not implement; streams can handle it instead */ diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 58b5b64e0e9c1..2b870b8a2bfd9 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -54,6 +54,8 @@ extern int php_get_gid_by_name(const char *name, gid_t *gid); #if defined(PHP_WIN32) # define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int)(st)) +#define fsync _commit +#define fdatasync fsync #else # define PLAIN_WRAP_BUF_SIZE(st) (st) #endif @@ -537,6 +539,28 @@ static int php_stdiop_flush(php_stream *stream) return 0; } + +static int php_stdiop_sync(php_stream *stream, int dataonly) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + FILE *fp; + int fd; + + if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS) == FAILURE) { + return -1; + } + + if (php_stdiop_flush(stream) == 0) { + PHP_STDIOP_GET_FD(fd, data); + if (dataonly) { + return fdatasync(fd); + } else { + return fsync(fd); + } + } + return -1; +} + static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; @@ -885,6 +909,16 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void #endif return PHP_STREAM_OPTION_RETURN_NOTIMPL; + case PHP_STREAM_OPTION_SYNC_API: + switch (value) { + case PHP_STREAM_SYNC_SUPPORTED: + return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; + case PHP_STREAM_SYNC_FSYNC: + return php_stdiop_sync(stream, 0) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + case PHP_STREAM_SYNC_FDSYNC: + return php_stdiop_sync(stream, 1) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + } + case PHP_STREAM_OPTION_TRUNCATE_API: switch (value) { case PHP_STREAM_TRUNCATE_SUPPORTED: diff --git a/main/streams/streams.c b/main/streams/streams.c index 96d3aeb41e54a..4965d671f4743 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1406,6 +1406,15 @@ PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, voi return ret; } +PHPAPI int _php_stream_sync(php_stream *stream, bool data_only) +{ + int op = PHP_STREAM_SYNC_FSYNC; + if (data_only) { + op = PHP_STREAM_SYNC_FDSYNC; + } + return php_stream_set_option(stream, PHP_STREAM_OPTION_SYNC_API, op, NULL); +} + PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize) { return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);