Skip to content

Commit b7aeb0a

Browse files
nielsdosdivinity76
andcommitted
Fix phpGH-19570: unable to fseek in /dev/zero and /dev/null
On Linux, these two character devices are exceptions in that they can be seeked. Check their major/minor device number. Co-authored-by: divinity76 <[email protected]>
1 parent 64c1d43 commit b7aeb0a

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ PHP NEWS
6464
causing an exception on sort). (nielsdos)
6565
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
6666
while COW violation flag is still set). (alexandre-daubois)
67+
. Fixed bug GH-19570 (unable to fseek in /dev/zero and /dev/null).
68+
(nielsdos, divinity76)
6769

6870
- Streams:
6971
. Fixed bug GH-19248 (Use strerror_r instead of strerror in main).
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
GH-19570 (unable to fseek in /dev/zero and /dev/null)
3+
--SKIPIF--
4+
<?php
5+
if (PHP_OS_FAMILY !== "Linux") die("skip only for Linux");
6+
?>
7+
--FILE--
8+
<?php
9+
$devices = [
10+
// Note: could test others but they're not necessarily available
11+
"/dev/zero",
12+
"/dev/null",
13+
"/dev/full",
14+
];
15+
foreach ($devices as $device) {
16+
var_dump(fseek(fopen($device, "rb"), 1*1024*1024, SEEK_SET));
17+
}
18+
?>
19+
--EXPECT--
20+
int(0)
21+
int(0)
22+
int(0)

main/streams/plain_wrapper.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343
# include <limits.h>
4444
#endif
4545

46+
#ifdef __linux__
47+
# include <sys/sysmacros.h>
48+
#endif
49+
4650
#define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC)
4751
#define php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC)
4852
#define php_stream_fopen_from_file_int(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC)
@@ -255,7 +259,28 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC)
255259
static void detect_is_seekable(php_stdio_stream_data *self) {
256260
#if defined(S_ISFIFO) && defined(S_ISCHR)
257261
if (self->fd >= 0 && do_fstat(self, 0) == 0) {
262+
#ifdef __linux__
263+
if (S_ISCHR(self->sb.st_mode)) {
264+
/* Some character devices are exceptions, check their major/minor ID
265+
* https://www.kernel.org/doc/Documentation/admin-guide/devices.txt */
266+
if (major(self->sb.st_rdev) == 1) {
267+
unsigned m = minor(self->sb.st_rdev);
268+
self->is_seekable =
269+
m == 1 || /* /dev/mem */
270+
m == 2 || /* /dev/kmem */
271+
m == 3 || /* /dev/null */
272+
m == 4 || /* /dev/port (seekable, offset = I/O port) */
273+
m == 5 || /* /dev/zero */
274+
m == 7; /* /dev/full */
275+
} else {
276+
self->is_seekable = false;
277+
}
278+
} else {
279+
self->is_seekable = !S_ISFIFO(self->sb.st_mode);
280+
}
281+
#else
258282
self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode));
283+
#endif
259284
self->is_pipe = S_ISFIFO(self->sb.st_mode);
260285
}
261286
#elif defined(PHP_WIN32)

0 commit comments

Comments
 (0)