xref: /third_party/lzma/C/7zFile.c (revision 370b324c)
1/* 7zFile.c -- File IO
22023-04-02 : Igor Pavlov : Public domain */
3
4#include "Precomp.h"
5
6#include "7zFile.h"
7
8#ifndef USE_WINDOWS_FILE
9
10  #include <errno.h>
11
12  #ifndef USE_FOPEN
13    #include <stdio.h>
14    #include <fcntl.h>
15    #ifdef _WIN32
16      #include <io.h>
17      typedef int ssize_t;
18      typedef int off_t;
19    #else
20      #include <unistd.h>
21    #endif
22  #endif
23
24#else
25
26/*
27   ReadFile and WriteFile functions in Windows have BUG:
28   If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
29   from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
30   (Insufficient system resources exist to complete the requested service).
31   Probably in some version of Windows there are problems with other sizes:
32   for 32 MB (maybe also for 16 MB).
33   And message can be "Network connection was lost"
34*/
35
36#endif
37
38#define kChunkSizeMax (1 << 22)
39
40void File_Construct(CSzFile *p)
41{
42  #ifdef USE_WINDOWS_FILE
43  p->handle = INVALID_HANDLE_VALUE;
44  #elif defined(USE_FOPEN)
45  p->file = NULL;
46  #else
47  p->fd = -1;
48  #endif
49}
50
51#if !defined(UNDER_CE) || !defined(USE_WINDOWS_FILE)
52
53static WRes File_Open(CSzFile *p, const char *name, int writeMode)
54{
55  #ifdef USE_WINDOWS_FILE
56
57  p->handle = CreateFileA(name,
58      writeMode ? GENERIC_WRITE : GENERIC_READ,
59      FILE_SHARE_READ, NULL,
60      writeMode ? CREATE_ALWAYS : OPEN_EXISTING,
61      FILE_ATTRIBUTE_NORMAL, NULL);
62  return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError();
63
64  #elif defined(USE_FOPEN)
65
66  p->file = fopen(name, writeMode ? "wb+" : "rb");
67  return (p->file != 0) ? 0 :
68    #ifdef UNDER_CE
69    2; /* ENOENT */
70    #else
71    errno;
72    #endif
73
74  #else
75
76  int flags = (writeMode ? (O_CREAT | O_EXCL | O_WRONLY) : O_RDONLY);
77  #ifdef O_BINARY
78  flags |= O_BINARY;
79  #endif
80  p->fd = open(name, flags, 0666);
81  return (p->fd != -1) ? 0 : errno;
82
83  #endif
84}
85
86WRes InFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 0); }
87
88WRes OutFile_Open(CSzFile *p, const char *name)
89{
90  #if defined(USE_WINDOWS_FILE) || defined(USE_FOPEN)
91  return File_Open(p, name, 1);
92  #else
93  p->fd = creat(name, 0666);
94  return (p->fd != -1) ? 0 : errno;
95  #endif
96}
97
98#endif
99
100
101#ifdef USE_WINDOWS_FILE
102static WRes File_OpenW(CSzFile *p, const WCHAR *name, int writeMode)
103{
104  p->handle = CreateFileW(name,
105      writeMode ? GENERIC_WRITE : GENERIC_READ,
106      FILE_SHARE_READ, NULL,
107      writeMode ? CREATE_ALWAYS : OPEN_EXISTING,
108      FILE_ATTRIBUTE_NORMAL, NULL);
109  return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError();
110}
111WRes InFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 0); }
112WRes OutFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 1); }
113#endif
114
115WRes File_Close(CSzFile *p)
116{
117  #ifdef USE_WINDOWS_FILE
118
119  if (p->handle != INVALID_HANDLE_VALUE)
120  {
121    if (!CloseHandle(p->handle))
122      return GetLastError();
123    p->handle = INVALID_HANDLE_VALUE;
124  }
125
126  #elif defined(USE_FOPEN)
127
128  if (p->file != NULL)
129  {
130    int res = fclose(p->file);
131    if (res != 0)
132    {
133      if (res == EOF)
134        return errno;
135      return res;
136    }
137    p->file = NULL;
138  }
139
140  #else
141
142  if (p->fd != -1)
143  {
144    if (close(p->fd) != 0)
145      return errno;
146    p->fd = -1;
147  }
148
149  #endif
150
151  return 0;
152}
153
154
155WRes File_Read(CSzFile *p, void *data, size_t *size)
156{
157  size_t originalSize = *size;
158  *size = 0;
159  if (originalSize == 0)
160    return 0;
161
162  #ifdef USE_WINDOWS_FILE
163
164  do
165  {
166    const DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
167    DWORD processed = 0;
168    const BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL);
169    data = (void *)((Byte *)data + processed);
170    originalSize -= processed;
171    *size += processed;
172    if (!res)
173      return GetLastError();
174    // debug : we can break here for partial reading mode
175    if (processed == 0)
176      break;
177  }
178  while (originalSize > 0);
179
180  #elif defined(USE_FOPEN)
181
182  do
183  {
184    const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize;
185    const size_t processed = fread(data, 1, curSize, p->file);
186    data = (void *)((Byte *)data + (size_t)processed);
187    originalSize -= processed;
188    *size += processed;
189    if (processed != curSize)
190      return ferror(p->file);
191    // debug : we can break here for partial reading mode
192    if (processed == 0)
193      break;
194  }
195  while (originalSize > 0);
196
197  #else
198
199  do
200  {
201    const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize;
202    const ssize_t processed = read(p->fd, data, curSize);
203    if (processed == -1)
204      return errno;
205    if (processed == 0)
206      break;
207    data = (void *)((Byte *)data + (size_t)processed);
208    originalSize -= (size_t)processed;
209    *size += (size_t)processed;
210    // debug : we can break here for partial reading mode
211    // break;
212  }
213  while (originalSize > 0);
214
215  #endif
216
217  return 0;
218}
219
220
221WRes File_Write(CSzFile *p, const void *data, size_t *size)
222{
223  size_t originalSize = *size;
224  *size = 0;
225  if (originalSize == 0)
226    return 0;
227
228  #ifdef USE_WINDOWS_FILE
229
230  do
231  {
232    const DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
233    DWORD processed = 0;
234    const BOOL res = WriteFile(p->handle, data, curSize, &processed, NULL);
235    data = (const void *)((const Byte *)data + processed);
236    originalSize -= processed;
237    *size += processed;
238    if (!res)
239      return GetLastError();
240    if (processed == 0)
241      break;
242  }
243  while (originalSize > 0);
244
245  #elif defined(USE_FOPEN)
246
247  do
248  {
249    const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize;
250    const size_t processed = fwrite(data, 1, curSize, p->file);
251    data = (void *)((Byte *)data + (size_t)processed);
252    originalSize -= processed;
253    *size += processed;
254    if (processed != curSize)
255      return ferror(p->file);
256    if (processed == 0)
257      break;
258  }
259  while (originalSize > 0);
260
261  #else
262
263  do
264  {
265    const size_t curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : originalSize;
266    const ssize_t processed = write(p->fd, data, curSize);
267    if (processed == -1)
268      return errno;
269    if (processed == 0)
270      break;
271    data = (const void *)((const Byte *)data + (size_t)processed);
272    originalSize -= (size_t)processed;
273    *size += (size_t)processed;
274  }
275  while (originalSize > 0);
276
277  #endif
278
279  return 0;
280}
281
282
283WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin)
284{
285  #ifdef USE_WINDOWS_FILE
286
287  DWORD moveMethod;
288  UInt32 low = (UInt32)*pos;
289  LONG high = (LONG)((UInt64)*pos >> 16 >> 16); /* for case when UInt64 is 32-bit only */
290  // (int) to eliminate clang warning
291  switch ((int)origin)
292  {
293    case SZ_SEEK_SET: moveMethod = FILE_BEGIN; break;
294    case SZ_SEEK_CUR: moveMethod = FILE_CURRENT; break;
295    case SZ_SEEK_END: moveMethod = FILE_END; break;
296    default: return ERROR_INVALID_PARAMETER;
297  }
298  low = SetFilePointer(p->handle, (LONG)low, &high, moveMethod);
299  if (low == (UInt32)0xFFFFFFFF)
300  {
301    WRes res = GetLastError();
302    if (res != NO_ERROR)
303      return res;
304  }
305  *pos = ((Int64)high << 32) | low;
306  return 0;
307
308  #else
309
310  int moveMethod; // = origin;
311
312  switch ((int)origin)
313  {
314    case SZ_SEEK_SET: moveMethod = SEEK_SET; break;
315    case SZ_SEEK_CUR: moveMethod = SEEK_CUR; break;
316    case SZ_SEEK_END: moveMethod = SEEK_END; break;
317    default: return EINVAL;
318  }
319
320  #if defined(USE_FOPEN)
321  {
322    int res = fseek(p->file, (long)*pos, moveMethod);
323    if (res == -1)
324      return errno;
325    *pos = ftell(p->file);
326    if (*pos == -1)
327      return errno;
328    return 0;
329  }
330  #else
331  {
332    off_t res = lseek(p->fd, (off_t)*pos, moveMethod);
333    if (res == -1)
334      return errno;
335    *pos = res;
336    return 0;
337  }
338
339  #endif // USE_FOPEN
340  #endif // USE_WINDOWS_FILE
341}
342
343
344WRes File_GetLength(CSzFile *p, UInt64 *length)
345{
346  #ifdef USE_WINDOWS_FILE
347
348  DWORD sizeHigh;
349  DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
350  if (sizeLow == 0xFFFFFFFF)
351  {
352    DWORD res = GetLastError();
353    if (res != NO_ERROR)
354      return res;
355  }
356  *length = (((UInt64)sizeHigh) << 32) + sizeLow;
357  return 0;
358
359  #elif defined(USE_FOPEN)
360
361  long pos = ftell(p->file);
362  int res = fseek(p->file, 0, SEEK_END);
363  *length = ftell(p->file);
364  fseek(p->file, pos, SEEK_SET);
365  return res;
366
367  #else
368
369  off_t pos;
370  *length = 0;
371  pos = lseek(p->fd, 0, SEEK_CUR);
372  if (pos != -1)
373  {
374    const off_t len2 = lseek(p->fd, 0, SEEK_END);
375    const off_t res2 = lseek(p->fd, pos, SEEK_SET);
376    if (len2 != -1)
377    {
378      *length = (UInt64)len2;
379      if (res2 != -1)
380        return 0;
381    }
382  }
383  return errno;
384
385  #endif
386}
387
388
389/* ---------- FileSeqInStream ---------- */
390
391static SRes FileSeqInStream_Read(ISeqInStreamPtr pp, void *buf, size_t *size)
392{
393  Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(CFileSeqInStream)
394  const WRes wres = File_Read(&p->file, buf, size);
395  p->wres = wres;
396  return (wres == 0) ? SZ_OK : SZ_ERROR_READ;
397}
398
399void FileSeqInStream_CreateVTable(CFileSeqInStream *p)
400{
401  p->vt.Read = FileSeqInStream_Read;
402}
403
404
405/* ---------- FileInStream ---------- */
406
407static SRes FileInStream_Read(ISeekInStreamPtr pp, void *buf, size_t *size)
408{
409  Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(CFileInStream)
410  const WRes wres = File_Read(&p->file, buf, size);
411  p->wres = wres;
412  return (wres == 0) ? SZ_OK : SZ_ERROR_READ;
413}
414
415static SRes FileInStream_Seek(ISeekInStreamPtr pp, Int64 *pos, ESzSeek origin)
416{
417  Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(CFileInStream)
418  const WRes wres = File_Seek(&p->file, pos, origin);
419  p->wres = wres;
420  return (wres == 0) ? SZ_OK : SZ_ERROR_READ;
421}
422
423void FileInStream_CreateVTable(CFileInStream *p)
424{
425  p->vt.Read = FileInStream_Read;
426  p->vt.Seek = FileInStream_Seek;
427}
428
429
430/* ---------- FileOutStream ---------- */
431
432static size_t FileOutStream_Write(ISeqOutStreamPtr pp, const void *data, size_t size)
433{
434  Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(CFileOutStream)
435  const WRes wres = File_Write(&p->file, data, &size);
436  p->wres = wres;
437  return size;
438}
439
440void FileOutStream_CreateVTable(CFileOutStream *p)
441{
442  p->vt.Write = FileOutStream_Write;
443}
444