|
|
2e9afc |
commit ae42bbc55a9e05976269026ddabcfb917f6e922f
|
|
|
2e9afc |
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
|
|
|
2e9afc |
Date: Mon Mar 17 18:42:53 2014 +0530
|
|
|
2e9afc |
|
|
|
2e9afc |
Change offset in fdopen only if setting O_APPEND
|
|
|
2e9afc |
|
|
|
2e9afc |
fdopen should only be allowed to change the offset in the file it
|
|
|
2e9afc |
attaches to if it is setting O_APPEND. If O_APPEND is already set, it
|
|
|
2e9afc |
should not change the state of the handle.
|
|
|
2e9afc |
|
|
|
2e9afc |
commit ea33158c96c53a64402a772186956c1f5cb556ae
|
|
|
2e9afc |
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
|
|
|
2e9afc |
Date: Tue Mar 11 17:04:49 2014 +0530
|
|
|
2e9afc |
|
|
|
2e9afc |
Fix offset caching for streams and use it for ftell (BZ #16680)
|
|
|
2e9afc |
|
|
|
2e9afc |
The ftell implementation was made conservative to ensure that
|
|
|
2e9afc |
incorrectly cached offsets never affect it. However, this causes
|
|
|
2e9afc |
problems for append mode when a file stream is rewound. Additionally,
|
|
|
2e9afc |
the 'clever' trick of using stat to get position for append mode files
|
|
|
2e9afc |
caused more problems than it solved and broke old behavior. I have
|
|
|
2e9afc |
described the various problems that it caused and then finally the
|
|
|
2e9afc |
solution.
|
|
|
2e9afc |
|
|
|
2e9afc |
For a and a+ mode files, rewinding the stream should result in ftell
|
|
|
2e9afc |
returning 0 as the offset, but the stat() trick caused it to
|
|
|
2e9afc |
(incorrectly) always return the end of file. Now I couldn't find
|
|
|
2e9afc |
anything in POSIX that specifies the stream position after rewind()
|
|
|
2e9afc |
for a file opened in 'a' mode, but for 'a+' mode it should be set to
|
|
|
2e9afc |
0. For 'a' mode too, it probably makes sense to keep it set to 0 in
|
|
|
2e9afc |
the interest of retaining old behavior.
|
|
|
2e9afc |
|
|
|
2e9afc |
The initial file position for append mode files is implementation
|
|
|
2e9afc |
defined, so the implementation could either retain the current file
|
|
|
2e9afc |
position or move the position to the end of file. The earlier ftell
|
|
|
2e9afc |
implementation would move the offset to end of file for append-only
|
|
|
2e9afc |
mode, but retain the old offset for a+ mode. It would also cache the
|
|
|
2e9afc |
offset (this detail is important). My patch broke this and would set
|
|
|
2e9afc |
the initial position to end of file for both append modes, thus
|
|
|
2e9afc |
breaking old behavior. I was ignorant enough to write an incorrect
|
|
|
2e9afc |
test case for it too.
|
|
|
2e9afc |
|
|
|
2e9afc |
The Change:
|
|
|
2e9afc |
|
|
|
2e9afc |
I have now brought back the behavior of seeking to end of file for
|
|
|
2e9afc |
append-only streams, but with a slight difference. I don't cache the
|
|
|
2e9afc |
offset though, since we would want ftell to query the current file
|
|
|
2e9afc |
position through lseek while the stream is not active. Since the
|
|
|
2e9afc |
offset is moved to the end of file, we can rely on the file position
|
|
|
2e9afc |
reported by lseek and we don't need to resort to the stat() nonsense.
|
|
|
2e9afc |
|
|
|
2e9afc |
Finally, the cache is always reliable, except when there are unflished
|
|
|
2e9afc |
writes in an append mode stream (i.e. both a and a+). In the latter
|
|
|
2e9afc |
case, it is safe to just do an lseek to SEEK_END. The value can be
|
|
|
2e9afc |
safely cached too, since the file handle is already active at this
|
|
|
2e9afc |
point. Incidentally, this is the only state change we affect in the
|
|
|
2e9afc |
file handle (apart from taking locks of course).
|
|
|
2e9afc |
|
|
|
2e9afc |
I have also updated the test case to correct my impression of the
|
|
|
2e9afc |
initial file position for a+ streams to the initial behavior. I have
|
|
|
2e9afc |
verified that this does not break any existing tests in the testsuite
|
|
|
2e9afc |
and also passes with the new tests.
|
|
|
2e9afc |
|
|
|
2e9afc |
commit b1dbb426e164ad1236c2c76268e03fec5c7a7bbe
|
|
|
2e9afc |
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
|
|
|
2e9afc |
Date: Mon Mar 10 16:20:01 2014 +0530
|
|
|
2e9afc |
|
|
|
2e9afc |
Fix up return codes for tests in tst-ftell-active-handler
|
|
|
2e9afc |
|
|
|
2e9afc |
The test functions used a variable ret to store failure codes for
|
|
|
2e9afc |
individual tests, but the variable was incorrectly used to record
|
|
|
2e9afc |
other failure codes too, resulting in overwriting of the tests status.
|
|
|
2e9afc |
This is now fixed by making sure that the ret variable is used only
|
|
|
2e9afc |
for recording test failures.
|
|
|
2e9afc |
|
|
|
2e9afc |
* libio/tst-ftell-active-handler.c (do_ftell_test): Don't mix
|
|
|
2e9afc |
up test status with function return status.
|
|
|
2e9afc |
(do_write_test): Likewise.
|
|
|
2e9afc |
(do_append_test): Likewise.
|
|
|
2e9afc |
diff --git a/libio/fileops.c b/libio/fileops.c
|
|
|
2e9afc |
index 2e7bc8d..cf68dbf 100644
|
|
|
2e9afc |
--- a/libio/fileops.c
|
|
|
2e9afc |
+++ b/libio/fileops.c
|
|
|
2e9afc |
@@ -232,13 +232,18 @@ _IO_file_open (fp, filename, posix_mode, prot, read_write, is32not64)
|
|
|
2e9afc |
return NULL;
|
|
|
2e9afc |
fp->_fileno = fdesc;
|
|
|
2e9afc |
_IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
|
|
|
2e9afc |
- if ((read_write & _IO_IS_APPENDING) && (read_write & _IO_NO_READS))
|
|
|
2e9afc |
- if (_IO_SEEKOFF (fp, (_IO_off64_t)0, _IO_seek_end, _IOS_INPUT|_IOS_OUTPUT)
|
|
|
2e9afc |
- == _IO_pos_BAD && errno != ESPIPE)
|
|
|
2e9afc |
- {
|
|
|
2e9afc |
- close_not_cancel (fdesc);
|
|
|
2e9afc |
- return NULL;
|
|
|
2e9afc |
- }
|
|
|
2e9afc |
+ /* For append mode, send the file offset to the end of the file. Don't
|
|
|
2e9afc |
+ update the offset cache though, since the file handle is not active. */
|
|
|
2e9afc |
+ if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS))
|
|
|
2e9afc |
+ == (_IO_IS_APPENDING | _IO_NO_READS))
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ _IO_off64_t new_pos = _IO_SYSSEEK (fp, 0, _IO_seek_end);
|
|
|
2e9afc |
+ if (new_pos == _IO_pos_BAD && errno != ESPIPE)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ close_not_cancel (fdesc);
|
|
|
2e9afc |
+ return NULL;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
_IO_link_in ((struct _IO_FILE_plus *) fp);
|
|
|
2e9afc |
return fp;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
@@ -929,43 +934,13 @@ _IO_file_sync_mmap (_IO_FILE *fp)
|
|
|
2e9afc |
return 0;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|
|
|
2e9afc |
-/* Get the current file offset using a system call. This is the safest method
|
|
|
2e9afc |
- to get the current file offset, since we are sure that we get the current
|
|
|
2e9afc |
- state of the file. Before the stream handle is activated (by using fread,
|
|
|
2e9afc |
- fwrite, etc.), an application may alter the state of the file descriptor
|
|
|
2e9afc |
- underlying it by calling read/write/lseek on it. Using a cached offset at
|
|
|
2e9afc |
- this point will result in returning the incorrect value. Same is the case
|
|
|
2e9afc |
- when one switches from reading in a+ mode to writing, where the buffer has
|
|
|
2e9afc |
- not been flushed - the cached offset would reflect the reading position
|
|
|
2e9afc |
- while the actual write position would be at the end of the file.
|
|
|
2e9afc |
-
|
|
|
2e9afc |
- do_ftell and do_ftell_wide may resort to using the cached offset in some
|
|
|
2e9afc |
- special cases instead of calling get_file_offset, but those cases should be
|
|
|
2e9afc |
- thoroughly described. */
|
|
|
2e9afc |
-_IO_off64_t
|
|
|
2e9afc |
-get_file_offset (_IO_FILE *fp)
|
|
|
2e9afc |
-{
|
|
|
2e9afc |
- if ((fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING)
|
|
|
2e9afc |
- {
|
|
|
2e9afc |
- struct stat64 st;
|
|
|
2e9afc |
- bool ret = (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode));
|
|
|
2e9afc |
- if (ret)
|
|
|
2e9afc |
- return st.st_size;
|
|
|
2e9afc |
- else
|
|
|
2e9afc |
- return EOF;
|
|
|
2e9afc |
- }
|
|
|
2e9afc |
- else
|
|
|
2e9afc |
- return _IO_SYSSEEK (fp, 0, _IO_seek_cur);
|
|
|
2e9afc |
-}
|
|
|
2e9afc |
-
|
|
|
2e9afc |
-
|
|
|
2e9afc |
-/* ftell{,o} implementation. Don't modify any state of the file pointer while
|
|
|
2e9afc |
- we try to get the current state of the stream. */
|
|
|
2e9afc |
+/* ftell{,o} implementation. The only time we modify the state of the stream
|
|
|
2e9afc |
+ is when we have unflushed writes. In that case we seek to the end and
|
|
|
2e9afc |
+ record that offset in the stream object. */
|
|
|
2e9afc |
static _IO_off64_t
|
|
|
2e9afc |
do_ftell (_IO_FILE *fp)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
- _IO_off64_t result = 0;
|
|
|
2e9afc |
- bool use_cached_offset = false;
|
|
|
2e9afc |
+ _IO_off64_t result, offset = 0;
|
|
|
2e9afc |
|
|
|
2e9afc |
/* No point looking at unflushed data if we haven't allocated buffers
|
|
|
2e9afc |
yet. */
|
|
|
2e9afc |
@@ -974,39 +949,37 @@ do_ftell (_IO_FILE *fp)
|
|
|
2e9afc |
bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base
|
|
|
2e9afc |
|| _IO_in_put_mode (fp));
|
|
|
2e9afc |
|
|
|
2e9afc |
+ bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ /* When we have unflushed writes in append mode, seek to the end of the
|
|
|
2e9afc |
+ file and record that offset. This is the only time we change the file
|
|
|
2e9afc |
+ stream state and it is safe since the file handle is active. */
|
|
|
2e9afc |
+ if (was_writing && append_mode)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
|
|
|
2e9afc |
+ if (result == _IO_pos_BAD)
|
|
|
2e9afc |
+ return EOF;
|
|
|
2e9afc |
+ else
|
|
|
2e9afc |
+ fp->_offset = result;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
/* Adjust for unflushed data. */
|
|
|
2e9afc |
if (!was_writing)
|
|
|
2e9afc |
- result -= fp->_IO_read_end - fp->_IO_read_ptr;
|
|
|
2e9afc |
+ offset -= fp->_IO_read_end - fp->_IO_read_ptr;
|
|
|
2e9afc |
else
|
|
|
2e9afc |
- result += fp->_IO_write_ptr - fp->_IO_read_end;
|
|
|
2e9afc |
-
|
|
|
2e9afc |
- /* It is safe to use the cached offset when available if there is
|
|
|
2e9afc |
- unbuffered data (indicating that the file handle is active) and the
|
|
|
2e9afc |
- handle is not for a file open in a+ mode. The latter condition is
|
|
|
2e9afc |
- because there could be a scenario where there is a switch from read
|
|
|
2e9afc |
- mode to write mode using an fseek to an arbitrary position. In this
|
|
|
2e9afc |
- case, there would be unbuffered data due to be appended to the end of
|
|
|
2e9afc |
- the file, but the offset may not necessarily be the end of the
|
|
|
2e9afc |
- file. It is fine to use the cached offset when the a+ stream is in
|
|
|
2e9afc |
- read mode though, since the offset is maintained correctly in that
|
|
|
2e9afc |
- case. Note that this is not a comprehensive set of cases when the
|
|
|
2e9afc |
- offset is reliable. The offset may be reliable even in some cases
|
|
|
2e9afc |
- where there is no unflushed input and the handle is active, but it's
|
|
|
2e9afc |
- just that we don't have a way to identify that condition reliably. */
|
|
|
2e9afc |
- use_cached_offset = (result != 0 && fp->_offset != _IO_pos_BAD
|
|
|
2e9afc |
- && ((fp->_flags & (_IO_IS_APPENDING | _IO_NO_READS))
|
|
|
2e9afc |
- == (_IO_IS_APPENDING | _IO_NO_READS)
|
|
|
2e9afc |
- && was_writing));
|
|
|
2e9afc |
+ offset += fp->_IO_write_ptr - fp->_IO_read_end;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|
|
|
2e9afc |
- if (use_cached_offset)
|
|
|
2e9afc |
- result += fp->_offset;
|
|
|
2e9afc |
+ if (fp->_offset != _IO_pos_BAD)
|
|
|
2e9afc |
+ result = fp->_offset;
|
|
|
2e9afc |
else
|
|
|
2e9afc |
- result += get_file_offset (fp);
|
|
|
2e9afc |
+ result = _IO_SYSSEEK (fp, 0, _IO_seek_cur);
|
|
|
2e9afc |
|
|
|
2e9afc |
if (result == EOF)
|
|
|
2e9afc |
return result;
|
|
|
2e9afc |
|
|
|
2e9afc |
+ result += offset;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
if (result < 0)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
__set_errno (EINVAL);
|
|
|
2e9afc |
@@ -1016,7 +989,6 @@ do_ftell (_IO_FILE *fp)
|
|
|
2e9afc |
return result;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|
|
|
2e9afc |
-
|
|
|
2e9afc |
_IO_off64_t
|
|
|
2e9afc |
_IO_new_file_seekoff (fp, offset, dir, mode)
|
|
|
2e9afc |
_IO_FILE *fp;
|
|
|
2e9afc |
diff --git a/libio/iofdopen.c b/libio/iofdopen.c
|
|
|
2e9afc |
index 3f266f7..b36d21d 100644
|
|
|
2e9afc |
--- a/libio/iofdopen.c
|
|
|
2e9afc |
+++ b/libio/iofdopen.c
|
|
|
2e9afc |
@@ -59,6 +59,11 @@ _IO_new_fdopen (fd, mode)
|
|
|
2e9afc |
int i;
|
|
|
2e9afc |
int use_mmap = 0;
|
|
|
2e9afc |
|
|
|
2e9afc |
+ /* Decide whether we modify the offset of the file we attach to and seek to
|
|
|
2e9afc |
+ the end of file. We only do this if the mode is 'a' and if the file
|
|
|
2e9afc |
+ descriptor did not have O_APPEND in its flags already. */
|
|
|
2e9afc |
+ bool do_seek = false;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
switch (*mode)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
case 'r':
|
|
|
2e9afc |
@@ -128,6 +133,7 @@ _IO_new_fdopen (fd, mode)
|
|
|
2e9afc |
*/
|
|
|
2e9afc |
if ((posix_mode & O_APPEND) && !(fd_flags & O_APPEND))
|
|
|
2e9afc |
{
|
|
|
2e9afc |
+ do_seek = true;
|
|
|
2e9afc |
#ifdef F_SETFL
|
|
|
2e9afc |
if (_IO_fcntl (fd, F_SETFL, fd_flags | O_APPEND) == -1)
|
|
|
2e9afc |
#endif
|
|
|
2e9afc |
@@ -167,6 +173,16 @@ _IO_new_fdopen (fd, mode)
|
|
|
2e9afc |
_IO_mask_flags (&new_f->fp.file, read_write,
|
|
|
2e9afc |
_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
|
|
|
2e9afc |
|
|
|
2e9afc |
+ /* For append mode, set the file offset to the end of the file if we added
|
|
|
2e9afc |
+ O_APPEND to the file descriptor flags. Don't update the offset cache
|
|
|
2e9afc |
+ though, since the file handle is not active. */
|
|
|
2e9afc |
+ if (do_seek && ((read_write & (_IO_IS_APPENDING | _IO_NO_READS))
|
|
|
2e9afc |
+ == (_IO_IS_APPENDING | _IO_NO_READS)))
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ _IO_off64_t new_pos = _IO_SYSSEEK (&new_f->fp.file, 0, _IO_seek_end);
|
|
|
2e9afc |
+ if (new_pos == _IO_pos_BAD && errno != ESPIPE)
|
|
|
2e9afc |
+ return NULL;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
return &new_f->fp.file;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
libc_hidden_ver (_IO_new_fdopen, _IO_fdopen)
|
|
|
2e9afc |
diff --git a/libio/tst-ftell-active-handler.c b/libio/tst-ftell-active-handler.c
|
|
|
2e9afc |
index 54bfe63..e9dc7b3 100644
|
|
|
2e9afc |
--- a/libio/tst-ftell-active-handler.c
|
|
|
2e9afc |
+++ b/libio/tst-ftell-active-handler.c
|
|
|
2e9afc |
@@ -88,6 +88,107 @@ static size_t file_len;
|
|
|
2e9afc |
typedef int (*fputs_func_t) (const void *data, FILE *fp);
|
|
|
2e9afc |
fputs_func_t fputs_func;
|
|
|
2e9afc |
|
|
|
2e9afc |
+/* Test that ftell output after a rewind is correct. */
|
|
|
2e9afc |
+static int
|
|
|
2e9afc |
+do_rewind_test (const char *filename)
|
|
|
2e9afc |
+{
|
|
|
2e9afc |
+ int ret = 0;
|
|
|
2e9afc |
+ struct test
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ const char *mode;
|
|
|
2e9afc |
+ int fd_mode;
|
|
|
2e9afc |
+ size_t old_off;
|
|
|
2e9afc |
+ size_t new_off;
|
|
|
2e9afc |
+ } test_modes[] = {
|
|
|
2e9afc |
+ {"w", O_WRONLY, 0, data_len},
|
|
|
2e9afc |
+ {"w+", O_RDWR, 0, data_len},
|
|
|
2e9afc |
+ {"r+", O_RDWR, 0, data_len},
|
|
|
2e9afc |
+ /* The new offsets for 'a' and 'a+' modes have to factor in the
|
|
|
2e9afc |
+ previous writes since they always append to the end of the
|
|
|
2e9afc |
+ file. */
|
|
|
2e9afc |
+ {"a", O_WRONLY, 0, 3 * data_len},
|
|
|
2e9afc |
+ {"a+", O_RDWR, 0, 4 * data_len},
|
|
|
2e9afc |
+ };
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ /* Empty the file before the test so that our offsets are simple to
|
|
|
2e9afc |
+ calculate. */
|
|
|
2e9afc |
+ FILE *fp = fopen (filename, "w");
|
|
|
2e9afc |
+ if (fp == NULL)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("Failed to open file for emptying\n");
|
|
|
2e9afc |
+ return 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ fclose (fp);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ for (int j = 0; j < 2; j++)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ FILE *fp;
|
|
|
2e9afc |
+ int fd;
|
|
|
2e9afc |
+ int fileret;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ printf ("\trewind: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
|
|
|
2e9afc |
+ test_modes[i].mode);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ if (j == 0)
|
|
|
2e9afc |
+ fileret = get_handles_fdopen (filename, fd, fp,
|
|
|
2e9afc |
+ test_modes[i].fd_mode,
|
|
|
2e9afc |
+ test_modes[i].mode);
|
|
|
2e9afc |
+ else
|
|
|
2e9afc |
+ fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ if (fileret != 0)
|
|
|
2e9afc |
+ return fileret;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ /* Write some content to the file, rewind and ensure that the ftell
|
|
|
2e9afc |
+ output after the rewind is 0. POSIX does not specify what the
|
|
|
2e9afc |
+ behavior is when a file is rewound in 'a' mode, so we retain
|
|
|
2e9afc |
+ current behavior, which is to keep the 0 offset. */
|
|
|
2e9afc |
+ size_t written = fputs_func (data, fp);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ if (written == EOF)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("fputs[1] failed to write data\n");
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ rewind (fp);
|
|
|
2e9afc |
+ long offset = ftell (fp);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ if (offset != test_modes[i].old_off)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("Incorrect old offset. Expected %zu, but got %ld, ",
|
|
|
2e9afc |
+ test_modes[i].old_off, offset);
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ else
|
|
|
2e9afc |
+ printf ("old offset = %ld, ", offset);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ written = fputs_func (data, fp);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ if (written == EOF)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("fputs[1] failed to write data\n");
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ /* After this write, the offset in append modes should factor in the
|
|
|
2e9afc |
+ implicit lseek to the end of file. */
|
|
|
2e9afc |
+ offset = ftell (fp);
|
|
|
2e9afc |
+ if (offset != test_modes[i].new_off)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("Incorrect new offset. Expected %zu, but got %ld\n",
|
|
|
2e9afc |
+ test_modes[i].new_off, offset);
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ else
|
|
|
2e9afc |
+ printf ("new offset = %ld\n", offset);
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ return ret;
|
|
|
2e9afc |
+}
|
|
|
2e9afc |
+
|
|
|
2e9afc |
/* Test that the value of ftell is not cached when the stream handle is not
|
|
|
2e9afc |
active. */
|
|
|
2e9afc |
static int
|
|
|
2e9afc |
@@ -107,11 +208,13 @@ do_ftell_test (const char *filename)
|
|
|
2e9afc |
{"w", O_WRONLY, 0, data_len},
|
|
|
2e9afc |
{"w+", O_RDWR, 0, data_len},
|
|
|
2e9afc |
{"r+", O_RDWR, 0, data_len},
|
|
|
2e9afc |
- /* For 'a' and 'a+' modes, the initial file position should be the
|
|
|
2e9afc |
+ /* For the 'a' mode, the initial file position should be the
|
|
|
2e9afc |
current end of file. After the write, the offset has data_len
|
|
|
2e9afc |
- added to the old value. */
|
|
|
2e9afc |
+ added to the old value. For a+ mode however, the initial file
|
|
|
2e9afc |
+ position is the file position of the underlying file descriptor,
|
|
|
2e9afc |
+ since it is initially assumed to be in read mode. */
|
|
|
2e9afc |
{"a", O_WRONLY, data_len, 2 * data_len},
|
|
|
2e9afc |
- {"a+", O_RDWR, 2 * data_len, 3 * data_len},
|
|
|
2e9afc |
+ {"a+", O_RDWR, 0, 3 * data_len},
|
|
|
2e9afc |
};
|
|
|
2e9afc |
for (int j = 0; j < 2; j++)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
@@ -119,17 +222,20 @@ do_ftell_test (const char *filename)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
FILE *fp;
|
|
|
2e9afc |
int fd;
|
|
|
2e9afc |
+ int fileret;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
printf ("\tftell: %s (file, \"%s\"): ", j == 0 ? "fdopen" : "fopen",
|
|
|
2e9afc |
test_modes[i].mode);
|
|
|
2e9afc |
|
|
|
2e9afc |
if (j == 0)
|
|
|
2e9afc |
- ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
|
|
|
2e9afc |
- test_modes[i].mode);
|
|
|
2e9afc |
+ fileret = get_handles_fdopen (filename, fd, fp,
|
|
|
2e9afc |
+ test_modes[i].fd_mode,
|
|
|
2e9afc |
+ test_modes[i].mode);
|
|
|
2e9afc |
else
|
|
|
2e9afc |
- ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
|
|
|
2e9afc |
+ fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
|
|
|
2e9afc |
|
|
|
2e9afc |
- if (ret != 0)
|
|
|
2e9afc |
- return ret;
|
|
|
2e9afc |
+ if (fileret != 0)
|
|
|
2e9afc |
+ return fileret;
|
|
|
2e9afc |
|
|
|
2e9afc |
long off = ftell (fp);
|
|
|
2e9afc |
if (off != test_modes[i].old_off)
|
|
|
2e9afc |
@@ -143,13 +249,18 @@ do_ftell_test (const char *filename)
|
|
|
2e9afc |
|
|
|
2e9afc |
/* The effect of this write on the offset should be seen in the ftell
|
|
|
2e9afc |
call that follows it. */
|
|
|
2e9afc |
- int ret = write (fd, data, data_len);
|
|
|
2e9afc |
+ int write_ret = write (fd, data, data_len);
|
|
|
2e9afc |
+ if (write_ret != data_len)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("write failed (%m)\n");
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
off = ftell (fp);
|
|
|
2e9afc |
|
|
|
2e9afc |
if (off != test_modes[i].new_off)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
printf ("Incorrect new offset. Expected %zu but got %ld\n",
|
|
|
2e9afc |
- test_modes[i].old_off, off);
|
|
|
2e9afc |
+ test_modes[i].new_off, off);
|
|
|
2e9afc |
ret |= 1;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
else
|
|
|
2e9afc |
@@ -184,21 +295,23 @@ do_write_test (const char *filename)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
+ int fileret;
|
|
|
2e9afc |
printf ("\twrite: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
|
|
|
2e9afc |
test_modes[i].mode);
|
|
|
2e9afc |
|
|
|
2e9afc |
if (j == 0)
|
|
|
2e9afc |
- ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
|
|
|
2e9afc |
+ fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
|
|
|
2e9afc |
else
|
|
|
2e9afc |
- ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
|
|
|
2e9afc |
- test_modes[i].mode);
|
|
|
2e9afc |
+ fileret = get_handles_fdopen (filename, fd, fp,
|
|
|
2e9afc |
+ test_modes[i].fd_mode,
|
|
|
2e9afc |
+ test_modes[i].mode);
|
|
|
2e9afc |
|
|
|
2e9afc |
- if (ret != 0)
|
|
|
2e9afc |
- return ret;
|
|
|
2e9afc |
+ if (fileret != 0)
|
|
|
2e9afc |
+ return fileret;
|
|
|
2e9afc |
|
|
|
2e9afc |
/* Move offset to just before the end of the file. */
|
|
|
2e9afc |
- off_t ret = lseek (fd, file_len - 1, SEEK_SET);
|
|
|
2e9afc |
- if (ret == -1)
|
|
|
2e9afc |
+ off_t seek_ret = lseek (fd, file_len - 1, SEEK_SET);
|
|
|
2e9afc |
+ if (seek_ret == -1)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
printf ("lseek failed: %m\n");
|
|
|
2e9afc |
ret |= 1;
|
|
|
2e9afc |
@@ -258,17 +371,20 @@ do_append_test (const char *filename)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
+ int fileret;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
printf ("\tappend: %s (file, \"%s\"): ", j == 0 ? "fopen" : "fdopen",
|
|
|
2e9afc |
test_modes[i].mode);
|
|
|
2e9afc |
|
|
|
2e9afc |
if (j == 0)
|
|
|
2e9afc |
- ret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
|
|
|
2e9afc |
+ fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode);
|
|
|
2e9afc |
else
|
|
|
2e9afc |
- ret = get_handles_fdopen (filename, fd, fp, test_modes[i].fd_mode,
|
|
|
2e9afc |
- test_modes[i].mode);
|
|
|
2e9afc |
+ fileret = get_handles_fdopen (filename, fd, fp,
|
|
|
2e9afc |
+ test_modes[i].fd_mode,
|
|
|
2e9afc |
+ test_modes[i].mode);
|
|
|
2e9afc |
|
|
|
2e9afc |
- if (ret != 0)
|
|
|
2e9afc |
- return ret;
|
|
|
2e9afc |
+ if (fileret != 0)
|
|
|
2e9afc |
+ return fileret;
|
|
|
2e9afc |
|
|
|
2e9afc |
/* Write some data. */
|
|
|
2e9afc |
size_t written = fputs_func (data, fp);
|
|
|
2e9afc |
@@ -298,6 +414,61 @@ do_append_test (const char *filename)
|
|
|
2e9afc |
}
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|
|
|
2e9afc |
+ /* For fdopen in 'a' mode, the file descriptor should not change if the file
|
|
|
2e9afc |
+ is already open with the O_APPEND flag set. */
|
|
|
2e9afc |
+ fd = open (filename, O_WRONLY | O_APPEND, 0);
|
|
|
2e9afc |
+ if (fd == -1)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("open(O_APPEND) failed: %m\n");
|
|
|
2e9afc |
+ return 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ off_t seek_ret = lseek (fd, file_len - 1, SEEK_SET);
|
|
|
2e9afc |
+ if (seek_ret == -1)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("lseek[O_APPEND][0] failed: %m\n");
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ fp = fdopen (fd, "a");
|
|
|
2e9afc |
+ if (fp == NULL)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("fdopen(O_APPEND) failed: %m\n");
|
|
|
2e9afc |
+ close (fd);
|
|
|
2e9afc |
+ return 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ off_t new_seek_ret = lseek (fd, 0, SEEK_CUR);
|
|
|
2e9afc |
+ if (seek_ret == -1)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("lseek[O_APPEND][1] failed: %m\n");
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ printf ("\tappend: fdopen (file, \"a\"): O_APPEND: ");
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ if (seek_ret != new_seek_ret)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf ("incorrectly modified file offset to %ld, should be %ld",
|
|
|
2e9afc |
+ new_seek_ret, seek_ret);
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ else
|
|
|
2e9afc |
+ printf ("retained current file offset %ld", seek_ret);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ new_seek_ret = ftello (fp);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ if (seek_ret != new_seek_ret)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ printf (", ftello reported incorrect offset %ld, should be %ld\n",
|
|
|
2e9afc |
+ new_seek_ret, seek_ret);
|
|
|
2e9afc |
+ ret |= 1;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+ else
|
|
|
2e9afc |
+ printf (", ftello reported correct offset %ld\n", seek_ret);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ fclose (fp);
|
|
|
2e9afc |
+
|
|
|
2e9afc |
return ret;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|
|
|
2e9afc |
@@ -309,6 +480,7 @@ do_one_test (const char *filename)
|
|
|
2e9afc |
ret |= do_ftell_test (filename);
|
|
|
2e9afc |
ret |= do_write_test (filename);
|
|
|
2e9afc |
ret |= do_append_test (filename);
|
|
|
2e9afc |
+ ret |= do_rewind_test (filename);
|
|
|
2e9afc |
|
|
|
2e9afc |
return ret;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
diff --git a/libio/wfileops.c b/libio/wfileops.c
|
|
|
2e9afc |
index 8b2e108..3199861 100644
|
|
|
2e9afc |
--- a/libio/wfileops.c
|
|
|
2e9afc |
+++ b/libio/wfileops.c
|
|
|
2e9afc |
@@ -597,12 +597,12 @@ done:
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|
|
|
2e9afc |
/* ftell{,o} implementation for wide mode. Don't modify any state of the file
|
|
|
2e9afc |
- pointer while we try to get the current state of the stream. */
|
|
|
2e9afc |
+ pointer while we try to get the current state of the stream except in one
|
|
|
2e9afc |
+ case, which is when we have unflushed writes in append mode. */
|
|
|
2e9afc |
static _IO_off64_t
|
|
|
2e9afc |
do_ftell_wide (_IO_FILE *fp)
|
|
|
2e9afc |
{
|
|
|
2e9afc |
_IO_off64_t result, offset = 0;
|
|
|
2e9afc |
- bool use_cached_offset = false;
|
|
|
2e9afc |
|
|
|
2e9afc |
/* No point looking for offsets in the buffer if it hasn't even been
|
|
|
2e9afc |
allocated. */
|
|
|
2e9afc |
@@ -615,6 +615,20 @@ do_ftell_wide (_IO_FILE *fp)
|
|
|
2e9afc |
> fp->_wide_data->_IO_write_base)
|
|
|
2e9afc |
|| _IO_in_put_mode (fp));
|
|
|
2e9afc |
|
|
|
2e9afc |
+ bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING;
|
|
|
2e9afc |
+
|
|
|
2e9afc |
+ /* When we have unflushed writes in append mode, seek to the end of the
|
|
|
2e9afc |
+ file and record that offset. This is the only time we change the file
|
|
|
2e9afc |
+ stream state and it is safe since the file handle is active. */
|
|
|
2e9afc |
+ if (was_writing && append_mode)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ result = _IO_SYSSEEK (fp, 0, _IO_seek_end);
|
|
|
2e9afc |
+ if (result == _IO_pos_BAD)
|
|
|
2e9afc |
+ return EOF;
|
|
|
2e9afc |
+ else
|
|
|
2e9afc |
+ fp->_offset = result;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
/* XXX For wide stream with backup store it is not very
|
|
|
2e9afc |
reasonable to determine the offset. The pushed-back
|
|
|
2e9afc |
character might require a state change and we need not be
|
|
|
2e9afc |
@@ -703,37 +717,24 @@ do_ftell_wide (_IO_FILE *fp)
|
|
|
2e9afc |
position is fp._offset - (_IO_read_end - new_write_ptr). */
|
|
|
2e9afc |
offset -= fp->_IO_read_end - fp->_IO_write_ptr;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
-
|
|
|
2e9afc |
- /* It is safe to use the cached offset when available if there is
|
|
|
2e9afc |
- unbuffered data (indicating that the file handle is active) and
|
|
|
2e9afc |
- the handle is not for a file open in a+ mode. The latter
|
|
|
2e9afc |
- condition is because there could be a scenario where there is a
|
|
|
2e9afc |
- switch from read mode to write mode using an fseek to an arbitrary
|
|
|
2e9afc |
- position. In this case, there would be unbuffered data due to be
|
|
|
2e9afc |
- appended to the end of the file, but the offset may not
|
|
|
2e9afc |
- necessarily be the end of the file. It is fine to use the cached
|
|
|
2e9afc |
- offset when the a+ stream is in read mode though, since the offset
|
|
|
2e9afc |
- is maintained correctly in that case. Note that this is not a
|
|
|
2e9afc |
- comprehensive set of cases when the offset is reliable. The
|
|
|
2e9afc |
- offset may be reliable even in some cases where there is no
|
|
|
2e9afc |
- unflushed input and the handle is active, but it's just that we
|
|
|
2e9afc |
- don't have a way to identify that condition reliably. */
|
|
|
2e9afc |
- use_cached_offset = (offset != 0 && fp->_offset != _IO_pos_BAD
|
|
|
2e9afc |
- && ((fp->_flags & (_IO_IS_APPENDING | _IO_NO_READS))
|
|
|
2e9afc |
- == (_IO_IS_APPENDING | _IO_NO_READS)
|
|
|
2e9afc |
- && was_writing));
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|
|
|
2e9afc |
- if (use_cached_offset)
|
|
|
2e9afc |
+ if (fp->_offset != _IO_pos_BAD)
|
|
|
2e9afc |
result = fp->_offset;
|
|
|
2e9afc |
else
|
|
|
2e9afc |
- result = get_file_offset (fp);
|
|
|
2e9afc |
+ result = _IO_SYSSEEK (fp, 0, _IO_seek_cur);
|
|
|
2e9afc |
|
|
|
2e9afc |
if (result == EOF)
|
|
|
2e9afc |
return result;
|
|
|
2e9afc |
|
|
|
2e9afc |
result += offset;
|
|
|
2e9afc |
|
|
|
2e9afc |
+ if (result < 0)
|
|
|
2e9afc |
+ {
|
|
|
2e9afc |
+ __set_errno (EINVAL);
|
|
|
2e9afc |
+ return EOF;
|
|
|
2e9afc |
+ }
|
|
|
2e9afc |
+
|
|
|
2e9afc |
return result;
|
|
|
2e9afc |
}
|
|
|
2e9afc |
|