Skip to content

Commit 055c092

Browse files
committed
FIX: improved handling of virtual files on POSIX systems
resolves: Oldes/Rebol-issues#2303
1 parent 44fa97e commit 055c092

File tree

4 files changed

+65
-16
lines changed

4 files changed

+65
-16
lines changed

src/core/p-file.c

+11-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
** REBOL [R3] Language Interpreter and Run-time Environment
44
**
55
** Copyright 2012 REBOL Technologies
6-
** Copyright 2012-2022 Rebol Open Source Contributors
6+
** Copyright 2012-2024 Rebol Open Source Contributors
77
** REBOL is a trademark of REBOL Technologies
88
**
99
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -275,15 +275,24 @@ REBINT Mode_Syms[] = {
275275
{
276276
REBSER *ser;
277277
REBVAL *ds = DS_RETURN;
278+
REBINT res;
278279

280+
resize:
279281
// Allocate read result buffer:
280282
ser = Make_Binary(len);
281283
Set_Series(REB_BINARY, ds, ser); //??? what if already set?
282284

283285
// Do the read, check for errors:
284286
file->data = BIN_HEAD(ser);
285287
file->length = len;
286-
if (OS_DO_DEVICE(file, RDC_READ) < 0) return; // Trap_Port(RE_READ_ERROR, port, file->error);
288+
res = OS_DO_DEVICE(file, RDC_READ);
289+
if (res == -RFE_RESIZE_SERIES) {
290+
// We are reading virtual file where the size was initialy reported as 0,
291+
// but now we have the real size calculated, so allocate the buffer again.
292+
len = file->file.size;
293+
goto resize;
294+
}
295+
if (res < 0) return; // Trap_Port(RE_READ_ERROR, port, file->error);
287296
// Not throwing the error from here!
288297
// We may want to close the port before error reporting.
289298
// It is passed above in the file->error

src/include/reb-file.h

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
** REBOL [R3] Language Interpreter and Run-time Environment
44
**
55
** Copyright 2012 REBOL Technologies
6+
** Copyright 2012-2024 Rebol Open Source Developers
67
** REBOL is a trademark of REBOL Technologies
78
**
89
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -53,6 +54,7 @@ enum {
5354
RFE_BAD_READ, // Read failed (general)
5455
RFE_BAD_WRITE, // Write failed (general)
5556
RFE_DISK_FULL, // No space on target volume
57+
RFE_RESIZE_SERIES, // Used on Posix to report, that the target series must be resized
5658
};
5759

5860
#define MAX_FILE_NAME 1022

src/os/posix/dev-file.c

+40-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
** REBOL [R3] Language Interpreter and Run-time Environment
44
**
55
** Copyright 2012 REBOL Technologies
6+
** Copyright 2012-2024 Rebol Open Source Developers
67
** REBOL is a trademark of REBOL Technologies
78
**
89
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -432,7 +433,22 @@ static int Get_File_Info(REBREQ *file)
432433
return DR_DONE;
433434
}
434435

435-
436+
// Resolves real size of virtual files (like /proc/cpuinfo)
437+
static size_t get_virtual_file_size(const char *filepath) {
438+
#define BUFFER_SIZE 4096
439+
char buffer[BUFFER_SIZE];
440+
size_t size = 0;
441+
int file = open(filepath, O_RDONLY, S_IREAD);
442+
if (file) {
443+
while (1) {
444+
size_t bytesRead = read(file, buffer, BUFFER_SIZE);
445+
if (bytesRead == 0) break;
446+
size += bytesRead;
447+
}
448+
close(file);
449+
}
450+
return size;
451+
}
436452
/***********************************************************************
437453
**
438454
*/ DEVICE_CMD Read_File(REBREQ *file)
@@ -463,16 +479,31 @@ static int Get_File_Info(REBREQ *file)
463479
if (!Seek_File_64(file)) return DR_ERROR;
464480
}
465481

466-
// printf("read %d len %d\n", file->id, file->length);
467-
num_bytes = read(file->id, file->data, file->length);
468-
if (num_bytes < 0) {
469-
file->error = -RFE_BAD_READ;
470-
return DR_ERROR;
471-
} else {
472-
file->actual = num_bytes;
473-
file->file.index += file->actual;
482+
// virtual files on Posix report its size as 0, so try to resolve the real one
483+
// but only in case, when user did not set /part
484+
if (file->file.size == 0 && file->length == 0) {
485+
file->file.size = get_virtual_file_size(file->file.path);
486+
if (file->file.size > 0 && file->length < file->file.size) {
487+
file->error = -RFE_RESIZE_SERIES;
488+
return DR_ERROR;
489+
}
474490
}
475491

492+
// printf("read %d len %d\n", file->id, file->length);
493+
file->actual = 0;
494+
// Using the loop, because the reading may be done in chunks!
495+
while (1) {
496+
num_bytes = read(file->id, file->data + file->actual, file->length - file->actual);
497+
if (num_bytes == 0) break;
498+
if (num_bytes < 0) {
499+
file->error = -RFE_BAD_READ;
500+
return DR_ERROR;
501+
}
502+
file->actual += num_bytes;
503+
// stop in case that we have enough data (requested just part of it)
504+
if (file->actual >= file->length) break;
505+
}
506+
file->file.index += file->actual;
476507
return DR_DONE;
477508
}
478509

src/tests/units/port-test.r3

+12-5
Original file line numberDiff line numberDiff line change
@@ -582,14 +582,21 @@ if system/platform = 'Windows [
582582
if exists? %/proc/cpuinfo [
583583
--test-- "Reading from /proc files on Linux"
584584
;@@ https://github.com/Oldes/Rebol-issues/issues/2303
585+
;; reading complete file
585586
--assert all [
586-
not error? info: try [read/string %/proc/cpuinfo]
587-
empty? info ;; empty, because to read this type of file, the size must be specified!
587+
not error? info: try [read %/proc/cpuinfo]
588+
0 < len: length? info
589+
print to string! info
588590
]
591+
;; test when requested longer part of the virtual file
589592
--assert all [
590-
not error? info: try [read/string/part %/proc/cpuinfo 10000]
591-
print info
592-
0 < length? info
593+
not error? info: try [read/part %/proc/cpuinfo len + 1000]
594+
len == length? info
595+
]
596+
;; test when requested just a short part of the virtual file
597+
--assert all [
598+
not error? info: try [read/part %/proc/cpuinfo 10]
599+
10 == length? info
593600
]
594601
]
595602
--test-- "Reading an empty file"

0 commit comments

Comments
 (0)