Skip to content

Commit cbcfd86

Browse files
dwgeblernikic
authored andcommitted
Add fsync() and fdatasync() functions
fsync is a straightforward wrapper around the same C function (implemented on Windows API as _commit() with identical signature). From the man pages: fsync() transfers ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file referred to by the file descriptor fd to the disk device (or other permanent storage device) so that all changed information can be retrieved even if the system crashes or is rebooted. This includes writing through or flushing a disk cache if present. The call blocks until the device reports that the transfer has completed. RFC: https://wiki.php.net/rfc/fsync_function Closes GH-6650.
1 parent 0c57118 commit cbcfd86

File tree

9 files changed

+259
-1
lines changed

9 files changed

+259
-1
lines changed

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,11 @@ PHP 8.1 UPGRADE NOTES
283283
. Added array_is_list(array $array), which will return true if the array keys are 0 .. count($array)-1 in that order.
284284
RFC: https://wiki.php.net/rfc/is_list
285285

286+
- Standard:
287+
. Added fsync() and fdatasync(), which instruct the operating system to
288+
flush its buffers to physical storage.
289+
RFC: https://wiki.php.net/rfc/fsync_function
290+
286291
========================================
287292
7. New Classes and Interfaces
288293
========================================

ext/standard/basic_functions.stub.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,12 @@ function ftell($stream): int|false {}
825825
/** @param resource $stream */
826826
function fflush($stream): bool {}
827827

828+
/** @param resource $stream */
829+
function fsync($stream): bool {}
830+
831+
/** @param resource $stream */
832+
function fdatasync($stream): bool {}
833+
828834
/** @param resource $stream */
829835
function fwrite($stream, string $data, ?int $length = null): int|false {}
830836

ext/standard/basic_functions_arginfo.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 469c5fbccef35aa6bd3893589495071d2250078f */
2+
* Stub hash: 2d92e992837a61a052eea6d0257837aaccc54be6 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
55
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
@@ -1270,6 +1270,10 @@ ZEND_END_ARG_INFO()
12701270

12711271
#define arginfo_fflush arginfo_rewind
12721272

1273+
#define arginfo_fsync arginfo_rewind
1274+
1275+
#define arginfo_fdatasync arginfo_rewind
1276+
12731277
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_fwrite, 0, 2, MAY_BE_LONG|MAY_BE_FALSE)
12741278
ZEND_ARG_INFO(0, stream)
12751279
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
@@ -2562,6 +2566,8 @@ ZEND_FUNCTION(fstat);
25622566
ZEND_FUNCTION(fseek);
25632567
ZEND_FUNCTION(ftell);
25642568
ZEND_FUNCTION(fflush);
2569+
ZEND_FUNCTION(fsync);
2570+
ZEND_FUNCTION(fdatasync);
25652571
ZEND_FUNCTION(fwrite);
25662572
ZEND_FUNCTION(mkdir);
25672573
ZEND_FUNCTION(rename);
@@ -3198,6 +3204,8 @@ static const zend_function_entry ext_functions[] = {
31983204
ZEND_FE(fseek, arginfo_fseek)
31993205
ZEND_FE(ftell, arginfo_ftell)
32003206
ZEND_FE(fflush, arginfo_fflush)
3207+
ZEND_FE(fsync, arginfo_fsync)
3208+
ZEND_FE(fdatasync, arginfo_fdatasync)
32013209
ZEND_FE(fwrite, arginfo_fwrite)
32023210
ZEND_FALIAS(fputs, fwrite, arginfo_fputs)
32033211
ZEND_FE(mkdir, arginfo_mkdir)

ext/standard/file.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,44 @@ PHP_FUNCTION(unlink)
14641464
}
14651465
/* }}} */
14661466

