Skip to content

Commit

Permalink
FIX: improved handling of virtual files on POSIX systems
Browse files Browse the repository at this point in the history
  • Loading branch information
Oldes committed May 28, 2024
1 parent 44fa97e commit 055c092
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 16 deletions.
13 changes: 11 additions & 2 deletions src/core/p-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2022 Rebol Open Source Contributors
** Copyright 2012-2024 Rebol Open Source Contributors
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -275,15 +275,24 @@ REBINT Mode_Syms[] = {
{
REBSER *ser;
REBVAL *ds = DS_RETURN;
REBINT res;

resize:
// Allocate read result buffer:
ser = Make_Binary(len);
Set_Series(REB_BINARY, ds, ser); //??? what if already set?

// Do the read, check for errors:
file->data = BIN_HEAD(ser);
file->length = len;
if (OS_DO_DEVICE(file, RDC_READ) < 0) return; // Trap_Port(RE_READ_ERROR, port, file->error);
res = OS_DO_DEVICE(file, RDC_READ);
if (res == -RFE_RESIZE_SERIES) {
// We are reading virtual file where the size was initialy reported as 0,
// but now we have the real size calculated, so allocate the buffer again.
len = file->file.size;
goto resize;
}
if (res < 0) return; // Trap_Port(RE_READ_ERROR, port, file->error);
// Not throwing the error from here!
// We may want to close the port before error reporting.
// It is passed above in the file->error
Expand Down
2 changes: 2 additions & 0 deletions src/include/reb-file.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2024 Rebol Open Source Developers
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -53,6 +54,7 @@ enum {
RFE_BAD_READ, // Read failed (general)
RFE_BAD_WRITE, // Write failed (general)
RFE_DISK_FULL, // No space on target volume
RFE_RESIZE_SERIES, // Used on Posix to report, that the target series must be resized
};

#define MAX_FILE_NAME 1022
49 changes: 40 additions & 9 deletions src/os/posix/dev-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2024 Rebol Open Source Developers
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -432,7 +433,22 @@ static int Get_File_Info(REBREQ *file)
return DR_DONE;
}


// Resolves real size of virtual files (like /proc/cpuinfo)
static size_t get_virtual_file_size(const char *filepath) {
#define BUFFER_SIZE 4096
char buffer[BUFFER_SIZE];
size_t size = 0;
int file = open(filepath, O_RDONLY, S_IREAD);
if (file) {
while (1) {
size_t bytesRead = read(file, buffer, BUFFER_SIZE);
if (bytesRead == 0) break;
size += bytesRead;
}
close(file);
}
return size;
}
/***********************************************************************
**
*/ DEVICE_CMD Read_File(REBREQ *file)
Expand Down Expand Up @@ -463,16 +479,31 @@ static int Get_File_Info(REBREQ *file)
if (!Seek_File_64(file)) return DR_ERROR;
}

// printf("read %d len %d\n", file->id, file->length);
num_bytes = read(file->id, file->data, file->length);
if (num_bytes < 0) {
file->error = -RFE_BAD_READ;
return DR_ERROR;
} else {
file->actual = num_bytes;
file->file.index += file->actual;
// virtual files on Posix report its size as 0, so try to resolve the real one
// but only in case, when user did not set /part
if (file->file.size == 0 && file->length == 0) {
file->file.size = get_virtual_file_size(file->file.path);
if (file->file.size > 0 && file->length < file->file.size) {
file->error = -RFE_RESIZE_SERIES;
return DR_ERROR;
}
}

// printf("read %d len %d\n", file->id, file->length);
file->actual = 0;
// Using the loop, because the reading may be done in chunks!
while (1) {
num_bytes = read(file->id, file->data + file->actual, file->length - file->actual);
if (num_bytes == 0) break;
if (num_bytes < 0) {
file->error = -RFE_BAD_READ;
return DR_ERROR;
}
file->actual += num_bytes;
// stop in case that we have enough data (requested just part of it)
if (file->actual >= file->length) break;
}
file->file.index += file->actual;
return DR_DONE;
}

Expand Down
17 changes: 12 additions & 5 deletions src/tests/units/port-test.r3
Original file line number Diff line number Diff line change
Expand Up @@ -582,14 +582,21 @@ if system/platform = 'Windows [
if exists? %/proc/cpuinfo [
--test-- "Reading from /proc files on Linux"
;@@ https://github.com/Oldes/Rebol-issues/issues/2303
;; reading complete file
--assert all [
not error? info: try [read/string %/proc/cpuinfo]
empty? info ;; empty, because to read this type of file, the size must be specified!
not error? info: try [read %/proc/cpuinfo]
0 < len: length? info
print to string! info
]
;; test when requested longer part of the virtual file
--assert all [
not error? info: try [read/string/part %/proc/cpuinfo 10000]
print info
0 < length? info
not error? info: try [read/part %/proc/cpuinfo len + 1000]
len == length? info
]
;; test when requested just a short part of the virtual file
--assert all [
not error? info: try [read/part %/proc/cpuinfo 10]
10 == length? info
]
]
--test-- "Reading an empty file"
Expand Down

0 comments on commit 055c092

Please sign in to comment.