diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index d34182a7b0..8a5d3acc5b 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -106,11 +106,14 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read, bool missing_ok) { bytea *buf; - size_t nbytes = 0; FILE *file; + size_t nbytes; + + /* Read zero byte? */ + Assert(bytes_to_read != 0); /* clamp request size to what we can actually deliver */ - if (bytes_to_read > (int64) (MaxAllocSize - VARHDRSZ)) + if (bytes_to_read > ((int64) MaxAllocSize - VARHDRSZ)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested length too large"))); @@ -126,72 +129,50 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read, filename))); } - if (fseeko(file, (off_t) seek_offset, - (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not seek in file \"%s\": %m", filename))); - + /* If passed explicit read size just do it */ if (bytes_to_read >= 0) { - /* If passed explicit read size just do it */ - buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ); + /* Virtual File? Can not be seekable. */ + Assert(seek_offset != 0); - nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file); + /* Avoid syscall fseeko if seek_offset is zero */ + if (seek_offset != 0 && + fseeko(file, (off_t) seek_offset, + (seek_offset > 0) ? SEEK_SET : SEEK_END) != 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not seek in file \"%s\": %m", filename))); + + buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ); + nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file); } - else + else { - /* Negative read size, read rest of file */ StringInfoData sbuf; + size_t rbytes; initStringInfo(&sbuf); + /* Leave room in the buffer for the varlena length word */ sbuf.len += VARHDRSZ; Assert(sbuf.len < sbuf.maxlen); - while (!(feof(file) || ferror(file))) - { - size_t rbytes; - - /* Minimum amount to read at a time */ #define MIN_READ_SIZE 4096 - - /* - * If not at end of file, and sbuf.len is equal to - * MaxAllocSize - 1, then either the file is too large, or - * there is nothing left to read. Attempt to read one more - * byte to see if the end of file has been reached. If not, - * the file is too large; we'd rather give the error message - * for that ourselves. - */ - if (sbuf.len == MaxAllocSize - 1) - { - char rbuf[1]; - - if (fread(rbuf, 1, 1, file) != 0 || !feof(file)) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("file length too large"))); - else - break; - } - - /* OK, ensure that we can read at least MIN_READ_SIZE */ - enlargeStringInfo(&sbuf, MIN_READ_SIZE); - - /* - * stringinfo.c likes to allocate in powers of 2, so it's likely - * that much more space is available than we asked for. Use all - * of it, rather than making more fread calls than necessary. - */ - rbytes = fread(sbuf.data + sbuf.len, 1, - (size_t) (sbuf.maxlen - sbuf.len - 1), file); + nbytes = 0; + do { + enlargeStringInfo(&sbuf, MIN_READ_SIZE); + rbytes = read(file, sbuf.data + sbuf.len, (size_t) (sbuf.maxlen - sbuf.len - 1)); sbuf.len += rbytes; nbytes += rbytes; - } + } while(rbytes > 0 && sbuf.len < (int64) (MaxAllocSize - VARHDRSZ)); + + if (sbuf.len > ((int64) MaxAllocSize - VARHDRSZ)) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("file length too large"))); /* Now we can commandeer the stringinfo's buffer as the result */ - buf = (bytea *) sbuf.data; + buf = (bytea *) sbuf.data; } if (ferror(file)) @@ -199,10 +180,10 @@ read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read, (errcode_for_file_access(), errmsg("could not read file \"%s\": %m", filename))); - SET_VARSIZE(buf, nbytes + VARHDRSZ); - FreeFile(file); + SET_VARSIZE(buf, nbytes + VARHDRSZ); + return buf; }