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