b2g/components/DirectoryProvider.js

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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 file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 const Cc = Components.classes;
michael@0 6 const Ci = Components.interfaces;
michael@0 7 const Cu = Components.utils;
michael@0 8 const Cr = Components.results;
michael@0 9
michael@0 10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 11 Cu.import("resource://gre/modules/Services.jsm");
michael@0 12
michael@0 13 const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
michael@0 14 const UPDATE_ARCHIVE_DIR = "UpdArchD"
michael@0 15 const LOCAL_DIR = "/data/local";
michael@0 16 const UPDATES_DIR = "updates/0";
michael@0 17 const FOTA_DIR = "updates/fota";
michael@0 18
michael@0 19 XPCOMUtils.defineLazyServiceGetter(Services, "env",
michael@0 20 "@mozilla.org/process/environment;1",
michael@0 21 "nsIEnvironment");
michael@0 22
michael@0 23 XPCOMUtils.defineLazyServiceGetter(Services, "um",
michael@0 24 "@mozilla.org/updates/update-manager;1",
michael@0 25 "nsIUpdateManager");
michael@0 26
michael@0 27 XPCOMUtils.defineLazyServiceGetter(Services, "volumeService",
michael@0 28 "@mozilla.org/telephony/volume-service;1",
michael@0 29 "nsIVolumeService");
michael@0 30
michael@0 31 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
michael@0 32 "@mozilla.org/childprocessmessagemanager;1",
michael@0 33 "nsISyncMessageSender");
michael@0 34
michael@0 35 XPCOMUtils.defineLazyGetter(this, "gExtStorage", function dp_gExtStorage() {
michael@0 36 return Services.env.get("EXTERNAL_STORAGE");
michael@0 37 });
michael@0 38
michael@0 39 // This exists to mark the affected code for bug 828858.
michael@0 40 const gUseSDCard = true;
michael@0 41
michael@0 42 const VERBOSE = 1;
michael@0 43 let log =
michael@0 44 VERBOSE ?
michael@0 45 function log_dump(msg) { dump("DirectoryProvider: " + msg + "\n"); } :
michael@0 46 function log_noop(msg) { };
michael@0 47
michael@0 48 function DirectoryProvider() {
michael@0 49 }
michael@0 50
michael@0 51 DirectoryProvider.prototype = {
michael@0 52 classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
michael@0 53
michael@0 54 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
michael@0 55 _xpcom_factory: XPCOMUtils.generateSingletonFactory(DirectoryProvider),
michael@0 56
michael@0 57 _profD: null,
michael@0 58
michael@0 59 getFile: function dp_getFile(prop, persistent) {
michael@0 60 #ifdef MOZ_WIDGET_GONK
michael@0 61 let localProps = ["cachePDir", "webappsDir", "PrefD", "indexedDBPDir",
michael@0 62 "permissionDBPDir", "UpdRootD"];
michael@0 63 if (localProps.indexOf(prop) != -1) {
michael@0 64 let file = Cc["@mozilla.org/file/local;1"]
michael@0 65 .createInstance(Ci.nsILocalFile)
michael@0 66 file.initWithPath(LOCAL_DIR);
michael@0 67 persistent.value = true;
michael@0 68 return file;
michael@0 69 }
michael@0 70 if (prop == "ProfD") {
michael@0 71 let dir = Cc["@mozilla.org/file/local;1"]
michael@0 72 .createInstance(Ci.nsILocalFile);
michael@0 73 dir.initWithPath(LOCAL_DIR+"/tests/profile");
michael@0 74 if (dir.exists()) {
michael@0 75 persistent.value = true;
michael@0 76 return dir;
michael@0 77 }
michael@0 78 }
michael@0 79 if (prop == "coreAppsDir") {
michael@0 80 let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
michael@0 81 file.initWithPath("/system/b2g");
michael@0 82 persistent.value = true;
michael@0 83 return file;
michael@0 84 }
michael@0 85 if (prop == UPDATE_ARCHIVE_DIR) {
michael@0 86 // getUpdateDir will set persistent to false since it may toggle between
michael@0 87 // /data/local/ and /mnt/sdcard based on free space and/or availability
michael@0 88 // of the sdcard.
michael@0 89 // before download, check if free space is 2.1 times of update.mar
michael@0 90 return this.getUpdateDir(persistent, UPDATES_DIR, 2.1);
michael@0 91 }
michael@0 92 if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
michael@0 93 // getUpdateDir will set persistent to false since it may toggle between
michael@0 94 // /data/local/ and /mnt/sdcard based on free space and/or availability
michael@0 95 // of the sdcard.
michael@0 96 // before apply, check if free space is 1.1 times of update.mar
michael@0 97 return this.getUpdateDir(persistent, FOTA_DIR, 1.1);
michael@0 98 }
michael@0 99 #else
michael@0 100 // In desktop builds, coreAppsDir is the same as the profile directory.
michael@0 101 // We just need to get the path from the parent, and it is then used to
michael@0 102 // build jar:remoteopenfile:// uris.
michael@0 103 if (prop == "coreAppsDir") {
michael@0 104 let appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
michael@0 105 appsDir.append("webapps");
michael@0 106 persistent.value = true;
michael@0 107 return appsDir;
michael@0 108 } else if (prop == "ProfD") {
michael@0 109 let inParent = Cc["@mozilla.org/xre/app-info;1"]
michael@0 110 .getService(Ci.nsIXULRuntime)
michael@0 111 .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
michael@0 112 if (inParent) {
michael@0 113 // Just bail out to use the default from toolkit.
michael@0 114 return null;
michael@0 115 }
michael@0 116 if (!this._profD) {
michael@0 117 this._profD = cpmm.sendSyncMessage("getProfD", {})[0];
michael@0 118 }
michael@0 119 let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
michael@0 120 file.initWithPath(this._profD);
michael@0 121 persistent.value = true;
michael@0 122 return file;
michael@0 123 }
michael@0 124 #endif
michael@0 125 return null;
michael@0 126 },
michael@0 127
michael@0 128 // The VolumeService only exists on the device, and not on desktop
michael@0 129 volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
michael@0 130 if (!volumePath) {
michael@0 131 return false;
michael@0 132 }
michael@0 133 if (!Services.volumeService) {
michael@0 134 return false;
michael@0 135 }
michael@0 136 let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
michael@0 137 if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
michael@0 138 return false;
michael@0 139 }
michael@0 140 let stat = volume.getStats();
michael@0 141 if (!stat) {
michael@0 142 return false;
michael@0 143 }
michael@0 144 return requiredSpace <= stat.freeBytes;
michael@0 145 },
michael@0 146
michael@0 147 findUpdateDirWithFreeSpace: function dp_findUpdateDirWithFreeSpace(requiredSpace, subdir) {
michael@0 148 if (!Services.volumeService) {
michael@0 149 return this.createUpdatesDir(LOCAL_DIR, subdir);
michael@0 150 }
michael@0 151
michael@0 152 let activeUpdate = Services.um.activeUpdate;
michael@0 153 if (gUseSDCard) {
michael@0 154 if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
michael@0 155 let extUpdateDir = this.createUpdatesDir(gExtStorage, subdir);
michael@0 156 if (extUpdateDir !== null) {
michael@0 157 return extUpdateDir;
michael@0 158 }
michael@0 159 log("Warning: " + gExtStorage + " has enough free space for update " +
michael@0 160 activeUpdate.name + ", but is not writable");
michael@0 161 }
michael@0 162 }
michael@0 163
michael@0 164 if (this.volumeHasFreeSpace(LOCAL_DIR, requiredSpace)) {
michael@0 165 let localUpdateDir = this.createUpdatesDir(LOCAL_DIR, subdir);
michael@0 166 if (localUpdateDir !== null) {
michael@0 167 return localUpdateDir;
michael@0 168 }
michael@0 169 log("Warning: " + LOCAL_DIR + " has enough free space for update " +
michael@0 170 activeUpdate.name + ", but is not writable");
michael@0 171 }
michael@0 172
michael@0 173 return null;
michael@0 174 },
michael@0 175
michael@0 176 getUpdateDir: function dp_getUpdateDir(persistent, subdir, multiple) {
michael@0 177 let defaultUpdateDir = this.getDefaultUpdateDir();
michael@0 178 persistent.value = false;
michael@0 179
michael@0 180 let activeUpdate = Services.um.activeUpdate;
michael@0 181 if (!activeUpdate) {
michael@0 182 log("Warning: No active update found, using default update dir: " +
michael@0 183 defaultUpdateDir);
michael@0 184 return defaultUpdateDir;
michael@0 185 }
michael@0 186
michael@0 187 let selectedPatch = activeUpdate.selectedPatch;
michael@0 188 if (!selectedPatch) {
michael@0 189 log("Warning: No selected patch, using default update dir: " +
michael@0 190 defaultUpdateDir);
michael@0 191 return defaultUpdateDir;
michael@0 192 }
michael@0 193
michael@0 194 let requiredSpace = selectedPatch.size * multiple;
michael@0 195 let updateDir = this.findUpdateDirWithFreeSpace(requiredSpace, subdir);
michael@0 196 if (updateDir) {
michael@0 197 return updateDir;
michael@0 198 }
michael@0 199
michael@0 200 // If we've gotten this far, there isn't enough free space to download the patch
michael@0 201 // on either external storage or /data/local. All we can do is report the
michael@0 202 // error and let upstream code handle it more gracefully.
michael@0 203 log("Error: No volume found with " + requiredSpace + " bytes for downloading"+
michael@0 204 " update " + activeUpdate.name);
michael@0 205 activeUpdate.errorCode = Cr.NS_ERROR_FILE_TOO_BIG;
michael@0 206 return null;
michael@0 207 },
michael@0 208
michael@0 209 createUpdatesDir: function dp_createUpdatesDir(root, subdir) {
michael@0 210 let dir = Cc["@mozilla.org/file/local;1"]
michael@0 211 .createInstance(Ci.nsILocalFile);
michael@0 212 dir.initWithPath(root);
michael@0 213 if (!dir.isWritable()) {
michael@0 214 log("Error: " + dir.path + " isn't writable");
michael@0 215 return null;
michael@0 216 }
michael@0 217 dir.appendRelativePath(subdir);
michael@0 218 if (dir.exists()) {
michael@0 219 if (dir.isDirectory() && dir.isWritable()) {
michael@0 220 return dir;
michael@0 221 }
michael@0 222 // subdir is either a file or isn't writable. In either case we
michael@0 223 // can't use it.
michael@0 224 log("Error: " + dir.path + " is a file or isn't writable");
michael@0 225 return null;
michael@0 226 }
michael@0 227 // subdir doesn't exist, and the parent is writable, so try to
michael@0 228 // create it. This can fail if a file named updates exists.
michael@0 229 try {
michael@0 230 dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770);
michael@0 231 } catch (e) {
michael@0 232 // The create failed for some reason. We can't use it.
michael@0 233 log("Error: " + dir.path + " unable to create directory");
michael@0 234 return null;
michael@0 235 }
michael@0 236 return dir;
michael@0 237 },
michael@0 238
michael@0 239 getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
michael@0 240 let path = gExtStorage;
michael@0 241 if (!path) {
michael@0 242 path = LOCAL_DIR;
michael@0 243 }
michael@0 244
michael@0 245 if (Services.volumeService) {
michael@0 246 let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
michael@0 247 if (!extVolume) {
michael@0 248 path = LOCAL_DIR;
michael@0 249 }
michael@0 250 }
michael@0 251
michael@0 252 let dir = Cc["@mozilla.org/file/local;1"]
michael@0 253 .createInstance(Ci.nsILocalFile)
michael@0 254 dir.initWithPath(path);
michael@0 255
michael@0 256 if (!dir.exists() && path != LOCAL_DIR) {
michael@0 257 // Fallback to LOCAL_DIR if we didn't fallback earlier
michael@0 258 dir.initWithPath(LOCAL_DIR);
michael@0 259
michael@0 260 if (!dir.exists()) {
michael@0 261 throw Cr.NS_ERROR_FILE_NOT_FOUND;
michael@0 262 }
michael@0 263 }
michael@0 264
michael@0 265 dir.appendRelativePath("updates");
michael@0 266 return dir;
michael@0 267 }
michael@0 268 };
michael@0 269
michael@0 270 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);

mercurial