Bug 24730

Summary: tail -f barfs on files bigger than 2 Gig
Product: Base System Reporter: chris <chris>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description chris 2001-01-30 09:30:01 UTC
`tail filename` and `tail -f filename` has problems when filename
is more than 2 Gig in size.  tail proceeds to 'cat' the entire
file from it's beginning, and reports a wierd error to stderr:

zeus(chris)$ tail bigfile.txt > /dev/null
tail: bigfile.txt: Invalid argument
^C

How-To-Repeat: Create a file bigger than 2 Gig and tail it.
Comment 1 dwmalone 2001-01-30 11:55:57 UTC
On Tue, Jan 30, 2001 at 01:21:15AM -0800, chris@zeus.foote.com.au wrote:

> >Synopsis:       tail -f barfs on files bigger than 2 Gig

I've been waiting for people to test the patch in:

	http://www.freebsd.org/cgi/query-pr.cgi?pr=14786

for a while - I've had atleast one report of success. The patch
below is against -current, and also fixes "tail -r".  Please let
me know if this patch fixes the problems you're seeing.

	David.


Index: usr.bin/tail/extern.h
===================================================================
RCS file: /cvs/FreeBSD-CVS/src/usr.bin/tail/extern.h,v
retrieving revision 1.4
diff -u -7 -r1.4 extern.h
--- usr.bin/tail/extern.h	1998/04/06 16:13:48	1.4
+++ usr.bin/tail/extern.h	2000/12/16 14:11:30
@@ -29,17 +29,20 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)extern.h	8.1 (Berkeley) 6/6/93
  */
 
-#define	WR(p, size) \
+#define	WR(p, size) do { \
 	if (write(STDOUT_FILENO, p, size) != size) \
-		oerr();
+		oerr(); \
+	} while(0)
+
+#define TAILMAPLEN (4<<20)
 
 enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
 
 void forward __P((FILE *, enum STYLE, long, struct stat *));
 void reverse __P((FILE *, enum STYLE, long, struct stat *));
 
 int bytes __P((FILE *, off_t));
