Based on previous issue pointed out by Andreas Schwab, I noted fmemopen
with append mode is showing some issue. The implementation does not
account the file position correctly. The following example shows the
failure:
---
int main ()
{
char buf[10] = "test";
FILE *fp = fmemopen (buf, 10, "a+");
fseek (fp, 0, SEEK_SET);
int gr;
if ((gr = getc (fp)) != 't' ||
(gr = getc (fp)) != 'e' ||
(gr = getc (fp)) != 's' ||
(gr = getc (fp)) != 't' ||
(gr = getc (fp)) != EOF)
{
printf ("%s: getc failed returned %i\n", __FUNCTION__, gr);
return 1;
}
return 0;
}
---
This is due both how read and write operation update the buffer position,
taking in consideration buffer lenght instead of maximum position defined
by the open mode. This patch fixes it and also fixes fseek not returning
EINVAL for invalid whence modes.
Tested on x86_64 and i686.
[BZ #20012]
* libio/fmemopen.c (fmemopen_read): Use buffer maximum position, not
length to calculate the buffer to read.
(fmemopen_write): Set the buffer position based on bytes written.
(fmemopen_seek): Return EINVAL for invalid whence modes.
---
ChangeLog | 8 ++++
libio/fmemopen.c | 15 +++----
stdio-common/tst-fmemopen3.c | 93 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 109 insertions(+), 7 deletions(-)
@@ -1,3 +1,11 @@
+2016-04-27 Adhemerval Zanella <adhemerval.zanella@linaro.org>
+
+ [BZ #20012]
+ * libio/fmemopen.c (fmemopen_read): Use buffer maximum position, not
+ length to calculate the buffer to read.
+ (fmemopen_write): Set the buffer position based on bytes written.
+ (fmemopen_seek): Return EINVAL for invalid whence modes.
+
2016-04-27 Florian Weimer <fweimer@redhat.com>
[BZ #19831]
@@ -50,16 +50,14 @@ fmemopen_read (void *cookie, char *b, size_t s)
if (c->pos + s > c->maxpos)
{
- if ((size_t) c->pos == c->maxpos)
- return 0;
- s = c->size - c->pos;
+ s = c->maxpos - c->pos;
+ if ((size_t) c->pos > c->maxpos)
+ s = 0;
}
memcpy (b, &(c->buffer[c->pos]), s);
c->pos += s;
- if ((size_t) c->pos > c->maxpos)
- c->maxpos = c->pos;
return s;
}
@@ -86,7 +84,7 @@ fmemopen_write (void *cookie, const char *b, size_t s)
memcpy (&(c->buffer[pos]), b, s);
- c->pos += s;
+ c->pos = pos + s;
if ((size_t) c->pos > c->maxpos)
{
c->maxpos = c->pos;
@@ -123,7 +121,10 @@ fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
}
if (np < 0 || (size_t) np > c->size)
- return -1;
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
*p = c->pos = np;
@@ -186,6 +186,97 @@ do_test_read_seek_negative (void)
}
static int
+do_test_write_append_2 (void)
+{
+ char buf[10] = "test";
+ FILE *fp = fmemopen (buf, 10, "a+");
+ size_t r = ftell (fp);
+ size_t e = strlen (buf);
+ if (r != e)
+ {
+ printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e);
+ return 1;
+ }
+
+ if (fseek (fp, 0, SEEK_SET) == -1)
+ {
+ printf ("%s: fseek returned -1\n", __FUNCTION__);
+ return 1;
+ }
+
+ int gr;
+ if ((gr = getc (fp)) != 't' ||
+ (gr = getc (fp)) != 'e' ||
+ (gr = getc (fp)) != 's' ||
+ (gr = getc (fp)) != 't' ||
+ (gr = getc (fp)) != EOF)
+ {
+ printf ("%s: getc failed returned %i\n", __FUNCTION__, gr);
+ return 1;
+ }
+
+ if (fseek (fp, e+1, SEEK_SET) == -1)
+ {
+ printf ("%s: fseek returned -1\n", __FUNCTION__);
+ return 1;
+ }
+
+ if ((r = ftell (fp)) != e+1)
+ {
+ printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e+1);
+ return 1;
+ }
+
+ if ((gr = getc (fp)) != EOF)
+ {
+ printf ("%s: getc failed returned %i\n", __FUNCTION__, gr);
+ return 1;
+ }
+
+ /* Check if internal position is not changed with a getc returning EOF. */
+ if ((r = ftell (fp)) != e+1)
+ {
+ printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e+1);
+ return 1;
+ }
+
+ if (fseek (fp, 0, SEEK_CUR) == -1)
+ {
+ printf ("%s: fseek returned -1\n", __FUNCTION__);
+ return 1;
+ }
+
+ /* This should be overwritten by fprintf + fflush. */
+ buf[e+2] = 'X';
+
+ if ((r = fprintf (fp, "%d", 101)) != 3)
+ {
+ printf ("%s: fprintf returned %zu, expected %d\n", __FUNCTION__, r, 3);
+ return 1;
+ }
+
+ fflush (fp);
+
+ /* Check if internal position is changed by 3 (strlen of '101'). */
+ if ((r = ftell (fp)) != e+3)
+ {
+ printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e+3);
+ return 1;
+ }
+
+ const char exp[] = "test101";
+ if (strcmp (buf, exp) != 0)
+ {
+ printf ("%s: check failed: %s != %s\n", __FUNCTION__, buf, exp);
+ return 1;
+ }
+
+ fclose(fp);
+
+ return 0;
+}
+
+static int
do_test (void)
{
int ret = 0;
@@ -199,6 +290,8 @@ do_test (void)
ret += do_test_read_seek_negative ();
+ ret += do_test_write_append_2 ();
+
return ret;
}