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