Index: usr.bin/tail/forward.c
===================================================================
RCS file: /cvs/FreeBSD-CVS/src/usr.bin/tail/forward.c,v
retrieving revision 1.17
diff -u -7 -r1.17 forward.c
--- usr.bin/tail/forward.c	2000/12/03 17:05:44	1.17
+++ usr.bin/tail/forward.c	2000/12/16 14:12:07
@@ -265,45 +265,73 @@
  */
 static void
 rlines(fp, off, sbp)
 	FILE *fp;
 	long off;
 	struct stat *sbp;
 {
-	off_t size;
+	off_t size, curoff;
 	char *p;
 	char *start;
+	off_t mapoff;
+	size_t maplen;
 
 	if (!(size = sbp->st_size))
 		return;
 
-	if (size > SIZE_T_MAX) {
-		errno = EFBIG;
-		ierr();
-		return;
+	start = NULL;
+	for (curoff = size - 1, mapoff = size; curoff >= 0; curoff--) {
+		if (curoff < mapoff) {
+			if (start && munmap(start, maplen)) {
+				ierr();
+				return;
+			}
+			mapoff = curoff & (~(TAILMAPLEN-1));
+			maplen = curoff - mapoff + 1;
+			if ((start = mmap(NULL, maplen, PROT_READ,
+			    MAP_SHARED, fileno(fp), mapoff)) == MAP_FAILED) {
+				ierr();
+				return;
+			}
+		}
+		p = start + (curoff - mapoff);
+		/* Last char is special, ignore whether newline or not. */
+		if (*p == '\n' && curoff != size -1 && !--off) {
+			curoff++;
+			break;
+		}
 	}
 
-	if ((start = mmap(NULL, (size_t)size,
-	    PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
-		ierr();
-		return;
-	}
+	if (curoff < 0)
+		curoff = 0;
 
-	/* Last char is special, ignore whether newline or not. */
-	for (p = start + size - 1; --size;)
-		if (*--p == '\n' && !--off) {
-			++p;
-			break;
+	while (curoff != size) {
+		if (curoff < mapoff || curoff >= mapoff + maplen) {
+			if (start && munmap(start, maplen)) {
+				ierr();
+				return;
+			}
+			mapoff = curoff & (~(TAILMAPLEN-1));
+			maplen = TAILMAPLEN;
+			if (mapoff + maplen > size)
+				maplen = size - mapoff;
+			if ((start = mmap(NULL, maplen, PROT_READ,
+			    MAP_SHARED, fileno(fp), mapoff)) == MAP_FAILED) {
+				ierr();
+				return;
+			}
 		}
+		p = start + (curoff - mapoff);
+		WR(p, maplen - (curoff - mapoff));
+		curoff += maplen - (curoff - mapoff);
+	}
 
 	/* Set the file pointer to reflect the length displayed. */
-	size = sbp->st_size - size;
-	WR(p, size);
-	if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
+	if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
 		ierr();
 		return;
 	}
-	if (munmap(start, (size_t)sbp->st_size)) {
+	if (start != NULL && munmap(start, maplen)) {
 		ierr();
 		return;
 	}
 }
Index: usr.bin/tail/reverse.c
===================================================================
RCS file: /cvs/FreeBSD-CVS/src/usr.bin/tail/reverse.c,v
retrieving revision 1.10
diff -u -7 -r1.10 reverse.c
--- usr.bin/tail/reverse.c	2000/12/03 17:05:45	1.10
+++ usr.bin/tail/reverse.c	2000/12/16 14:54:55
@@ -110,51 +110,90 @@
 static void
 r_reg(fp, style, off, sbp)
 	FILE *fp;
 	enum STYLE style;
 	long off;
 	struct stat *sbp;
 {
-	off_t size;
+	off_t size, curoff;
 	int llen;
 	char *p;
 	char *start;
+	off_t mapoff;
+	size_t maplen;
 
 	if (!(size = sbp->st_size))
 		return;
 
-	if (size > SIZE_T_MAX) {
-		errno = EFBIG;
-		ierr();
-		return;
-	}
-
-	if ((start = mmap(NULL, (size_t)size,
-	    PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
-		ierr();
-		return;
-	}
-	p = start + size - 1;
-
 	if (style == RBYTES && off < size)
 		size = off;
 
-	/* Last char is special, ignore whether newline or not. */
-	for (llen = 1; --size; ++llen)
-		if (*--p == '\n') {
-			WR(p + 1, llen);
+	start = NULL;
+	for (llen = 0, curoff = size - 1, mapoff = size; curoff >= 0; ++llen, curoff--) {
+		if (curoff < mapoff) {
+			if (start && munmap(start, maplen)) {
+				ierr();
+				return;
+			}
+			mapoff = curoff & (~(TAILMAPLEN-1));
+			maplen = curoff - mapoff + 1;
+			if ((start = mmap(NULL, maplen, PROT_READ,
+			    MAP_SHARED, fileno(fp), mapoff)) == MAP_FAILED) {
+				ierr();
+				return;
+			}
+		}
+		p = start + (curoff - mapoff);
+		/* Last char is special, ignore whether newline or not. */
+		if (*p == '\n' && curoff != size - 1) {
+			if (curoff + 1 + llen <= mapoff + maplen)
+				WR(p + 1, llen);
+			else {
+				char *q;
+				if ((q = mmap(NULL, llen, PROT_READ, MAP_SHARED,
+				    fileno(fp), curoff + 1)) ==  MAP_FAILED) {
+					ierr();
+					return;
+				}
+				fprintf(stderr, "Got 1\n");
+				WR(q, llen);
+				if (munmap(q, llen)) {
+					ierr();
+					return;
+				}
+			}
 			llen = 0;
 			if (style == RLINES && !--off) {
 				++p;
 				break;
 			}
+		}
+	}
+	if (curoff < 0)
+		curoff = 0;
+
+	if (llen) {
+		if (curoff + llen <= mapoff + maplen)
+			WR(p, llen);
+		else {
+			char *q;
+			if ((q = mmap(NULL, llen, PROT_READ, MAP_SHARED,
+			    fileno(fp), curoff)) ==  MAP_FAILED) {
+				ierr();
+				return;
+			}
+			fprintf(stderr, "Got 2\n");
+			WR(q, llen);
+			if (munmap(q, llen)) {
+				ierr();
+				return;
+			}
 		}
-	if (llen)
-		WR(p, llen);
-	if (munmap(start, (size_t)sbp->st_size))
+	}
+	if (start != NULL && munmap(start, maplen))
 		ierr();
 }
 
 typedef struct bf {
 	struct bf *next;
 	struct bf *prev;
 	int len;
Comment 2 dwmalone freebsd_committer freebsd_triage 2001-06-05 20:15:28 UTC
State Changed
From-To: open->closed

Tail should be OK on big files on -current and -stable now.