Hey there,
I stumbled across a problem with apr_file_trunc not updating its
internal buffer state if the previous operation was a read. This
is a follow-up to a similar problem fixed in 2010.
Fix, test and commit message below. Developed against 1.4.5,
the functional patch should also cleanly apply to /trunk.
-- Stefan^2.
[[[
Fix another issue with apr_file_trunc on buffered files.
If the file was not in write mode, the buffer content and size
information would not be clipped to the truncation offset.
* file_io/unix/seek.c
(apr_file_trunc): Clip the buffer in read mode as well.
* test/testfile.c
(test_truncate): Extend this test to discover stale data and
invalid size information.
]]]
[[[
Index: file_io/unix/seek.c
===================================================================
--- file_io/unix/seek.c (revision 1618575)
+++ file_io/unix/seek.c (working copy)
@@ -117,6 +117,24 @@ apr_status_t apr_file_trunc(apr_file_t *
/* 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;
Index: test/testfile.c
===================================================================
--- test/testfile.c (revision 1618575)
+++ test/testfile.c (working copy)
@@ -777,8 +777,11 @@ static void test_truncate(abts_case *tc,
apr_file_t *f;
const char *fname = "data/testtruncate.dat";
const char *s;
+ apr_off_t offset;
apr_size_t nbytes;
apr_finfo_t finfo;
+ const char teststring = "Hello World";
+ char *buffer = apr_pcalloc(p, sizeof(teststring));
apr_file_remove(fname, p);
@@ -806,6 +809,81 @@ static void test_truncate(abts_case *tc,
ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
ABTS_ASSERT(tc, "File size mismatch, expected 0 (empty)",
finfo.size == 0);
+ /* Regression test: File read buffer did not get truncated. */
+
+ /* Setup. */
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_READ | APR_FOPEN_WRITE |
APR_FOPEN_BUFFERED |
+ APR_FOPEN_TRUNCATE, APR_UREAD | APR_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nbytes = sizeof(teststring);
+ rv = apr_file_write(f, "Hello World", &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_flush(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ offset = 0;
+ rv = apr_file_seek(f, APR_SET, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_read(f, buffer, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_trunc(f, 5);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Test. Attempting to read data should return nothing but it used to
+ return stale data. */
+ nbytes = 6;
+ buffer[0] = 0;
+ rv = apr_file_read(f, buffer, &nbytes);
+ ABTS_ASSERT(tc, "Read stale buffered data beyond EOF",
+ nbytes == 0 && buffer[0] == 0);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EOF(rv));
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Regression test: File read buffer state had been inconsistent. */
+
+ /* Setup. */
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_READ | APR_FOPEN_WRITE |
APR_FOPEN_BUFFERED |
+ APR_FOPEN_TRUNCATE, APR_UREAD | APR_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nbytes = sizeof(teststring);
+ rv = apr_file_write(f, "Hello World", &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_flush(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ offset = 0;
+ rv = apr_file_seek(f, APR_SET, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_read(f, buffer, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, sizeof(teststring), nbytes);
+ rv = apr_file_trunc(f, 5);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Test. Superficially, seek() seems to work ... */
+ offset = 0;
+ rv = apr_file_seek(f, APR_CUR, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File pointer is not at EOF", offset == 5);
+
+ /* ... but the internal buffer state is inconsistent making it fail
+ further down the road. */
+ nbytes = 1;
+ rv = apr_file_write(f, "X", &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ offset = 0;
+ rv = apr_file_seek(f, APR_CUR, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "Buffered file pointer is off", offset == 6);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Cleanup */
rv = apr_file_remove(fname, p);
ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
}
]]]