1 | /* 7zFile.c -- File IO |
2 | 2009-11-24 : Igor Pavlov : Public domain */ |
3 | |
4 | #include "7zFile.h" |
5 | |
6 | #ifndef USE_WINDOWS_FILE |
7 | |
8 | #ifndef UNDER_CE |
9 | #include <errno.h> |
10 | #endif |
11 | |
12 | #else |
13 | |
14 | /* |
15 | ReadFile and WriteFile functions in Windows have BUG: |
16 | If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) |
17 | from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES |
18 | (Insufficient system resources exist to complete the requested service). |
19 | Probably in some version of Windows there are problems with other sizes: |
20 | for 32 MB (maybe also for 16 MB). |
21 | And message can be "Network connection was lost" |
22 | */ |
23 | |
24 | #define kChunkSizeMax (1 << 22) |
25 | |
26 | #endif |
27 | |
28 | void File_Construct(CSzFile *p) |
29 | { |
30 | #ifdef USE_WINDOWS_FILE |
31 | p->handle = INVALID_HANDLE_VALUE; |
32 | #else |
33 | p->file = NULL; |
34 | #endif |
35 | } |
36 | |
37 | #if !defined(UNDER_CE) || !defined(USE_WINDOWS_FILE) |
38 | static WRes File_Open(CSzFile *p, const char *name, int writeMode) |
39 | { |
40 | #ifdef USE_WINDOWS_FILE |
41 | p->handle = CreateFileA(name, |
42 | writeMode ? GENERIC_WRITE : GENERIC_READ, |
43 | FILE_SHARE_READ, NULL, |
44 | writeMode ? CREATE_ALWAYS : OPEN_EXISTING, |
45 | FILE_ATTRIBUTE_NORMAL, NULL); |
46 | return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError(); |
47 | #else |
48 | p->file = fopen(name, writeMode ? "wb+" : "rb" ); |
49 | return (p->file != 0) ? 0 : |
50 | #ifdef UNDER_CE |
51 | 2; /* ENOENT */ |
52 | #else |
53 | errno; |
54 | #endif |
55 | #endif |
56 | } |
57 | |
58 | WRes InFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 0); } |
59 | WRes OutFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 1); } |
60 | #endif |
61 | |
62 | #ifdef USE_WINDOWS_FILE |
63 | static WRes File_OpenW(CSzFile *p, const WCHAR *name, int writeMode) |
64 | { |
65 | p->handle = CreateFileW(name, |
66 | writeMode ? GENERIC_WRITE : GENERIC_READ, |
67 | FILE_SHARE_READ, NULL, |
68 | writeMode ? CREATE_ALWAYS : OPEN_EXISTING, |
69 | FILE_ATTRIBUTE_NORMAL, NULL); |
70 | return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError(); |
71 | } |
72 | WRes InFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 0); } |
73 | WRes OutFile_OpenW(CSzFile *p, const WCHAR *name) { return File_OpenW(p, name, 1); } |
74 | #endif |
75 | |
76 | WRes File_Close(CSzFile *p) |
77 | { |
78 | #ifdef USE_WINDOWS_FILE |
79 | if (p->handle != INVALID_HANDLE_VALUE) |
80 | { |
81 | if (!CloseHandle(p->handle)) |
82 | return GetLastError(); |
83 | p->handle = INVALID_HANDLE_VALUE; |
84 | } |
85 | #else |
86 | if (p->file != NULL) |
87 | { |
88 | int res = fclose(p->file); |
89 | if (res != 0) |
90 | return res; |
91 | p->file = NULL; |
92 | } |
93 | #endif |
94 | return 0; |
95 | } |
96 | |
97 | WRes File_Read(CSzFile *p, void *data, size_t *size) |
98 | { |
99 | size_t originalSize = *size; |
100 | if (originalSize == 0) |
101 | return 0; |
102 | |
103 | #ifdef USE_WINDOWS_FILE |
104 | |
105 | *size = 0; |
106 | do |
107 | { |
108 | DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize; |
109 | DWORD processed = 0; |
110 | BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL); |
111 | data = (void *)((Byte *)data + processed); |
112 | originalSize -= processed; |
113 | *size += processed; |
114 | if (!res) |
115 | return GetLastError(); |
116 | if (processed == 0) |
117 | break; |
118 | } |
119 | while (originalSize > 0); |
120 | return 0; |
121 | |
122 | #else |
123 | |
124 | *size = fread(data, 1, originalSize, p->file); |
125 | if (*size == originalSize) |
126 | return 0; |
127 | return ferror(p->file); |
128 | |
129 | #endif |
130 | } |
131 | |
132 | WRes File_Write(CSzFile *p, const void *data, size_t *size) |
133 | { |
134 | size_t originalSize = *size; |
135 | if (originalSize == 0) |
136 | return 0; |
137 | |
138 | #ifdef USE_WINDOWS_FILE |
139 | |
140 | *size = 0; |
141 | do |
142 | { |
143 | DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize; |
144 | DWORD processed = 0; |
145 | BOOL res = WriteFile(p->handle, data, curSize, &processed, NULL); |
146 | data = (void *)((Byte *)data + processed); |
147 | originalSize -= processed; |
148 | *size += processed; |
149 | if (!res) |
150 | return GetLastError(); |
151 | if (processed == 0) |
152 | break; |
153 | } |
154 | while (originalSize > 0); |
155 | return 0; |
156 | |
157 | #else |
158 | |
159 | *size = fwrite(data, 1, originalSize, p->file); |
160 | if (*size == originalSize) |
161 | return 0; |
162 | return ferror(p->file); |
163 | |
164 | #endif |
165 | } |
166 | |
167 | WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin) |
168 | { |
169 | #ifdef USE_WINDOWS_FILE |
170 | |
171 | LARGE_INTEGER value; |
172 | DWORD moveMethod; |
173 | value.LowPart = (DWORD)*pos; |
174 | value.HighPart = (LONG)((UInt64)*pos >> 16 >> 16); /* for case when UInt64 is 32-bit only */ |
175 | switch (origin) |
176 | { |
177 | case SZ_SEEK_SET: moveMethod = FILE_BEGIN; break; |
178 | case SZ_SEEK_CUR: moveMethod = FILE_CURRENT; break; |
179 | case SZ_SEEK_END: moveMethod = FILE_END; break; |
180 | default: return ERROR_INVALID_PARAMETER; |
181 | } |
182 | value.LowPart = SetFilePointer(p->handle, value.LowPart, &value.HighPart, moveMethod); |
183 | if (value.LowPart == 0xFFFFFFFF) |
184 | { |
185 | WRes res = GetLastError(); |
186 | if (res != NO_ERROR) |
187 | return res; |
188 | } |
189 | *pos = ((Int64)value.HighPart << 32) | value.LowPart; |
190 | return 0; |
191 | |
192 | #else |
193 | |
194 | int moveMethod; |
195 | int res; |
196 | switch (origin) |
197 | { |
198 | case SZ_SEEK_SET: moveMethod = SEEK_SET; break; |
199 | case SZ_SEEK_CUR: moveMethod = SEEK_CUR; break; |
200 | case SZ_SEEK_END: moveMethod = SEEK_END; break; |
201 | default: return 1; |
202 | } |
203 | res = fseek(p->file, (long)*pos, moveMethod); |
204 | *pos = ftell(p->file); |
205 | return res; |
206 | |
207 | #endif |
208 | } |
209 | |
210 | WRes File_GetLength(CSzFile *p, UInt64 *length) |
211 | { |
212 | #ifdef USE_WINDOWS_FILE |
213 | |
214 | DWORD sizeHigh; |
215 | DWORD sizeLow = GetFileSize(p->handle, &sizeHigh); |
216 | if (sizeLow == 0xFFFFFFFF) |
217 | { |
218 | DWORD res = GetLastError(); |
219 | if (res != NO_ERROR) |
220 | return res; |
221 | } |
222 | *length = (((UInt64)sizeHigh) << 32) + sizeLow; |
223 | return 0; |
224 | |
225 | #else |
226 | |
227 | long pos = ftell(p->file); |
228 | int res = fseek(p->file, 0, SEEK_END); |
229 | *length = ftell(p->file); |
230 | fseek(p->file, pos, SEEK_SET); |
231 | return res; |
232 | |
233 | #endif |
234 | } |
235 | |
236 | |
237 | /* ---------- FileSeqInStream ---------- */ |
238 | |
239 | static SRes FileSeqInStream_Read(void *pp, void *buf, size_t *size) |
240 | { |
241 | CFileSeqInStream *p = (CFileSeqInStream *)pp; |
242 | return File_Read(&p->file, buf, size) == 0 ? SZ_OK : SZ_ERROR_READ; |
243 | } |
244 | |
245 | void FileSeqInStream_CreateVTable(CFileSeqInStream *p) |
246 | { |
247 | p->s.Read = FileSeqInStream_Read; |
248 | } |
249 | |
250 | |
251 | /* ---------- FileInStream ---------- */ |
252 | |
253 | static SRes FileInStream_Read(void *pp, void *buf, size_t *size) |
254 | { |
255 | CFileInStream *p = (CFileInStream *)pp; |
256 | return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ; |
257 | } |
258 | |
259 | static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin) |
260 | { |
261 | CFileInStream *p = (CFileInStream *)pp; |
262 | return File_Seek(&p->file, pos, origin); |
263 | } |
264 | |
265 | void FileInStream_CreateVTable(CFileInStream *p) |
266 | { |
267 | p->s.Read = FileInStream_Read; |
268 | p->s.Seek = FileInStream_Seek; |
269 | } |
270 | |
271 | |
272 | /* ---------- FileOutStream ---------- */ |
273 | |
274 | static size_t FileOutStream_Write(void *pp, const void *data, size_t size) |
275 | { |
276 | CFileOutStream *p = (CFileOutStream *)pp; |
277 | File_Write(&p->file, data, &size); |
278 | return size; |
279 | } |
280 | |
281 | void FileOutStream_CreateVTable(CFileOutStream *p) |
282 | { |
283 | p->s.Write = FileOutStream_Write; |
284 | } |
285 | |