startupcache/StartupCacheUtils.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "nsCOMPtr.h"
michael@0 6 #include "nsIInputStream.h"
michael@0 7 #include "nsIStringStream.h"
michael@0 8 #include "nsNetUtil.h"
michael@0 9 #include "nsIJARURI.h"
michael@0 10 #include "nsIResProtocolHandler.h"
michael@0 11 #include "nsIChromeRegistry.h"
michael@0 12 #include "nsAutoPtr.h"
michael@0 13 #include "StartupCacheUtils.h"
michael@0 14 #include "mozilla/scache/StartupCache.h"
michael@0 15 #include "mozilla/Omnijar.h"
michael@0 16
michael@0 17 namespace mozilla {
michael@0 18 namespace scache {
michael@0 19
michael@0 20 NS_EXPORT nsresult
michael@0 21 NewObjectInputStreamFromBuffer(char* buffer, uint32_t len,
michael@0 22 nsIObjectInputStream** stream)
michael@0 23 {
michael@0 24 nsCOMPtr<nsIStringInputStream> stringStream
michael@0 25 = do_CreateInstance("@mozilla.org/io/string-input-stream;1");
michael@0 26 nsCOMPtr<nsIObjectInputStream> objectInput
michael@0 27 = do_CreateInstance("@mozilla.org/binaryinputstream;1");
michael@0 28
michael@0 29 stringStream->AdoptData(buffer, len);
michael@0 30 objectInput->SetInputStream(stringStream);
michael@0 31
michael@0 32 objectInput.forget(stream);
michael@0 33 return NS_OK;
michael@0 34 }
michael@0 35
michael@0 36 NS_EXPORT nsresult
michael@0 37 NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream,
michael@0 38 nsIStorageStream** stream,
michael@0 39 bool wantDebugStream)
michael@0 40 {
michael@0 41 nsCOMPtr<nsIStorageStream> storageStream;
michael@0 42
michael@0 43 nsresult rv = NS_NewStorageStream(256, UINT32_MAX, getter_AddRefs(storageStream));
michael@0 44 NS_ENSURE_SUCCESS(rv, rv);
michael@0 45
michael@0 46 nsCOMPtr<nsIObjectOutputStream> objectOutput
michael@0 47 = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
michael@0 48 nsCOMPtr<nsIOutputStream> outputStream
michael@0 49 = do_QueryInterface(storageStream);
michael@0 50
michael@0 51 objectOutput->SetOutputStream(outputStream);
michael@0 52
michael@0 53 #ifdef DEBUG
michael@0 54 if (wantDebugStream) {
michael@0 55 // Wrap in debug stream to detect unsupported writes of
michael@0 56 // multiply-referenced non-singleton objects
michael@0 57 StartupCache* sc = StartupCache::GetSingleton();
michael@0 58 NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED);
michael@0 59 nsCOMPtr<nsIObjectOutputStream> debugStream;
michael@0 60 sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream));
michael@0 61 debugStream.forget(wrapperStream);
michael@0 62 } else {
michael@0 63 objectOutput.forget(wrapperStream);
michael@0 64 }
michael@0 65 #else
michael@0 66 objectOutput.forget(wrapperStream);
michael@0 67 #endif
michael@0 68
michael@0 69 storageStream.forget(stream);
michael@0 70 return NS_OK;
michael@0 71 }
michael@0 72
michael@0 73 NS_EXPORT nsresult
michael@0 74 NewBufferFromStorageStream(nsIStorageStream *storageStream,
michael@0 75 char** buffer, uint32_t* len)
michael@0 76 {
michael@0 77 nsresult rv;
michael@0 78 nsCOMPtr<nsIInputStream> inputStream;
michael@0 79 rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream));
michael@0 80 NS_ENSURE_SUCCESS(rv, rv);
michael@0 81
michael@0 82 uint64_t avail64;
michael@0 83 rv = inputStream->Available(&avail64);
michael@0 84 NS_ENSURE_SUCCESS(rv, rv);
michael@0 85 NS_ENSURE_TRUE(avail64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
michael@0 86
michael@0 87 uint32_t avail = (uint32_t)avail64;
michael@0 88 nsAutoArrayPtr<char> temp (new char[avail]);
michael@0 89 uint32_t read;
michael@0 90 rv = inputStream->Read(temp, avail, &read);
michael@0 91 if (NS_SUCCEEDED(rv) && avail != read)
michael@0 92 rv = NS_ERROR_UNEXPECTED;
michael@0 93
michael@0 94 if (NS_FAILED(rv)) {
michael@0 95 return rv;
michael@0 96 }
michael@0 97
michael@0 98 *len = avail;
michael@0 99 *buffer = temp.forget();
michael@0 100 return NS_OK;
michael@0 101 }
michael@0 102
michael@0 103 static const char baseName[2][5] = { "gre/", "app/" };
michael@0 104
michael@0 105 static inline bool
michael@0 106 canonicalizeBase(nsAutoCString &spec,
michael@0 107 nsACString &out)
michael@0 108 {
michael@0 109 nsAutoCString greBase, appBase;
michael@0 110 nsresult rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greBase);
michael@0 111 if (NS_FAILED(rv) || !greBase.Length())
michael@0 112 return false;
michael@0 113
michael@0 114 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appBase);
michael@0 115 if (NS_FAILED(rv))
michael@0 116 return false;
michael@0 117
michael@0 118 bool underGre = !greBase.Compare(spec.get(), false, greBase.Length());
michael@0 119 bool underApp = appBase.Length() &&
michael@0 120 !appBase.Compare(spec.get(), false, appBase.Length());
michael@0 121
michael@0 122 if (!underGre && !underApp)
michael@0 123 return false;
michael@0 124
michael@0 125 /**
michael@0 126 * At this point, if both underGre and underApp are true, it can be one
michael@0 127 * of the two following cases:
michael@0 128 * - the GRE directory points to a subdirectory of the APP directory,
michael@0 129 * meaning spec points under GRE.
michael@0 130 * - the APP directory points to a subdirectory of the GRE directory,
michael@0 131 * meaning spec points under APP.
michael@0 132 * Checking the GRE and APP path length is enough to know in which case
michael@0 133 * we are.
michael@0 134 */
michael@0 135 if (underGre && underApp && greBase.Length() < appBase.Length())
michael@0 136 underGre = false;
michael@0 137
michael@0 138 out.Append("/resource/");
michael@0 139 out.Append(baseName[underGre ? mozilla::Omnijar::GRE : mozilla::Omnijar::APP]);
michael@0 140 out.Append(Substring(spec, underGre ? greBase.Length() : appBase.Length()));
michael@0 141 return true;
michael@0 142 }
michael@0 143
michael@0 144 /**
michael@0 145 * PathifyURI transforms uris into useful zip paths
michael@0 146 * to make it easier to manipulate startup cache entries
michael@0 147 * using standard zip tools.
michael@0 148 * Transformations applied:
michael@0 149 * * resource:// URIs are resolved to their corresponding file/jar URI to
michael@0 150 * canonicalize resources URIs other than gre and app.
michael@0 151 * * Paths under GRE or APP directory have their base path replaced with
michael@0 152 * resource/gre or resource/app to avoid depending on install location.
michael@0 153 * * jar:file:///path/to/file.jar!/sub/path urls are replaced with
michael@0 154 * /path/to/file.jar/sub/path
michael@0 155 *
michael@0 156 * The result is appended to the string passed in. Adding a prefix before
michael@0 157 * calling is recommended to avoid colliding with other cache users.
michael@0 158 *
michael@0 159 * For example, in the js loader (string is prefixed with jsloader by caller):
michael@0 160 * resource://gre/modules/XPCOMUtils.jsm or
michael@0 161 * file://$GRE_DIR/modules/XPCOMUtils.jsm or
michael@0 162 * jar:file://$GRE_DIR/omni.jar!/modules/XPCOMUtils.jsm becomes
michael@0 163 * jsloader/resource/gre/modules/XPCOMUtils.jsm
michael@0 164 * file://$PROFILE_DIR/extensions/{uuid}/components/component.js becomes
michael@0 165 * jsloader/$PROFILE_DIR/extensions/%7Buuid%7D/components/component.js
michael@0 166 * jar:file://$PROFILE_DIR/extensions/some.xpi!/components/component.js becomes
michael@0 167 * jsloader/$PROFILE_DIR/extensions/some.xpi/components/component.js
michael@0 168 */
michael@0 169 NS_EXPORT nsresult
michael@0 170 PathifyURI(nsIURI *in, nsACString &out)
michael@0 171 {
michael@0 172 bool equals;
michael@0 173 nsresult rv;
michael@0 174 nsCOMPtr<nsIURI> uri = in;
michael@0 175 nsAutoCString spec;
michael@0 176
michael@0 177 // Resolve resource:// URIs. At the end of this if/else block, we
michael@0 178 // have both spec and uri variables identifying the same URI.
michael@0 179 if (NS_SUCCEEDED(in->SchemeIs("resource", &equals)) && equals) {
michael@0 180 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
michael@0 181 NS_ENSURE_SUCCESS(rv, rv);
michael@0 182
michael@0 183 nsCOMPtr<nsIProtocolHandler> ph;
michael@0 184 rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph));
michael@0 185 NS_ENSURE_SUCCESS(rv, rv);
michael@0 186
michael@0 187 nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv));
michael@0 188 NS_ENSURE_SUCCESS(rv, rv);
michael@0 189
michael@0 190 rv = irph->ResolveURI(in, spec);
michael@0 191 NS_ENSURE_SUCCESS(rv, rv);
michael@0 192
michael@0 193 rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri));
michael@0 194 NS_ENSURE_SUCCESS(rv, rv);
michael@0 195 } else {
michael@0 196 if (NS_SUCCEEDED(in->SchemeIs("chrome", &equals)) && equals) {
michael@0 197 nsCOMPtr<nsIChromeRegistry> chromeReg =
michael@0 198 mozilla::services::GetChromeRegistryService();
michael@0 199 if (!chromeReg)
michael@0 200 return NS_ERROR_UNEXPECTED;
michael@0 201
michael@0 202 rv = chromeReg->ConvertChromeURL(in, getter_AddRefs(uri));
michael@0 203 NS_ENSURE_SUCCESS(rv, rv);
michael@0 204 }
michael@0 205
michael@0 206 rv = uri->GetSpec(spec);
michael@0 207 NS_ENSURE_SUCCESS(rv, rv);
michael@0 208 }
michael@0 209
michael@0 210 if (!canonicalizeBase(spec, out)) {
michael@0 211 if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) {
michael@0 212 nsCOMPtr<nsIFileURL> baseFileURL;
michael@0 213 baseFileURL = do_QueryInterface(uri, &rv);
michael@0 214 NS_ENSURE_SUCCESS(rv, rv);
michael@0 215
michael@0 216 nsAutoCString path;
michael@0 217 rv = baseFileURL->GetPath(path);
michael@0 218 NS_ENSURE_SUCCESS(rv, rv);
michael@0 219
michael@0 220 out.Append(path);
michael@0 221 } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) {
michael@0 222 nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
michael@0 223 NS_ENSURE_SUCCESS(rv, rv);
michael@0 224
michael@0 225 nsCOMPtr<nsIURI> jarFileURI;
michael@0 226 rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI));
michael@0 227 NS_ENSURE_SUCCESS(rv, rv);
michael@0 228
michael@0 229 rv = PathifyURI(jarFileURI, out);
michael@0 230 NS_ENSURE_SUCCESS(rv, rv);
michael@0 231
michael@0 232 nsAutoCString path;
michael@0 233 rv = jarURI->GetJAREntry(path);
michael@0 234 NS_ENSURE_SUCCESS(rv, rv);
michael@0 235 out.Append("/");
michael@0 236 out.Append(path);
michael@0 237 } else { // Very unlikely
michael@0 238 nsAutoCString spec;
michael@0 239 rv = uri->GetSpec(spec);
michael@0 240 NS_ENSURE_SUCCESS(rv, rv);
michael@0 241
michael@0 242 out.Append("/");
michael@0 243 out.Append(spec);
michael@0 244 }
michael@0 245 }
michael@0 246 return NS_OK;
michael@0 247 }
michael@0 248
michael@0 249 }
michael@0 250 }

mercurial