1/*	$OpenBSD: fread.c,v 1.12 2014/05/01 16:40:36 deraadt Exp $ */
2/*-
3 * Copyright (c) 1990, 1993
4 *	The Regents of the University of California.  All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <stdio.h>
35#include <string.h>
36#include <stdint.h>
37#include <errno.h>
38#include <sys/param.h>
39#include "local.h"
40
41#define MUL_NO_OVERFLOW	(1UL << (sizeof(size_t) * 4))
42
43size_t
44fread(void *buf, size_t size, size_t count, FILE *fp)
45{
46	/*
47	 * Extension:  Catch integer overflow.
48	 */
49	if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) &&
50	    size > 0 && SIZE_MAX / size < count) {
51		errno = EOVERFLOW;
52		fp->_flags |= __SERR;
53		return (0);
54	}
55
56	const size_t desired_total = count * size;
57	size_t total = desired_total;
58
59	/*
60	 * ANSI and SUSv2 require a return value of 0 if size or count are 0.
61	 */
62	if (total == 0) {
63		return (0);
64	}
65
66	FLOCKFILE(fp);
67	_SET_ORIENTATION(fp, -1);
68
69	// TODO: how can this ever happen?!
70	if (fp->_r < 0)
71		fp->_r = 0;
72
73	/*
74	 * Ensure _bf._size is valid.
75	 */
76	if (fp->_bf._base == NULL) {
77		__smakebuf(fp);
78	}
79
80	char* dst = buf;
81
82	while (total > 0) {
83		/*
84		 * Copy data out of the buffer.
85		 */
86		size_t buffered_bytes = MIN((size_t) fp->_r, total);
87		memcpy(dst, fp->_p, buffered_bytes);
88		fp->_p += buffered_bytes;
89		fp->_r -= buffered_bytes;
90		dst += buffered_bytes;
91		total -= buffered_bytes;
92
93		/*
94		 * Are we done?
95		 */
96		if (total == 0) {
97			goto out;
98		}
99
100		/*
101		 * Do we have so much more to read that we should
102		 * avoid copying it through the buffer?
103		 */
104		if (total > (size_t) fp->_bf._size) {
105			/*
106			 * Make sure that fseek doesn't think it can
107			 * reuse the buffer since we are going to read
108			 * directly from the file descriptor.
109			 */
110			fp->_flags |= __SMOD;
111			break;
112		}
113
114		/*
115		 * Less than a buffer to go, so refill the buffer and
116		 * go around the loop again.
117		 */
118		if (__srefill(fp)) {
119			goto out;
120		}
121	}
122
123	/*
124	 * Read directly into the caller's buffer.
125	 */
126	while (total > 0) {
127		ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
128		if (bytes_read <= 0) {
129			fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
130			break;
131		}
132		dst += bytes_read;
133		total -= bytes_read;
134	}
135
136out:
137	FUNLOCKFILE(fp);
138	return ((desired_total - total) / size);
139}
140