- File: stat() only after a successful open() (previously ran first with its result ignored, feeding a missing file's garbage st_size into a realloc - could crash the whole APIServer on a request for a nonexistent static asset) - JString::setString(): always quote+escape the value instead of going through set()'s literal-value guess, which misclassified any string starting with digits (e.g. a UUID) as a bare numeric literal and wrote it unquoted, corrupting later edits to the same JSON document - MString::remove() (both overloads): fixed an off-by-one that read one byte past the buffer on a completely full string (found via ASan under concurrent load) - CGIFormData: added fields() so callers can iterate every parsed field without knowing names in advance (used to merge POST body into jet::Global::variables the same way query-string params already are)
105 lines
2.8 KiB
C++
105 lines
2.8 KiB
C++
#include "File.h"
|
|
#include "Exception.h"
|
|
#include <sys/stat.h>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
namespace coreutils {
|
|
|
|
File::File(coreutils::ZString fileName, int mode, int authority) {
|
|
open(fileName, mode, authority);
|
|
}
|
|
|
|
void File::open(coreutils::ZString fileName, int mode, int authority) {
|
|
|
|
this->fileName = fileName;
|
|
|
|
buffer = NULL;
|
|
|
|
fd = ::open(fileName.c_str(), mode, authority);
|
|
if(fd < 0) {
|
|
std::stringstream temp;
|
|
temp << "Error opening file " << fileName << ".";
|
|
throw Exception(temp.str(), __FILE__, __LINE__);
|
|
}
|
|
|
|
// stat() only after a successful open(), and only used to size the
|
|
// read buffer - previously this ran BEFORE the open()/fd check with
|
|
// its return value ignored, so a missing file left the stat struct fully
|
|
// uninitialized and fed that garbage st_size straight into
|
|
// setBufferSize()'s realloc(), before the open-failure path ever
|
|
// got a chance to throw its clean Exception. Found while adding a
|
|
// static-asset route (GET /assets/{filename}) that legitimately
|
|
// hits this path on every request for a nonexistent file - a
|
|
// garbage-sized realloc there could abort/crash the whole
|
|
// long-running APIServer process (shared with every other live
|
|
// route) instead of cleanly 500ing just that one request.
|
|
struct stat status;
|
|
if (fstat(fd, &status) != 0) {
|
|
std::stringstream temp;
|
|
temp << "Error statting file " << fileName << ".";
|
|
throw Exception(temp.str(), __FILE__, __LINE__);
|
|
}
|
|
|
|
setBufferSize(status.st_size);
|
|
}
|
|
|
|
File::~File() {
|
|
close(fd);
|
|
free(buffer);
|
|
}
|
|
|
|
void File::setBufferSize(size_t size) {
|
|
buffer = (char *)realloc(buffer, size);
|
|
this->size = size;
|
|
}
|
|
|
|
int File::read() {
|
|
size = ::read(fd, buffer, size);
|
|
setBufferSize(size);
|
|
return size;
|
|
}
|
|
|
|
int File::readLine() {
|
|
setBufferSize(4096);
|
|
size = 0;
|
|
int len = ::read(fd, buffer + size, 1);
|
|
if(len == 0)
|
|
mEof = true;
|
|
while(!mEof && (*(buffer + size) != '\n')) {
|
|
if(*(buffer + size) != '\r') {
|
|
// setBufferSize(size);
|
|
++size;
|
|
}
|
|
len = ::read(fd, buffer + size, 1);
|
|
if(len == 0)
|
|
mEof = true;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void File::write(std::string data) {
|
|
::write(fd, data.c_str(), data.length());
|
|
}
|
|
|
|
void File::write(coreutils::ZString &data) {
|
|
int len = ::write(fd, data.getData(), data.getLength());
|
|
}
|
|
|
|
std::string File::asString() {
|
|
return std::string(buffer, size);
|
|
}
|
|
|
|
coreutils::ZString File::asZString() {
|
|
return ZString(buffer, size);
|
|
}
|
|
|
|
bool File::eof() {
|
|
return mEof;
|
|
}
|
|
|
|
}
|