/src/mozilla-central/modules/libjar/nsJAR.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | #include <string.h> |
7 | | #include "nsJARInputStream.h" |
8 | | #include "nsJAR.h" |
9 | | #include "nsIFile.h" |
10 | | #include "nsIConsoleService.h" |
11 | | #include "mozilla/DebugOnly.h" |
12 | | #include "mozilla/Omnijar.h" |
13 | | #include "mozilla/Unused.h" |
14 | | |
15 | | #ifdef XP_UNIX |
16 | | #include <sys/stat.h> |
17 | | #elif defined (XP_WIN) |
18 | | #include <io.h> |
19 | | #endif |
20 | | |
21 | | using namespace mozilla; |
22 | | |
23 | | //---------------------------------------------- |
24 | | // nsJAR constructor/destructor |
25 | | //---------------------------------------------- |
26 | | |
27 | | // The following initialization makes a guess of 10 entries per jarfile. |
28 | | nsJAR::nsJAR(): mZip(new nsZipArchive()), |
29 | | mReleaseTime(PR_INTERVAL_NO_TIMEOUT), |
30 | | mCache(nullptr), |
31 | | mLock("nsJAR::mLock"), |
32 | | mMtime(0), |
33 | | mOpened(false), |
34 | | mIsOmnijar(false) |
35 | 1 | { |
36 | 1 | } |
37 | | |
38 | | nsJAR::~nsJAR() |
39 | 0 | { |
40 | 0 | Close(); |
41 | 0 | } |
42 | | |
43 | | NS_IMPL_QUERY_INTERFACE(nsJAR, nsIZipReader) |
44 | | NS_IMPL_ADDREF(nsJAR) |
45 | | |
46 | | // Custom Release method works with nsZipReaderCache... |
47 | | // Release might be called from multi-thread, we have to |
48 | | // take this function carefully to avoid delete-after-use. |
49 | | MozExternalRefCountType nsJAR::Release(void) |
50 | 10 | { |
51 | 10 | nsrefcnt count; |
52 | 10 | MOZ_ASSERT(0 != mRefCnt, "dup release"); |
53 | 10 | |
54 | 10 | RefPtr<nsZipReaderCache> cache; |
55 | 10 | if (mRefCnt == 2) { // don't use a lock too frequently |
56 | 5 | // Use a mutex here to guarantee mCache is not racing and the target instance |
57 | 5 | // is still valid to increase ref-count. |
58 | 5 | MutexAutoLock lock(mLock); |
59 | 5 | cache = mCache; |
60 | 5 | mCache = nullptr; |
61 | 5 | } |
62 | 10 | if (cache) { |
63 | 1 | DebugOnly<nsresult> rv = cache->ReleaseZip(this); |
64 | 1 | MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to release zip file"); |
65 | 1 | } |
66 | 10 | |
67 | 10 | count = --mRefCnt; // don't access any member variable after this line |
68 | 10 | NS_LOG_RELEASE(this, count, "nsJAR"); |
69 | 10 | if (0 == count) { |
70 | 0 | mRefCnt = 1; /* stabilize */ |
71 | 0 | /* enable this to find non-threadsafe destructors: */ |
72 | 0 | /* NS_ASSERT_OWNINGTHREAD(nsJAR); */ |
73 | 0 | delete this; |
74 | 0 | return 0; |
75 | 0 | } |
76 | 10 | |
77 | 10 | return count; |
78 | 10 | } |
79 | | |
80 | | //---------------------------------------------- |
81 | | // nsIZipReader implementation |
82 | | //---------------------------------------------- |
83 | | |
84 | | NS_IMETHODIMP |
85 | | nsJAR::Open(nsIFile* zipFile) |
86 | 1 | { |
87 | 1 | NS_ENSURE_ARG_POINTER(zipFile); |
88 | 1 | if (mOpened) return NS_ERROR_FAILURE; // Already open! |
89 | 1 | |
90 | 1 | mZipFile = zipFile; |
91 | 1 | mOuterZipEntry.Truncate(); |
92 | 1 | mOpened = true; |
93 | 1 | |
94 | 1 | // The omnijar is special, it is opened early on and closed late |
95 | 1 | // this avoids reopening it |
96 | 1 | RefPtr<nsZipArchive> zip = mozilla::Omnijar::GetReader(zipFile); |
97 | 1 | if (zip) { |
98 | 1 | mZip = zip; |
99 | 1 | mIsOmnijar = true; |
100 | 1 | return NS_OK; |
101 | 1 | } |
102 | 0 | return mZip->OpenArchive(zipFile); |
103 | 0 | } |
104 | | |
105 | | NS_IMETHODIMP |
106 | | nsJAR::OpenInner(nsIZipReader *aZipReader, const nsACString &aZipEntry) |
107 | 0 | { |
108 | 0 | NS_ENSURE_ARG_POINTER(aZipReader); |
109 | 0 | if (mOpened) return NS_ERROR_FAILURE; // Already open! |
110 | 0 | |
111 | 0 | bool exist; |
112 | 0 | nsresult rv = aZipReader->HasEntry(aZipEntry, &exist); |
113 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
114 | 0 | NS_ENSURE_TRUE(exist, NS_ERROR_FILE_NOT_FOUND); |
115 | 0 |
|
116 | 0 | rv = aZipReader->GetFile(getter_AddRefs(mZipFile)); |
117 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
118 | 0 |
|
119 | 0 | mOpened = true; |
120 | 0 |
|
121 | 0 | mOuterZipEntry.Assign(aZipEntry); |
122 | 0 |
|
123 | 0 | RefPtr<nsZipHandle> handle; |
124 | 0 | rv = nsZipHandle::Init(static_cast<nsJAR*>(aZipReader)->mZip.get(), PromiseFlatCString(aZipEntry).get(), |
125 | 0 | getter_AddRefs(handle)); |
126 | 0 | if (NS_FAILED(rv)) |
127 | 0 | return rv; |
128 | 0 | |
129 | 0 | return mZip->OpenArchive(handle); |
130 | 0 | } |
131 | | |
132 | | NS_IMETHODIMP |
133 | | nsJAR::OpenMemory(void* aData, uint32_t aLength) |
134 | 0 | { |
135 | 0 | NS_ENSURE_ARG_POINTER(aData); |
136 | 0 | if (mOpened) return NS_ERROR_FAILURE; // Already open! |
137 | 0 | |
138 | 0 | mOpened = true; |
139 | 0 |
|
140 | 0 | RefPtr<nsZipHandle> handle; |
141 | 0 | nsresult rv = nsZipHandle::Init(static_cast<uint8_t*>(aData), aLength, |
142 | 0 | getter_AddRefs(handle)); |
143 | 0 | if (NS_FAILED(rv)) |
144 | 0 | return rv; |
145 | 0 | |
146 | 0 | return mZip->OpenArchive(handle); |
147 | 0 | } |
148 | | |
149 | | NS_IMETHODIMP |
150 | | nsJAR::GetFile(nsIFile* *result) |
151 | 0 | { |
152 | 0 | *result = mZipFile; |
153 | 0 | NS_IF_ADDREF(*result); |
154 | 0 | return NS_OK; |
155 | 0 | } |
156 | | |
157 | | NS_IMETHODIMP |
158 | | nsJAR::Close() |
159 | 0 | { |
160 | 0 | if (!mOpened) { |
161 | 0 | return NS_ERROR_FAILURE; // Never opened or already closed. |
162 | 0 | } |
163 | 0 | |
164 | 0 | mOpened = false; |
165 | 0 |
|
166 | 0 | if (mIsOmnijar) { |
167 | 0 | // Reset state, but don't close the omnijar because we did not open it. |
168 | 0 | mIsOmnijar = false; |
169 | 0 | mZip = new nsZipArchive(); |
170 | 0 | return NS_OK; |
171 | 0 | } |
172 | 0 | |
173 | 0 | return mZip->CloseArchive(); |
174 | 0 | } |
175 | | |
176 | | NS_IMETHODIMP |
177 | | nsJAR::Test(const nsACString &aEntryName) |
178 | 0 | { |
179 | 0 | return mZip->Test(aEntryName.IsEmpty()? nullptr : PromiseFlatCString(aEntryName).get()); |
180 | 0 | } |
181 | | |
182 | | NS_IMETHODIMP |
183 | | nsJAR::Extract(const nsACString &aEntryName, nsIFile* outFile) |
184 | 0 | { |
185 | 0 | // nsZipArchive and zlib are not thread safe |
186 | 0 | // we need to use a lock to prevent bug #51267 |
187 | 0 | MutexAutoLock lock(mLock); |
188 | 0 |
|
189 | 0 | nsZipItem *item = mZip->GetItem(PromiseFlatCString(aEntryName).get()); |
190 | 0 | NS_ENSURE_TRUE(item, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST); |
191 | 0 |
|
192 | 0 | // Remove existing file or directory so we set permissions correctly. |
193 | 0 | // If it's a directory that already exists and contains files, throw |
194 | 0 | // an exception and return. |
195 | 0 |
|
196 | 0 | nsresult rv = outFile->Remove(false); |
197 | 0 | if (rv == NS_ERROR_FILE_DIR_NOT_EMPTY || |
198 | 0 | rv == NS_ERROR_FAILURE) |
199 | 0 | return rv; |
200 | 0 | |
201 | 0 | if (item->IsDirectory()) |
202 | 0 | { |
203 | 0 | rv = outFile->Create(nsIFile::DIRECTORY_TYPE, item->Mode()); |
204 | 0 | //XXX Do this in nsZipArchive? It would be nice to keep extraction |
205 | 0 | //XXX code completely there, but that would require a way to get a |
206 | 0 | //XXX PRDir from outFile. |
207 | 0 | } |
208 | 0 | else |
209 | 0 | { |
210 | 0 | PRFileDesc* fd; |
211 | 0 | rv = outFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, item->Mode(), &fd); |
212 | 0 | if (NS_FAILED(rv)) return rv; |
213 | 0 | |
214 | 0 | // ExtractFile also closes the fd handle and resolves the symlink if needed |
215 | 0 | rv = mZip->ExtractFile(item, outFile, fd); |
216 | 0 | } |
217 | 0 | if (NS_FAILED(rv)) return rv; |
218 | 0 | |
219 | 0 | // nsIFile needs milliseconds, while prtime is in microseconds. |
220 | 0 | // non-fatal if this fails, ignore errors |
221 | 0 | outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC); |
222 | 0 |
|
223 | 0 | return NS_OK; |
224 | 0 | } |
225 | | |
226 | | NS_IMETHODIMP |
227 | | nsJAR::GetEntry(const nsACString &aEntryName, nsIZipEntry* *result) |
228 | 0 | { |
229 | 0 | nsZipItem* zipItem = mZip->GetItem(PromiseFlatCString(aEntryName).get()); |
230 | 0 | NS_ENSURE_TRUE(zipItem, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST); |
231 | 0 |
|
232 | 0 | nsJARItem* jarItem = new nsJARItem(zipItem); |
233 | 0 |
|
234 | 0 | NS_ADDREF(*result = jarItem); |
235 | 0 | return NS_OK; |
236 | 0 | } |
237 | | |
238 | | NS_IMETHODIMP |
239 | | nsJAR::HasEntry(const nsACString &aEntryName, bool *result) |
240 | 0 | { |
241 | 0 | *result = mZip->GetItem(PromiseFlatCString(aEntryName).get()) != nullptr; |
242 | 0 | return NS_OK; |
243 | 0 | } |
244 | | |
245 | | NS_IMETHODIMP |
246 | | nsJAR::FindEntries(const nsACString &aPattern, nsIUTF8StringEnumerator **result) |
247 | 0 | { |
248 | 0 | NS_ENSURE_ARG_POINTER(result); |
249 | 0 |
|
250 | 0 | nsZipFind *find; |
251 | 0 | nsresult rv = mZip->FindInit(aPattern.IsEmpty()? nullptr : PromiseFlatCString(aPattern).get(), &find); |
252 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
253 | 0 |
|
254 | 0 | nsIUTF8StringEnumerator *zipEnum = new nsJAREnumerator(find); |
255 | 0 |
|
256 | 0 | NS_ADDREF(*result = zipEnum); |
257 | 0 | return NS_OK; |
258 | 0 | } |
259 | | |
260 | | NS_IMETHODIMP |
261 | | nsJAR::GetInputStream(const nsACString &aFilename, nsIInputStream** result) |
262 | 5 | { |
263 | 5 | return GetInputStreamWithSpec(EmptyCString(), aFilename, result); |
264 | 5 | } |
265 | | |
266 | | NS_IMETHODIMP |
267 | | nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec, |
268 | | const nsACString &aEntryName, nsIInputStream** result) |
269 | 5 | { |
270 | 5 | NS_ENSURE_ARG_POINTER(result); |
271 | 5 | |
272 | 5 | // Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case! |
273 | 5 | nsZipItem *item = nullptr; |
274 | 5 | const nsCString& entry = PromiseFlatCString(aEntryName); |
275 | 5 | if (*entry.get()) { |
276 | 5 | // First check if item exists in jar |
277 | 5 | item = mZip->GetItem(entry.get()); |
278 | 5 | if (!item) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; |
279 | 5 | } |
280 | 5 | nsJARInputStream* jis = new nsJARInputStream(); |
281 | 5 | // addref now so we can call InitFile/InitDirectory() |
282 | 5 | NS_ADDREF(*result = jis); |
283 | 5 | |
284 | 5 | nsresult rv = NS_OK; |
285 | 5 | if (!item || item->IsDirectory()) { |
286 | 0 | rv = jis->InitDirectory(this, aJarDirSpec, entry.get()); |
287 | 5 | } else { |
288 | 5 | rv = jis->InitFile(this, item); |
289 | 5 | } |
290 | 5 | if (NS_FAILED(rv)) { |
291 | 0 | NS_RELEASE(*result); |
292 | 0 | } |
293 | 5 | return rv; |
294 | 5 | } |
295 | | |
296 | | nsresult |
297 | | nsJAR::GetJarPath(nsACString& aResult) |
298 | 0 | { |
299 | 0 | NS_ENSURE_ARG_POINTER(mZipFile); |
300 | 0 |
|
301 | 0 | return mZipFile->GetPersistentDescriptor(aResult); |
302 | 0 | } |
303 | | |
304 | | nsresult |
305 | | nsJAR::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc) |
306 | 0 | { |
307 | 0 | if (!aNSPRFileDesc) { |
308 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
309 | 0 | } |
310 | 0 | *aNSPRFileDesc = nullptr; |
311 | 0 |
|
312 | 0 | if (!mZip) { |
313 | 0 | return NS_ERROR_FAILURE; |
314 | 0 | } |
315 | 0 | |
316 | 0 | RefPtr<nsZipHandle> handle = mZip->GetFD(); |
317 | 0 | if (!handle) { |
318 | 0 | return NS_ERROR_FAILURE; |
319 | 0 | } |
320 | 0 | |
321 | 0 | return handle->GetNSPRFileDesc(aNSPRFileDesc); |
322 | 0 | } |
323 | | |
324 | | //---------------------------------------------- |
325 | | // nsJAR private implementation |
326 | | //---------------------------------------------- |
327 | | nsresult |
328 | | nsJAR::LoadEntry(const nsACString& aFilename, nsCString& aBuf) |
329 | 0 | { |
330 | 0 | //-- Get a stream for reading the file |
331 | 0 | nsresult rv; |
332 | 0 | nsCOMPtr<nsIInputStream> manifestStream; |
333 | 0 | rv = GetInputStream(aFilename, getter_AddRefs(manifestStream)); |
334 | 0 | if (NS_FAILED(rv)) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; |
335 | 0 | |
336 | 0 | //-- Read the manifest file into memory |
337 | 0 | char* buf; |
338 | 0 | uint64_t len64; |
339 | 0 | rv = manifestStream->Available(&len64); |
340 | 0 | if (NS_FAILED(rv)) return rv; |
341 | 0 | if (len64 >= UINT32_MAX) { // bug 164695 |
342 | 0 | nsZipArchive::sFileCorruptedReason = "nsJAR: invalid manifest size"; |
343 | 0 | return NS_ERROR_FILE_CORRUPTED; |
344 | 0 | } |
345 | 0 | uint32_t len = (uint32_t)len64; |
346 | 0 | buf = (char*)malloc(len+1); |
347 | 0 | if (!buf) return NS_ERROR_OUT_OF_MEMORY; |
348 | 0 | uint32_t bytesRead; |
349 | 0 | rv = manifestStream->Read(buf, len, &bytesRead); |
350 | 0 | if (bytesRead != len) { |
351 | 0 | nsZipArchive::sFileCorruptedReason = "nsJAR: manifest too small"; |
352 | 0 | rv = NS_ERROR_FILE_CORRUPTED; |
353 | 0 | } |
354 | 0 | if (NS_FAILED(rv)) { |
355 | 0 | free(buf); |
356 | 0 | return rv; |
357 | 0 | } |
358 | 0 | buf[len] = '\0'; //Null-terminate the buffer |
359 | 0 | aBuf.Adopt(buf, len); |
360 | 0 | return NS_OK; |
361 | 0 | } |
362 | | |
363 | | |
364 | | int32_t |
365 | | nsJAR::ReadLine(const char** src) |
366 | 0 | { |
367 | 0 | if (!*src) { |
368 | 0 | return 0; |
369 | 0 | } |
370 | 0 | |
371 | 0 | //--Moves pointer to beginning of next line and returns line length |
372 | 0 | // not including CR/LF. |
373 | 0 | int32_t length; |
374 | 0 | char* eol = PL_strpbrk(*src, "\r\n"); |
375 | 0 |
|
376 | 0 | if (eol == nullptr) // Probably reached end of file before newline |
377 | 0 | { |
378 | 0 | length = strlen(*src); |
379 | 0 | if (length == 0) // immediate end-of-file |
380 | 0 | *src = nullptr; |
381 | 0 | else // some data left on this line |
382 | 0 | *src += length; |
383 | 0 | } |
384 | 0 | else |
385 | 0 | { |
386 | 0 | length = eol - *src; |
387 | 0 | if (eol[0] == '\r' && eol[1] == '\n') // CR LF, so skip 2 |
388 | 0 | *src = eol+2; |
389 | 0 | else // Either CR or LF, so skip 1 |
390 | 0 | *src = eol+1; |
391 | 0 | } |
392 | 0 | return length; |
393 | 0 | } |
394 | | |
395 | | NS_IMPL_ISUPPORTS(nsJAREnumerator, nsIUTF8StringEnumerator, |
396 | | nsIStringEnumerator) |
397 | | |
398 | | //---------------------------------------------- |
399 | | // nsJAREnumerator::HasMore |
400 | | //---------------------------------------------- |
401 | | NS_IMETHODIMP |
402 | | nsJAREnumerator::HasMore(bool* aResult) |
403 | 0 | { |
404 | 0 | // try to get the next element |
405 | 0 | if (!mName) { |
406 | 0 | NS_ASSERTION(mFind, "nsJAREnumerator: Missing zipFind."); |
407 | 0 | nsresult rv = mFind->FindNext( &mName, &mNameLen ); |
408 | 0 | if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) { |
409 | 0 | *aResult = false; // No more matches available |
410 | 0 | return NS_OK; |
411 | 0 | } |
412 | 0 | NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // no error translation |
413 | 0 | } |
414 | 0 |
|
415 | 0 | *aResult = true; |
416 | 0 | return NS_OK; |
417 | 0 | } |
418 | | |
419 | | //---------------------------------------------- |
420 | | // nsJAREnumerator::GetNext |
421 | | //---------------------------------------------- |
422 | | NS_IMETHODIMP |
423 | | nsJAREnumerator::GetNext(nsACString& aResult) |
424 | 0 | { |
425 | 0 | // check if the current item is "stale" |
426 | 0 | if (!mName) { |
427 | 0 | bool bMore; |
428 | 0 | nsresult rv = HasMore(&bMore); |
429 | 0 | if (NS_FAILED(rv) || !bMore) |
430 | 0 | return NS_ERROR_FAILURE; // no error translation |
431 | 0 | } |
432 | 0 | aResult.Assign(mName, mNameLen); |
433 | 0 | mName = 0; // we just gave this one away |
434 | 0 | return NS_OK; |
435 | 0 | } |
436 | | |
437 | | |
438 | | NS_IMPL_ISUPPORTS(nsJARItem, nsIZipEntry) |
439 | | |
440 | | nsJARItem::nsJARItem(nsZipItem* aZipItem) |
441 | | : mSize(aZipItem->Size()), |
442 | | mRealsize(aZipItem->RealSize()), |
443 | | mCrc32(aZipItem->CRC32()), |
444 | | mLastModTime(aZipItem->LastModTime()), |
445 | | mCompression(aZipItem->Compression()), |
446 | | mPermissions(aZipItem->Mode()), |
447 | | mIsDirectory(aZipItem->IsDirectory()), |
448 | | mIsSynthetic(aZipItem->isSynthetic) |
449 | 0 | { |
450 | 0 | } |
451 | | |
452 | | //------------------------------------------ |
453 | | // nsJARItem::GetCompression |
454 | | //------------------------------------------ |
455 | | NS_IMETHODIMP |
456 | | nsJARItem::GetCompression(uint16_t *aCompression) |
457 | 0 | { |
458 | 0 | NS_ENSURE_ARG_POINTER(aCompression); |
459 | 0 |
|
460 | 0 | *aCompression = mCompression; |
461 | 0 | return NS_OK; |
462 | 0 | } |
463 | | |
464 | | //------------------------------------------ |
465 | | // nsJARItem::GetSize |
466 | | //------------------------------------------ |
467 | | NS_IMETHODIMP |
468 | | nsJARItem::GetSize(uint32_t *aSize) |
469 | 0 | { |
470 | 0 | NS_ENSURE_ARG_POINTER(aSize); |
471 | 0 |
|
472 | 0 | *aSize = mSize; |
473 | 0 | return NS_OK; |
474 | 0 | } |
475 | | |
476 | | //------------------------------------------ |
477 | | // nsJARItem::GetRealSize |
478 | | //------------------------------------------ |
479 | | NS_IMETHODIMP |
480 | | nsJARItem::GetRealSize(uint32_t *aRealsize) |
481 | 0 | { |
482 | 0 | NS_ENSURE_ARG_POINTER(aRealsize); |
483 | 0 |
|
484 | 0 | *aRealsize = mRealsize; |
485 | 0 | return NS_OK; |
486 | 0 | } |
487 | | |
488 | | //------------------------------------------ |
489 | | // nsJARItem::GetCrc32 |
490 | | //------------------------------------------ |
491 | | NS_IMETHODIMP |
492 | | nsJARItem::GetCRC32(uint32_t *aCrc32) |
493 | 0 | { |
494 | 0 | NS_ENSURE_ARG_POINTER(aCrc32); |
495 | 0 |
|
496 | 0 | *aCrc32 = mCrc32; |
497 | 0 | return NS_OK; |
498 | 0 | } |
499 | | |
500 | | //------------------------------------------ |
501 | | // nsJARItem::GetIsDirectory |
502 | | //------------------------------------------ |
503 | | NS_IMETHODIMP |
504 | | nsJARItem::GetIsDirectory(bool *aIsDirectory) |
505 | 0 | { |
506 | 0 | NS_ENSURE_ARG_POINTER(aIsDirectory); |
507 | 0 |
|
508 | 0 | *aIsDirectory = mIsDirectory; |
509 | 0 | return NS_OK; |
510 | 0 | } |
511 | | |
512 | | //------------------------------------------ |
513 | | // nsJARItem::GetIsSynthetic |
514 | | //------------------------------------------ |
515 | | NS_IMETHODIMP |
516 | | nsJARItem::GetIsSynthetic(bool *aIsSynthetic) |
517 | 0 | { |
518 | 0 | NS_ENSURE_ARG_POINTER(aIsSynthetic); |
519 | 0 |
|
520 | 0 | *aIsSynthetic = mIsSynthetic; |
521 | 0 | return NS_OK; |
522 | 0 | } |
523 | | |
524 | | //------------------------------------------ |
525 | | // nsJARItem::GetLastModifiedTime |
526 | | //------------------------------------------ |
527 | | NS_IMETHODIMP |
528 | | nsJARItem::GetLastModifiedTime(PRTime* aLastModTime) |
529 | 0 | { |
530 | 0 | NS_ENSURE_ARG_POINTER(aLastModTime); |
531 | 0 |
|
532 | 0 | *aLastModTime = mLastModTime; |
533 | 0 | return NS_OK; |
534 | 0 | } |
535 | | |
536 | | //------------------------------------------ |
537 | | // nsJARItem::GetPermissions |
538 | | //------------------------------------------ |
539 | | NS_IMETHODIMP |
540 | | nsJARItem::GetPermissions(uint32_t* aPermissions) |
541 | 0 | { |
542 | 0 | NS_ENSURE_ARG_POINTER(aPermissions); |
543 | 0 |
|
544 | 0 | *aPermissions = mPermissions; |
545 | 0 | return NS_OK; |
546 | 0 | } |
547 | | |
548 | | //////////////////////////////////////////////////////////////////////////////// |
549 | | // nsIZipReaderCache |
550 | | |
551 | | NS_IMPL_ISUPPORTS(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference) |
552 | | |
553 | | nsZipReaderCache::nsZipReaderCache() |
554 | | : mLock("nsZipReaderCache.mLock") |
555 | | , mCacheSize(0) |
556 | | , mZips() |
557 | | #ifdef ZIP_CACHE_HIT_RATE |
558 | | , |
559 | | mZipCacheLookups(0), |
560 | | mZipCacheHits(0), |
561 | | mZipCacheFlushes(0), |
562 | | mZipSyncMisses(0) |
563 | | #endif |
564 | 3 | { |
565 | 3 | } |
566 | | |
567 | | NS_IMETHODIMP |
568 | | nsZipReaderCache::Init(uint32_t cacheSize) |
569 | 3 | { |
570 | 3 | mCacheSize = cacheSize; |
571 | 3 | |
572 | 3 | // Register as a memory pressure observer |
573 | 3 | nsCOMPtr<nsIObserverService> os = |
574 | 3 | do_GetService("@mozilla.org/observer-service;1"); |
575 | 3 | if (os) |
576 | 3 | { |
577 | 3 | os->AddObserver(this, "memory-pressure", true); |
578 | 3 | os->AddObserver(this, "chrome-flush-caches", true); |
579 | 3 | os->AddObserver(this, "flush-cache-entry", true); |
580 | 3 | } |
581 | 3 | // ignore failure of the observer registration. |
582 | 3 | |
583 | 3 | return NS_OK; |
584 | 3 | } |
585 | | |
586 | | nsZipReaderCache::~nsZipReaderCache() |
587 | 0 | { |
588 | 0 | for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) { |
589 | 0 | iter.UserData()->SetZipReaderCache(nullptr); |
590 | 0 | } |
591 | 0 |
|
592 | | #ifdef ZIP_CACHE_HIT_RATE |
593 | | printf("nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed %d\n", |
594 | | mCacheSize, mZipCacheHits, mZipCacheLookups, |
595 | | (float)mZipCacheHits / mZipCacheLookups, |
596 | | mZipCacheFlushes, mZipSyncMisses); |
597 | | #endif |
598 | | } |
599 | | |
600 | | NS_IMETHODIMP |
601 | | nsZipReaderCache::IsCached(nsIFile* zipFile, bool* aResult) |
602 | 0 | { |
603 | 0 | NS_ENSURE_ARG_POINTER(zipFile); |
604 | 0 | nsresult rv; |
605 | 0 | MutexAutoLock lock(mLock); |
606 | 0 |
|
607 | 0 | nsAutoCString uri; |
608 | 0 | rv = zipFile->GetPersistentDescriptor(uri); |
609 | 0 | if (NS_FAILED(rv)) |
610 | 0 | return rv; |
611 | 0 | |
612 | 0 | uri.InsertLiteral("file:", 0); |
613 | 0 |
|
614 | 0 | *aResult = mZips.Contains(uri); |
615 | 0 | return NS_OK; |
616 | 0 | } |
617 | | |
618 | | nsresult |
619 | | nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result, |
620 | | bool failOnMiss) |
621 | 5 | { |
622 | 5 | NS_ENSURE_ARG_POINTER(zipFile); |
623 | 5 | nsresult rv; |
624 | 5 | MutexAutoLock lock(mLock); |
625 | 5 | |
626 | | #ifdef ZIP_CACHE_HIT_RATE |
627 | | mZipCacheLookups++; |
628 | | #endif |
629 | | |
630 | 5 | nsAutoCString uri; |
631 | 5 | rv = zipFile->GetPersistentDescriptor(uri); |
632 | 5 | if (NS_FAILED(rv)) return rv; |
633 | 5 | |
634 | 5 | uri.InsertLiteral("file:", 0); |
635 | 5 | |
636 | 5 | RefPtr<nsJAR> zip; |
637 | 5 | mZips.Get(uri, getter_AddRefs(zip)); |
638 | 5 | if (zip) { |
639 | | #ifdef ZIP_CACHE_HIT_RATE |
640 | | mZipCacheHits++; |
641 | | #endif |
642 | | zip->ClearReleaseTime(); |
643 | 4 | } else { |
644 | 1 | if (failOnMiss) { |
645 | 0 | return NS_ERROR_CACHE_KEY_NOT_FOUND; |
646 | 0 | } |
647 | 1 | |
648 | 1 | zip = new nsJAR(); |
649 | 1 | zip->SetZipReaderCache(this); |
650 | 1 | rv = zip->Open(zipFile); |
651 | 1 | if (NS_FAILED(rv)) { |
652 | 0 | return rv; |
653 | 0 | } |
654 | 1 | |
655 | 1 | MOZ_ASSERT(!mZips.Contains(uri)); |
656 | 1 | mZips.Put(uri, zip); |
657 | 1 | } |
658 | 5 | zip.forget(result); |
659 | 5 | return rv; |
660 | 5 | } |
661 | | |
662 | | NS_IMETHODIMP |
663 | | nsZipReaderCache::GetZipIfCached(nsIFile* zipFile, nsIZipReader* *result) |
664 | 0 | { |
665 | 0 | return GetZip(zipFile, result, true); |
666 | 0 | } |
667 | | |
668 | | NS_IMETHODIMP |
669 | | nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result) |
670 | 5 | { |
671 | 5 | return GetZip(zipFile, result, false); |
672 | 5 | } |
673 | | |
674 | | NS_IMETHODIMP |
675 | | nsZipReaderCache::GetInnerZip(nsIFile* zipFile, const nsACString &entry, |
676 | | nsIZipReader* *result) |
677 | 0 | { |
678 | 0 | NS_ENSURE_ARG_POINTER(zipFile); |
679 | 0 |
|
680 | 0 | nsCOMPtr<nsIZipReader> outerZipReader; |
681 | 0 | nsresult rv = GetZip(zipFile, getter_AddRefs(outerZipReader)); |
682 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
683 | 0 |
|
684 | 0 | MutexAutoLock lock(mLock); |
685 | 0 |
|
686 | | #ifdef ZIP_CACHE_HIT_RATE |
687 | | mZipCacheLookups++; |
688 | | #endif |
689 | |
|
690 | 0 | nsAutoCString uri; |
691 | 0 | rv = zipFile->GetPersistentDescriptor(uri); |
692 | 0 | if (NS_FAILED(rv)) return rv; |
693 | 0 | |
694 | 0 | uri.InsertLiteral("jar:", 0); |
695 | 0 | uri.AppendLiteral("!/"); |
696 | 0 | uri.Append(entry); |
697 | 0 |
|
698 | 0 | RefPtr<nsJAR> zip; |
699 | 0 | mZips.Get(uri, getter_AddRefs(zip)); |
700 | 0 | if (zip) { |
701 | | #ifdef ZIP_CACHE_HIT_RATE |
702 | | mZipCacheHits++; |
703 | | #endif |
704 | | zip->ClearReleaseTime(); |
705 | 0 | } else { |
706 | 0 | zip = new nsJAR(); |
707 | 0 | zip->SetZipReaderCache(this); |
708 | 0 |
|
709 | 0 | rv = zip->OpenInner(outerZipReader, entry); |
710 | 0 | if (NS_FAILED(rv)) { |
711 | 0 | return rv; |
712 | 0 | } |
713 | 0 | |
714 | 0 | MOZ_ASSERT(!mZips.Contains(uri)); |
715 | 0 | mZips.Put(uri, zip); |
716 | 0 | } |
717 | 0 | zip.forget(result); |
718 | 0 | return rv; |
719 | 0 | } |
720 | | |
721 | | NS_IMETHODIMP |
722 | | nsZipReaderCache::GetFd(nsIFile* zipFile, PRFileDesc** aRetVal) |
723 | 0 | { |
724 | | #if defined(XP_WIN) |
725 | | MOZ_CRASH("Not implemented"); |
726 | | return NS_ERROR_NOT_IMPLEMENTED; |
727 | | #else |
728 | 0 | if (!zipFile) { |
729 | 0 | return NS_ERROR_FAILURE; |
730 | 0 | } |
731 | 0 | |
732 | 0 | nsresult rv; |
733 | 0 | nsAutoCString uri; |
734 | 0 | rv = zipFile->GetPersistentDescriptor(uri); |
735 | 0 | if (NS_FAILED(rv)) { |
736 | 0 | return rv; |
737 | 0 | } |
738 | 0 | uri.InsertLiteral("file:", 0); |
739 | 0 |
|
740 | 0 | MutexAutoLock lock(mLock); |
741 | 0 | RefPtr<nsJAR> zip; |
742 | 0 | mZips.Get(uri, getter_AddRefs(zip)); |
743 | 0 | if (!zip) { |
744 | 0 | return NS_ERROR_FAILURE; |
745 | 0 | } |
746 | 0 | |
747 | 0 | zip->ClearReleaseTime(); |
748 | 0 | rv = zip->GetNSPRFileDesc(aRetVal); |
749 | 0 | // Do this to avoid possible deadlock on mLock with ReleaseZip(). |
750 | 0 | MutexAutoUnlock unlock(mLock); |
751 | 0 | RefPtr<nsJAR> zipTemp = zip.forget(); |
752 | 0 | return rv; |
753 | 0 | #endif /* XP_WIN */ |
754 | 0 | } |
755 | | |
756 | | nsresult |
757 | | nsZipReaderCache::ReleaseZip(nsJAR* zip) |
758 | 1 | { |
759 | 1 | nsresult rv; |
760 | 1 | MutexAutoLock lock(mLock); |
761 | 1 | |
762 | 1 | // It is possible that two thread compete for this zip. The dangerous |
763 | 1 | // case is where one thread Releases the zip and discovers that the ref |
764 | 1 | // count has gone to one. Before it can call this ReleaseZip method |
765 | 1 | // another thread calls our GetZip method. The ref count goes to two. That |
766 | 1 | // second thread then Releases the zip and the ref count goes to one. It |
767 | 1 | // then tries to enter this ReleaseZip method and blocks while the first |
768 | 1 | // thread is still here. The first thread continues and remove the zip from |
769 | 1 | // the cache and calls its Release method sending the ref count to 0 and |
770 | 1 | // deleting the zip. However, the second thread is still blocked at the |
771 | 1 | // start of ReleaseZip, but the 'zip' param now hold a reference to a |
772 | 1 | // deleted zip! |
773 | 1 | // |
774 | 1 | // So, we are going to try safeguarding here by searching our hashtable while |
775 | 1 | // locked here for the zip. We return fast if it is not found. |
776 | 1 | |
777 | 1 | bool found = false; |
778 | 1 | for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) { |
779 | 1 | if (zip == iter.UserData()) { |
780 | 1 | found = true; |
781 | 1 | break; |
782 | 1 | } |
783 | 1 | } |
784 | 1 | |
785 | 1 | if (!found) { |
786 | | #ifdef ZIP_CACHE_HIT_RATE |
787 | | mZipSyncMisses++; |
788 | | #endif |
789 | | return NS_OK; |
790 | 0 | } |
791 | 1 | |
792 | 1 | zip->SetReleaseTime(); |
793 | 1 | |
794 | 1 | if (mZips.Count() <= mCacheSize) |
795 | 1 | return NS_OK; |
796 | 0 | |
797 | 0 | // Find the oldest zip. |
798 | 0 | nsJAR* oldest = nullptr; |
799 | 0 | for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) { |
800 | 0 | nsJAR* current = iter.UserData(); |
801 | 0 | PRIntervalTime currentReleaseTime = current->GetReleaseTime(); |
802 | 0 | if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) { |
803 | 0 | if (oldest == nullptr || |
804 | 0 | currentReleaseTime < oldest->GetReleaseTime()) { |
805 | 0 | oldest = current; |
806 | 0 | } |
807 | 0 | } |
808 | 0 | } |
809 | 0 |
|
810 | 0 | // Because of the craziness above it is possible that there is no zip that |
811 | 0 | // needs removing. |
812 | 0 | if (!oldest) |
813 | 0 | return NS_OK; |
814 | 0 | |
815 | | #ifdef ZIP_CACHE_HIT_RATE |
816 | | mZipCacheFlushes++; |
817 | | #endif |
818 | | |
819 | 0 | // remove from hashtable |
820 | 0 | nsAutoCString uri; |
821 | 0 | rv = oldest->GetJarPath(uri); |
822 | 0 | if (NS_FAILED(rv)) |
823 | 0 | return rv; |
824 | 0 | |
825 | 0 | if (oldest->mOuterZipEntry.IsEmpty()) { |
826 | 0 | uri.InsertLiteral("file:", 0); |
827 | 0 | } else { |
828 | 0 | uri.InsertLiteral("jar:", 0); |
829 | 0 | uri.AppendLiteral("!/"); |
830 | 0 | uri.Append(oldest->mOuterZipEntry); |
831 | 0 | } |
832 | 0 |
|
833 | 0 | // Retrieving and removing the JAR must be done without an extra AddRef |
834 | 0 | // and Release, or we'll trigger nsJAR::Release's magic refcount 1 case |
835 | 0 | // an extra time and trigger a deadlock. |
836 | 0 | RefPtr<nsJAR> removed; |
837 | 0 | mZips.Remove(uri, getter_AddRefs(removed)); |
838 | 0 | NS_ASSERTION(removed, "botched"); |
839 | 0 | NS_ASSERTION(oldest == removed, "removed wrong entry"); |
840 | 0 |
|
841 | 0 | if (removed) |
842 | 0 | removed->SetZipReaderCache(nullptr); |
843 | 0 |
|
844 | 0 | return NS_OK; |
845 | 0 | } |
846 | | |
847 | | NS_IMETHODIMP |
848 | | nsZipReaderCache::Observe(nsISupports *aSubject, |
849 | | const char *aTopic, |
850 | | const char16_t *aSomeData) |
851 | 0 | { |
852 | 0 | if (strcmp(aTopic, "memory-pressure") == 0) { |
853 | 0 | MutexAutoLock lock(mLock); |
854 | 0 | for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) { |
855 | 0 | RefPtr<nsJAR>& current = iter.Data(); |
856 | 0 | if (current->GetReleaseTime() != PR_INTERVAL_NO_TIMEOUT) { |
857 | 0 | current->SetZipReaderCache(nullptr); |
858 | 0 | iter.Remove(); |
859 | 0 | } |
860 | 0 | } |
861 | 0 | } |
862 | 0 | else if (strcmp(aTopic, "chrome-flush-caches") == 0) { |
863 | 0 | MutexAutoLock lock(mLock); |
864 | 0 | for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) { |
865 | 0 | iter.UserData()->SetZipReaderCache(nullptr); |
866 | 0 | } |
867 | 0 | mZips.Clear(); |
868 | 0 | } |
869 | 0 | else if (strcmp(aTopic, "flush-cache-entry") == 0) { |
870 | 0 | nsCOMPtr<nsIFile> file; |
871 | 0 | if (aSubject) { |
872 | 0 | file = do_QueryInterface(aSubject); |
873 | 0 | } else if (aSomeData) { |
874 | 0 | nsDependentString fileName(aSomeData); |
875 | 0 | Unused << NS_NewLocalFile(fileName, false, getter_AddRefs(file)); |
876 | 0 | } |
877 | 0 |
|
878 | 0 | if (!file) |
879 | 0 | return NS_OK; |
880 | 0 | |
881 | 0 | nsAutoCString uri; |
882 | 0 | if (NS_FAILED(file->GetPersistentDescriptor(uri))) |
883 | 0 | return NS_OK; |
884 | 0 | |
885 | 0 | uri.InsertLiteral("file:", 0); |
886 | 0 |
|
887 | 0 | MutexAutoLock lock(mLock); |
888 | 0 |
|
889 | 0 | RefPtr<nsJAR> zip; |
890 | 0 | mZips.Get(uri, getter_AddRefs(zip)); |
891 | 0 | if (!zip) |
892 | 0 | return NS_OK; |
893 | 0 | |
894 | | #ifdef ZIP_CACHE_HIT_RATE |
895 | | mZipCacheFlushes++; |
896 | | #endif |
897 | | |
898 | 0 | zip->SetZipReaderCache(nullptr); |
899 | 0 |
|
900 | 0 | mZips.Remove(uri); |
901 | 0 | } |
902 | 0 | return NS_OK; |
903 | 0 | } |
904 | | |
905 | | //////////////////////////////////////////////////////////////////////////////// |