The current Unix implementation of apr_file_trunk()
has a number of issues due to incompletely updating
the buffer state. Note that this is different from the
delayed flush() issue fixed in r1044440.
The following code snippet demonstrates the two
problems, one being access to stale data and the
other being the reported current read position being
wrong:
apr_file_t *file;
apr_size_t count;
apr_off_t offset;
char buffer[11];
/* Issue 1: File read buffer does not get truncated. */
apr_file_open(&file, "demo",
APR_READ | APR_WRITE | APR_BUFFERED | APR_CREATE,
APR_OS_DEFAULT, pool);
count = 11;
apr_file_write(file, "Hello World", &count);
apr_file_flush(file);
offset = 0;
apr_file_seek(file, APR_SET, &offset);
apr_file_read(file, buffer, &count);
apr_file_trunc(file, 5);
/* Attempting to read data should return nothing but it returns old data.
*/
count = 6;
buffer[0] = 0;
apr_file_read(file, buffer, &count);
apr_file_close(file);
assert(count == 0 && buffer[0] == 0);
/* Issue 2: File read buffer state is inconsistent. */
apr_file_open(&file, "demo",
APR_READ | APR_WRITE | APR_BUFFERED | APR_TRUNCATE,
APR_OS_DEFAULT, pool);
count = 11;
apr_file_write(file, "Hello World", &count);
apr_file_flush(file);
offset = 0;
apr_file_seek(file, APR_SET, &offset);
apr_file_read(file, buffer, &count);
apr_file_trunc(file, 5);
/* Superficially, seek seems to work.. */
offset = 0;
apr_file_seek(file, APR_CUR, &offset);
assert(offset == 5);
/* .. but the internal buffer state is inconsistent making it fail later.
*/
count = 1;
apr_file_write(file, "X", &count);
offset = 0;
apr_file_seek(file, APR_CUR, &offset);
apr_file_close(file);
assert(offset == 6);
I attached a patch against /trunk.
-- Stefan^2.
[[[
Prevent stale buffer data and bogus file pointers to be returned
after apr_file_flush() on Unix.
* file_io/unix/seek.c
(apr_file_trunc): Limit the data buffer to what will remain
beyond EOF after trunc(). If the trunc()
fails, having shortended buffer contents
does not effect the result of any APR call.
Patch by: stefan2
]]]
Index: file_io/unix/seek.c
===================================================================
--- file_io/unix/seek.c (revision 1606446)
+++ file_io/unix/seek.c (working copy)
@@ -117,6 +117,24 @@
/* Reset buffer positions for write mode */
fp->bufpos = fp->direction = fp->dataRead = 0;
}
+
+ /* Limit the buffered data to what will remain of the actual file.
+ * That prevents stale data from being read and wrong file locations
+ * being reported. */
+ if (fp->filePtr >= offset) {
+ /* The entire buffer will lie beyond EOF now. Put it at EOF. */
+ fp->dataRead = fp->bufpos = 0;
+ fp->filePtr = offset;
+ }
+ else if (fp->filePtr + fp->dataRead > offset) {
+ /* Only part of the buffer will be beyond EOF.
+ * Truncate it and adjust the read pointer if necessary. */
+ fp->dataRead = offset - fp->filePtr;
+ if (fp->dataRead < fp->bufpos) {
+ fp->bufpos = fp->dataRead;
+ }
+ }
+
file_unlock(fp);
if (rc) {
return rc;