xref: /netbsd-src/sys/arch/hpc/stand/hpcboot/file_manager.cpp (revision 569b7f924fd547a1a14b66aa1fdab30e6ee501c2)
1 /* -*-C++-*-	$NetBSD: file_manager.cpp,v 1.9 2010/04/06 16:20:27 nonaka Exp $	*/
2 
3 /*-
4  * Copyright(c) 1996, 2001, 2004 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matthias Drochner. and UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <console.h>
33 #include <file.h>
34 #include <limits.h>
35 
36 __BEGIN_DECLS
37 #include <string.h>
38 #include <zlib.h>
39 uLong crc32(uLong crc, const Bytef *buf, uInt len);
40 __END_DECLS
41 
42 static struct z_stream_s __stream;	// XXX for namespace.
43 
44 void
_reset()45 FileManager::_reset()
46 {
47 	_stream = &__stream;
48 	memset(_stream, 0, sizeof(struct z_stream_s));
49 	_z_err = 0;
50 	_z_eof = 0;
51 	_crc = 0;
52 	_compressed = 0;
53 }
54 
~FileManager()55 FileManager::~FileManager()
56 {
57 	delete _file;
58 }
59 
60 BOOL
setRoot(TCHAR * drive)61 FileManager::setRoot(TCHAR *drive)
62 {
63 	return _file->setRoot(drive);
64 }
65 
66 BOOL
open(const TCHAR * name,uint32_t flags)67 FileManager::open(const TCHAR *name, uint32_t flags)
68 {
69 	if (!_file->open(name, flags))
70 		return FALSE;
71 
72 	_reset();
73 
74 	if (inflateInit2(_stream, -15) != Z_OK)
75 		goto errout;
76 	_stream->next_in = _inbuf;
77 
78 	_check_header(); // skip the .gz header
79 
80 	return TRUE;
81  errout:
82 	_file->close();
83 	return FALSE;
84 }
85 
86 size_t
read(void * buf,size_t len,off_t ofs)87 FileManager::read(void *buf, size_t len, off_t ofs)
88 {
89 	if (ofs != -1)
90 		seek(ofs);
91 
92 	return _read(buf, len);
93 }
94 
95 size_t
_read(void * buf,size_t len)96 FileManager::_read(void *buf, size_t len)
97 {
98 	// starting point for crc computation
99 	uint8_t *start = reinterpret_cast<uint8_t *>(buf);
100 
101 	if (_z_err == Z_DATA_ERROR || _z_err == Z_ERRNO) {
102 		return -1;
103 	}
104 	if (_z_err == Z_STREAM_END) {
105 		return 0;  // EOF
106 	}
107 	_stream->next_out = reinterpret_cast<uint8_t *>(buf);
108 	_stream->avail_out = len;
109 
110 	int got;
111 	while (_stream->avail_out != 0) {
112 		if (!_compressed) {
113 			// Copy first the lookahead bytes
114 			uint32_t n = _stream->avail_in;
115 			if (n > _stream->avail_out)
116 				n = _stream->avail_out;
117 			if (n > 0) {
118 				memcpy(_stream->next_out, _stream->next_in, n);
119 				_stream->next_out  += n;
120 				_stream->next_in   += n;
121 				_stream->avail_out -= n;
122 				_stream->avail_in  -= n;
123 			}
124 			if (_stream->avail_out > 0) {
125 				got = _file->read(_stream->next_out,
126 				    _stream->avail_out);
127 				if (got == -1) {
128 					return(got);
129 				}
130 				_stream->avail_out -= got;
131 			}
132 			return(int)(len - _stream->avail_out);
133 		}
134 
135 		if (_stream->avail_in == 0 && !_z_eof) {
136 			got = _file->read(_inbuf, Z_BUFSIZE);
137 			if (got <= 0)
138 				_z_eof = 1;
139 
140 			_stream->avail_in = got;
141 			_stream->next_in = _inbuf;
142 		}
143 
144 		_z_err = inflate(_stream, Z_NO_FLUSH);
145 
146 		if (_z_err == Z_STREAM_END) {
147 			/* Check CRC and original size */
148 			_crc = crc32(_crc, start,(unsigned int)
149 			    (_stream->next_out - start));
150 			start = _stream->next_out;
151 
152 			if (_get_long() != _crc ||
153 			    _get_long() != _stream->total_out) {
154 				_z_err = Z_DATA_ERROR;
155 			} else {
156 				/* Check for concatenated .gz files: */
157 				_check_header();
158 				if (_z_err == Z_OK) {
159 					inflateReset(_stream);
160 					_crc = crc32(0L, Z_NULL, 0);
161 				}
162 			}
163 		}
164 		if (_z_err != Z_OK || _z_eof)
165 			break;
166 	}
167 
168 	_crc = crc32(_crc, start,(unsigned int)(_stream->next_out - start));
169 
170 	return(int)(len - _stream->avail_out);
171 }
172 
173 size_t
write(const void * buf,size_t bytes,off_t ofs)174 FileManager::write(const void *buf, size_t bytes, off_t ofs)
175 {
176 	return _file->write(buf, bytes, ofs);
177 }
178 
179 size_t
size()180 FileManager::size()
181 {
182 	return _file->size();
183 }
184 
185 BOOL
close()186 FileManager::close()
187 {
188 	inflateEnd(_stream);
189 
190 	return _file->close();
191 }
192 
193 size_t
_skip_compressed(off_t toskip)194 FileManager::_skip_compressed(off_t toskip)
195 {
196 #define	DUMMYBUFSIZE 256
197 	char dummybuf[DUMMYBUFSIZE];
198 
199 	size_t skipped = 0;
200 
201 	while (toskip > 0) {
202 		size_t toread = toskip;
203 		if (toread > DUMMYBUFSIZE)
204 			toread = DUMMYBUFSIZE;
205 
206 		size_t nread = _read(dummybuf, toread);
207 		if ((int)nread < 0)
208 			return nread;
209 
210 		toskip  -= nread;
211 		skipped += nread;
212 
213 		if (nread != toread)
214 			break;
215 	}
216 
217 	return skipped;
218 }
219 
220 size_t
realsize()221 FileManager::realsize()
222 {
223 	if (!_compressed)
224 		return size();
225 
226 	off_t pos = _stream->total_out;
227 	size_t sz = _skip_compressed(INT_MAX);
228 	seek(pos);
229 
230 	return sz;
231 }
232 
233 BOOL
seek(off_t offset)234 FileManager::seek(off_t offset)
235 {
236 
237 	if (!_compressed) {
238 		_file->seek(offset);
239 		_stream->avail_in = 0;
240 
241 		return TRUE;
242 	}
243 	/* if seek backwards, simply start from the beginning */
244 	if (offset < _stream->total_out) {
245 		_file->seek(0);
246 
247 		inflateEnd(_stream);
248 		_reset(); /* this resets total_out to 0! */
249 		inflateInit2(_stream, -15);
250 		_stream->next_in = _inbuf;
251 
252 		_check_header(); /* skip the .gz header */
253 	}
254 
255 	/* to seek forwards, throw away data */
256 	if (offset > _stream->total_out) {
257 		off_t toskip = offset - _stream->total_out;
258 		size_t skipped = _skip_compressed(toskip);
259 
260 		if (skipped != toskip)
261 			return FALSE;
262 	}
263 
264 	return TRUE;
265 }
266 
267 //
268 // GZIP util.
269 //
270 int
_get_byte()271 FileManager::_get_byte()
272 {
273 
274 	if (_z_eof)
275 		return(EOF);
276 
277 	if (_stream->avail_in == 0) {
278 		int got;
279 
280 		got = _file->read(_inbuf, Z_BUFSIZE);
281 		if (got <= 0) {
282 			_z_eof = 1;
283 			return EOF;
284 		}
285 		_stream->avail_in = got;
286 		_stream->next_in = _inbuf;
287 	}
288 	_stream->avail_in--;
289 	return *(_stream->next_in)++;
290 }
291 
292 uint32_t
_get_long()293 FileManager::_get_long()
294 {
295 	uint32_t x = static_cast<uint32_t>(_get_byte());
296 	int c;
297 
298 	x +=(static_cast<uint32_t>(_get_byte())) << 8;
299 	x +=(static_cast<uint32_t>(_get_byte())) << 16;
300 	c = _get_byte();
301 	if (c == EOF)
302 		_z_err = Z_DATA_ERROR;
303 	x +=(static_cast<uint32_t>(c)) << 24;
304 
305 	return x;
306 }
307 
308 void
_check_header()309 FileManager::_check_header()
310 {
311 	int method; /* method byte */
312 	int flags;  /* flags byte */
313 	unsigned int len;
314 	int c;
315 
316 	/* Check the gzip magic header */
317 	for (len = 0; len < 2; len++) {
318 		c = _get_byte();
319 		if (c == _gz_magic[len])
320 			continue;
321 		if ((c == EOF) &&(len == 0))  {
322 			/*
323 			 * We must not change _compressed if we are at EOF;
324 			 * we may have come to the end of a gzipped file and be
325 			 * check to see if another gzipped file is concatenated
326 			 * to this one. If one isn't, we still need to be able
327 			 * to lseek on this file as a compressed file.
328 			 */
329 			return;
330 		}
331 		_compressed = 0;
332 		if (c != EOF) {
333 			_stream->avail_in++;
334 			_stream->next_in--;
335 		}
336 		_z_err = _stream->avail_in != 0 ? Z_OK : Z_STREAM_END;
337 		return;
338 	}
339 	_compressed = 1;
340 	method = _get_byte();
341 	flags = _get_byte();
342 	if (method != Z_DEFLATED ||(flags & RESERVED) != 0) {
343 		_z_err = Z_DATA_ERROR;
344 		return;
345 	}
346 
347 	/* Discard time, xflags and OS code: */
348 	for (len = 0; len < 6; len++)
349 		(void)_get_byte();
350 
351 	if ((flags & EXTRA_FIELD) != 0) {
352 		/* skip the extra field */
353 		len  = (unsigned int)_get_byte();
354 		len +=((unsigned int)_get_byte()) << 8;
355 		/* len is garbage if EOF but the loop below will quit anyway */
356 		while (len-- != 0 && _get_byte() != EOF) /*void*/;
357 	}
358 	if ((flags & ORIG_NAME) != 0) {
359 		/* skip the original file name */
360 		while ((c = _get_byte()) != 0 && c != EOF) /*void*/;
361 	}
362 	if ((flags & COMMENT) != 0) {
363 		/* skip the .gz file comment */
364 		while ((c = _get_byte()) != 0 && c != EOF) /*void*/;
365 	}
366 	if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
367 		for (len = 0; len < 2; len++)
368 			(void)_get_byte();
369 	}
370 	_z_err = _z_eof ? Z_DATA_ERROR : Z_OK;
371 }
372