/src/mozilla-central/modules/libjar/zipwriter/nsZipWriter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | */ |
5 | | |
6 | | #include "nsZipWriter.h" |
7 | | |
8 | | #include <algorithm> |
9 | | |
10 | | #include "StreamFunctions.h" |
11 | | #include "nsZipDataStream.h" |
12 | | #include "nsISeekableStream.h" |
13 | | #include "nsIAsyncStreamCopier.h" |
14 | | #include "nsIStreamListener.h" |
15 | | #include "nsIInputStreamPump.h" |
16 | | #include "nsILoadInfo.h" |
17 | | #include "nsComponentManagerUtils.h" |
18 | | #include "nsMemory.h" |
19 | | #include "nsError.h" |
20 | | #include "nsStreamUtils.h" |
21 | | #include "nsThreadUtils.h" |
22 | | #include "nsNetUtil.h" |
23 | | #include "nsIChannel.h" |
24 | | #include "nsIFile.h" |
25 | | #include "prio.h" |
26 | | |
27 | 0 | #define ZIP_EOCDR_HEADER_SIZE 22 |
28 | 0 | #define ZIP_EOCDR_HEADER_SIGNATURE 0x06054b50 |
29 | | |
30 | | using namespace mozilla; |
31 | | |
32 | | /** |
33 | | * nsZipWriter is used to create and add to zip files. |
34 | | * It is based on the spec available at |
35 | | * http://www.pkware.com/documents/casestudies/APPNOTE.TXT. |
36 | | * |
37 | | * The basic structure of a zip file created is slightly simpler than that |
38 | | * illustrated in the spec because certain features of the zip format are |
39 | | * unsupported: |
40 | | * |
41 | | * [local file header 1] |
42 | | * [file data 1] |
43 | | * . |
44 | | * . |
45 | | * . |
46 | | * [local file header n] |
47 | | * [file data n] |
48 | | * [central directory] |
49 | | * [end of central directory record] |
50 | | */ |
51 | | NS_IMPL_ISUPPORTS(nsZipWriter, nsIZipWriter, |
52 | | nsIRequestObserver) |
53 | | |
54 | | nsZipWriter::nsZipWriter() |
55 | | : mCDSOffset(0) |
56 | | , mCDSDirty(false) |
57 | | , mInQueue(false) |
58 | 0 | {} |
59 | | |
60 | | nsZipWriter::~nsZipWriter() |
61 | 0 | { |
62 | 0 | if (mStream && !mInQueue) |
63 | 0 | Close(); |
64 | 0 | } |
65 | | |
66 | | NS_IMETHODIMP nsZipWriter::GetComment(nsACString & aComment) |
67 | 0 | { |
68 | 0 | if (!mStream) |
69 | 0 | return NS_ERROR_NOT_INITIALIZED; |
70 | 0 | |
71 | 0 | aComment = mComment; |
72 | 0 | return NS_OK; |
73 | 0 | } |
74 | | |
75 | | NS_IMETHODIMP nsZipWriter::SetComment(const nsACString & aComment) |
76 | 0 | { |
77 | 0 | if (!mStream) |
78 | 0 | return NS_ERROR_NOT_INITIALIZED; |
79 | 0 | |
80 | 0 | mComment = aComment; |
81 | 0 | mCDSDirty = true; |
82 | 0 | return NS_OK; |
83 | 0 | } |
84 | | |
85 | | NS_IMETHODIMP nsZipWriter::GetInQueue(bool *aInQueue) |
86 | 0 | { |
87 | 0 | *aInQueue = mInQueue; |
88 | 0 | return NS_OK; |
89 | 0 | } |
90 | | |
91 | | NS_IMETHODIMP nsZipWriter::GetFile(nsIFile **aFile) |
92 | 0 | { |
93 | 0 | if (!mFile) |
94 | 0 | return NS_ERROR_NOT_INITIALIZED; |
95 | 0 | |
96 | 0 | nsCOMPtr<nsIFile> file; |
97 | 0 | nsresult rv = mFile->Clone(getter_AddRefs(file)); |
98 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
99 | 0 |
|
100 | 0 | NS_ADDREF(*aFile = file); |
101 | 0 | return NS_OK; |
102 | 0 | } |
103 | | |
104 | | /* |
105 | | * Reads file entries out of an existing zip file. |
106 | | */ |
107 | | nsresult nsZipWriter::ReadFile(nsIFile *aFile) |
108 | 0 | { |
109 | 0 | int64_t size; |
110 | 0 | nsresult rv = aFile->GetFileSize(&size); |
111 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
112 | 0 |
|
113 | 0 | // If the file is too short, it cannot be a valid archive, thus we fail |
114 | 0 | // without even attempting to open it |
115 | 0 | NS_ENSURE_TRUE(size > ZIP_EOCDR_HEADER_SIZE, NS_ERROR_FILE_CORRUPTED); |
116 | 0 |
|
117 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
118 | 0 | rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile); |
119 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
120 | 0 |
|
121 | 0 | uint8_t buf[1024]; |
122 | 0 | int64_t seek = size - 1024; |
123 | 0 | uint32_t length = 1024; |
124 | 0 |
|
125 | 0 | nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream); |
126 | 0 |
|
127 | 0 | while (true) { |
128 | 0 | if (seek < 0) { |
129 | 0 | length += (int32_t)seek; |
130 | 0 | seek = 0; |
131 | 0 | } |
132 | 0 |
|
133 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, seek); |
134 | 0 | if (NS_FAILED(rv)) { |
135 | 0 | inputStream->Close(); |
136 | 0 | return rv; |
137 | 0 | } |
138 | 0 | rv = ZW_ReadData(inputStream, (char *)buf, length); |
139 | 0 | if (NS_FAILED(rv)) { |
140 | 0 | inputStream->Close(); |
141 | 0 | return rv; |
142 | 0 | } |
143 | 0 | |
144 | 0 | /* |
145 | 0 | * We have to backtrack from the end of the file until we find the |
146 | 0 | * CDS signature |
147 | 0 | */ |
148 | 0 | // We know it's at least this far from the end |
149 | 0 | for (uint32_t pos = length - ZIP_EOCDR_HEADER_SIZE; |
150 | 0 | (int32_t)pos >= 0; pos--) { |
151 | 0 | uint32_t sig = PEEK32(buf + pos); |
152 | 0 | if (sig == ZIP_EOCDR_HEADER_SIGNATURE) { |
153 | 0 | // Skip down to entry count |
154 | 0 | pos += 10; |
155 | 0 | uint32_t entries = READ16(buf, &pos); |
156 | 0 | // Skip past CDS size |
157 | 0 | pos += 4; |
158 | 0 | mCDSOffset = READ32(buf, &pos); |
159 | 0 | uint32_t commentlen = READ16(buf, &pos); |
160 | 0 |
|
161 | 0 | if (commentlen == 0) |
162 | 0 | mComment.Truncate(); |
163 | 0 | else if (pos + commentlen <= length) |
164 | 0 | mComment.Assign((const char *)buf + pos, commentlen); |
165 | 0 | else { |
166 | 0 | if ((seek + pos + commentlen) > size) { |
167 | 0 | inputStream->Close(); |
168 | 0 | return NS_ERROR_FILE_CORRUPTED; |
169 | 0 | } |
170 | 0 | auto field = MakeUnique<char[]>(commentlen); |
171 | 0 | NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY); |
172 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
173 | 0 | seek + pos); |
174 | 0 | if (NS_FAILED(rv)) { |
175 | 0 | inputStream->Close(); |
176 | 0 | return rv; |
177 | 0 | } |
178 | 0 | rv = ZW_ReadData(inputStream, field.get(), length); |
179 | 0 | if (NS_FAILED(rv)) { |
180 | 0 | inputStream->Close(); |
181 | 0 | return rv; |
182 | 0 | } |
183 | 0 | mComment.Assign(field.get(), commentlen); |
184 | 0 | } |
185 | 0 |
|
186 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
187 | 0 | mCDSOffset); |
188 | 0 | if (NS_FAILED(rv)) { |
189 | 0 | inputStream->Close(); |
190 | 0 | return rv; |
191 | 0 | } |
192 | 0 | |
193 | 0 | for (uint32_t entry = 0; entry < entries; entry++) { |
194 | 0 | nsZipHeader* header = new nsZipHeader(); |
195 | 0 | if (!header) { |
196 | 0 | inputStream->Close(); |
197 | 0 | mEntryHash.Clear(); |
198 | 0 | mHeaders.Clear(); |
199 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
200 | 0 | } |
201 | 0 | rv = header->ReadCDSHeader(inputStream); |
202 | 0 | if (NS_FAILED(rv)) { |
203 | 0 | inputStream->Close(); |
204 | 0 | mEntryHash.Clear(); |
205 | 0 | mHeaders.Clear(); |
206 | 0 | return rv; |
207 | 0 | } |
208 | 0 | mEntryHash.Put(header->mName, mHeaders.Count()); |
209 | 0 | if (!mHeaders.AppendObject(header)) |
210 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
211 | 0 | } |
212 | 0 |
|
213 | 0 | return inputStream->Close(); |
214 | 0 | } |
215 | 0 | } |
216 | 0 |
|
217 | 0 | if (seek == 0) { |
218 | 0 | // We've reached the start with no signature found. Corrupt. |
219 | 0 | inputStream->Close(); |
220 | 0 | return NS_ERROR_FILE_CORRUPTED; |
221 | 0 | } |
222 | 0 | |
223 | 0 | // Overlap by the size of the end of cdr |
224 | 0 | seek -= (1024 - ZIP_EOCDR_HEADER_SIZE); |
225 | 0 | } |
226 | 0 | // Will never reach here in reality |
227 | 0 | MOZ_ASSERT_UNREACHABLE("Loop should never complete"); |
228 | 0 | return NS_ERROR_UNEXPECTED; |
229 | 0 | } |
230 | | |
231 | | NS_IMETHODIMP nsZipWriter::Open(nsIFile *aFile, int32_t aIoFlags) |
232 | 0 | { |
233 | 0 | if (mStream) |
234 | 0 | return NS_ERROR_ALREADY_INITIALIZED; |
235 | 0 | |
236 | 0 | NS_ENSURE_ARG_POINTER(aFile); |
237 | 0 |
|
238 | 0 | // Need to be able to write to the file |
239 | 0 | if (aIoFlags & PR_RDONLY) |
240 | 0 | return NS_ERROR_FAILURE; |
241 | 0 | |
242 | 0 | nsresult rv = aFile->Clone(getter_AddRefs(mFile)); |
243 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
244 | 0 |
|
245 | 0 | bool exists; |
246 | 0 | rv = mFile->Exists(&exists); |
247 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
248 | 0 | if (!exists && !(aIoFlags & PR_CREATE_FILE)) |
249 | 0 | return NS_ERROR_FILE_NOT_FOUND; |
250 | 0 | |
251 | 0 | if (exists && !(aIoFlags & (PR_TRUNCATE | PR_WRONLY))) { |
252 | 0 | rv = ReadFile(mFile); |
253 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
254 | 0 | mCDSDirty = false; |
255 | 0 | } |
256 | 0 | else { |
257 | 0 | mCDSOffset = 0; |
258 | 0 | mCDSDirty = true; |
259 | 0 | mComment.Truncate(); |
260 | 0 | } |
261 | 0 |
|
262 | 0 | // Silently drop PR_APPEND |
263 | 0 | aIoFlags &= 0xef; |
264 | 0 |
|
265 | 0 | nsCOMPtr<nsIOutputStream> stream; |
266 | 0 | rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), mFile, aIoFlags); |
267 | 0 | if (NS_FAILED(rv)) { |
268 | 0 | mHeaders.Clear(); |
269 | 0 | mEntryHash.Clear(); |
270 | 0 | return rv; |
271 | 0 | } |
272 | 0 | |
273 | 0 | rv = NS_NewBufferedOutputStream(getter_AddRefs(mStream), stream.forget(), |
274 | 0 | 64 * 1024); |
275 | 0 | if (NS_FAILED(rv)) { |
276 | 0 | mHeaders.Clear(); |
277 | 0 | mEntryHash.Clear(); |
278 | 0 | return rv; |
279 | 0 | } |
280 | 0 | |
281 | 0 | if (mCDSOffset > 0) { |
282 | 0 | rv = SeekCDS(); |
283 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
284 | 0 | } |
285 | 0 |
|
286 | 0 | return NS_OK; |
287 | 0 | } |
288 | | |
289 | | NS_IMETHODIMP nsZipWriter::GetEntry(const nsACString & aZipEntry, |
290 | | nsIZipEntry **_retval) |
291 | 0 | { |
292 | 0 | int32_t pos; |
293 | 0 | if (mEntryHash.Get(aZipEntry, &pos)) |
294 | 0 | NS_ADDREF(*_retval = mHeaders[pos]); |
295 | 0 | else |
296 | 0 | *_retval = nullptr; |
297 | 0 |
|
298 | 0 | return NS_OK; |
299 | 0 | } |
300 | | |
301 | | NS_IMETHODIMP nsZipWriter::HasEntry(const nsACString & aZipEntry, |
302 | | bool *_retval) |
303 | 0 | { |
304 | 0 | *_retval = mEntryHash.Get(aZipEntry, nullptr); |
305 | 0 |
|
306 | 0 | return NS_OK; |
307 | 0 | } |
308 | | |
309 | | NS_IMETHODIMP nsZipWriter::AddEntryDirectory(const nsACString & aZipEntry, |
310 | | PRTime aModTime, bool aQueue) |
311 | 0 | { |
312 | 0 | if (!mStream) |
313 | 0 | return NS_ERROR_NOT_INITIALIZED; |
314 | 0 | |
315 | 0 | if (aQueue) { |
316 | 0 | nsZipQueueItem item; |
317 | 0 | item.mOperation = OPERATION_ADD; |
318 | 0 | item.mZipEntry = aZipEntry; |
319 | 0 | item.mModTime = aModTime; |
320 | 0 | item.mPermissions = PERMISSIONS_DIR; |
321 | 0 | if (!mQueue.AppendElement(item)) |
322 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
323 | 0 | return NS_OK; |
324 | 0 | } |
325 | 0 | |
326 | 0 | if (mInQueue) |
327 | 0 | return NS_ERROR_IN_PROGRESS; |
328 | 0 | return InternalAddEntryDirectory(aZipEntry, aModTime, PERMISSIONS_DIR); |
329 | 0 | } |
330 | | |
331 | | NS_IMETHODIMP nsZipWriter::AddEntryFile(const nsACString & aZipEntry, |
332 | | int32_t aCompression, nsIFile *aFile, |
333 | | bool aQueue) |
334 | 0 | { |
335 | 0 | NS_ENSURE_ARG_POINTER(aFile); |
336 | 0 | if (!mStream) |
337 | 0 | return NS_ERROR_NOT_INITIALIZED; |
338 | 0 | |
339 | 0 | nsresult rv; |
340 | 0 | if (aQueue) { |
341 | 0 | nsZipQueueItem item; |
342 | 0 | item.mOperation = OPERATION_ADD; |
343 | 0 | item.mZipEntry = aZipEntry; |
344 | 0 | item.mCompression = aCompression; |
345 | 0 | rv = aFile->Clone(getter_AddRefs(item.mFile)); |
346 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
347 | 0 | if (!mQueue.AppendElement(item)) |
348 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
349 | 0 | return NS_OK; |
350 | 0 | } |
351 | 0 | |
352 | 0 | if (mInQueue) |
353 | 0 | return NS_ERROR_IN_PROGRESS; |
354 | 0 | |
355 | 0 | bool exists; |
356 | 0 | rv = aFile->Exists(&exists); |
357 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
358 | 0 | if (!exists) |
359 | 0 | return NS_ERROR_FILE_NOT_FOUND; |
360 | 0 | |
361 | 0 | bool isdir; |
362 | 0 | rv = aFile->IsDirectory(&isdir); |
363 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
364 | 0 |
|
365 | 0 | PRTime modtime; |
366 | 0 | rv = aFile->GetLastModifiedTime(&modtime); |
367 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
368 | 0 | modtime *= PR_USEC_PER_MSEC; |
369 | 0 |
|
370 | 0 | uint32_t permissions; |
371 | 0 | rv = aFile->GetPermissions(&permissions); |
372 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
373 | 0 |
|
374 | 0 | if (isdir) |
375 | 0 | return InternalAddEntryDirectory(aZipEntry, modtime, permissions); |
376 | 0 | |
377 | 0 | if (mEntryHash.Get(aZipEntry, nullptr)) |
378 | 0 | return NS_ERROR_FILE_ALREADY_EXISTS; |
379 | 0 | |
380 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
381 | 0 | rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), |
382 | 0 | aFile); |
383 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
384 | 0 |
|
385 | 0 | rv = AddEntryStream(aZipEntry, modtime, aCompression, inputStream, |
386 | 0 | false, permissions); |
387 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
388 | 0 |
|
389 | 0 | return inputStream->Close(); |
390 | 0 | } |
391 | | |
392 | | NS_IMETHODIMP nsZipWriter::AddEntryChannel(const nsACString & aZipEntry, |
393 | | PRTime aModTime, |
394 | | int32_t aCompression, |
395 | | nsIChannel *aChannel, |
396 | | bool aQueue) |
397 | 0 | { |
398 | 0 | NS_ENSURE_ARG_POINTER(aChannel); |
399 | 0 | if (!mStream) |
400 | 0 | return NS_ERROR_NOT_INITIALIZED; |
401 | 0 | |
402 | 0 | if (aQueue) { |
403 | 0 | nsZipQueueItem item; |
404 | 0 | item.mOperation = OPERATION_ADD; |
405 | 0 | item.mZipEntry = aZipEntry; |
406 | 0 | item.mModTime = aModTime; |
407 | 0 | item.mCompression = aCompression; |
408 | 0 | item.mPermissions = PERMISSIONS_FILE; |
409 | 0 | item.mChannel = aChannel; |
410 | 0 | if (!mQueue.AppendElement(item)) |
411 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
412 | 0 | return NS_OK; |
413 | 0 | } |
414 | 0 | |
415 | 0 | if (mInQueue) |
416 | 0 | return NS_ERROR_IN_PROGRESS; |
417 | 0 | if (mEntryHash.Get(aZipEntry, nullptr)) |
418 | 0 | return NS_ERROR_FILE_ALREADY_EXISTS; |
419 | 0 | |
420 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
421 | 0 | nsresult rv = NS_MaybeOpenChannelUsingOpen2(aChannel, |
422 | 0 | getter_AddRefs(inputStream)); |
423 | 0 |
|
424 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
425 | 0 |
|
426 | 0 | rv = AddEntryStream(aZipEntry, aModTime, aCompression, inputStream, |
427 | 0 | false, PERMISSIONS_FILE); |
428 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
429 | 0 |
|
430 | 0 | return inputStream->Close(); |
431 | 0 | } |
432 | | |
433 | | NS_IMETHODIMP nsZipWriter::AddEntryStream(const nsACString & aZipEntry, |
434 | | PRTime aModTime, |
435 | | int32_t aCompression, |
436 | | nsIInputStream *aStream, |
437 | | bool aQueue) |
438 | 0 | { |
439 | 0 | return AddEntryStream(aZipEntry, aModTime, aCompression, aStream, aQueue, |
440 | 0 | PERMISSIONS_FILE); |
441 | 0 | } |
442 | | |
443 | | nsresult nsZipWriter::AddEntryStream(const nsACString & aZipEntry, |
444 | | PRTime aModTime, |
445 | | int32_t aCompression, |
446 | | nsIInputStream *aStream, |
447 | | bool aQueue, |
448 | | uint32_t aPermissions) |
449 | 0 | { |
450 | 0 | NS_ENSURE_ARG_POINTER(aStream); |
451 | 0 | if (!mStream) |
452 | 0 | return NS_ERROR_NOT_INITIALIZED; |
453 | 0 | |
454 | 0 | if (aQueue) { |
455 | 0 | nsZipQueueItem item; |
456 | 0 | item.mOperation = OPERATION_ADD; |
457 | 0 | item.mZipEntry = aZipEntry; |
458 | 0 | item.mModTime = aModTime; |
459 | 0 | item.mCompression = aCompression; |
460 | 0 | item.mPermissions = aPermissions; |
461 | 0 | item.mStream = aStream; |
462 | 0 | if (!mQueue.AppendElement(item)) |
463 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
464 | 0 | return NS_OK; |
465 | 0 | } |
466 | 0 | |
467 | 0 | if (mInQueue) |
468 | 0 | return NS_ERROR_IN_PROGRESS; |
469 | 0 | if (mEntryHash.Get(aZipEntry, nullptr)) |
470 | 0 | return NS_ERROR_FILE_ALREADY_EXISTS; |
471 | 0 | |
472 | 0 | RefPtr<nsZipHeader> header = new nsZipHeader(); |
473 | 0 | NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); |
474 | 0 | header->Init(aZipEntry, aModTime, ZIP_ATTRS(aPermissions, ZIP_ATTRS_FILE), |
475 | 0 | mCDSOffset); |
476 | 0 | nsresult rv = header->WriteFileHeader(mStream); |
477 | 0 | if (NS_FAILED(rv)) { |
478 | 0 | SeekCDS(); |
479 | 0 | return rv; |
480 | 0 | } |
481 | 0 | |
482 | 0 | RefPtr<nsZipDataStream> stream = new nsZipDataStream(); |
483 | 0 | if (!stream) { |
484 | 0 | SeekCDS(); |
485 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
486 | 0 | } |
487 | 0 | rv = stream->Init(this, mStream, header, aCompression); |
488 | 0 | if (NS_FAILED(rv)) { |
489 | 0 | SeekCDS(); |
490 | 0 | return rv; |
491 | 0 | } |
492 | 0 | |
493 | 0 | rv = stream->ReadStream(aStream); |
494 | 0 | if (NS_FAILED(rv)) |
495 | 0 | SeekCDS(); |
496 | 0 | return rv; |
497 | 0 | } |
498 | | |
499 | | NS_IMETHODIMP nsZipWriter::RemoveEntry(const nsACString & aZipEntry, |
500 | | bool aQueue) |
501 | 0 | { |
502 | 0 | if (!mStream) |
503 | 0 | return NS_ERROR_NOT_INITIALIZED; |
504 | 0 | |
505 | 0 | if (aQueue) { |
506 | 0 | nsZipQueueItem item; |
507 | 0 | item.mOperation = OPERATION_REMOVE; |
508 | 0 | item.mZipEntry = aZipEntry; |
509 | 0 | if (!mQueue.AppendElement(item)) |
510 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
511 | 0 | return NS_OK; |
512 | 0 | } |
513 | 0 | |
514 | 0 | if (mInQueue) |
515 | 0 | return NS_ERROR_IN_PROGRESS; |
516 | 0 | |
517 | 0 | int32_t pos; |
518 | 0 | if (mEntryHash.Get(aZipEntry, &pos)) { |
519 | 0 | // Flush any remaining data before we seek. |
520 | 0 | nsresult rv = mStream->Flush(); |
521 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
522 | 0 | if (pos < mHeaders.Count() - 1) { |
523 | 0 | // This is not the last entry, pull back the data. |
524 | 0 | nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream); |
525 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
526 | 0 | mHeaders[pos]->mOffset); |
527 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
528 | 0 |
|
529 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
530 | 0 | rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), |
531 | 0 | mFile); |
532 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
533 | 0 | seekable = do_QueryInterface(inputStream); |
534 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
535 | 0 | mHeaders[pos + 1]->mOffset); |
536 | 0 | if (NS_FAILED(rv)) { |
537 | 0 | inputStream->Close(); |
538 | 0 | return rv; |
539 | 0 | } |
540 | 0 | |
541 | 0 | uint32_t count = mCDSOffset - mHeaders[pos + 1]->mOffset; |
542 | 0 | uint32_t read = 0; |
543 | 0 | char buf[4096]; |
544 | 0 | while (count > 0) { |
545 | 0 | read = std::min(count, (uint32_t) sizeof(buf)); |
546 | 0 |
|
547 | 0 | rv = inputStream->Read(buf, read, &read); |
548 | 0 | if (NS_FAILED(rv)) { |
549 | 0 | inputStream->Close(); |
550 | 0 | Cleanup(); |
551 | 0 | return rv; |
552 | 0 | } |
553 | 0 | |
554 | 0 | rv = ZW_WriteData(mStream, buf, read); |
555 | 0 | if (NS_FAILED(rv)) { |
556 | 0 | inputStream->Close(); |
557 | 0 | Cleanup(); |
558 | 0 | return rv; |
559 | 0 | } |
560 | 0 | |
561 | 0 | count -= read; |
562 | 0 | } |
563 | 0 | inputStream->Close(); |
564 | 0 |
|
565 | 0 | // Rewrite header offsets and update hash |
566 | 0 | uint32_t shift = (mHeaders[pos + 1]->mOffset - |
567 | 0 | mHeaders[pos]->mOffset); |
568 | 0 | mCDSOffset -= shift; |
569 | 0 | int32_t pos2 = pos + 1; |
570 | 0 | while (pos2 < mHeaders.Count()) { |
571 | 0 | mEntryHash.Put(mHeaders[pos2]->mName, pos2-1); |
572 | 0 | mHeaders[pos2]->mOffset -= shift; |
573 | 0 | pos2++; |
574 | 0 | } |
575 | 0 | } |
576 | 0 | else { |
577 | 0 | // Remove the last entry is just a case of moving the CDS |
578 | 0 | mCDSOffset = mHeaders[pos]->mOffset; |
579 | 0 | rv = SeekCDS(); |
580 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
581 | 0 | } |
582 | 0 |
|
583 | 0 | mEntryHash.Remove(mHeaders[pos]->mName); |
584 | 0 | mHeaders.RemoveObjectAt(pos); |
585 | 0 | mCDSDirty = true; |
586 | 0 |
|
587 | 0 | return NS_OK; |
588 | 0 | } |
589 | 0 | |
590 | 0 | return NS_ERROR_FILE_NOT_FOUND; |
591 | 0 | } |
592 | | |
593 | | NS_IMETHODIMP nsZipWriter::ProcessQueue(nsIRequestObserver *aObserver, |
594 | | nsISupports *aContext) |
595 | 0 | { |
596 | 0 | if (!mStream) |
597 | 0 | return NS_ERROR_NOT_INITIALIZED; |
598 | 0 | if (mInQueue) |
599 | 0 | return NS_ERROR_IN_PROGRESS; |
600 | 0 | |
601 | 0 | mProcessObserver = aObserver; |
602 | 0 | mProcessContext = aContext; |
603 | 0 | mInQueue = true; |
604 | 0 |
|
605 | 0 | if (mProcessObserver) |
606 | 0 | mProcessObserver->OnStartRequest(nullptr, mProcessContext); |
607 | 0 |
|
608 | 0 | BeginProcessingNextItem(); |
609 | 0 |
|
610 | 0 | return NS_OK; |
611 | 0 | } |
612 | | |
613 | | NS_IMETHODIMP nsZipWriter::Close() |
614 | 0 | { |
615 | 0 | if (!mStream) |
616 | 0 | return NS_ERROR_NOT_INITIALIZED; |
617 | 0 | if (mInQueue) |
618 | 0 | return NS_ERROR_IN_PROGRESS; |
619 | 0 | |
620 | 0 | if (mCDSDirty) { |
621 | 0 | uint32_t size = 0; |
622 | 0 | for (int32_t i = 0; i < mHeaders.Count(); i++) { |
623 | 0 | nsresult rv = mHeaders[i]->WriteCDSHeader(mStream); |
624 | 0 | if (NS_FAILED(rv)) { |
625 | 0 | Cleanup(); |
626 | 0 | return rv; |
627 | 0 | } |
628 | 0 | size += mHeaders[i]->GetCDSHeaderLength(); |
629 | 0 | } |
630 | 0 |
|
631 | 0 | uint8_t buf[ZIP_EOCDR_HEADER_SIZE]; |
632 | 0 | uint32_t pos = 0; |
633 | 0 | WRITE32(buf, &pos, ZIP_EOCDR_HEADER_SIGNATURE); |
634 | 0 | WRITE16(buf, &pos, 0); |
635 | 0 | WRITE16(buf, &pos, 0); |
636 | 0 | WRITE16(buf, &pos, mHeaders.Count()); |
637 | 0 | WRITE16(buf, &pos, mHeaders.Count()); |
638 | 0 | WRITE32(buf, &pos, size); |
639 | 0 | WRITE32(buf, &pos, mCDSOffset); |
640 | 0 | WRITE16(buf, &pos, mComment.Length()); |
641 | 0 |
|
642 | 0 | nsresult rv = ZW_WriteData(mStream, (const char *)buf, pos); |
643 | 0 | if (NS_FAILED(rv)) { |
644 | 0 | Cleanup(); |
645 | 0 | return rv; |
646 | 0 | } |
647 | 0 | |
648 | 0 | rv = ZW_WriteData(mStream, mComment.get(), mComment.Length()); |
649 | 0 | if (NS_FAILED(rv)) { |
650 | 0 | Cleanup(); |
651 | 0 | return rv; |
652 | 0 | } |
653 | 0 | |
654 | 0 | nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream); |
655 | 0 | rv = seekable->SetEOF(); |
656 | 0 | if (NS_FAILED(rv)) { |
657 | 0 | Cleanup(); |
658 | 0 | return rv; |
659 | 0 | } |
660 | 0 | |
661 | 0 | // Go back and rewrite the file headers |
662 | 0 | for (int32_t i = 0; i < mHeaders.Count(); i++) { |
663 | 0 | nsZipHeader *header = mHeaders[i]; |
664 | 0 | if (!header->mWriteOnClose) |
665 | 0 | continue; |
666 | 0 | |
667 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, header->mOffset); |
668 | 0 | if (NS_FAILED(rv)) { |
669 | 0 | Cleanup(); |
670 | 0 | return rv; |
671 | 0 | } |
672 | 0 | rv = header->WriteFileHeader(mStream); |
673 | 0 | if (NS_FAILED(rv)) { |
674 | 0 | Cleanup(); |
675 | 0 | return rv; |
676 | 0 | } |
677 | 0 | } |
678 | 0 | } |
679 | 0 |
|
680 | 0 | nsresult rv = mStream->Close(); |
681 | 0 | mStream = nullptr; |
682 | 0 | mHeaders.Clear(); |
683 | 0 | mEntryHash.Clear(); |
684 | 0 | mQueue.Clear(); |
685 | 0 |
|
686 | 0 | return rv; |
687 | 0 | } |
688 | | |
689 | | // Our nsIRequestObserver monitors removal operations performed on the queue |
690 | | NS_IMETHODIMP nsZipWriter::OnStartRequest(nsIRequest *aRequest, |
691 | | nsISupports *aContext) |
692 | 0 | { |
693 | 0 | return NS_OK; |
694 | 0 | } |
695 | | |
696 | | NS_IMETHODIMP nsZipWriter::OnStopRequest(nsIRequest *aRequest, |
697 | | nsISupports *aContext, |
698 | | nsresult aStatusCode) |
699 | 0 | { |
700 | 0 | if (NS_FAILED(aStatusCode)) { |
701 | 0 | FinishQueue(aStatusCode); |
702 | 0 | Cleanup(); |
703 | 0 | } |
704 | 0 |
|
705 | 0 | nsresult rv = mStream->Flush(); |
706 | 0 | if (NS_FAILED(rv)) { |
707 | 0 | FinishQueue(rv); |
708 | 0 | Cleanup(); |
709 | 0 | return rv; |
710 | 0 | } |
711 | 0 | rv = SeekCDS(); |
712 | 0 | if (NS_FAILED(rv)) { |
713 | 0 | FinishQueue(rv); |
714 | 0 | return rv; |
715 | 0 | } |
716 | 0 | |
717 | 0 | BeginProcessingNextItem(); |
718 | 0 |
|
719 | 0 | return NS_OK; |
720 | 0 | } |
721 | | |
722 | | /* |
723 | | * Make all stored(uncompressed) files align to given alignment size. |
724 | | */ |
725 | | NS_IMETHODIMP nsZipWriter::AlignStoredFiles(uint16_t aAlignSize) |
726 | 0 | { |
727 | 0 | nsresult rv; |
728 | 0 |
|
729 | 0 | // Check for range and power of 2. |
730 | 0 | if (aAlignSize < 2 || aAlignSize > 32768 || |
731 | 0 | (aAlignSize & (aAlignSize - 1)) != 0) { |
732 | 0 | return NS_ERROR_INVALID_ARG; |
733 | 0 | } |
734 | 0 | |
735 | 0 | for (int i = 0; i < mHeaders.Count(); i++) { |
736 | 0 | nsZipHeader *header = mHeaders[i]; |
737 | 0 |
|
738 | 0 | // Check whether this entry is file and compression method is stored. |
739 | 0 | bool isdir; |
740 | 0 | rv = header->GetIsDirectory(&isdir); |
741 | 0 | if (NS_FAILED(rv)) { |
742 | 0 | return rv; |
743 | 0 | } |
744 | 0 | if (isdir || header->mMethod != 0) { |
745 | 0 | continue; |
746 | 0 | } |
747 | 0 | // Pad extra field to align data starting position to specified size. |
748 | 0 | uint32_t old_len = header->mLocalFieldLength; |
749 | 0 | rv = header->PadExtraField(header->mOffset, aAlignSize); |
750 | 0 | if (NS_FAILED(rv)) { |
751 | 0 | continue; |
752 | 0 | } |
753 | 0 | // No padding means data already aligned. |
754 | 0 | uint32_t shift = header->mLocalFieldLength - old_len; |
755 | 0 | if (shift == 0) { |
756 | 0 | continue; |
757 | 0 | } |
758 | 0 | |
759 | 0 | // Flush any remaining data before we start. |
760 | 0 | rv = mStream->Flush(); |
761 | 0 | if (NS_FAILED(rv)) { |
762 | 0 | return rv; |
763 | 0 | } |
764 | 0 | |
765 | 0 | // Open zip file for reading. |
766 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
767 | 0 | rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), mFile); |
768 | 0 | if (NS_FAILED(rv)) { |
769 | 0 | return rv; |
770 | 0 | } |
771 | 0 | |
772 | 0 | nsCOMPtr<nsISeekableStream> in_seekable = do_QueryInterface(inputStream); |
773 | 0 | nsCOMPtr<nsISeekableStream> out_seekable = do_QueryInterface(mStream); |
774 | 0 |
|
775 | 0 | uint32_t data_offset = header->mOffset + header->GetFileHeaderLength() - shift; |
776 | 0 | uint32_t count = mCDSOffset - data_offset; |
777 | 0 | uint32_t read; |
778 | 0 | char buf[4096]; |
779 | 0 |
|
780 | 0 | // Shift data to aligned postion. |
781 | 0 | while (count > 0) { |
782 | 0 | read = std::min(count, (uint32_t) sizeof(buf)); |
783 | 0 |
|
784 | 0 | rv = in_seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
785 | 0 | data_offset + count - read); |
786 | 0 | if (NS_FAILED(rv)) { |
787 | 0 | break; |
788 | 0 | } |
789 | 0 | |
790 | 0 | rv = inputStream->Read(buf, read, &read); |
791 | 0 | if (NS_FAILED(rv)) { |
792 | 0 | break; |
793 | 0 | } |
794 | 0 | |
795 | 0 | rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
796 | 0 | data_offset + count - read + shift); |
797 | 0 | if (NS_FAILED(rv)) { |
798 | 0 | break; |
799 | 0 | } |
800 | 0 | |
801 | 0 | rv = ZW_WriteData(mStream, buf, read); |
802 | 0 | if (NS_FAILED(rv)) { |
803 | 0 | break; |
804 | 0 | } |
805 | 0 | |
806 | 0 | count -= read; |
807 | 0 | } |
808 | 0 | inputStream->Close(); |
809 | 0 | if (NS_FAILED(rv)) { |
810 | 0 | Cleanup(); |
811 | 0 | return rv; |
812 | 0 | } |
813 | 0 | |
814 | 0 | // Update current header |
815 | 0 | rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
816 | 0 | header->mOffset); |
817 | 0 | if (NS_FAILED(rv)) { |
818 | 0 | Cleanup(); |
819 | 0 | return rv; |
820 | 0 | } |
821 | 0 | rv = header->WriteFileHeader(mStream); |
822 | 0 | if (NS_FAILED(rv)) { |
823 | 0 | Cleanup(); |
824 | 0 | return rv; |
825 | 0 | } |
826 | 0 | |
827 | 0 | // Update offset of all other headers |
828 | 0 | int pos = i + 1; |
829 | 0 | while (pos < mHeaders.Count()) { |
830 | 0 | mHeaders[pos]->mOffset += shift; |
831 | 0 | pos++; |
832 | 0 | } |
833 | 0 | mCDSOffset += shift; |
834 | 0 | rv = SeekCDS(); |
835 | 0 | if (NS_FAILED(rv)) { |
836 | 0 | return rv; |
837 | 0 | } |
838 | 0 | mCDSDirty = true; |
839 | 0 | } |
840 | 0 |
|
841 | 0 | return NS_OK; |
842 | 0 | } |
843 | | |
844 | | nsresult nsZipWriter::InternalAddEntryDirectory(const nsACString & aZipEntry, |
845 | | PRTime aModTime, |
846 | | uint32_t aPermissions) |
847 | 0 | { |
848 | 0 | RefPtr<nsZipHeader> header = new nsZipHeader(); |
849 | 0 | NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); |
850 | 0 |
|
851 | 0 | uint32_t zipAttributes = ZIP_ATTRS(aPermissions, ZIP_ATTRS_DIRECTORY); |
852 | 0 |
|
853 | 0 | if (aZipEntry.Last() != '/') { |
854 | 0 | nsCString dirPath; |
855 | 0 | dirPath.Assign(aZipEntry + NS_LITERAL_CSTRING("/")); |
856 | 0 | header->Init(dirPath, aModTime, zipAttributes, mCDSOffset); |
857 | 0 | } |
858 | 0 | else |
859 | 0 | header->Init(aZipEntry, aModTime, zipAttributes, mCDSOffset); |
860 | 0 |
|
861 | 0 | if (mEntryHash.Get(header->mName, nullptr)) |
862 | 0 | return NS_ERROR_FILE_ALREADY_EXISTS; |
863 | 0 | |
864 | 0 | nsresult rv = header->WriteFileHeader(mStream); |
865 | 0 | if (NS_FAILED(rv)) { |
866 | 0 | Cleanup(); |
867 | 0 | return rv; |
868 | 0 | } |
869 | 0 | |
870 | 0 | mCDSDirty = true; |
871 | 0 | mCDSOffset += header->GetFileHeaderLength(); |
872 | 0 | mEntryHash.Put(header->mName, mHeaders.Count()); |
873 | 0 |
|
874 | 0 | if (!mHeaders.AppendObject(header)) { |
875 | 0 | Cleanup(); |
876 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
877 | 0 | } |
878 | 0 | |
879 | 0 | return NS_OK; |
880 | 0 | } |
881 | | |
882 | | /* |
883 | | * Recovering from an error while adding a new entry is simply a case of |
884 | | * seeking back to the CDS. If we fail trying to do that though then cleanup |
885 | | * and bail out. |
886 | | */ |
887 | | nsresult nsZipWriter::SeekCDS() |
888 | 0 | { |
889 | 0 | nsresult rv; |
890 | 0 | nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream, &rv); |
891 | 0 | if (NS_FAILED(rv)) { |
892 | 0 | Cleanup(); |
893 | 0 | return rv; |
894 | 0 | } |
895 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mCDSOffset); |
896 | 0 | if (NS_FAILED(rv)) |
897 | 0 | Cleanup(); |
898 | 0 | return rv; |
899 | 0 | } |
900 | | |
901 | | /* |
902 | | * In a bad error condition this essentially closes down the component as best |
903 | | * it can. |
904 | | */ |
905 | | void nsZipWriter::Cleanup() |
906 | 0 | { |
907 | 0 | mHeaders.Clear(); |
908 | 0 | mEntryHash.Clear(); |
909 | 0 | if (mStream) |
910 | 0 | mStream->Close(); |
911 | 0 | mStream = nullptr; |
912 | 0 | mFile = nullptr; |
913 | 0 | } |
914 | | |
915 | | /* |
916 | | * Called when writing a file to the zip is complete. |
917 | | */ |
918 | | nsresult nsZipWriter::EntryCompleteCallback(nsZipHeader* aHeader, |
919 | | nsresult aStatus) |
920 | 0 | { |
921 | 0 | if (NS_SUCCEEDED(aStatus)) { |
922 | 0 | mEntryHash.Put(aHeader->mName, mHeaders.Count()); |
923 | 0 | if (!mHeaders.AppendObject(aHeader)) { |
924 | 0 | mEntryHash.Remove(aHeader->mName); |
925 | 0 | SeekCDS(); |
926 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
927 | 0 | } |
928 | 0 | mCDSDirty = true; |
929 | 0 | mCDSOffset += aHeader->mCSize + aHeader->GetFileHeaderLength(); |
930 | 0 |
|
931 | 0 | if (mInQueue) |
932 | 0 | BeginProcessingNextItem(); |
933 | 0 |
|
934 | 0 | return NS_OK; |
935 | 0 | } |
936 | 0 |
|
937 | 0 | nsresult rv = SeekCDS(); |
938 | 0 | if (mInQueue) |
939 | 0 | FinishQueue(aStatus); |
940 | 0 | return rv; |
941 | 0 | } |
942 | | |
943 | | inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem, |
944 | | bool* complete) |
945 | 0 | { |
946 | 0 | if (aItem->mFile) { |
947 | 0 | bool exists; |
948 | 0 | nsresult rv = aItem->mFile->Exists(&exists); |
949 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
950 | 0 |
|
951 | 0 | if (!exists) return NS_ERROR_FILE_NOT_FOUND; |
952 | 0 | |
953 | 0 | bool isdir; |
954 | 0 | rv = aItem->mFile->IsDirectory(&isdir); |
955 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
956 | 0 |
|
957 | 0 | rv = aItem->mFile->GetLastModifiedTime(&aItem->mModTime); |
958 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
959 | 0 | aItem->mModTime *= PR_USEC_PER_MSEC; |
960 | 0 |
|
961 | 0 | rv = aItem->mFile->GetPermissions(&aItem->mPermissions); |
962 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
963 | 0 |
|
964 | 0 | if (!isdir) { |
965 | 0 | // Set up for fall through to stream reader |
966 | 0 | rv = NS_NewLocalFileInputStream(getter_AddRefs(aItem->mStream), |
967 | 0 | aItem->mFile); |
968 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
969 | 0 | } |
970 | 0 | // If a dir then this will fall through to the plain dir addition |
971 | 0 | } |
972 | 0 |
|
973 | 0 | uint32_t zipAttributes = ZIP_ATTRS(aItem->mPermissions, ZIP_ATTRS_FILE); |
974 | 0 |
|
975 | 0 | if (aItem->mStream || aItem->mChannel) { |
976 | 0 | RefPtr<nsZipHeader> header = new nsZipHeader(); |
977 | 0 | NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); |
978 | 0 |
|
979 | 0 | header->Init(aItem->mZipEntry, aItem->mModTime, zipAttributes, |
980 | 0 | mCDSOffset); |
981 | 0 | nsresult rv = header->WriteFileHeader(mStream); |
982 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
983 | 0 |
|
984 | 0 | RefPtr<nsZipDataStream> stream = new nsZipDataStream(); |
985 | 0 | NS_ENSURE_TRUE(stream, NS_ERROR_OUT_OF_MEMORY); |
986 | 0 | rv = stream->Init(this, mStream, header, aItem->mCompression); |
987 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
988 | 0 |
|
989 | 0 | if (aItem->mStream) { |
990 | 0 | nsCOMPtr<nsIInputStreamPump> pump; |
991 | 0 | nsCOMPtr<nsIInputStream> tmpStream = aItem->mStream; |
992 | 0 | rv = NS_NewInputStreamPump(getter_AddRefs(pump), |
993 | 0 | tmpStream.forget(), 0, 0, true); |
994 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
995 | 0 |
|
996 | 0 | rv = pump->AsyncRead(stream, nullptr); |
997 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
998 | 0 | } |
999 | 0 | else { |
1000 | 0 | rv = NS_MaybeOpenChannelUsingAsyncOpen2(aItem->mChannel, stream); |
1001 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1002 | 0 | } |
1003 | 0 |
|
1004 | 0 | return NS_OK; |
1005 | 0 | } |
1006 | 0 | |
1007 | 0 | // Must be plain directory addition |
1008 | 0 | *complete = true; |
1009 | 0 | return InternalAddEntryDirectory(aItem->mZipEntry, aItem->mModTime, |
1010 | 0 | aItem->mPermissions); |
1011 | 0 | } |
1012 | | |
1013 | | inline nsresult nsZipWriter::BeginProcessingRemoval(int32_t aPos) |
1014 | 0 | { |
1015 | 0 | // Open the zip file for reading |
1016 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
1017 | 0 | nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), |
1018 | 0 | mFile); |
1019 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1020 | 0 | nsCOMPtr<nsIInputStreamPump> pump; |
1021 | 0 | nsCOMPtr<nsIInputStream> tmpStream = inputStream; |
1022 | 0 | rv = NS_NewInputStreamPump(getter_AddRefs(pump), tmpStream.forget(), 0, 0, |
1023 | 0 | true); |
1024 | 0 | if (NS_FAILED(rv)) { |
1025 | 0 | inputStream->Close(); |
1026 | 0 | return rv; |
1027 | 0 | } |
1028 | 0 | nsCOMPtr<nsIStreamListener> listener; |
1029 | 0 | rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), mStream, this); |
1030 | 0 | if (NS_FAILED(rv)) { |
1031 | 0 | inputStream->Close(); |
1032 | 0 | return rv; |
1033 | 0 | } |
1034 | 0 | |
1035 | 0 | nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream); |
1036 | 0 | rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, |
1037 | 0 | mHeaders[aPos]->mOffset); |
1038 | 0 | if (NS_FAILED(rv)) { |
1039 | 0 | inputStream->Close(); |
1040 | 0 | return rv; |
1041 | 0 | } |
1042 | 0 | |
1043 | 0 | uint32_t shift = (mHeaders[aPos + 1]->mOffset - |
1044 | 0 | mHeaders[aPos]->mOffset); |
1045 | 0 | mCDSOffset -= shift; |
1046 | 0 | int32_t pos2 = aPos + 1; |
1047 | 0 | while (pos2 < mHeaders.Count()) { |
1048 | 0 | mEntryHash.Put(mHeaders[pos2]->mName, pos2 - 1); |
1049 | 0 | mHeaders[pos2]->mOffset -= shift; |
1050 | 0 | pos2++; |
1051 | 0 | } |
1052 | 0 |
|
1053 | 0 | mEntryHash.Remove(mHeaders[aPos]->mName); |
1054 | 0 | mHeaders.RemoveObjectAt(aPos); |
1055 | 0 | mCDSDirty = true; |
1056 | 0 |
|
1057 | 0 | rv = pump->AsyncRead(listener, nullptr); |
1058 | 0 | if (NS_FAILED(rv)) { |
1059 | 0 | inputStream->Close(); |
1060 | 0 | Cleanup(); |
1061 | 0 | return rv; |
1062 | 0 | } |
1063 | 0 | return NS_OK; |
1064 | 0 | } |
1065 | | |
1066 | | /* |
1067 | | * Starts processing on the next item in the queue. |
1068 | | */ |
1069 | | void nsZipWriter::BeginProcessingNextItem() |
1070 | 0 | { |
1071 | 0 | while (!mQueue.IsEmpty()) { |
1072 | 0 |
|
1073 | 0 | nsZipQueueItem next = mQueue[0]; |
1074 | 0 | mQueue.RemoveElementAt(0); |
1075 | 0 |
|
1076 | 0 | if (next.mOperation == OPERATION_REMOVE) { |
1077 | 0 | int32_t pos = -1; |
1078 | 0 | if (mEntryHash.Get(next.mZipEntry, &pos)) { |
1079 | 0 | if (pos < mHeaders.Count() - 1) { |
1080 | 0 | nsresult rv = BeginProcessingRemoval(pos); |
1081 | 0 | if (NS_FAILED(rv)) FinishQueue(rv); |
1082 | 0 | return; |
1083 | 0 | } |
1084 | 0 |
|
1085 | 0 | mCDSOffset = mHeaders[pos]->mOffset; |
1086 | 0 | nsresult rv = SeekCDS(); |
1087 | 0 | if (NS_FAILED(rv)) { |
1088 | 0 | FinishQueue(rv); |
1089 | 0 | return; |
1090 | 0 | } |
1091 | 0 | mEntryHash.Remove(mHeaders[pos]->mName); |
1092 | 0 | mHeaders.RemoveObjectAt(pos); |
1093 | 0 | } |
1094 | 0 | else { |
1095 | 0 | FinishQueue(NS_ERROR_FILE_NOT_FOUND); |
1096 | 0 | return; |
1097 | 0 | } |
1098 | 0 | } |
1099 | 0 | else if (next.mOperation == OPERATION_ADD) { |
1100 | 0 | if (mEntryHash.Get(next.mZipEntry, nullptr)) { |
1101 | 0 | FinishQueue(NS_ERROR_FILE_ALREADY_EXISTS); |
1102 | 0 | return; |
1103 | 0 | } |
1104 | 0 | |
1105 | 0 | bool complete = false; |
1106 | 0 | nsresult rv = BeginProcessingAddition(&next, &complete); |
1107 | 0 | if (NS_FAILED(rv)) { |
1108 | 0 | SeekCDS(); |
1109 | 0 | FinishQueue(rv); |
1110 | 0 | return; |
1111 | 0 | } |
1112 | 0 | if (!complete) |
1113 | 0 | return; |
1114 | 0 | } |
1115 | 0 | } |
1116 | 0 |
|
1117 | 0 | FinishQueue(NS_OK); |
1118 | 0 | } |
1119 | | |
1120 | | /* |
1121 | | * Ends processing with the given status. |
1122 | | */ |
1123 | | void nsZipWriter::FinishQueue(nsresult aStatus) |
1124 | 0 | { |
1125 | 0 | nsCOMPtr<nsIRequestObserver> observer = mProcessObserver; |
1126 | 0 | nsCOMPtr<nsISupports> context = mProcessContext; |
1127 | 0 | // Clean up everything first in case the observer decides to queue more |
1128 | 0 | // things |
1129 | 0 | mProcessObserver = nullptr; |
1130 | 0 | mProcessContext = nullptr; |
1131 | 0 | mInQueue = false; |
1132 | 0 |
|
1133 | 0 | if (observer) |
1134 | 0 | observer->OnStopRequest(nullptr, context, aStatus); |
1135 | 0 | } |