diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index d34182a7b0..250e55ffe0 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -106,11 +106,11 @@ 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 = 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,69 +126,60 @@ 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) + /* 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))); - if (bytes_to_read >= 0) + if (bytes_to_read > 0) { /* If passed explicit read size just do it */ buf = (bytea *) palloc((Size) bytes_to_read + VARHDRSZ); + /* Bypass fread buffer: less copies. */ + setvbuf(file, VARDATA(buf), _IOFBF, (size_t) bytes_to_read); + nbytes = fread(VARDATA(buf), 1, (size_t) bytes_to_read, file); } else { /* Negative read size, read rest of file */ - StringInfoData sbuf; + StringInfoData sbuf; + size_t rbytes; + int fd; 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 */ + /* 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); - sbuf.len += rbytes; - nbytes += rbytes; - } + fd = fileno(file); + nbytes = 0; + do { + /* 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 read calls than necessary. + */ + rbytes = read(fd, 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; @@ -199,10 +190,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; }