1 /* file.cpp --
2
3 This file is part of the UPX executable compressor.
4
5 Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
6 Copyright (C) 1996-2020 Laszlo Molnar
7 All Rights Reserved.
8
9 UPX and the UCL library are free software; you can redistribute them
10 and/or modify them under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; see the file COPYING.
21 If not, write to the Free Software Foundation, Inc.,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 Markus F.X.J. Oberhumer Laszlo Molnar
25 <markus@oberhumer.com> <ezerotven+github@gmail.com>
26 */
27
28
29 #include "conf.h"
30 #include "file.h"
31 #include "mem.h"
32
33
34 /*************************************************************************
35 //
36 **************************************************************************/
37
chmod(const char * name,int mode)38 void File::chmod(const char *name, int mode)
39 {
40 #if (HAVE_CHMOD)
41 if (::chmod(name,mode) != 0)
42 throwIOException(name,errno);
43 #else
44 UNUSED(name); UNUSED(mode);
45 #endif
46 }
47
48
rename(const char * old_,const char * new_)49 void File::rename(const char *old_, const char *new_)
50 {
51 #if (ACC_OS_DOS32) && defined(__DJGPP__)
52 if (::_rename(old_,new_) != 0)
53 #else
54 if (::rename(old_,new_) != 0)
55 #endif
56 throwIOException("rename error",errno);
57 }
58
59
unlink(const char * name)60 void File::unlink(const char *name)
61 {
62 if (::unlink(name) != 0)
63 throwIOException(name,errno);
64 }
65
66
67 /*************************************************************************
68 //
69 **************************************************************************/
70
FileBase()71 FileBase::FileBase() :
72 _fd(-1), _flags(0), _shflags(0), _mode(0), _name(NULL), _offset(0), _length(0)
73 {
74 memset(&st,0,sizeof(st));
75 }
76
77
~FileBase()78 FileBase::~FileBase()
79 {
80 #if 0 && defined(__GNUC__) // debug
81 if (isOpen())
82 fprintf(stderr,"%s: %s\n", _name, __PRETTY_FUNCTION__);
83 #endif
84
85 // FIXME: we should use close() during exception unwinding but
86 // closex() otherwise
87 closex();
88 }
89
90
do_sopen()91 bool FileBase::do_sopen()
92 {
93 if (_shflags < 0)
94 _fd = ::open(_name, _flags, _mode);
95 else
96 {
97 #if (ACC_OS_DOS32) && defined(__DJGPP__)
98 _fd = ::open(_name,_flags | _shflags, _mode);
99 #elif defined(__MINT__)
100 _fd = ::open(_name,_flags | (_shflags & O_SHMODE), _mode);
101 #elif defined(SH_DENYRW)
102 _fd = ::sopen(_name, _flags, _shflags, _mode);
103 #else
104 throwInternalError("bad usage of do_sopen()");
105 #endif
106 }
107 if (_fd < 0)
108 return false;
109 if (::fstat(_fd, &st) != 0)
110 throwIOException(_name, errno);
111 _length = st.st_size;
112 return true;
113 }
114
115
close()116 bool FileBase::close()
117 {
118 bool ok = true;
119 if (isOpen() && _fd != STDIN_FILENO && _fd != STDOUT_FILENO && _fd != STDERR_FILENO)
120 if (::close(_fd) == -1)
121 ok = false;
122 _fd = -1;
123 _flags = 0;
124 _mode = 0;
125 _name = NULL;
126 _offset = 0;
127 _length = 0;
128 return ok;
129 }
130
131
closex()132 void FileBase::closex()
133 {
134 if (!close())
135 throwIOException("close failed",errno);
136 }
137
138
read(void * buf,int len)139 int FileBase::read(void *buf, int len)
140 {
141 if (!isOpen() || len < 0)
142 throwIOException("bad read");
143 mem_size_assert(1, len); // sanity check
144 errno = 0;
145 long l = acc_safe_hread(_fd, buf, len);
146 if (errno)
147 throwIOException("read error",errno);
148 return (int) l;
149 }
150
151
readx(void * buf,int len)152 int FileBase::readx(void *buf, int len)
153 {
154 int l = this->read(buf, len);
155 if (l != len)
156 throwEOFException();
157 return l;
158 }
159
160
write(const void * buf,int len)161 void FileBase::write(const void *buf, int len)
162 {
163 if (!isOpen() || len < 0)
164 throwIOException("bad write");
165 mem_size_assert(1, len); // sanity check
166 errno = 0;
167 long l = acc_safe_hwrite(_fd, buf, len);
168 if (l != len)
169 throwIOException("write error",errno);
170 }
171
172
seek(upx_int64_t off64,int whence)173 off_t FileBase::seek(upx_int64_t off64, int whence)
174 {
175 mem_size_assert(1, off64 >= 0 ? off64 : -off64); // sanity check
176 off_t off = ACC_ICONV(off_t, off64);
177 if (!isOpen())
178 throwIOException("bad seek 1");
179 if (whence == SEEK_SET) {
180 if (off < 0)
181 throwIOException("bad seek 2");
182 off += _offset;
183 }
184 if (whence == SEEK_END) {
185 if (off > 0)
186 throwIOException("bad seek 3");
187 off += _offset + _length;
188 whence = SEEK_SET;
189 }
190 if (::lseek(_fd,off,whence) < 0)
191 throwIOException("seek error",errno);
192 return off - _offset;
193 }
194
195
tell() const196 off_t FileBase::tell() const
197 {
198 if (!isOpen())
199 throwIOException("bad tell");
200 off_t l = ::lseek(_fd, 0, SEEK_CUR);
201 if (l < 0)
202 throwIOException("tell error",errno);
203 return l - _offset;
204 }
205
206
set_extent(off_t offset,off_t length)207 void FileBase::set_extent(off_t offset, off_t length)
208 {
209 _offset = offset;
210 _length = length;
211 }
212
st_size() const213 off_t FileBase::st_size() const
214 {
215 return _length;
216 }
217
218
219 /*************************************************************************
220 //
221 **************************************************************************/
222
InputFile()223 InputFile::InputFile()
224 {
225 }
226
227
~InputFile()228 InputFile::~InputFile()
229 {
230 }
231
232
sopen(const char * name,int flags,int shflags)233 void InputFile::sopen(const char *name, int flags, int shflags)
234 {
235 close();
236 _name = name;
237 _flags = flags;
238 _shflags = shflags;
239 _mode = 0;
240 _offset = 0;
241 _length = 0;
242 if (!FileBase::do_sopen())
243 {
244 if (errno == ENOENT)
245 throw FileNotFoundException(_name, errno);
246 else if (errno == EEXIST)
247 throw FileAlreadyExistsException(_name, errno);
248 else
249 throwIOException(_name, errno);
250 }
251 _length_orig = _length;
252 }
253
254
read(void * buf,int len)255 int InputFile::read(void *buf, int len)
256 {
257 return super::read(buf, len);
258 }
259
readx(void * buf,int len)260 int InputFile::readx(void *buf, int len)
261 {
262 return super::readx(buf, len);
263 }
264
265
read(MemBuffer * buf,int len)266 int InputFile::read(MemBuffer *buf, int len)
267 {
268 buf->checkState();
269 assert((unsigned)len <= buf->getSize());
270 return read(buf->getVoidPtr(), len);
271 }
272
readx(MemBuffer * buf,int len)273 int InputFile::readx(MemBuffer *buf, int len)
274 {
275 buf->checkState();
276 assert((unsigned)len <= buf->getSize());
277 return read(buf->getVoidPtr(), len);
278 }
279
280
read(MemBuffer & buf,int len)281 int InputFile::read(MemBuffer &buf, int len)
282 {
283 return read(&buf, len);
284 }
285
readx(MemBuffer & buf,int len)286 int InputFile::readx(MemBuffer &buf, int len)
287 {
288 return readx(&buf, len);
289 }
290
291
seek(upx_int64_t off64,int whence)292 off_t InputFile::seek(upx_int64_t off64, int whence)
293 {
294 off_t pos = super::seek(off64, whence);
295 if (_length < pos)
296 throwIOException("bad seek 4");
297 return pos;
298 }
299
300
tell() const301 off_t InputFile::tell() const
302 {
303 return super::tell();
304 }
305
st_size_orig() const306 off_t InputFile::st_size_orig() const
307 {
308 return _length_orig;
309 }
310
311 /*************************************************************************
312 //
313 **************************************************************************/
314
OutputFile()315 OutputFile::OutputFile() :
316 bytes_written(0)
317 {
318 }
319
320
~OutputFile()321 OutputFile::~OutputFile()
322 {
323 }
324
325
sopen(const char * name,int flags,int shflags,int mode)326 void OutputFile::sopen(const char *name, int flags, int shflags, int mode)
327 {
328 close();
329 _name = name;
330 _flags = flags;
331 _shflags = shflags;
332 _mode = mode;
333 _offset = 0;
334 _length = 0;
335 if (!FileBase::do_sopen())
336 {
337 #if 0
338 // don't throw FileNotFound here -- this is confusing
339 if (errno == ENOENT)
340 throw FileNotFoundException(_name,errno);
341 else
342 #endif
343 if (errno == EEXIST)
344 throw FileAlreadyExistsException(_name,errno);
345 else
346 throwIOException(_name,errno);
347 }
348 }
349
350
openStdout(int flags,bool force)351 bool OutputFile::openStdout(int flags, bool force)
352 {
353 close();
354 int fd = STDOUT_FILENO;
355 if (!force && acc_isatty(fd))
356 return false;
357 _name = "<stdout>";
358 _flags = flags;
359 _shflags = -1;
360 _mode = 0;
361 _offset = 0;
362 _length = 0;
363 if (flags && acc_set_binmode(fd, 1) == -1)
364 throwIOException(_name, errno);
365 _fd = fd;
366 return true;
367 }
368
369
write(const void * buf,int len)370 void OutputFile::write(const void *buf, int len)
371 {
372 super::write(buf, len);
373 bytes_written += len;
374 }
375
st_size() const376 off_t OutputFile::st_size() const
377 {
378 if (opt->to_stdout) { // might be a pipe ==> .st_size is invalid
379 return bytes_written; // too big if seek()+write() instead of rewrite()
380 }
381 struct stat my_st;
382 my_st.st_size = 0;
383 if (::fstat(_fd, &my_st) != 0)
384 throwIOException(_name, errno);
385 return my_st.st_size;
386 }
387
388
write(const MemBuffer * buf,int len)389 void OutputFile::write(const MemBuffer *buf, int len)
390 {
391 buf->checkState();
392 assert((unsigned)len <= buf->getSize());
393 write(buf->getVoidPtr(), len);
394 }
395
396
write(const MemBuffer & buf,int len)397 void OutputFile::write(const MemBuffer &buf, int len)
398 {
399 write(&buf, len);
400 }
401
rewrite(const void * buf,int len)402 void OutputFile::rewrite(const void *buf, int len)
403 {
404 assert(!opt->to_stdout);
405 write(buf, len);
406 bytes_written -= len; // restore
407 }
408
seek(upx_int64_t off64,int whence)409 off_t OutputFile::seek(upx_int64_t off64, int whence)
410 {
411 mem_size_assert(1, off64 >= 0 ? off64 : -off64); // sanity check
412 off_t off = ACC_ICONV(off_t, off64);
413 assert(!opt->to_stdout);
414 switch (whence) {
415 case SEEK_SET: {
416 if (bytes_written < off) {
417 bytes_written = off;
418 }
419 _length = bytes_written; // cheap, lazy update; needed?
420 } break;
421 case SEEK_END: {
422 _length = bytes_written; // necessary
423 } break;
424 }
425 return super::seek(off,whence);
426 }
427
428 // WARNING: fsync() does not exist in some Windows environments.
429 // This trick works only on UNIX-like systems.
430 //int OutputFile::read(void *buf, int len)
431 //{
432 // fsync(_fd);
433 // InputFile infile;
434 // infile.open(this->getName(), O_RDONLY | O_BINARY);
435 // infile.seek(this->tell(), SEEK_SET);
436 // return infile.read(buf, len);
437 //}
438
set_extent(off_t offset,off_t length)439 void OutputFile::set_extent(off_t offset, off_t length)
440 {
441 super::set_extent(offset, length);
442 bytes_written = 0;
443 if (0==offset && (off_t)~0u==length) {
444 if (::fstat(_fd, &st) != 0)
445 throwIOException(_name, errno);
446 _length = st.st_size - offset;
447 }
448 }
449
unset_extent()450 off_t OutputFile::unset_extent()
451 {
452 off_t l = ::lseek(_fd, 0, SEEK_END);
453 if (l < 0)
454 throwIOException("lseek error", errno);
455 _offset = 0;
456 _length = l;
457 bytes_written = _length;
458 return _length;
459 }
460
dump(const char * name,const void * buf,int len,int flags)461 void OutputFile::dump(const char *name, const void *buf, int len, int flags)
462 {
463 if (flags < 0)
464 flags = O_CREAT | O_TRUNC;
465 flags |= O_WRONLY | O_BINARY;
466 OutputFile f;
467 f.open(name, flags, 0600);
468 f.write(buf, len);
469 f.closex();
470 }
471
472
473 /*************************************************************************
474 //
475 **************************************************************************/
476
477 #if 0
478
479 MemoryOutputFile::MemoryOutputFile() :
480 b(NULL), b_size(0), b_pos(0), bytes_written(0)
481 {
482 }
483
484
485 void MemoryOutputFile::write(const void *buf, int len)
486 {
487 if (!isOpen() || len < 0)
488 throwIOException("bad write");
489 if (len == 0)
490 return;
491 if (b_pos + len > b_size)
492 throwIOException("write error",ENOSPC);
493 memcpy(b + b_pos, buf, len);
494 b_pos += len;
495 bytes_written += len;
496 }
497
498
499 #endif /* if 0 */
500
501 /* vim:set ts=4 sw=4 et: */
502