mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-23 09:52:23 +00:00
Support pinned Arduino 3 filesystem buffer checks
This commit is contained in:
1 parent
19d02c0dfa
commit
74a2bb0723
10 files changed
+1711
-36
No files matched your search
@@ -44,17 +44,55 @@ STRICT_METHOD = ORIGINAL_METHOD.replace(
|
||||
&& static_cast<size_t>(_f->_bf._size) == size));''')
|
||||
|
||||
|
||||
def patched_vfs_source(source):
|
||||
LAYOUT_GUARD_ARDUINO3 = LAYOUT_GUARD.replace(
|
||||
"Arduino 2.0.17/newlib 3.3.0", "Arduino 3.1.3/3.3.11/newlib 4.3.0"
|
||||
).replace("__NEWLIB__ != 3", "__NEWLIB__ != 4")
|
||||
ORIGINAL_METHOD_ARDUINO3 = '''bool VFSFileImpl::setBufferSize(size_t size) {
|
||||
if (_isDirectory || !_f) {
|
||||
return 0;
|
||||
}
|
||||
int res = setvbuf(_f, NULL, _IOFBF, size);
|
||||
return res == 0;
|
||||
}'''
|
||||
STRICT_METHOD_ARDUINO3 = ORIGINAL_METHOD_ARDUINO3.replace(
|
||||
" return res == 0;",
|
||||
''' // Reject a successful newlib fallback to a different allocation size.
|
||||
// size==0 retains the public choose-default behavior.
|
||||
return res == 0 && (size == 0 || (_f->_bf._size > 0
|
||||
&& static_cast<size_t>(_f->_bf._size) == size));''')
|
||||
FRAMEWORK_PINS = {
|
||||
(2, 0, 17): (PINNED_VFS_SHA256, PATCHED_VFS_SHA256),
|
||||
(3, 1, 3): (
|
||||
'b879998d67c5c6fc36845831af60b03338365031b4ca15d638d5ff6389a5c155',
|
||||
'5facd9c1543aad0105008ce6c93db0b771396834bf18cec6c62cceadee08d109'),
|
||||
(3, 3, 11): (
|
||||
'3d345bcb3b764f08c6e923dbacc96f27da10edb12beb774de439228057d14a54',
|
||||
'0cfa80cc29a38e854fa17401605dbe9adbd6157195ec5444f0a90d68c344cd0c'),
|
||||
}
|
||||
|
||||
|
||||
def patched_vfs_source(source, framework_version=(2, 0, 17)):
|
||||
try:
|
||||
pinned_digest, patched_digest = FRAMEWORK_PINS[framework_version]
|
||||
except KeyError as error:
|
||||
raise RuntimeError("ESP32 FS buffer fix: unreviewed framework version") from error
|
||||
source = source.replace("\r\n", "\n")
|
||||
digest = hashlib.sha256(source.encode("utf-8")).hexdigest()
|
||||
if digest == PATCHED_VFS_SHA256:
|
||||
if digest == patched_digest:
|
||||
return source
|
||||
if (digest != PINNED_VFS_SHA256 or source.count(INCLUDE) != 1
|
||||
or source.count(ORIGINAL_METHOD) != 1):
|
||||
raise RuntimeError("ESP32 FS buffer fix: changed pinned2.0.17 source; review SDK update")
|
||||
patched = source.replace(INCLUDE, INCLUDE + LAYOUT_GUARD, 1).replace(
|
||||
ORIGINAL_METHOD, STRICT_METHOD, 1)
|
||||
if hashlib.sha256(patched.encode("utf-8")).hexdigest() != PATCHED_VFS_SHA256:
|
||||
original, strict, layout = ((ORIGINAL_METHOD, STRICT_METHOD, LAYOUT_GUARD)
|
||||
if framework_version == (2, 0, 17) else
|
||||
(ORIGINAL_METHOD_ARDUINO3, STRICT_METHOD_ARDUINO3,
|
||||
LAYOUT_GUARD_ARDUINO3))
|
||||
if (digest != pinned_digest or source.count(INCLUDE) != 1
|
||||
or source.count(original) != 1):
|
||||
raise RuntimeError("ESP32 FS buffer fix: changed pinned source; review SDK update")
|
||||
patched = source.replace(INCLUDE, INCLUDE + layout, 1).replace(original, strict, 1)
|
||||
# Only the reviewed 3.3.11 comments contain these characters. Keep generated
|
||||
# project copies ASCII without changing the shared SDK or executable text.
|
||||
for character, replacement in (("\u2192", "->"), ("\u2013", "-"), ("\u2014", "-")):
|
||||
patched = patched.replace(character, replacement)
|
||||
if hashlib.sha256(patched.encode("utf-8")).hexdigest() != patched_digest:
|
||||
raise RuntimeError("ESP32 FS buffer fix: unexpected transform result")
|
||||
return patched
|
||||
|
||||
@@ -79,8 +117,13 @@ def require_pinned_framework(source):
|
||||
raise RuntimeError("ESP32 FS buffer fix: missing framework version header") from error
|
||||
entries = re.findall(r"^#define ESP_ARDUINO_VERSION_(MAJOR|MINOR|PATCH)\s+(\d+)\s*$",
|
||||
header, re.MULTILINE)
|
||||
if (len(entries) != 3 or dict(entries) != {"MAJOR": "2", "MINOR": "0", "PATCH": "17"}):
|
||||
values = dict(entries)
|
||||
if len(entries) != 3 or set(values) != {"MAJOR", "MINOR", "PATCH"}:
|
||||
raise RuntimeError("ESP32 FS buffer fix: changed/malformed pinned framework version")
|
||||
version = tuple(int(values[key]) for key in ("MAJOR", "MINOR", "PATCH"))
|
||||
if version not in FRAMEWORK_PINS:
|
||||
raise RuntimeError("ESP32 FS buffer fix: changed/malformed pinned framework version")
|
||||
return version
|
||||
|
||||
|
||||
def replace_vfs_source(build_env, node):
|
||||
@@ -89,8 +132,8 @@ def replace_vfs_source(build_env, node):
|
||||
source = Path(node.srcnode().get_abspath())
|
||||
if source.parts[-4:] != SOURCE_SUFFIX:
|
||||
return node
|
||||
require_pinned_framework(source)
|
||||
patched = patched_vfs_source(source.read_text(encoding="utf-8"))
|
||||
version = require_pinned_framework(source)
|
||||
patched = patched_vfs_source(source.read_text(encoding="utf-8"), version)
|
||||
destination = Path(build_env.subst("$BUILD_DIR")) / "patched-esp32-fs" / source.name
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not destination.exists() or destination.read_text(encoding="utf-8") != patched:
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Major version number (X.x.x) */
|
||||
#define ESP_ARDUINO_VERSION_MAJOR 3
|
||||
/** Minor version number (x.X.x) */
|
||||
#define ESP_ARDUINO_VERSION_MINOR 1
|
||||
/** Patch version number (x.x.X) */
|
||||
#define ESP_ARDUINO_VERSION_PATCH 3
|
||||
|
||||
/**
|
||||
* Macro to convert ARDUINO version number into an integer
|
||||
*
|
||||
* To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
||||
*/
|
||||
#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
|
||||
|
||||
/**
|
||||
* Current ARDUINO version, as an integer
|
||||
*
|
||||
* To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
||||
*/
|
||||
#define ESP_ARDUINO_VERSION ESP_ARDUINO_VERSION_VAL(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
|
||||
|
||||
/**
|
||||
* Current ARDUINO version, as string
|
||||
*/
|
||||
#define df2xstr(s) #s
|
||||
#define df2str(s) df2xstr(s)
|
||||
#define ESP_ARDUINO_VERSION_STR df2str(ESP_ARDUINO_VERSION_MAJOR) "." df2str(ESP_ARDUINO_VERSION_MINOR) "." df2str(ESP_ARDUINO_VERSION_PATCH)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+532
@@ -0,0 +1,532 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "vfs_api.h"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
#define DEFAULT_FILE_BUFFER_SIZE 4096
|
||||
|
||||
FileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create) {
|
||||
if (!_mountpoint) {
|
||||
log_e("File system is not mounted");
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
if (!fpath || fpath[0] != '/') {
|
||||
log_e("%s does not start with /", fpath);
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 2);
|
||||
if (!temp) {
|
||||
log_e("malloc failed");
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
strcpy(temp, _mountpoint);
|
||||
strcat(temp, fpath);
|
||||
|
||||
struct stat st;
|
||||
//file found
|
||||
if (!stat(temp, &st)) {
|
||||
free(temp);
|
||||
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
}
|
||||
log_e("%s has wrong mode 0x%08X", fpath, st.st_mode);
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
//try to open this as directory (might be mount point)
|
||||
DIR *d = opendir(temp);
|
||||
if (d) {
|
||||
closedir(d);
|
||||
free(temp);
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
}
|
||||
|
||||
//file not found but mode permits file creation without folder creation
|
||||
if ((mode && mode[0] != 'r') && (!create)) {
|
||||
free(temp);
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
}
|
||||
|
||||
////file not found but mode permits file creation and folder creation
|
||||
if ((mode && mode[0] != 'r') && create) {
|
||||
|
||||
char *token;
|
||||
char *folder = (char *)malloc(strlen(fpath));
|
||||
|
||||
int start_index = 0;
|
||||
int end_index = 0;
|
||||
|
||||
token = strchr(fpath + 1, '/');
|
||||
end_index = (token - fpath);
|
||||
|
||||
while (token != NULL) {
|
||||
memcpy(folder, fpath + start_index, end_index - start_index);
|
||||
folder[end_index - start_index] = '\0';
|
||||
|
||||
if (!VFSImpl::mkdir(folder)) {
|
||||
log_e("Creating folder: %s failed!", folder);
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
token = strchr(token + 1, '/');
|
||||
if (token != NULL) {
|
||||
end_index = (token - fpath);
|
||||
memset(folder, 0, strlen(folder));
|
||||
}
|
||||
}
|
||||
|
||||
free(folder);
|
||||
free(temp);
|
||||
return std::make_shared<VFSFileImpl>(this, fpath, mode);
|
||||
}
|
||||
|
||||
log_e("%s does not exist, no permits for creation", temp);
|
||||
free(temp);
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
bool VFSImpl::exists(const char *fpath) {
|
||||
if (!_mountpoint) {
|
||||
log_e("File system is not mounted");
|
||||
return false;
|
||||
}
|
||||
|
||||
VFSFileImpl f(this, fpath, "r");
|
||||
if (f) {
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VFSImpl::rename(const char *pathFrom, const char *pathTo) {
|
||||
if (!_mountpoint) {
|
||||
log_e("File system is not mounted");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pathFrom || pathFrom[0] != '/' || !pathTo || pathTo[0] != '/') {
|
||||
log_e("bad arguments");
|
||||
return false;
|
||||
}
|
||||
if (!exists(pathFrom)) {
|
||||
log_e("%s does not exists", pathFrom);
|
||||
return false;
|
||||
}
|
||||
size_t mountpointLen = strlen(_mountpoint);
|
||||
char *temp1 = (char *)malloc(strlen(pathFrom) + mountpointLen + 1);
|
||||
if (!temp1) {
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
char *temp2 = (char *)malloc(strlen(pathTo) + mountpointLen + 1);
|
||||
if (!temp2) {
|
||||
free(temp1);
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(temp1, _mountpoint);
|
||||
strcat(temp1, pathFrom);
|
||||
|
||||
strcpy(temp2, _mountpoint);
|
||||
strcat(temp2, pathTo);
|
||||
|
||||
auto rc = ::rename(temp1, temp2);
|
||||
free(temp1);
|
||||
free(temp2);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool VFSImpl::remove(const char *fpath) {
|
||||
if (!_mountpoint) {
|
||||
log_e("File system is not mounted");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fpath || fpath[0] != '/') {
|
||||
log_e("bad arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
VFSFileImpl f(this, fpath, "r");
|
||||
if (!f || f.isDirectory()) {
|
||||
if (f) {
|
||||
f.close();
|
||||
}
|
||||
log_e("%s does not exists or is directory", fpath);
|
||||
return false;
|
||||
}
|
||||
f.close();
|
||||
|
||||
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
|
||||
if (!temp) {
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(temp, _mountpoint);
|
||||
strcat(temp, fpath);
|
||||
|
||||
auto rc = unlink(temp);
|
||||
free(temp);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool VFSImpl::mkdir(const char *fpath) {
|
||||
if (!_mountpoint) {
|
||||
log_e("File system is not mounted");
|
||||
return false;
|
||||
}
|
||||
|
||||
VFSFileImpl f(this, fpath, "r");
|
||||
if (f && f.isDirectory()) {
|
||||
f.close();
|
||||
//log_w("%s already exists", fpath);
|
||||
return true;
|
||||
} else if (f) {
|
||||
f.close();
|
||||
log_e("%s is a file", fpath);
|
||||
return false;
|
||||
}
|
||||
|
||||
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
|
||||
if (!temp) {
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(temp, _mountpoint);
|
||||
strcat(temp, fpath);
|
||||
|
||||
auto rc = ::mkdir(temp, ACCESSPERMS);
|
||||
free(temp);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool VFSImpl::rmdir(const char *fpath) {
|
||||
if (!_mountpoint) {
|
||||
log_e("File system is not mounted");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(_mountpoint, "/spiffs") == 0) {
|
||||
log_e("rmdir is unnecessary in SPIFFS");
|
||||
return false;
|
||||
}
|
||||
|
||||
VFSFileImpl f(this, fpath, "r");
|
||||
if (!f || !f.isDirectory()) {
|
||||
if (f) {
|
||||
f.close();
|
||||
}
|
||||
log_e("%s does not exists or is a file", fpath);
|
||||
return false;
|
||||
}
|
||||
f.close();
|
||||
|
||||
char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);
|
||||
if (!temp) {
|
||||
log_e("malloc failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(temp, _mountpoint);
|
||||
strcat(temp, fpath);
|
||||
|
||||
auto rc = ::rmdir(temp);
|
||||
free(temp);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs(fs), _f(NULL), _d(NULL), _path(NULL), _isDirectory(false), _written(false) {
|
||||
char *temp = (char *)malloc(strlen(fpath) + strlen(_fs->_mountpoint) + 1);
|
||||
if (!temp) {
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(temp, _fs->_mountpoint);
|
||||
strcat(temp, fpath);
|
||||
|
||||
_path = strdup(fpath);
|
||||
if (!_path) {
|
||||
log_e("strdup(%s) failed", fpath);
|
||||
free(temp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stat(temp, &_stat)) {
|
||||
//file found
|
||||
if (S_ISREG(_stat.st_mode)) {
|
||||
_isDirectory = false;
|
||||
_f = fopen(temp, mode);
|
||||
if (!_f) {
|
||||
log_e("fopen(%s) failed", temp);
|
||||
}
|
||||
if (_f && (_stat.st_blksize == 0)) {
|
||||
setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE);
|
||||
}
|
||||
} else if (S_ISDIR(_stat.st_mode)) {
|
||||
_isDirectory = true;
|
||||
_d = opendir(temp);
|
||||
if (!_d) {
|
||||
log_e("opendir(%s) failed", temp);
|
||||
}
|
||||
} else {
|
||||
log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp);
|
||||
}
|
||||
} else {
|
||||
//file not found
|
||||
if (!mode || mode[0] == 'r') {
|
||||
//try to open as directory
|
||||
_d = opendir(temp);
|
||||
if (_d) {
|
||||
_isDirectory = true;
|
||||
} else {
|
||||
_isDirectory = false;
|
||||
//log_w("stat(%s) failed", temp);
|
||||
}
|
||||
} else {
|
||||
//lets create this new file
|
||||
_isDirectory = false;
|
||||
_f = fopen(temp, mode);
|
||||
if (!_f) {
|
||||
log_e("fopen(%s) failed", temp);
|
||||
}
|
||||
if (_f && (_stat.st_blksize == 0)) {
|
||||
setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
|
||||
VFSFileImpl::~VFSFileImpl() {
|
||||
close();
|
||||
}
|
||||
|
||||
void VFSFileImpl::close() {
|
||||
if (_path) {
|
||||
free(_path);
|
||||
_path = NULL;
|
||||
}
|
||||
if (_isDirectory && _d) {
|
||||
closedir(_d);
|
||||
_d = NULL;
|
||||
_isDirectory = false;
|
||||
} else if (_f) {
|
||||
fclose(_f);
|
||||
_f = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
VFSFileImpl::operator bool() {
|
||||
return (_isDirectory && _d != NULL) || _f != NULL;
|
||||
}
|
||||
|
||||
time_t VFSFileImpl::getLastWrite() {
|
||||
_getStat();
|
||||
return _stat.st_mtime;
|
||||
}
|
||||
|
||||
void VFSFileImpl::_getStat() const {
|
||||
if (!_path) {
|
||||
return;
|
||||
}
|
||||
char *temp = (char *)malloc(strlen(_path) + strlen(_fs->_mountpoint) + 1);
|
||||
if (!temp) {
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(temp, _fs->_mountpoint);
|
||||
strcat(temp, _path);
|
||||
|
||||
if (!stat(temp, &_stat)) {
|
||||
_written = false;
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
|
||||
size_t VFSFileImpl::write(const uint8_t *buf, size_t size) {
|
||||
if (_isDirectory || !_f || !buf || !size) {
|
||||
return 0;
|
||||
}
|
||||
_written = true;
|
||||
return fwrite(buf, 1, size, _f);
|
||||
}
|
||||
|
||||
size_t VFSFileImpl::read(uint8_t *buf, size_t size) {
|
||||
if (_isDirectory || !_f || !buf || !size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fread(buf, 1, size, _f);
|
||||
}
|
||||
|
||||
void VFSFileImpl::flush() {
|
||||
if (_isDirectory || !_f) {
|
||||
return;
|
||||
}
|
||||
fflush(_f);
|
||||
// workaround for https://github.com/espressif/arduino-esp32/issues/1293
|
||||
fsync(fileno(_f));
|
||||
}
|
||||
|
||||
bool VFSFileImpl::seek(uint32_t pos, SeekMode mode) {
|
||||
if (_isDirectory || !_f) {
|
||||
return false;
|
||||
}
|
||||
auto rc = fseek(_f, pos, mode);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
size_t VFSFileImpl::position() const {
|
||||
if (_isDirectory || !_f) {
|
||||
return 0;
|
||||
}
|
||||
return ftell(_f);
|
||||
}
|
||||
|
||||
size_t VFSFileImpl::size() const {
|
||||
if (_isDirectory || !_f) {
|
||||
return 0;
|
||||
}
|
||||
if (_written) {
|
||||
_getStat();
|
||||
}
|
||||
return _stat.st_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change size of files internal buffer used for read / write operations.
|
||||
* Need to be called right after opening file before any other operation!
|
||||
*/
|
||||
bool VFSFileImpl::setBufferSize(size_t size) {
|
||||
if (_isDirectory || !_f) {
|
||||
return 0;
|
||||
}
|
||||
int res = setvbuf(_f, NULL, _IOFBF, size);
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
const char *VFSFileImpl::path() const {
|
||||
return (const char *)_path;
|
||||
}
|
||||
|
||||
const char *VFSFileImpl::name() const {
|
||||
return pathToFileName(path());
|
||||
}
|
||||
|
||||
//to implement
|
||||
boolean VFSFileImpl::isDirectory(void) {
|
||||
return _isDirectory;
|
||||
}
|
||||
|
||||
FileImplPtr VFSFileImpl::openNextFile(const char *mode) {
|
||||
if (!_isDirectory || !_d) {
|
||||
return FileImplPtr();
|
||||
}
|
||||
struct dirent *file = readdir(_d);
|
||||
if (file == NULL) {
|
||||
return FileImplPtr();
|
||||
}
|
||||
if (file->d_type != DT_REG && file->d_type != DT_DIR) {
|
||||
return openNextFile(mode);
|
||||
}
|
||||
|
||||
size_t pathLen = strlen(_path);
|
||||
size_t fileNameLen = strlen(file->d_name);
|
||||
char *name = (char *)malloc(pathLen + fileNameLen + 2);
|
||||
|
||||
if (name == NULL) {
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
strcpy(name, _path);
|
||||
|
||||
if ((file->d_name[0] != '/') && (_path[pathLen - 1] != '/')) {
|
||||
strcat(name, "/");
|
||||
}
|
||||
|
||||
strcat(name, file->d_name);
|
||||
|
||||
FileImplPtr fileImplPtr = std::make_shared<VFSFileImpl>(_fs, name, mode);
|
||||
free(name);
|
||||
return fileImplPtr;
|
||||
}
|
||||
|
||||
boolean VFSFileImpl::seekDir(long position) {
|
||||
if (!_d) {
|
||||
return false;
|
||||
}
|
||||
seekdir(_d, position);
|
||||
return true;
|
||||
}
|
||||
|
||||
String VFSFileImpl::getNextFileName() {
|
||||
if (!_isDirectory || !_d) {
|
||||
return "";
|
||||
}
|
||||
struct dirent *file = readdir(_d);
|
||||
if (file == NULL) {
|
||||
return "";
|
||||
}
|
||||
if (file->d_type != DT_REG && file->d_type != DT_DIR) {
|
||||
return "";
|
||||
}
|
||||
String fname = String(file->d_name);
|
||||
String name = String(_path);
|
||||
if (!fname.startsWith("/") && !name.endsWith("/")) {
|
||||
name += "/";
|
||||
}
|
||||
name += fname;
|
||||
return name;
|
||||
}
|
||||
|
||||
String VFSFileImpl::getNextFileName(bool *isDir) {
|
||||
if (!_isDirectory || !_d) {
|
||||
return "";
|
||||
}
|
||||
struct dirent *file = readdir(_d);
|
||||
if (file == NULL) {
|
||||
return "";
|
||||
}
|
||||
if (file->d_type != DT_REG && file->d_type != DT_DIR) {
|
||||
return "";
|
||||
}
|
||||
String fname = String(file->d_name);
|
||||
String name = String(_path);
|
||||
if (!fname.startsWith("/") && !name.endsWith("/")) {
|
||||
name += "/";
|
||||
}
|
||||
name += fname;
|
||||
|
||||
// check entry is a directory
|
||||
if (isDir) {
|
||||
*isDir = (file->d_type == DT_DIR);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void VFSFileImpl::rewindDirectory(void) {
|
||||
if (!_isDirectory || !_d) {
|
||||
return;
|
||||
}
|
||||
rewinddir(_d);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"source": "// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"vfs_api.h\"\n\nusing namespace fs;\n\n#define DEFAULT_FILE_BUFFER_SIZE 4096\n\nFileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create) {\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return FileImplPtr();\n }\n\n if (!fpath || fpath[0] != '/') {\n log_e(\"%s does not start with /\", fpath);\n return FileImplPtr();\n }\n\n char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 2);\n if (!temp) {\n log_e(\"malloc failed\");\n return FileImplPtr();\n }\n\n strcpy(temp, _mountpoint);\n strcat(temp, fpath);\n\n struct stat st;\n //file found\n if (!stat(temp, &st)) {\n free(temp);\n if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {\n return std::make_shared<VFSFileImpl>(this, fpath, mode);\n }\n log_e(\"%s has wrong mode 0x%08X\", fpath, st.st_mode);\n return FileImplPtr();\n }\n\n //try to open this as directory (might be mount point)\n DIR *d = opendir(temp);\n if (d) {\n closedir(d);\n free(temp);\n return std::make_shared<VFSFileImpl>(this, fpath, mode);\n }\n\n //file not found but mode permits file creation without folder creation\n if ((mode && mode[0] != 'r') && (!create)) {\n free(temp);\n return std::make_shared<VFSFileImpl>(this, fpath, mode);\n }\n\n ////file not found but mode permits file creation and folder creation\n if ((mode && mode[0] != 'r') && create) {\n\n char *token;\n char *folder = (char *)malloc(strlen(fpath));\n\n int start_index = 0;\n int end_index = 0;\n\n token = strchr(fpath + 1, '/');\n end_index = (token - fpath);\n\n while (token != NULL) {\n memcpy(folder, fpath + start_index, end_index - start_index);\n folder[end_index - start_index] = '\\0';\n\n if (!VFSImpl::mkdir(folder)) {\n log_e(\"Creating folder: %s failed!\", folder);\n return FileImplPtr();\n }\n\n token = strchr(token + 1, '/');\n if (token != NULL) {\n end_index = (token - fpath);\n memset(folder, 0, strlen(folder));\n }\n }\n\n free(folder);\n free(temp);\n return std::make_shared<VFSFileImpl>(this, fpath, mode);\n }\n\n log_e(\"%s does not exist, no permits for creation\", temp);\n free(temp);\n return FileImplPtr();\n}\n\nbool VFSImpl::exists(const char *fpath) {\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n VFSFileImpl f(this, fpath, \"r\");\n if (f) {\n f.close();\n return true;\n }\n return false;\n}\n\nbool VFSImpl::rename(const char *pathFrom, const char *pathTo) {\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n if (!pathFrom || pathFrom[0] != '/' || !pathTo || pathTo[0] != '/') {\n log_e(\"bad arguments\");\n return false;\n }\n if (!exists(pathFrom)) {\n log_e(\"%s does not exists\", pathFrom);\n return false;\n }\n size_t mountpointLen = strlen(_mountpoint);\n char *temp1 = (char *)malloc(strlen(pathFrom) + mountpointLen + 1);\n if (!temp1) {\n log_e(\"malloc failed\");\n return false;\n }\n char *temp2 = (char *)malloc(strlen(pathTo) + mountpointLen + 1);\n if (!temp2) {\n free(temp1);\n log_e(\"malloc failed\");\n return false;\n }\n\n strcpy(temp1, _mountpoint);\n strcat(temp1, pathFrom);\n\n strcpy(temp2, _mountpoint);\n strcat(temp2, pathTo);\n\n auto rc = ::rename(temp1, temp2);\n free(temp1);\n free(temp2);\n return rc == 0;\n}\n\nbool VFSImpl::remove(const char *fpath) {\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n if (!fpath || fpath[0] != '/') {\n log_e(\"bad arguments\");\n return false;\n }\n\n VFSFileImpl f(this, fpath, \"r\");\n if (!f || f.isDirectory()) {\n if (f) {\n f.close();\n }\n log_e(\"%s does not exists or is directory\", fpath);\n return false;\n }\n f.close();\n\n char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1);\n if (!temp) {\n log_e(\"malloc failed\");\n return false;\n }\n\n strcpy(temp, _mountpoint);\n strcat(temp, fpath);\n\n auto rc = unlink(temp);\n free(temp);\n return rc == 0;\n}\n\nbool VFSImpl::mkdir(const char *fpath) {\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n VFSFileImpl f(Line truncated
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Major version number (X.x.x) */
|
||||
#define ESP_ARDUINO_VERSION_MAJOR 3
|
||||
/** Minor version number (x.X.x) */
|
||||
#define ESP_ARDUINO_VERSION_MINOR 3
|
||||
/** Patch version number (x.x.X) */
|
||||
#define ESP_ARDUINO_VERSION_PATCH 11
|
||||
|
||||
/**
|
||||
* Macro to convert ARDUINO version number into an integer
|
||||
*
|
||||
* To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
||||
*/
|
||||
#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
|
||||
|
||||
/**
|
||||
* Current ARDUINO version, as an integer
|
||||
*
|
||||
* To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
||||
*/
|
||||
#define ESP_ARDUINO_VERSION ESP_ARDUINO_VERSION_VAL(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
|
||||
|
||||
/**
|
||||
* Current ARDUINO version, as string
|
||||
*/
|
||||
#define df2xstr(s) #s
|
||||
#define df2str(s) df2xstr(s)
|
||||
#define ESP_ARDUINO_VERSION_STR df2str(ESP_ARDUINO_VERSION_MAJOR) "." df2str(ESP_ARDUINO_VERSION_MINOR) "." df2str(ESP_ARDUINO_VERSION_PATCH)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"source": "// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"Arduino.h\"\n#include \"vfs_api.h\"\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n\nusing namespace fs;\n\n#define DEFAULT_FILE_BUFFER_SIZE 4096\n\nFileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create) {\n auto lock = fsLock();\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return FileImplPtr();\n }\n\n if (!fpath || fpath[0] != '/') {\n log_e(\"%s does not start with /\", fpath);\n return FileImplPtr();\n }\n\n // Try to open as file first - let the file operation handle errors\n if (mode && mode[0] != 'r') {\n // For write modes, attempt to create directories if needed\n if (create) {\n char *token;\n char *folder = (char *)malloc(strlen(fpath) + 1);\n\n if (!folder) {\n log_e(\"Folder name malloc failed\");\n return FileImplPtr();\n }\n\n int start_index = 0;\n int end_index = 0;\n\n token = strchr(fpath + 1, '/');\n if (token != NULL) {\n end_index = (token - fpath);\n }\n\n while (token != NULL) {\n memcpy(folder, fpath + start_index, end_index - start_index);\n folder[end_index - start_index] = '\\0';\n\n if (!VFSImpl::mkdir(folder)) {\n log_e(\"Creating folder: %s failed!\", folder);\n free(folder);\n return FileImplPtr();\n }\n\n token = strchr(token + 1, '/');\n if (token != NULL) {\n end_index = (token - fpath);\n memset(folder, 0, strlen(folder));\n }\n }\n\n free(folder);\n }\n\n // Try to open the file directly - let fopen handle errors\n return std::make_shared<VFSFileImpl>(this, fpath, mode);\n }\n\n // For read mode, let the VFSFileImpl constructor handle the file opening\n // This avoids the TOCTOU race condition while maintaining proper functionality\n return std::make_shared<VFSFileImpl>(this, fpath, mode);\n}\n\nbool VFSImpl::exists(const char *fpath) {\n auto lock = fsLock();\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n VFSFileImpl f(this, fpath, \"r\");\n if (f) {\n f.close();\n return true;\n }\n return false;\n}\n\nbool VFSImpl::rename(const char *pathFrom, const char *pathTo) {\n auto lock = fsLock();\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n if (!pathFrom || pathFrom[0] != '/' || !pathTo || pathTo[0] != '/') {\n log_e(\"bad arguments\");\n return false;\n }\n\n size_t mountpointLen = strlen(_mountpoint);\n size_t temp1Len = mountpointLen + strlen(pathFrom) + 1;\n char *temp1 = (char *)malloc(temp1Len);\n if (!temp1) {\n log_e(\"malloc failed\");\n return false;\n }\n size_t temp2Len = mountpointLen + strlen(pathTo) + 1;\n char *temp2 = (char *)malloc(temp2Len);\n if (!temp2) {\n free(temp1);\n log_e(\"malloc failed\");\n return false;\n }\n\n snprintf(temp1, temp1Len, \"%s%s\", _mountpoint, pathFrom);\n snprintf(temp2, temp2Len, \"%s%s\", _mountpoint, pathTo);\n\n // Let rename() handle the error if source doesn't exist\n auto rc = ::rename(temp1, temp2);\n free(temp1);\n free(temp2);\n return rc == 0;\n}\n\nbool VFSImpl::remove(const char *fpath) {\n auto lock = fsLock();\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n if (!fpath || fpath[0] != '/') {\n log_e(\"bad arguments\");\n return false;\n }\n\n size_t tempLen = strlen(_mountpoint) + strlen(fpath) + 1;\n char *temp = (char *)malloc(tempLen);\n if (!temp) {\n log_e(\"malloc failed\");\n return false;\n }\n\n snprintf(temp, tempLen, \"%s%s\", _mountpoint, fpath);\n\n // Let unlink() handle the error if file doesn't exist\n auto rc = unlink(temp);\n free(temp);\n return rc == 0;\n}\n\nbool VFSImpl::mkdir(const char *fpath) {\n auto lock = fsLock();\n if (!_mountpoint) {\n log_e(\"File system is not mounted\");\n return false;\n }\n\n size_t tempLen = strlen(_mountpoint) + strlen(fpath) + 1;\n char *temp = (char *)malloc(tempLen);\n if (!temp) {\n log_e(\"malloc failed\");\n return false;\n }\n\n snprintf(temp, tempLen, \"%s%s\", _mountpoint, fpath);\n\n // Note: the stat() \u2192 opendir() \u2192 ::mkdir() sequence below contains a\n // TOCTOU (time-of-check / timeLine truncated
|
||||
}
|
||||
+21
@@ -27,3 +27,24 @@ model 32-bit pointer widths rather than borrowing the host libc's FILE layout.
|
||||
|
||||
Backend buffer admission is not a wall-clock guarantee: SPIFFS metadata,
|
||||
garbage collection, flush, close and publication still require hardware tests.
|
||||
|
||||
Arduino-ESP32 3.1.3 and 3.3.11:
|
||||
The versioned directories contain exact upstream version headers and VFS source
|
||||
encoded in the JSON "source" field. JSON escaping preserves the upstream bytes
|
||||
while keeping repository fixture files ASCII. The build copy replaces only the
|
||||
reviewed Unicode comment punctuation; the shared SDK remains unmodified.
|
||||
https://github.com/espressif/arduino-esp32/blob/3.1.3/libraries/FS/src/vfs_api.cpp
|
||||
https://github.com/espressif/arduino-esp32/blob/3.3.11/libraries/FS/src/vfs_api.cpp
|
||||
Both source variants retain their complete upstream copyright/license notices.
|
||||
3.1.3 source SHA256 b879998d67c5c6fc36845831af60b03338365031b4ca15d638d5ff6389a5c155
|
||||
3.3.11 source SHA256 3d345bcb3b764f08c6e923dbacc96f27da10edb12beb774de439228057d14a54
|
||||
|
||||
newlib4/ contains the exact shared FILE-layout and generated version headers
|
||||
from Xtensa 14.2.0+20260121 and RISC-V 13.2.0+20240530, newlib 4.3.0.
|
||||
Both installed reent.h files match these complete upstream files byte for byte:
|
||||
https://github.com/espressif/newlib-esp32/blob/esp-4.3.0_20260121/newlib/libc/include/sys/reent.h
|
||||
https://github.com/espressif/newlib-esp32/blob/esp-4.3.0_20240530/newlib/libc/include/sys/reent.h
|
||||
https://github.com/espressif/newlib-esp32/blob/esp-4.3.0_20260121/COPYING.NEWLIB
|
||||
reent SHA256 c7ba657f911f7e27b7449bda36aa4efa80a025c726768dabb8d4a792f535ec9a
|
||||
The reviewed FILE prefix has the same 32-bit _bf/_size offsets as newlib 3.3.0.
|
||||
Arduino 3 uses a separate newlib 4.3.0 version guard; other versions fail closed.
|
||||
@@ -0,0 +1,11 @@
|
||||
/* _newlib_version.h. Generated from _newlib_version.hin by configure. */
|
||||
/* Version macros for internal and downstream use. */
|
||||
#ifndef _NEWLIB_VERSION_H__
|
||||
#define _NEWLIB_VERSION_H__ 1
|
||||
|
||||
#define _NEWLIB_VERSION "4.3.0"
|
||||
#define __NEWLIB__ 4
|
||||
#define __NEWLIB_MINOR__ 3
|
||||
#define __NEWLIB_PATCHLEVEL__ 0
|
||||
|
||||
#endif /* !_NEWLIB_VERSION_H__ */
|
||||
@@ -0,0 +1,896 @@
|
||||
/* This header file provides the reentrancy. */
|
||||
|
||||
/* WARNING: All identifiers here must begin with an underscore. This file is
|
||||
included by stdio.h and others and we therefore must only use identifiers
|
||||
in the namespace allotted to us. */
|
||||
|
||||
#ifndef _SYS_REENT_H_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define _SYS_REENT_H_
|
||||
|
||||
#include <_ansi.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/_types.h>
|
||||
|
||||
#define _NULL 0
|
||||
|
||||
#ifndef __Long
|
||||
#if __LONG_MAX__ == 2147483647L
|
||||
#define __Long long
|
||||
typedef unsigned __Long __ULong;
|
||||
#elif __INT_MAX__ == 2147483647
|
||||
#define __Long int
|
||||
typedef unsigned __Long __ULong;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined( __Long)
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifndef __machine_flock_t_defined
|
||||
#include <sys/lock.h>
|
||||
typedef _LOCK_RECURSIVE_T _flock_t;
|
||||
#endif
|
||||
|
||||
#ifndef __Long
|
||||
#define __Long __int32_t
|
||||
typedef __uint32_t __ULong;
|
||||
#endif
|
||||
|
||||
struct _reent;
|
||||
|
||||
struct __locale_t;
|
||||
|
||||
/*
|
||||
* If _REENT_SMALL is defined, we make struct _reent as small as possible,
|
||||
* by having nearly everything possible allocated at first use.
|
||||
*/
|
||||
|
||||
struct _Bigint
|
||||
{
|
||||
struct _Bigint *_next;
|
||||
int _k, _maxwds, _sign, _wds;
|
||||
__ULong _x[1];
|
||||
};
|
||||
|
||||
/* needed by reentrant structure */
|
||||
struct __tm
|
||||
{
|
||||
int __tm_sec;
|
||||
int __tm_min;
|
||||
int __tm_hour;
|
||||
int __tm_mday;
|
||||
int __tm_mon;
|
||||
int __tm_year;
|
||||
int __tm_wday;
|
||||
int __tm_yday;
|
||||
int __tm_isdst;
|
||||
};
|
||||
|
||||
/*
|
||||
* atexit() support.
|
||||
*/
|
||||
|
||||
#define _ATEXIT_SIZE 32 /* must be at least 32 to guarantee ANSI conformance */
|
||||
|
||||
struct _on_exit_args {
|
||||
void * _fnargs[_ATEXIT_SIZE]; /* user fn args */
|
||||
void * _dso_handle[_ATEXIT_SIZE];
|
||||
/* Bitmask is set if user function takes arguments. */
|
||||
__ULong _fntypes; /* type of exit routine -
|
||||
Must have at least _ATEXIT_SIZE bits */
|
||||
/* Bitmask is set if function was registered via __cxa_atexit. */
|
||||
__ULong _is_cxa;
|
||||
};
|
||||
|
||||
#ifdef _REENT_SMALL
|
||||
struct _atexit {
|
||||
struct _atexit *_next; /* next in list */
|
||||
int _ind; /* next index in this table */
|
||||
void (*_fns[_ATEXIT_SIZE])(void); /* the table itself */
|
||||
struct _on_exit_args * _on_exit_args_ptr;
|
||||
};
|
||||
# define _ATEXIT_INIT {_NULL, 0, {_NULL}, _NULL}
|
||||
#else
|
||||
struct _atexit {
|
||||
struct _atexit *_next; /* next in list */
|
||||
int _ind; /* next index in this table */
|
||||
/* Some entries may already have been called, and will be NULL. */
|
||||
void (*_fns[_ATEXIT_SIZE])(void); /* the table itself */
|
||||
struct _on_exit_args _on_exit_args;
|
||||
};
|
||||
# define _ATEXIT_INIT {_NULL, 0, {_NULL}, {{_NULL}, {_NULL}, 0, 0}}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Stdio buffers.
|
||||
*
|
||||
* This and __FILE are defined here because we need them for struct _reent,
|
||||
* but we don't want stdio.h included when stdlib.h is.
|
||||
*/
|
||||
|
||||
struct __sbuf {
|
||||
unsigned char *_base;
|
||||
int _size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Stdio state variables.
|
||||
*
|
||||
* The following always hold:
|
||||
*
|
||||
* if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR),
|
||||
* _lbfsize is -_bf._size, else _lbfsize is 0
|
||||
* if _flags&__SRD, _w is 0
|
||||
* if _flags&__SWR, _r is 0
|
||||
*
|
||||
* This ensures that the getc and putc macros (or inline functions) never
|
||||
* try to write or read from a file that is in `read' or `write' mode.
|
||||
* (Moreover, they can, and do, automatically switch from read mode to
|
||||
* write mode, and back, on "r+" and "w+" files.)
|
||||
*
|
||||
* _lbfsize is used only to make the inline line-buffered output stream
|
||||
* code as compact as possible.
|
||||
*
|
||||
* _ub, _up, and _ur are used when ungetc() pushes back more characters
|
||||
* than fit in the current _bf, or when ungetc() pushes back a character
|
||||
* that does not match the previous one in _bf. When this happens,
|
||||
* _ub._base becomes non-nil (i.e., a stream has ungetc() data iff
|
||||
* _ub._base!=NULL) and _up and _ur save the current values of _p and _r.
|
||||
*/
|
||||
|
||||
#define _REENT_SMALL_CHECK_INIT(ptr) /* nothing */
|
||||
|
||||
/* Cygwin must use __sFILE64 for backward compatibility, even though
|
||||
it's not defining __LARGE64_FILES anymore. To make sure that __sFILE
|
||||
is never defined, disable it here explicitely. */
|
||||
#ifndef __CYGWIN__
|
||||
|
||||
struct __sFILE {
|
||||
unsigned char *_p; /* current position in (some) buffer */
|
||||
int _r; /* read space left for getc() */
|
||||
int _w; /* write space left for putc() */
|
||||
short _flags; /* flags, below; this FILE is free if 0 */
|
||||
short _file; /* fileno, if Unix descriptor, else -1 */
|
||||
struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
|
||||
int _lbfsize; /* 0 or -_bf._size, for inline putc */
|
||||
|
||||
#ifdef _REENT_SMALL
|
||||
struct _reent *_data;
|
||||
#endif
|
||||
|
||||
/* operations */
|
||||
void * _cookie; /* cookie passed to io functions */
|
||||
|
||||
_READ_WRITE_RETURN_TYPE (*_read) (struct _reent *, void *,
|
||||
char *, _READ_WRITE_BUFSIZE_TYPE);
|
||||
_READ_WRITE_RETURN_TYPE (*_write) (struct _reent *, void *,
|
||||
const char *,
|
||||
_READ_WRITE_BUFSIZE_TYPE);
|
||||
_fpos_t (*_seek) (struct _reent *, void *, _fpos_t, int);
|
||||
int (*_close) (struct _reent *, void *);
|
||||
|
||||
/* separate buffer for long sequences of ungetc() */
|
||||
struct __sbuf _ub; /* ungetc buffer */
|
||||
unsigned char *_up; /* saved _p when _p is doing ungetc data */
|
||||
int _ur; /* saved _r when _r is counting ungetc data */
|
||||
|
||||
/* tricks to meet minimum requirements even when malloc() fails */
|
||||
unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */
|
||||
unsigned char _nbuf[1]; /* guarantee a getc() buffer */
|
||||
|
||||
/* separate buffer for fgetline() when line crosses buffer boundary */
|
||||
struct __sbuf _lb; /* buffer for fgetline() */
|
||||
|
||||
/* Unix stdio files get aligned to block boundaries on fseek() */
|
||||
int _blksize; /* stat.st_blksize (may be != _bf._size) */
|
||||
_off_t _offset; /* current lseek offset */
|
||||
|
||||
#ifndef _REENT_SMALL
|
||||
struct _reent *_data; /* Here for binary compatibility? Remove? */
|
||||
#endif
|
||||
|
||||
#ifndef __SINGLE_THREAD__
|
||||
_flock_t _lock; /* for thread-safety locking */
|
||||
#endif
|
||||
_mbstate_t _mbstate; /* for wide char stdio functions. */
|
||||
int _flags2; /* for future use */
|
||||
};
|
||||
|
||||
#endif /* !__CYGWIN__ */
|
||||
|
||||
#ifdef __CUSTOM_FILE_IO__
|
||||
|
||||
/* Get custom _FILE definition. */
|
||||
#include <sys/custom_file.h>
|
||||
|
||||
#else /* !__CUSTOM_FILE_IO__ */
|
||||
/* Cygwin must use __sFILE64 for backward compatibility, even though
|
||||
it's not defining __LARGE64_FILES anymore. It also has to make
|
||||
sure the name is the same to satisfy C++ name mangling. Overloading
|
||||
_fpos64_t just fixes a build problem. The _seek64 function is
|
||||
actually never used without __LARGE64_FILES being defined. */
|
||||
#if defined (__LARGE64_FILES) || defined (__CYGWIN__)
|
||||
#ifdef __CYGWIN__
|
||||
#define _fpos64_t _fpos_t
|
||||
#endif
|
||||
|
||||
struct __sFILE64 {
|
||||
unsigned char *_p; /* current position in (some) buffer */
|
||||
int _r; /* read space left for getc() */
|
||||
int _w; /* write space left for putc() */
|
||||
short _flags; /* flags, below; this FILE is free if 0 */
|
||||
short _file; /* fileno, if Unix descriptor, else -1 */
|
||||
struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
|
||||
int _lbfsize; /* 0 or -_bf._size, for inline putc */
|
||||
|
||||
struct _reent *_data;
|
||||
|
||||
/* operations */
|
||||
void * _cookie; /* cookie passed to io functions */
|
||||
|
||||
_READ_WRITE_RETURN_TYPE (*_read) (struct _reent *, void *,
|
||||
char *, _READ_WRITE_BUFSIZE_TYPE);
|
||||
_READ_WRITE_RETURN_TYPE (*_write) (struct _reent *, void *,
|
||||
const char *,
|
||||
_READ_WRITE_BUFSIZE_TYPE);
|
||||
_fpos_t (*_seek) (struct _reent *, void *, _fpos_t, int);
|
||||
int (*_close) (struct _reent *, void *);
|
||||
|
||||
/* separate buffer for long sequences of ungetc() */
|
||||
struct __sbuf _ub; /* ungetc buffer */
|
||||
unsigned char *_up; /* saved _p when _p is doing ungetc data */
|
||||
int _ur; /* saved _r when _r is counting ungetc data */
|
||||
|
||||
/* tricks to meet minimum requirements even when malloc() fails */
|
||||
unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */
|
||||
unsigned char _nbuf[1]; /* guarantee a getc() buffer */
|
||||
|
||||
/* separate buffer for fgetline() when line crosses buffer boundary */
|
||||
struct __sbuf _lb; /* buffer for fgetline() */
|
||||
|
||||
/* Unix stdio files get aligned to block boundaries on fseek() */
|
||||
int _blksize; /* stat.st_blksize (may be != _bf._size) */
|
||||
int _flags2; /* for future use */
|
||||
|
||||
_off64_t _offset; /* current lseek offset */
|
||||
_fpos64_t (*_seek64) (struct _reent *, void *, _fpos64_t, int);
|
||||
|
||||
#ifndef __SINGLE_THREAD__
|
||||
_flock_t _lock; /* for thread-safety locking */
|
||||
#endif
|
||||
_mbstate_t _mbstate; /* for wide char stdio functions. */
|
||||
};
|
||||
typedef struct __sFILE64 __FILE;
|
||||
#else
|
||||
typedef struct __sFILE __FILE;
|
||||
#endif /* __LARGE64_FILES */
|
||||
#endif /* !__CUSTOM_FILE_IO__ */
|
||||
|
||||
extern __FILE __sf[3];
|
||||
|
||||
struct _glue
|
||||
{
|
||||
struct _glue *_next;
|
||||
int _niobs;
|
||||
__FILE *_iobs;
|
||||
};
|
||||
|
||||
extern struct _glue __sglue;
|
||||
|
||||
/*
|
||||
* rand48 family support
|
||||
*
|
||||
* Copyright (c) 1993 Martin Birgmeier
|
||||
* All rights reserved.
|
||||
*
|
||||
* You may redistribute unmodified or modified versions of this source
|
||||
* code provided that the above copyright notice and this and the
|
||||
* following conditions are retained.
|
||||
*
|
||||
* This software is provided ``as is'', and comes with no warranties
|
||||
* of any kind. I shall in no event be liable for anything that happens
|
||||
* to anyone/anything when using this software.
|
||||
*/
|
||||
#define _RAND48_SEED_0 (0x330e)
|
||||
#define _RAND48_SEED_1 (0xabcd)
|
||||
#define _RAND48_SEED_2 (0x1234)
|
||||
#define _RAND48_MULT_0 (0xe66d)
|
||||
#define _RAND48_MULT_1 (0xdeec)
|
||||
#define _RAND48_MULT_2 (0x0005)
|
||||
#define _RAND48_ADD (0x000b)
|
||||
struct _rand48 {
|
||||
unsigned short _seed[3];
|
||||
unsigned short _mult[3];
|
||||
unsigned short _add;
|
||||
#ifdef _REENT_SMALL
|
||||
/* Put this in here as well, for good luck. */
|
||||
__extension__ unsigned long long _rand_next;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* How big the some arrays are. */
|
||||
#define _REENT_EMERGENCY_SIZE 25
|
||||
#define _REENT_ASCTIME_SIZE 26
|
||||
#define _REENT_SIGNAL_SIZE 24
|
||||
|
||||
#ifndef _REENT_THREAD_LOCAL
|
||||
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
#define _REENT_INIT_RESERVED_0 0,
|
||||
#define _REENT_INIT_RESERVED_1 0,
|
||||
#define _REENT_INIT_RESERVED_2 0,
|
||||
#define _REENT_INIT_RESERVED_6_7 _NULL, _ATEXIT_INIT,
|
||||
#define _REENT_INIT_RESERVED_8 {_NULL, 0, _NULL},
|
||||
#else
|
||||
#define _REENT_INIT_RESERVED_0 /* Nothing to initialize */
|
||||
#define _REENT_INIT_RESERVED_1 /* Nothing to initialize */
|
||||
#define _REENT_INIT_RESERVED_2 /* Nothing to initialize */
|
||||
#define _REENT_INIT_RESERVED_6_7 /* Nothing to initialize */
|
||||
#define _REENT_INIT_RESERVED_8 /* Nothing to initialize */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct _reent
|
||||
*
|
||||
* This structure contains the thread-local objects needed by the library.
|
||||
* It's raison d'etre is to facilitate threads by making all library routines
|
||||
* reentrant. The exit handler support and FILE maintenance use dedicated
|
||||
* global objects which are not included in this structure.
|
||||
*/
|
||||
|
||||
#ifdef _REENT_SMALL
|
||||
|
||||
struct _mprec
|
||||
{
|
||||
/* used by mprec routines */
|
||||
struct _Bigint *_result;
|
||||
int _result_k;
|
||||
struct _Bigint *_p5s;
|
||||
struct _Bigint **_freelist;
|
||||
};
|
||||
|
||||
|
||||
struct _misc_reent
|
||||
{
|
||||
/* miscellaneous reentrant data */
|
||||
char *_strtok_last;
|
||||
_mbstate_t _mblen_state;
|
||||
_mbstate_t _wctomb_state;
|
||||
_mbstate_t _mbtowc_state;
|
||||
char _l64a_buf[8];
|
||||
int _getdate_err;
|
||||
_mbstate_t _mbrlen_state;
|
||||
_mbstate_t _mbrtowc_state;
|
||||
_mbstate_t _mbsrtowcs_state;
|
||||
_mbstate_t _wcrtomb_state;
|
||||
_mbstate_t _wcsrtombs_state;
|
||||
};
|
||||
|
||||
/* This version of _reent is laid out with "int"s in pairs, to help
|
||||
* ports with 16-bit int's but 32-bit pointers, align nicely. */
|
||||
struct _reent
|
||||
{
|
||||
/* As an exception to the above put _errno first for binary
|
||||
compatibility with non _REENT_SMALL targets. */
|
||||
int _errno; /* local copy of errno */
|
||||
|
||||
/* FILE is a big struct and may change over time. To try to achieve binary
|
||||
compatibility with future versions, put stdin,stdout,stderr here.
|
||||
These are pointers into member __sf defined below. */
|
||||
__FILE *_stdin, *_stdout, *_stderr; /* XXX */
|
||||
|
||||
int _inc; /* used by tmpnam */
|
||||
|
||||
char *_emergency;
|
||||
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
int _reserved_0;
|
||||
int _reserved_1;
|
||||
#endif
|
||||
struct __locale_t *_locale;/* per-thread locale */
|
||||
|
||||
struct _mprec *_mp;
|
||||
|
||||
void (*__cleanup) (struct _reent *);
|
||||
|
||||
int _gamma_signgam;
|
||||
|
||||
/* used by some fp conversion routines */
|
||||
int _cvtlen; /* should be size_t */
|
||||
char *_cvtbuf;
|
||||
|
||||
struct _rand48 *_r48;
|
||||
struct __tm *_localtime_buf;
|
||||
char *_asctime_buf;
|
||||
|
||||
/* signal info */
|
||||
void (** _sig_func)(int);
|
||||
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
struct _atexit *_reserved_6;
|
||||
struct _atexit _reserved_7;
|
||||
struct _glue _reserved_8;
|
||||
#endif
|
||||
|
||||
__FILE *__sf; /* file descriptors */
|
||||
struct _misc_reent *_misc; /* strtok, multibyte states */
|
||||
char *_signal_buf; /* strsignal */
|
||||
};
|
||||
|
||||
# define _REENT_INIT(var) \
|
||||
{ 0, \
|
||||
&__sf[0], \
|
||||
&__sf[1], \
|
||||
&__sf[2], \
|
||||
0, \
|
||||
_NULL, \
|
||||
_REENT_INIT_RESERVED_0 \
|
||||
_REENT_INIT_RESERVED_1 \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
0, \
|
||||
0, \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
_REENT_INIT_RESERVED_6_7 \
|
||||
_REENT_INIT_RESERVED_8 \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
_NULL \
|
||||
}
|
||||
|
||||
#define _REENT_INIT_PTR_ZEROED(var) \
|
||||
{ (var)->_stdin = &__sf[0]; \
|
||||
(var)->_stdout = &__sf[1]; \
|
||||
(var)->_stderr = &__sf[2]; \
|
||||
}
|
||||
|
||||
/* Specify how to handle reent_check malloc failures. */
|
||||
#ifdef _REENT_CHECK_VERIFY
|
||||
#include <assert.h>
|
||||
#define __reent_assert(x) ((x) ? (void)0 : __assert_func(__FILE__, __LINE__, (char *)0, "REENT malloc succeeded"))
|
||||
#else
|
||||
#define __reent_assert(x) ((void)0)
|
||||
#endif
|
||||
|
||||
#ifdef __CUSTOM_FILE_IO__
|
||||
#error Custom FILE I/O and _REENT_SMALL not currently supported.
|
||||
#endif
|
||||
|
||||
/* Generic _REENT check macro. */
|
||||
#define _REENT_CHECK(var, what, type, size, init) do { \
|
||||
struct _reent *_r = (var); \
|
||||
if (_r->what == NULL) { \
|
||||
_r->what = (type)malloc(size); \
|
||||
__reent_assert(_r->what); \
|
||||
init; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define _REENT_CHECK_TM(var) \
|
||||
_REENT_CHECK(var, _localtime_buf, struct __tm *, sizeof *((var)->_localtime_buf), \
|
||||
/* nothing */)
|
||||
|
||||
#define _REENT_CHECK_ASCTIME_BUF(var) \
|
||||
_REENT_CHECK(var, _asctime_buf, char *, _REENT_ASCTIME_SIZE, \
|
||||
memset((var)->_asctime_buf, 0, _REENT_ASCTIME_SIZE))
|
||||
|
||||
/* Handle the dynamically allocated rand48 structure. */
|
||||
#define _REENT_INIT_RAND48(var) do { \
|
||||
struct _reent *_r = (var); \
|
||||
_r->_r48->_seed[0] = _RAND48_SEED_0; \
|
||||
_r->_r48->_seed[1] = _RAND48_SEED_1; \
|
||||
_r->_r48->_seed[2] = _RAND48_SEED_2; \
|
||||
_r->_r48->_mult[0] = _RAND48_MULT_0; \
|
||||
_r->_r48->_mult[1] = _RAND48_MULT_1; \
|
||||
_r->_r48->_mult[2] = _RAND48_MULT_2; \
|
||||
_r->_r48->_add = _RAND48_ADD; \
|
||||
_r->_r48->_rand_next = 1; \
|
||||
} while (0)
|
||||
#define _REENT_CHECK_RAND48(var) \
|
||||
_REENT_CHECK(var, _r48, struct _rand48 *, sizeof *((var)->_r48), _REENT_INIT_RAND48((var)))
|
||||
|
||||
#define _REENT_INIT_MP(var) do { \
|
||||
struct _reent *_r = (var); \
|
||||
_r->_mp->_result_k = 0; \
|
||||
_r->_mp->_result = _r->_mp->_p5s = _NULL; \
|
||||
_r->_mp->_freelist = _NULL; \
|
||||
} while (0)
|
||||
#define _REENT_CHECK_MP(var) \
|
||||
_REENT_CHECK(var, _mp, struct _mprec *, sizeof *((var)->_mp), _REENT_INIT_MP(var))
|
||||
|
||||
#define _REENT_CHECK_EMERGENCY(var) \
|
||||
_REENT_CHECK(var, _emergency, char *, _REENT_EMERGENCY_SIZE, /* nothing */)
|
||||
|
||||
#define _REENT_INIT_MISC(var) do { \
|
||||
struct _reent *_r = (var); \
|
||||
_r->_misc->_strtok_last = _NULL; \
|
||||
_r->_misc->_mblen_state.__count = 0; \
|
||||
_r->_misc->_mblen_state.__value.__wch = 0; \
|
||||
_r->_misc->_wctomb_state.__count = 0; \
|
||||
_r->_misc->_wctomb_state.__value.__wch = 0; \
|
||||
_r->_misc->_mbtowc_state.__count = 0; \
|
||||
_r->_misc->_mbtowc_state.__value.__wch = 0; \
|
||||
_r->_misc->_mbrlen_state.__count = 0; \
|
||||
_r->_misc->_mbrlen_state.__value.__wch = 0; \
|
||||
_r->_misc->_mbrtowc_state.__count = 0; \
|
||||
_r->_misc->_mbrtowc_state.__value.__wch = 0; \
|
||||
_r->_misc->_mbsrtowcs_state.__count = 0; \
|
||||
_r->_misc->_mbsrtowcs_state.__value.__wch = 0; \
|
||||
_r->_misc->_wcrtomb_state.__count = 0; \
|
||||
_r->_misc->_wcrtomb_state.__value.__wch = 0; \
|
||||
_r->_misc->_wcsrtombs_state.__count = 0; \
|
||||
_r->_misc->_wcsrtombs_state.__value.__wch = 0; \
|
||||
_r->_misc->_l64a_buf[0] = '\0'; \
|
||||
_r->_misc->_getdate_err = 0; \
|
||||
} while (0)
|
||||
#define _REENT_CHECK_MISC(var) \
|
||||
_REENT_CHECK(var, _misc, struct _misc_reent *, sizeof *((var)->_misc), _REENT_INIT_MISC(var))
|
||||
|
||||
#define _REENT_CHECK_SIGNAL_BUF(var) \
|
||||
_REENT_CHECK(var, _signal_buf, char *, _REENT_SIGNAL_SIZE, /* nothing */)
|
||||
|
||||
#define _REENT_SIGNGAM(ptr) ((ptr)->_gamma_signgam)
|
||||
#define _REENT_RAND_NEXT(ptr) ((ptr)->_r48->_rand_next)
|
||||
#define _REENT_RAND48_SEED(ptr) ((ptr)->_r48->_seed)
|
||||
#define _REENT_RAND48_MULT(ptr) ((ptr)->_r48->_mult)
|
||||
#define _REENT_RAND48_ADD(ptr) ((ptr)->_r48->_add)
|
||||
#define _REENT_MP_RESULT(ptr) ((ptr)->_mp->_result)
|
||||
#define _REENT_MP_RESULT_K(ptr) ((ptr)->_mp->_result_k)
|
||||
#define _REENT_MP_P5S(ptr) ((ptr)->_mp->_p5s)
|
||||
#define _REENT_MP_FREELIST(ptr) ((ptr)->_mp->_freelist)
|
||||
#define _REENT_ASCTIME_BUF(ptr) ((ptr)->_asctime_buf)
|
||||
#define _REENT_TM(ptr) ((ptr)->_localtime_buf)
|
||||
#define _REENT_STRTOK_LAST(ptr) ((ptr)->_misc->_strtok_last)
|
||||
#define _REENT_MBLEN_STATE(ptr) ((ptr)->_misc->_mblen_state)
|
||||
#define _REENT_MBTOWC_STATE(ptr)((ptr)->_misc->_mbtowc_state)
|
||||
#define _REENT_WCTOMB_STATE(ptr)((ptr)->_misc->_wctomb_state)
|
||||
#define _REENT_MBRLEN_STATE(ptr) ((ptr)->_misc->_mbrlen_state)
|
||||
#define _REENT_MBRTOWC_STATE(ptr) ((ptr)->_misc->_mbrtowc_state)
|
||||
#define _REENT_MBSRTOWCS_STATE(ptr) ((ptr)->_misc->_mbsrtowcs_state)
|
||||
#define _REENT_WCRTOMB_STATE(ptr) ((ptr)->_misc->_wcrtomb_state)
|
||||
#define _REENT_WCSRTOMBS_STATE(ptr) ((ptr)->_misc->_wcsrtombs_state)
|
||||
#define _REENT_L64A_BUF(ptr) ((ptr)->_misc->_l64a_buf)
|
||||
#define _REENT_GETDATE_ERR_P(ptr) (&((ptr)->_misc->_getdate_err))
|
||||
#define _REENT_SIGNAL_BUF(ptr) ((ptr)->_signal_buf)
|
||||
|
||||
#else /* !_REENT_SMALL */
|
||||
|
||||
struct _reent
|
||||
{
|
||||
int _errno; /* local copy of errno */
|
||||
|
||||
/* FILE is a big struct and may change over time. To try to achieve binary
|
||||
compatibility with future versions, put stdin,stdout,stderr here.
|
||||
These are pointers into member __sf defined below. */
|
||||
__FILE *_stdin, *_stdout, *_stderr;
|
||||
|
||||
int _inc; /* used by tmpnam */
|
||||
char _emergency[_REENT_EMERGENCY_SIZE];
|
||||
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
int _reserved_1;
|
||||
#endif
|
||||
struct __locale_t *_locale;/* per-thread locale */
|
||||
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
int _reserved_0;
|
||||
#endif
|
||||
|
||||
void (*__cleanup) (struct _reent *);
|
||||
|
||||
/* used by mprec routines */
|
||||
struct _Bigint *_result;
|
||||
int _result_k;
|
||||
struct _Bigint *_p5s;
|
||||
struct _Bigint **_freelist;
|
||||
|
||||
/* used by some fp conversion routines */
|
||||
int _cvtlen; /* should be size_t */
|
||||
char *_cvtbuf;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
unsigned int _reserved_2;
|
||||
#endif
|
||||
char * _strtok_last;
|
||||
char _asctime_buf[_REENT_ASCTIME_SIZE];
|
||||
struct __tm _localtime_buf;
|
||||
int _gamma_signgam;
|
||||
__extension__ unsigned long long _rand_next;
|
||||
struct _rand48 _r48;
|
||||
_mbstate_t _mblen_state;
|
||||
_mbstate_t _mbtowc_state;
|
||||
_mbstate_t _wctomb_state;
|
||||
char _l64a_buf[8];
|
||||
char _signal_buf[_REENT_SIGNAL_SIZE];
|
||||
int _getdate_err;
|
||||
_mbstate_t _mbrlen_state;
|
||||
_mbstate_t _mbrtowc_state;
|
||||
_mbstate_t _mbsrtowcs_state;
|
||||
_mbstate_t _wcrtomb_state;
|
||||
_mbstate_t _wcsrtombs_state;
|
||||
int _h_errno;
|
||||
} _reent;
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
struct
|
||||
{
|
||||
unsigned char * _reserved_3[30];
|
||||
unsigned int _reserved_4[30];
|
||||
} _reserved_5;
|
||||
#endif
|
||||
} _new;
|
||||
|
||||
#ifdef _REENT_BACKWARD_BINARY_COMPAT
|
||||
struct _atexit *_reserved_6;
|
||||
struct _atexit _reserved_7;
|
||||
#endif
|
||||
|
||||
/* signal info */
|
||||
void (**_sig_func)(int);
|
||||
};
|
||||
|
||||
#define _REENT_INIT(var) \
|
||||
{ 0, \
|
||||
&__sf[0], \
|
||||
&__sf[1], \
|
||||
&__sf[2], \
|
||||
0, \
|
||||
"", \
|
||||
_REENT_INIT_RESERVED_1 \
|
||||
_NULL, \
|
||||
_REENT_INIT_RESERVED_0 \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
0, \
|
||||
_NULL, \
|
||||
_NULL, \
|
||||
0, \
|
||||
_NULL, \
|
||||
{ \
|
||||
{ \
|
||||
_REENT_INIT_RESERVED_2 \
|
||||
_NULL, \
|
||||
"", \
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, \
|
||||
0, \
|
||||
1, \
|
||||
{ \
|
||||
{_RAND48_SEED_0, _RAND48_SEED_1, _RAND48_SEED_2}, \
|
||||
{_RAND48_MULT_0, _RAND48_MULT_1, _RAND48_MULT_2}, \
|
||||
_RAND48_ADD \
|
||||
}, \
|
||||
{0, {0}}, \
|
||||
{0, {0}}, \
|
||||
{0, {0}}, \
|
||||
"", \
|
||||
"", \
|
||||
0, \
|
||||
{0, {0}}, \
|
||||
{0, {0}}, \
|
||||
{0, {0}}, \
|
||||
{0, {0}}, \
|
||||
{0, {0}} \
|
||||
} \
|
||||
}, \
|
||||
_REENT_INIT_RESERVED_6_7 \
|
||||
_NULL \
|
||||
}
|
||||
|
||||
#define _REENT_INIT_PTR_ZEROED(var) \
|
||||
{ (var)->_stdin = &__sf[0]; \
|
||||
(var)->_stdout = &__sf[1]; \
|
||||
(var)->_stderr = &__sf[2]; \
|
||||
(var)->_new._reent._rand_next = 1; \
|
||||
(var)->_new._reent._r48._seed[0] = _RAND48_SEED_0; \
|
||||
(var)->_new._reent._r48._seed[1] = _RAND48_SEED_1; \
|
||||
(var)->_new._reent._r48._seed[2] = _RAND48_SEED_2; \
|
||||
(var)->_new._reent._r48._mult[0] = _RAND48_MULT_0; \
|
||||
(var)->_new._reent._r48._mult[1] = _RAND48_MULT_1; \
|
||||
(var)->_new._reent._r48._mult[2] = _RAND48_MULT_2; \
|
||||
(var)->_new._reent._r48._add = _RAND48_ADD; \
|
||||
}
|
||||
|
||||
#define _REENT_CHECK_RAND48(ptr) /* nothing */
|
||||
#define _REENT_CHECK_MP(ptr) /* nothing */
|
||||
#define _REENT_CHECK_TM(ptr) /* nothing */
|
||||
#define _REENT_CHECK_ASCTIME_BUF(ptr) /* nothing */
|
||||
#define _REENT_CHECK_EMERGENCY(ptr) /* nothing */
|
||||
#define _REENT_CHECK_MISC(ptr) /* nothing */
|
||||
#define _REENT_CHECK_SIGNAL_BUF(ptr) /* nothing */
|
||||
|
||||
#define _REENT_SIGNGAM(ptr) ((ptr)->_new._reent._gamma_signgam)
|
||||
#define _REENT_RAND_NEXT(ptr) ((ptr)->_new._reent._rand_next)
|
||||
#define _REENT_RAND48_SEED(ptr) ((ptr)->_new._reent._r48._seed)
|
||||
#define _REENT_RAND48_MULT(ptr) ((ptr)->_new._reent._r48._mult)
|
||||
#define _REENT_RAND48_ADD(ptr) ((ptr)->_new._reent._r48._add)
|
||||
#define _REENT_MP_RESULT(ptr) ((ptr)->_result)
|
||||
#define _REENT_MP_RESULT_K(ptr) ((ptr)->_result_k)
|
||||
#define _REENT_MP_P5S(ptr) ((ptr)->_p5s)
|
||||
#define _REENT_MP_FREELIST(ptr) ((ptr)->_freelist)
|
||||
#define _REENT_ASCTIME_BUF(ptr) ((ptr)->_new._reent._asctime_buf)
|
||||
#define _REENT_TM(ptr) (&(ptr)->_new._reent._localtime_buf)
|
||||
#define _REENT_STRTOK_LAST(ptr) ((ptr)->_new._reent._strtok_last)
|
||||
#define _REENT_MBLEN_STATE(ptr) ((ptr)->_new._reent._mblen_state)
|
||||
#define _REENT_MBTOWC_STATE(ptr)((ptr)->_new._reent._mbtowc_state)
|
||||
#define _REENT_WCTOMB_STATE(ptr)((ptr)->_new._reent._wctomb_state)
|
||||
#define _REENT_MBRLEN_STATE(ptr)((ptr)->_new._reent._mbrlen_state)
|
||||
#define _REENT_MBRTOWC_STATE(ptr)((ptr)->_new._reent._mbrtowc_state)
|
||||
#define _REENT_MBSRTOWCS_STATE(ptr)((ptr)->_new._reent._mbsrtowcs_state)
|
||||
#define _REENT_WCRTOMB_STATE(ptr)((ptr)->_new._reent._wcrtomb_state)
|
||||
#define _REENT_WCSRTOMBS_STATE(ptr)((ptr)->_new._reent._wcsrtombs_state)
|
||||
#define _REENT_L64A_BUF(ptr) ((ptr)->_new._reent._l64a_buf)
|
||||
#define _REENT_SIGNAL_BUF(ptr) ((ptr)->_new._reent._signal_buf)
|
||||
#define _REENT_GETDATE_ERR_P(ptr) (&((ptr)->_new._reent._getdate_err))
|
||||
|
||||
#endif /* !_REENT_SMALL */
|
||||
|
||||
#define _REENT_CLEANUP(_ptr) ((_ptr)->__cleanup)
|
||||
#define _REENT_CVTBUF(_ptr) ((_ptr)->_cvtbuf)
|
||||
#define _REENT_CVTLEN(_ptr) ((_ptr)->_cvtlen)
|
||||
#define _REENT_EMERGENCY(_ptr) ((_ptr)->_emergency)
|
||||
#define _REENT_ERRNO(_ptr) ((_ptr)->_errno)
|
||||
#define _REENT_INC(_ptr) ((_ptr)->_inc)
|
||||
#define _REENT_LOCALE(_ptr) ((_ptr)->_locale)
|
||||
#define _REENT_SIG_FUNC(_ptr) ((_ptr)->_sig_func)
|
||||
#define _REENT_STDIN(_ptr) ((_ptr)->_stdin)
|
||||
#define _REENT_STDOUT(_ptr) ((_ptr)->_stdout)
|
||||
#define _REENT_STDERR(_ptr) ((_ptr)->_stderr)
|
||||
|
||||
#define _REENT_INIT_PTR(var) \
|
||||
{ memset((var), 0, sizeof(*(var))); \
|
||||
_REENT_INIT_PTR_ZEROED(var); \
|
||||
}
|
||||
|
||||
/*
|
||||
* All references to struct _reent are via this pointer.
|
||||
* Internally, newlib routines that need to reference it should use _REENT.
|
||||
*/
|
||||
|
||||
#ifndef __ATTRIBUTE_IMPURE_PTR__
|
||||
#define __ATTRIBUTE_IMPURE_PTR__
|
||||
#endif
|
||||
|
||||
extern struct _reent *_impure_ptr __ATTRIBUTE_IMPURE_PTR__;
|
||||
|
||||
#ifndef __ATTRIBUTE_IMPURE_DATA__
|
||||
#define __ATTRIBUTE_IMPURE_DATA__
|
||||
#endif
|
||||
|
||||
extern struct _reent _impure_data __ATTRIBUTE_IMPURE_DATA__;
|
||||
|
||||
/* #define _REENT_ONLY define this to get only reentrant routines */
|
||||
|
||||
#if defined(__DYNAMIC_REENT__) && !defined(__SINGLE_THREAD__)
|
||||
#ifndef __getreent
|
||||
struct _reent * __getreent (void);
|
||||
#endif
|
||||
# define _REENT (__getreent())
|
||||
#else /* __SINGLE_THREAD__ || !__DYNAMIC_REENT__ */
|
||||
# define _REENT _impure_ptr
|
||||
#endif /* __SINGLE_THREAD__ || !__DYNAMIC_REENT__ */
|
||||
|
||||
#define _REENT_IS_NULL(_ptr) ((_ptr) == NULL)
|
||||
|
||||
#define _GLOBAL_REENT (&_impure_data)
|
||||
|
||||
#else /* _REENT_THREAD_LOCAL */
|
||||
|
||||
#define _REENT NULL
|
||||
#define _GLOBAL_REENT NULL
|
||||
|
||||
/*
|
||||
* Since _REENT is defined as NULL, this macro ensures that calls to
|
||||
* CHECK_INIT() do not automatically fail.
|
||||
*/
|
||||
#define _REENT_IS_NULL(_ptr) 0
|
||||
|
||||
#define _REENT_CHECK_RAND48(ptr) /* nothing */
|
||||
#define _REENT_CHECK_MP(ptr) /* nothing */
|
||||
#define _REENT_CHECK_TM(ptr) /* nothing */
|
||||
#define _REENT_CHECK_ASCTIME_BUF(ptr) /* nothing */
|
||||
#define _REENT_CHECK_EMERGENCY(ptr) /* nothing */
|
||||
#define _REENT_CHECK_MISC(ptr) /* nothing */
|
||||
#define _REENT_CHECK_SIGNAL_BUF(ptr) /* nothing */
|
||||
|
||||
extern _Thread_local char _tls_asctime_buf[_REENT_ASCTIME_SIZE];
|
||||
#define _REENT_ASCTIME_BUF(_ptr) (_tls_asctime_buf)
|
||||
extern _Thread_local char *_tls_cvtbuf;
|
||||
#define _REENT_CVTBUF(_ptr) (_tls_cvtbuf)
|
||||
extern _Thread_local int _tls_cvtlen;
|
||||
#define _REENT_CVTLEN(_ptr) (_tls_cvtlen)
|
||||
extern _Thread_local void (*_tls_cleanup)(struct _reent *);
|
||||
#define _REENT_CLEANUP(_ptr) (_tls_cleanup)
|
||||
extern _Thread_local char _tls_emergency;
|
||||
#define _REENT_EMERGENCY(_ptr) (_tls_emergency)
|
||||
extern _Thread_local int _tls_errno;
|
||||
#define _REENT_ERRNO(_ptr) (_tls_errno)
|
||||
extern _Thread_local int _tls_getdate_err;
|
||||
#define _REENT_GETDATE_ERR_P(_ptr) (&_tls_getdate_err)
|
||||
extern _Thread_local int _tls_inc;
|
||||
#define _REENT_INC(_ptr) (_tls_inc)
|
||||
extern _Thread_local char _tls_l64a_buf[8];
|
||||
#define _REENT_L64A_BUF(_ptr) (_tls_l64a_buf)
|
||||
extern _Thread_local struct __locale_t *_tls_locale;
|
||||
#define _REENT_LOCALE(_ptr) (_tls_locale)
|
||||
extern _Thread_local _mbstate_t _tls_mblen_state;
|
||||
#define _REENT_MBLEN_STATE(_ptr) (_tls_mblen_state)
|
||||
extern _Thread_local _mbstate_t _tls_mbrlen_state;
|
||||
#define _REENT_MBRLEN_STATE(_ptr) (_tls_mbrlen_state)
|
||||
extern _Thread_local _mbstate_t _tls_mbrtowc_state;
|
||||
#define _REENT_MBRTOWC_STATE(_ptr) (_tls_mbrtowc_state)
|
||||
extern _Thread_local _mbstate_t _tls_mbsrtowcs_state;
|
||||
#define _REENT_MBSRTOWCS_STATE(_ptr) (_tls_mbsrtowcs_state)
|
||||
extern _Thread_local _mbstate_t _tls_mbtowc_state;
|
||||
#define _REENT_MBTOWC_STATE(_ptr) (_tls_mbtowc_state)
|
||||
extern _Thread_local struct _Bigint **_tls_mp_freelist;
|
||||
#define _REENT_MP_FREELIST(_ptr) (_tls_mp_freelist)
|
||||
extern _Thread_local struct _Bigint *_tls_mp_p5s;
|
||||
#define _REENT_MP_P5S(_ptr) (_tls_mp_p5s)
|
||||
extern _Thread_local struct _Bigint *_tls_mp_result;
|
||||
#define _REENT_MP_RESULT(_ptr) (_tls_mp_result)
|
||||
extern _Thread_local int _tls_mp_result_k;
|
||||
#define _REENT_MP_RESULT_K(_ptr) (_tls_mp_result_k)
|
||||
extern _Thread_local unsigned short _tls_rand48_add;
|
||||
#define _REENT_RAND48_ADD(_ptr) (_tls_rand48_add)
|
||||
extern _Thread_local unsigned short _tls_rand48_mult[3];
|
||||
#define _REENT_RAND48_MULT(_ptr) (_tls_rand48_mult)
|
||||
extern _Thread_local unsigned short _tls_rand48_seed[3];
|
||||
#define _REENT_RAND48_SEED(_ptr) (_tls_rand48_seed)
|
||||
extern _Thread_local unsigned long long _tls_rand_next;
|
||||
#define _REENT_RAND_NEXT(_ptr) (_tls_rand_next)
|
||||
extern _Thread_local void (**_tls_sig_func)(int);
|
||||
#define _REENT_SIG_FUNC(_ptr) (_tls_sig_func)
|
||||
extern _Thread_local char _tls_signal_buf[_REENT_SIGNAL_SIZE];
|
||||
#define _REENT_SIGNAL_BUF(_ptr) (_tls_signal_buf)
|
||||
extern _Thread_local int _tls_gamma_signgam;
|
||||
#define _REENT_SIGNGAM(_ptr) (_tls_gamma_signgam)
|
||||
extern _Thread_local __FILE *_tls_stdin;
|
||||
#define _REENT_STDIN(_ptr) (_tls_stdin)
|
||||
extern _Thread_local __FILE *_tls_stdout;
|
||||
#define _REENT_STDOUT(_ptr) (_tls_stdout)
|
||||
extern _Thread_local __FILE *_tls_stderr;
|
||||
#define _REENT_STDERR(_ptr) (_tls_stderr)
|
||||
extern _Thread_local char *_tls_strtok_last;
|
||||
#define _REENT_STRTOK_LAST(_ptr) (_tls_strtok_last)
|
||||
extern _Thread_local struct __tm _tls_localtime_buf;
|
||||
#define _REENT_TM(_ptr) (&_tls_localtime_buf)
|
||||
extern _Thread_local _mbstate_t _tls_wctomb_state;
|
||||
#define _REENT_WCTOMB_STATE(_ptr) (_tls_wctomb_state)
|
||||
extern _Thread_local _mbstate_t _tls_wcrtomb_state;
|
||||
#define _REENT_WCRTOMB_STATE(_ptr) (_tls_wcrtomb_state)
|
||||
extern _Thread_local _mbstate_t _tls_wcsrtombs_state;
|
||||
#define _REENT_WCSRTOMBS_STATE(_ptr) (_tls_wcsrtombs_state)
|
||||
|
||||
#endif /* !_REENT_THREAD_LOCAL */
|
||||
|
||||
/* This value is used in stdlib/misc.c. reent/reent.c has to know it
|
||||
as well to make sure the freelist is correctly free'd. Therefore
|
||||
we define it here, rather than in stdlib/misc.c, as before. */
|
||||
#define _Kmax (sizeof (size_t) << 3)
|
||||
|
||||
extern struct _atexit *__atexit; /* points to head of LIFO stack */
|
||||
extern struct _atexit __atexit0; /* one guaranteed table, required by ANSI */
|
||||
|
||||
extern void (*__stdio_exit_handler) (void);
|
||||
|
||||
void _reclaim_reent (struct _reent *);
|
||||
|
||||
extern int _fwalk_sglue (struct _reent *, int (*)(struct _reent *, __FILE *),
|
||||
struct _glue *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _SYS_REENT_H_ */
|
||||
@@ -2,6 +2,7 @@
|
||||
"""Portable witnesses for the exact pinned Arduino/newlib buffer admission fix."""
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
@@ -14,6 +15,11 @@ FIX = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(FIX)
|
||||
RAW = (FIXTURES / "vfs_api.cpp").read_text()
|
||||
PATCHED = FIX.patched_vfs_source(RAW)
|
||||
SDK_VARIANTS = [((2, 0, 17), RAW, PATCHED, FIXTURES)]
|
||||
for version in ((3, 1, 3), (3, 3, 11)):
|
||||
fixture = FIXTURES / ".".join(map(str, version))
|
||||
raw = json.loads((fixture / "vfs_api.json").read_text(encoding="ascii"))["source"]
|
||||
SDK_VARIANTS.append((version, raw, FIX.patched_vfs_source(raw, version), fixture))
|
||||
|
||||
|
||||
def method(source, signature):
|
||||
@@ -156,6 +162,23 @@ int main() {
|
||||
'''
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, path): self.path = path
|
||||
def srcnode(self): return self
|
||||
def get_abspath(self): return str(self.path)
|
||||
class Env(dict):
|
||||
def __init__(self, defines, build):
|
||||
super().__init__(CPPDEFINES=defines)
|
||||
self.build, self.paths, self.middleware = build, [], []
|
||||
def subst(self, value):
|
||||
assert value == "$BUILD_DIR"
|
||||
return str(self.build)
|
||||
def File(self, path): return path
|
||||
def AppendUnique(self, **kwargs):
|
||||
for path in kwargs["CPPPATH"]:
|
||||
if path not in self.paths: self.paths.append(path)
|
||||
def AddBuildMiddleware(self, callback, pattern): self.middleware.append((callback, pattern))
|
||||
|
||||
class FileBufferFixTest(unittest.TestCase):
|
||||
def compile_run(self, source, *, success=True, compile_failure=False, headers=None):
|
||||
with tempfile.TemporaryDirectory(prefix="mesh-fs-buffer-") as directory:
|
||||
@@ -183,25 +206,31 @@ class FileBufferFixTest(unittest.TestCase):
|
||||
self.assertIn("file.setBufferSize(251)", result.stderr)
|
||||
|
||||
def test_actual_method_honored_fallback_errors_zero_and_no_io(self):
|
||||
self.compile_run(METHOD_PREFIX + method(PATCHED, "bool VFSFileImpl::setBufferSize(") + METHOD_MAIN)
|
||||
for version, _, patched, _ in SDK_VARIANTS:
|
||||
with self.subTest(version=version):
|
||||
self.compile_run(METHOD_PREFIX + method(patched, "bool VFSFileImpl::setBufferSize(") + METHOD_MAIN)
|
||||
|
||||
def test_upstream_success_only_negative_control_rejects_fallback(self):
|
||||
self.compile_run(METHOD_PREFIX + method(RAW, "bool VFSFileImpl::setBufferSize(") + METHOD_MAIN,
|
||||
success=False)
|
||||
for version, raw, _, _ in SDK_VARIANTS:
|
||||
with self.subTest(version=version):
|
||||
self.compile_run(METHOD_PREFIX + method(raw, "bool VFSFileImpl::setBufferSize(") + METHOD_MAIN,
|
||||
success=False)
|
||||
|
||||
def test_actual_transaction_rejects_writer_and_verifier_fallback_before_io(self):
|
||||
transaction = (ROOT / "src/helpers/ContactFileTransaction.h").read_text().replace(
|
||||
'#include "IdentityStore.h"', '#include "identity_seam.h"').replace(
|
||||
'#include "PersistentStoreFormat.h"', '#include <helpers/PersistentStoreFormat.h>').replace(
|
||||
'#pragma once\n', '', 1)
|
||||
self.compile_run(METHOD_PREFIX + method(PATCHED, "bool VFSFileImpl::setBufferSize(")
|
||||
+ TRANSACTION_MOCK + transaction + TRANSACTION_MAIN,
|
||||
headers={"identity_seam.h": "#pragma once\n"})
|
||||
for version, _, patched, _ in SDK_VARIANTS:
|
||||
with self.subTest(version=version):
|
||||
self.compile_run(METHOD_PREFIX + method(patched, "bool VFSFileImpl::setBufferSize(")
|
||||
+ TRANSACTION_MOCK + transaction + TRANSACTION_MAIN,
|
||||
headers={"identity_seam.h": "#pragma once\n"})
|
||||
|
||||
def layout_source(self):
|
||||
def layout_source(self, fixture=FIXTURES, guard=FIX.LAYOUT_GUARD):
|
||||
# Execute the exact header's reviewed prefix with 32-bit pointer values
|
||||
# on a native 64-bit host. The target copy additionally asserts void*32.
|
||||
reent = (FIXTURES / "newlib_sys_reent.h").read_text()
|
||||
reent = (fixture / "newlib_sys_reent.h").read_text()
|
||||
sbuf = method(reent, "struct __sbuf {") + ";\n"
|
||||
start = reent.index("struct __sFILE {")
|
||||
end = reent.index("#ifdef _REENT_SMALL", start)
|
||||
@@ -209,7 +238,7 @@ class FileBufferFixTest(unittest.TestCase):
|
||||
fields = (sbuf + prefix).replace("unsigned char *", "ModelPointer ")
|
||||
source = "#include <cstdint>\nusing ModelPointer = uint32_t;\n" + fields
|
||||
source += "using FILE = __sFILE;\n"
|
||||
source += FIX.LAYOUT_GUARD.replace("sizeof(void*)", "sizeof(ModelPointer)")
|
||||
source += guard.replace("sizeof(void*)", "sizeof(ModelPointer)")
|
||||
source += "int main() {}\n"
|
||||
return source
|
||||
|
||||
@@ -223,6 +252,57 @@ class FileBufferFixTest(unittest.TestCase):
|
||||
self.compile_run(self.layout_source(), headers={"newlib.h": version.replace(
|
||||
"#define __NEWLIB__ 3", "#define __NEWLIB__ 4")}, compile_failure=True)
|
||||
|
||||
def test_arduino3_actual_newlib_prefix_rejects_changed_layout_and_abi(self):
|
||||
fixture = FIXTURES / "newlib4"
|
||||
version = (fixture / "_newlib_version.h").read_text(encoding="ascii")
|
||||
source = self.layout_source(fixture, FIX.LAYOUT_GUARD_ARDUINO3)
|
||||
self.compile_run(source, headers={"newlib.h": version})
|
||||
for broken in (source.replace("struct __sbuf _bf;", "int added; struct __sbuf _bf;"),
|
||||
source.replace("int\t_size;", "long long\t_size;")):
|
||||
self.compile_run(broken, headers={"newlib.h": version}, compile_failure=True)
|
||||
self.compile_run(source, headers={"newlib.h": version.replace(
|
||||
"#define __NEWLIB_MINOR__ 3", "#define __NEWLIB_MINOR__ 4")}, compile_failure=True)
|
||||
self.assertEqual(hashlib.sha256((fixture / "newlib_sys_reent.h").read_bytes()).hexdigest(),
|
||||
"c7ba657f911f7e27b7449bda36aa4efa80a025c726768dabb8d4a792f535ec9a")
|
||||
|
||||
def test_every_supported_sdk_uses_its_own_source_pin_and_keeps_shared_sdk_unchanged(self):
|
||||
with tempfile.TemporaryDirectory(prefix="mesh-fs-multi-sdk-") as directory:
|
||||
temp = Path(directory)
|
||||
source = temp / "sdk/libraries/FS/src/vfs_api.cpp"
|
||||
source.parent.mkdir(parents=True)
|
||||
header = temp / "sdk/cores/esp32/esp_arduino_version.h"
|
||||
header.parent.mkdir(parents=True)
|
||||
node = Node(source)
|
||||
for version, raw, patched, fixture in SDK_VARIANTS:
|
||||
with self.subTest(version=version):
|
||||
original_header = (fixture / "esp_arduino_version.h").read_bytes()
|
||||
source.write_text(raw, encoding="utf-8")
|
||||
header.write_bytes(original_header)
|
||||
self.assertEqual(FIX.require_pinned_framework(source), version)
|
||||
self.assertTrue(patched.isascii())
|
||||
self.assertEqual(FIX.patched_vfs_source(patched, version), patched)
|
||||
self.assertEqual(FIX.patched_vfs_source(raw.replace("\n", "\r\n"), version), patched)
|
||||
for mode in (None, 0, 1):
|
||||
defines = ["ESP32_PLATFORM"]
|
||||
if mode is not None:
|
||||
defines += [("ARDUINO_USB_MODE", mode), ("ARDUINO_USB_CDC_ON_BOOT", 1)]
|
||||
env = Env(defines, temp / (str(version) + str(mode)))
|
||||
result = Path(FIX.replace_vfs_source(env, node))
|
||||
self.assertEqual(result.read_text(encoding="ascii"), patched)
|
||||
self.assertEqual(source.read_text(encoding="utf-8"), raw)
|
||||
self.assertEqual(header.read_bytes(), original_header)
|
||||
before = result.stat().st_mtime_ns
|
||||
self.assertEqual(Path(FIX.replace_vfs_source(env, node)).stat().st_mtime_ns, before)
|
||||
for changed in (raw + "\n", patched + "\n",
|
||||
raw.replace("return res == 0;", "return true;", 1)):
|
||||
with self.assertRaises(RuntimeError): FIX.patched_vfs_source(changed, version)
|
||||
for other_version, _, _, _ in SDK_VARIANTS:
|
||||
if other_version != version:
|
||||
with self.assertRaises(RuntimeError): FIX.patched_vfs_source(raw, other_version)
|
||||
header.write_bytes(original_header + b"\n#define ESP_ARDUINO_VERSION_MAJOR 9\n")
|
||||
with self.assertRaises(RuntimeError): FIX.replace_vfs_source(env, node)
|
||||
with self.assertRaises(RuntimeError): FIX.patched_vfs_source(RAW, (3, 3, 12))
|
||||
|
||||
def test_exact_fingerprints_shape_normalization_and_idempotence(self):
|
||||
self.assertEqual(hashlib.sha256((FIXTURES / "vfs_api.cpp").read_bytes()).hexdigest(), FIX.PINNED_VFS_SHA256)
|
||||
self.assertEqual(FIX.patched_vfs_source(RAW.replace("\n", "\r\n")), PATCHED)
|
||||
@@ -240,22 +320,6 @@ class FileBufferFixTest(unittest.TestCase):
|
||||
self.assertEqual(hashlib.sha256((FIXTURES / name).read_bytes()).hexdigest(), digest)
|
||||
|
||||
def test_build_local_copy_all_usb_modes_guards_and_shared_bytes(self):
|
||||
class Node:
|
||||
def __init__(self, path): self.path = path
|
||||
def srcnode(self): return self
|
||||
def get_abspath(self): return str(self.path)
|
||||
class Env(dict):
|
||||
def __init__(self, defines, build):
|
||||
super().__init__(CPPDEFINES=defines)
|
||||
self.build, self.paths, self.middleware = build, [], []
|
||||
def subst(self, value):
|
||||
assert value == "$BUILD_DIR"
|
||||
return str(self.build)
|
||||
def File(self, path): return path
|
||||
def AppendUnique(self, **kwargs):
|
||||
for path in kwargs["CPPPATH"]:
|
||||
if path not in self.paths: self.paths.append(path)
|
||||
def AddBuildMiddleware(self, callback, pattern): self.middleware.append((callback, pattern))
|
||||
with tempfile.TemporaryDirectory(prefix="mesh-fs-copy-") as directory:
|
||||
temp = Path(directory)
|
||||
source = temp / "sdk/libraries/FS/src/vfs_api.cpp"
|
||||
|
||||
Reference in new issue
Block a user