Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 10 Aug 2001 23:29:40 +0400
From:      "Andrey A. Chernov" <ache@nagual.pp.ru>
To:        arch@freebsd.org
Cc:        bugs@freebsd.org
Subject:   CFR: fseek<0 + feof error (with fix)
Message-ID:  <20010810232939.A14964@nagual.pp.ru>

next in thread | raw e-mail | index | archive | help
According to POSIX fseek{o}() description, it must return

[EINVAL]
          The whence argument is invalid. The resulting file-position
          indicator would be set to a negative value.

which expicetly disallows seek beyond beginning of a file. Currently we
not implement this. But this situation is worse due to additional feof()
bug in that case. Try following program with zero-sized "test" file:

#include <stdio.h>

main() {

FILE *f;
int i, c;

f = fopen("test", "r");

c = fgetc(f);
printf("c %d\n", c);
printf("feof %d\n", feof(f));

i = fseek(f, -30, 0);
printf("fseek %d\n", i);
printf("feof %d\n", feof(f));

c = fgetc(f);
printf("c %d\n", c);
printf("feof %d\n", feof(f));
}

Currently it produce following output:

c -1
feof 1
fseek 0 (must be -1, per POSIX)
feof 0  (if prev. one is 0, can be 0, but must be 1 otherwise)
c -1
feof 0  (true bug, must be 1 !!!)

I.e. if someone use

while (!feof(f)) {
	c = fgetc(f);
	...
}

loop after occasional negative seek, it loops forever since feof(f) will
be always 0 despite the fact that returned c == EOF. I.e. broken
fseek() broke feof() too forever (this is real life example from ARC
archiver).

Here is a patch which fix this situation and makes fseek() POSIXed in some
obvious cases like regular files (other types of files require much more
work, but partial problem fix for 99% cases is much better than no fix at
all). I plan to commit it. Please comment.

--- fseek.c.old	Fri Aug 10 23:11:10 2001
+++ fseek.c	Fri Aug 10 23:07:28 2001
@@ -133,11 +133,19 @@
 			curoff += fp->_p - fp->_bf._base;
 
 		offset += curoff;
+		if (offset < 0) {
+			errno = EINVAL;
+			return (EOF);
+		}
 		whence = SEEK_SET;
 		havepos = 1;
 		break;
 
 	case SEEK_SET:
+		if (offset < 0) {
+			errno = EINVAL;
+			return (EOF);
+		}
 	case SEEK_END:
 		curoff = 0;		/* XXX just to keep gcc quiet */
 		havepos = 0;
@@ -181,6 +189,10 @@
 		if (_fstat(fp->_file, &st))
 			goto dumb;
 		target = st.st_size + offset;
+		if (target < 0) {
+			errno = EINVAL;
+			return (EOF);
+		}
 	}
 
 	if (!havepos) {

-- 
Andrey A. Chernov
http://ache.pp.ru/

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010810232939.A14964>