1467+
PHP_FUNCTION(fsync)
1468+
{
1469+
zval *res;
1470+
php_stream *stream;
1471+
1472+
ZEND_PARSE_PARAMETERS_START(1, 1)
1473+
Z_PARAM_RESOURCE(res)
1474+
ZEND_PARSE_PARAMETERS_END();
1475+
1476+
PHP_STREAM_TO_ZVAL(stream, res);
1477+
1478+
if (!php_stream_sync_supported(stream)) {
1479+
php_error_docref(NULL, E_WARNING, "Can't fsync this stream!");
1480+
RETURN_FALSE;
1481+
}
1482+
1483+
RETURN_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0);
1484+
}
1485+
1486+
PHP_FUNCTION(fdatasync)
1487+
{
1488+
zval *res;
1489+
php_stream *stream;
1490+
1491+
ZEND_PARSE_PARAMETERS_START(1, 1)
1492+
Z_PARAM_RESOURCE(res)
1493+
ZEND_PARSE_PARAMETERS_END();
1494+
1495+
PHP_STREAM_TO_ZVAL(stream, res);
1496+
1497+
if (!php_stream_sync_supported(stream)) {
1498+
php_error_docref(NULL, E_WARNING, "Can't fsync this stream!");
1499+
RETURN_FALSE;
1500+
}
1501+
1502+
RETURN_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0);
1503+
}
1504+
14671505
/* {{{ Truncate file to 'size' length */
14681506
PHP_FUNCTION(ftruncate)
14691507
{
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
--TEST--
2+
Test fdatasync() function: basic functionality
3+
--FILE--
4+
<?php
5+
6+
echo "*** Testing fdatasync(): writing to a file and reading the contents ***\n";
7+
$data = <<<EOD
8+
first line of string
9+
second line of string
10+
third line of string
11+
EOD;
12+
13+
$file_path = __DIR__;
14+
$filename = "$file_path/fdatasync_basic.tmp";
15+
16+
// opening a file
17+
$file_handle = fopen($filename, "w");
18+
if($file_handle == false)
19+
exit("Error:failed to open file $filename");
20+
21+
if(PHP_OS_FAMILY == 'Windows') {
22+
$data = str_replace("\r",'', $data);
23+
}
24+
25+
// writing data to the file
26+
var_dump( fwrite($file_handle, $data) );
27+
var_dump( fdatasync($file_handle) );
28+
var_dump( readfile($filename) );
29+
30+
echo "\n*** Testing fdatasync(): for return type ***\n";
31+
$return_value = fdatasync($file_handle);
32+
var_dump( is_bool($return_value) );
33+
fclose($file_handle);
34+
35+
echo "\n*** Testing fdatasync(): attempting to sync stdin ***\n";
36+
$file_handle = fopen("php://stdin", "w");
37+
var_dump(fdatasync($file_handle));
38+
fclose($file_handle);
39+
40+
echo "\n*** Testing fdatasync(): for non-file stream ***\n";
41+
$file_handle = fopen("php://memory", "w");
42+
$return_value = fdatasync($file_handle);
43+
var_dump( ($return_value) );
44+
fclose($file_handle);
45+
46+
echo "\n*** Done ***";
47+
?>
48+
--CLEAN--
49+
<?php
50+
$file_path = __DIR__;
51+
$filename = "$file_path/fdatasync_basic.tmp";
52+
unlink($filename);
53+
?>
54+
--EXPECTF--
55+
*** Testing fdatasync(): writing to a file and reading the contents ***
56+
int(63)
57+
bool(true)
58+
first line of string
59+
second line of string
60+
third line of stringint(63)
61+
62+
*** Testing fdatasync(): for return type ***
63+
bool(true)
64+
65+
*** Testing fdatasync(): attempting to sync stdin ***
66+
bool(false)
67+
68+
*** Testing fdatasync(): for non-file stream ***
69+
70+
Warning: fdatasync(): Can't fsync this stream! in %s on line %d
71+
bool(false)
72+
73+
*** Done ***

ext/standard/tests/file/fsync.phpt

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
--TEST--
2+
Test fsync() function: basic functionality
3+
--FILE--
4+
<?php
5+
6+
echo "*** Testing fsync(): writing to a file and reading the contents ***\n";
7+
$data = <<<EOD
8+
first line of string
9+
second line of string
10+
third line of string
11+
EOD;
12+
13+
$file_path = __DIR__;
14+
$filename = "$file_path/fsync_basic.tmp";
15+
16+
// opening a file
17+
$file_handle = fopen($filename, "w");
18+
if($file_handle == false)
19+
exit("Error:failed to open file $filename");
20+
21+
if(PHP_OS_FAMILY == 'Windows') {
22+
$data = str_replace("\r",'', $data);
23+
}
24+
25+
// writing data to the file
26+
var_dump( fwrite($file_handle, $data) );
27+
var_dump( fsync($file_handle) );
28+
var_dump( readfile($filename) );
29+
30+
echo "\n*** Testing fsync(): for return type ***\n";
31+
$return_value = fsync($file_handle);
32+
var_dump( is_bool($return_value) );
33+
fclose($file_handle);
34+
35+
echo "\n*** Testing fsync(): attempting to sync stdin ***\n";
36+
$file_handle = fopen("php://stdin", "w");
37+
var_dump(fsync($file_handle));
38+
fclose($file_handle);
39+
40+
echo "\n*** Testing fsync(): for non-file stream ***\n";
41+
$file_handle = fopen("php://memory", "w");
42+
$return_value = fsync($file_handle);
43+
var_dump( ($return_value) );
44+
fclose($file_handle);
45+
46+
echo "\n*** Done ***";
47+
?>
48+
--CLEAN--
49+
<?php
50+
$file_path = __DIR__;
51+
$filename = "$file_path/fsync_basic.tmp";
52+
unlink($filename);
53+
?>
54+
--EXPECTF--
55+
*** Testing fsync(): writing to a file and reading the contents ***
56+
int(63)
57+
bool(true)
58+
first line of string
59+
second line of string
60+
third line of stringint(63)
61+
62+
*** Testing fsync(): for return type ***
63+
bool(true)
64+
65+
*** Testing fsync(): attempting to sync stdin ***
66+
bool(false)
67+
68+
*** Testing fsync(): for non-file stream ***
69+
70+
Warning: fsync(): Can't fsync this stream! in %s on line %d
71+
bool(false)
72+
73+
*** Done ***

main/php_streams.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,9 @@ PHPAPI int _php_stream_putc(php_stream *stream, int c);
334334
PHPAPI int _php_stream_flush(php_stream *stream, int closing);
335335
#define php_stream_flush(stream) _php_stream_flush((stream), 0)
336336

337+
PHPAPI int _php_stream_sync(php_stream *stream, bool data_only);
338+
#define php_stream_sync(stream, d) _php_stream_sync((stream), (d))
339+
337340
PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, size_t *returned_len);
338341
#define php_stream_gets(stream, buf, maxlen) _php_stream_get_line((stream), (buf), (maxlen), NULL)
339342

