1f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes/*	$OpenBSD: fread.c,v 1.12 2014/05/01 16:40:36 deraadt Exp $ */
29d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes/*-
39d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * Copyright (c) 1990, 1993
49d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *	The Regents of the University of California.  All rights reserved.
59d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *
69d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * This code is derived from software contributed to Berkeley by
79d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * Chris Torek.
89d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *
99d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * Redistribution and use in source and binary forms, with or without
109d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * modification, are permitted provided that the following conditions
119d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * are met:
129d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * 1. Redistributions of source code must retain the above copyright
139d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *    notice, this list of conditions and the following disclaimer.
149d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * 2. Redistributions in binary form must reproduce the above copyright
159d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *    notice, this list of conditions and the following disclaimer in the
169d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *    documentation and/or other materials provided with the distribution.
179d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * 3. Neither the name of the University nor the names of its contributors
189d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *    may be used to endorse or promote products derived from this software
199d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *    without specific prior written permission.
209d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes *
219d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
229d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
239d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
249d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
259d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
269d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
279d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
289d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
299d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
309d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
319d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes * SUCH DAMAGE.
329d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes */
339d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes
349d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes#include <stdio.h>
359d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes#include <string.h>
36f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes#include <stdint.h>
37f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes#include <errno.h>
3875b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes#include <sys/param.h>
399d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes#include "local.h"
409d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes
41f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes#define MUL_NO_OVERFLOW	(1UL << (sizeof(size_t) * 4))
42f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes
439d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughessize_t
447cc779f15c524e1622f7d5b1c7e82e6ffc6677fdGeorge Burgess IVfread(void *buf, size_t size, size_t count, FILE *fp) __overloadable
459d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes{
469d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	/*
4775b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	 * Extension:  Catch integer overflow.
48f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes	 */
49f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes	if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) &&
50f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes	    size > 0 && SIZE_MAX / size < count) {
51f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes		errno = EOVERFLOW;
52f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes		fp->_flags |= __SERR;
53f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes		return (0);
54f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes	}
55f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes
5675b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	const size_t desired_total = count * size;
5775b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	size_t total = desired_total;
5875b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
59f1ada79a83f6ac42f5efd995bf04374005ac532bElliott Hughes	/*
609d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	 * ANSI and SUSv2 require a return value of 0 if size or count are 0.
619d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	 */
6275b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	if (total == 0) {
639d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes		return (0);
6475b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	}
6575b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
669d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	FLOCKFILE(fp);
679d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	_SET_ORIENTATION(fp, -1);
6875b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
6975b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	// TODO: how can this ever happen?!
709d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	if (fp->_r < 0)
719d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes		fp->_r = 0;
7220841a137beac5caa824e3586c7bd91d879ff92eElliott Hughes
7375b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	/*
7475b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	 * Ensure _bf._size is valid.
7575b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	 */
7675b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	if (fp->_bf._base == NULL) {
7775b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		__smakebuf(fp);
7820841a137beac5caa824e3586c7bd91d879ff92eElliott Hughes	}
7920841a137beac5caa824e3586c7bd91d879ff92eElliott Hughes
8075b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	char* dst = buf;
8175b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
8275b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	while (total > 0) {
8375b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		/*
8475b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 * Copy data out of the buffer.
8575b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 */
86e69e6458cca9adb9669850ac4055df38a20a70d1Elliott Hughes		size_t buffered_bytes = MIN((size_t) fp->_r, total);
8775b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		memcpy(dst, fp->_p, buffered_bytes);
8875b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		fp->_p += buffered_bytes;
8975b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		fp->_r -= buffered_bytes;
9075b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		dst += buffered_bytes;
9175b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		total -= buffered_bytes;
9275b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
9375b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		/*
9475b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 * Are we done?
9575b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 */
9675b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		if (total == 0) {
9775b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes			goto out;
9875b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		}
9975b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
10075b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		/*
10175b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 * Do we have so much more to read that we should
10275b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 * avoid copying it through the buffer?
10375b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 */
10475b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		if (total > (size_t) fp->_bf._size) {
105cc9ca1051dbf5bd2af1b801de13d43a399521cf9Christopher Ferris			/*
106cc9ca1051dbf5bd2af1b801de13d43a399521cf9Christopher Ferris			 * Make sure that fseek doesn't think it can
107cc9ca1051dbf5bd2af1b801de13d43a399521cf9Christopher Ferris			 * reuse the buffer since we are going to read
108cc9ca1051dbf5bd2af1b801de13d43a399521cf9Christopher Ferris			 * directly from the file descriptor.
109cc9ca1051dbf5bd2af1b801de13d43a399521cf9Christopher Ferris			 */
110cc9ca1051dbf5bd2af1b801de13d43a399521cf9Christopher Ferris			fp->_flags |= __SMOD;
11175b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes			break;
11275b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		}
11375b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
11475b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		/*
11575b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 * Less than a buffer to go, so refill the buffer and
11675b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 * go around the loop again.
11775b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		 */
1189d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes		if (__srefill(fp)) {
11975b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes			goto out;
12075b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		}
12175b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	}
12275b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
12375b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	/*
12475b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	 * Read directly into the caller's buffer.
12575b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	 */
12675b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	while (total > 0) {
12775b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
12875b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		if (bytes_read <= 0) {
129e6bb5a27769cc974c4c6c1bfc96dcd07f0c0f5efElliott Hughes			fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
13075b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes			break;
1319d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes		}
13275b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		dst += bytes_read;
13375b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes		total -= bytes_read;
1349d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	}
13575b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes
13675b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughesout:
1379d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes	FUNLOCKFILE(fp);
13875b99387dd3a8833f09e2139e7062be5d38c5511Elliott Hughes	return ((desired_total - total) / size);
1399d3c2dd11f5e796cd814cddc5b907494f859058eElliott Hughes}
140