@@ -442,6 +445,15 @@ END_EXTERN_C()
442445
/* Enable/disable blocking reads on anonymous pipes on Windows. */
443446
#define PHP_STREAM_OPTION_PIPE_BLOCKING 13
444447

448+
/* Stream can support fsync operation */
449+
#define PHP_STREAM_OPTION_SYNC_API 14
450+
#define PHP_STREAM_SYNC_SUPPORTED 0
451+
#define PHP_STREAM_SYNC_FSYNC 1
452+
#define PHP_STREAM_SYNC_FDSYNC 2
453+
454+
#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)
455+
456+
445457
#define PHP_STREAM_OPTION_RETURN_OK 0 /* option set OK */
446458
#define PHP_STREAM_OPTION_RETURN_ERR -1 /* problem setting option */
447459
#define PHP_STREAM_OPTION_RETURN_NOTIMPL -2 /* underlying stream does not implement; streams can handle it instead */

main/streams/plain_wrapper.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ extern int php_get_gid_by_name(const char *name, gid_t *gid);
5454

5555
#if defined(PHP_WIN32)
5656
# define PLAIN_WRAP_BUF_SIZE(st) (((st) > UINT_MAX) ? UINT_MAX : (unsigned int)(st))
57+
#define fsync _commit
58+
#define fdatasync fsync
5759
#else
5860
# define PLAIN_WRAP_BUF_SIZE(st) (st)
5961
#endif
@@ -537,6 +539,28 @@ static int php_stdiop_flush(php_stream *stream)
537539
return 0;
538540
}
539541

542+
543+
static int php_stdiop_sync(php_stream *stream, bool dataonly)
544+
{
545+
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
546+
FILE *fp;
547+
int fd;
548+
549+
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS) == FAILURE) {
550+
return -1;
551+
}
552+
553+
if (php_stdiop_flush(stream) == 0) {
554+
PHP_STDIOP_GET_FD(fd, data);
555+
if (dataonly) {
556+
return fdatasync(fd);
557+
} else {
558+
return fsync(fd);
559+
}
560+
}
561+
return -1;
562+
}
563+
540564
static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset)
541565
{
542566
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
885909
#endif
886910
return PHP_STREAM_OPTION_RETURN_NOTIMPL;
887911

912+
case PHP_STREAM_OPTION_SYNC_API:
913+
switch (value) {
914+
case PHP_STREAM_SYNC_SUPPORTED:
915+
return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
916+
case PHP_STREAM_SYNC_FSYNC:
917+
return php_stdiop_sync(stream, 0) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
918+
case PHP_STREAM_SYNC_FDSYNC:
919+
return php_stdiop_sync(stream, 1) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
920+
}
921+
888922
case PHP_STREAM_OPTION_TRUNCATE_API:
889923
switch (value) {
890924
case PHP_STREAM_TRUNCATE_SUPPORTED:

main/streams/streams.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,15 @@ PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, voi
14061406
return ret;
14071407
}
14081408

1409+
PHPAPI int _php_stream_sync(php_stream *stream, bool data_only)
1410+
{
1411+
int op = PHP_STREAM_SYNC_FSYNC;
1412+
if (data_only) {
1413+
op = PHP_STREAM_SYNC_FDSYNC;
1414+
}
1415+
return php_stream_set_option(stream, PHP_STREAM_OPTION_SYNC_API, op, NULL);
1416+
}
1417+
14091418
PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize)
14101419
{
14111420
return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize);

0 commit comments

Comments
 (0)