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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const Cc = Components.classes;
6 const Ci = Components.interfaces;
7 const Cu = Components.utils;
8 const Cr = Components.results;
10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
11 Cu.import("resource://gre/modules/Services.jsm");
13 const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
14 const UPDATE_ARCHIVE_DIR = "UpdArchD"
15 const LOCAL_DIR = "/data/local";
16 const UPDATES_DIR = "updates/0";
17 const FOTA_DIR = "updates/fota";
19 XPCOMUtils.defineLazyServiceGetter(Services, "env",
20 "@mozilla.org/process/environment;1",
23 XPCOMUtils.defineLazyServiceGetter(Services, "um",
24 "@mozilla.org/updates/update-manager;1",
27 XPCOMUtils.defineLazyServiceGetter(Services, "volumeService",
28 "@mozilla.org/telephony/volume-service;1",
31 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
32 "@mozilla.org/childprocessmessagemanager;1",
33 "nsISyncMessageSender");
35 XPCOMUtils.defineLazyGetter(this, "gExtStorage", function dp_gExtStorage() {
36 return Services.env.get("EXTERNAL_STORAGE");
39 // This exists to mark the affected code for bug 828858.
40 const gUseSDCard = true;
45 function log_dump(msg) { dump("DirectoryProvider: " + msg + "\n"); } :
46 function log_noop(msg) { };
48 function DirectoryProvider() {
51 DirectoryProvider.prototype = {
52 classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
54 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
55 _xpcom_factory: XPCOMUtils.generateSingletonFactory(DirectoryProvider),
59 getFile: function dp_getFile(prop, persistent) {
60 #ifdef MOZ_WIDGET_GONK
61 let localProps = ["cachePDir", "webappsDir", "PrefD", "indexedDBPDir",
62 "permissionDBPDir", "UpdRootD"];
63 if (localProps.indexOf(prop) != -1) {
64 let file = Cc["@mozilla.org/file/local;1"]
65 .createInstance(Ci.nsILocalFile)
66 file.initWithPath(LOCAL_DIR);
67 persistent.value = true;
70 if (prop == "ProfD") {
71 let dir = Cc["@mozilla.org/file/local;1"]
72 .createInstance(Ci.nsILocalFile);
73 dir.initWithPath(LOCAL_DIR+"/tests/profile");
75 persistent.value = true;
79 if (prop == "coreAppsDir") {
80 let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
81 file.initWithPath("/system/b2g");
82 persistent.value = true;
85 if (prop == UPDATE_ARCHIVE_DIR) {
86 // getUpdateDir will set persistent to false since it may toggle between
87 // /data/local/ and /mnt/sdcard based on free space and/or availability
89 // before download, check if free space is 2.1 times of update.mar
90 return this.getUpdateDir(persistent, UPDATES_DIR, 2.1);
92 if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
93 // getUpdateDir will set persistent to false since it may toggle between
94 // /data/local/ and /mnt/sdcard based on free space and/or availability
96 // before apply, check if free space is 1.1 times of update.mar
97 return this.getUpdateDir(persistent, FOTA_DIR, 1.1);
100 // In desktop builds, coreAppsDir is the same as the profile directory.
101 // We just need to get the path from the parent, and it is then used to
102 // build jar:remoteopenfile:// uris.
103 if (prop == "coreAppsDir") {
104 let appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
105 appsDir.append("webapps");
106 persistent.value = true;
108 } else if (prop == "ProfD") {
109 let inParent = Cc["@mozilla.org/xre/app-info;1"]
110 .getService(Ci.nsIXULRuntime)
111 .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
113 // Just bail out to use the default from toolkit.
117 this._profD = cpmm.sendSyncMessage("getProfD", {})[0];
119 let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
120 file.initWithPath(this._profD);
121 persistent.value = true;
128 // The VolumeService only exists on the device, and not on desktop
129 volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
133 if (!Services.volumeService) {
136 let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
137 if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
140 let stat = volume.getStats();
144 return requiredSpace <= stat.freeBytes;
147 findUpdateDirWithFreeSpace: function dp_findUpdateDirWithFreeSpace(requiredSpace, subdir) {
148 if (!Services.volumeService) {
149 return this.createUpdatesDir(LOCAL_DIR, subdir);
152 let activeUpdate = Services.um.activeUpdate;
154 if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
155 let extUpdateDir = this.createUpdatesDir(gExtStorage, subdir);
156 if (extUpdateDir !== null) {
159 log("Warning: " + gExtStorage + " has enough free space for update " +
160 activeUpdate.name + ", but is not writable");
164 if (this.volumeHasFreeSpace(LOCAL_DIR, requiredSpace)) {
165 let localUpdateDir = this.createUpdatesDir(LOCAL_DIR, subdir);
166 if (localUpdateDir !== null) {
167 return localUpdateDir;
169 log("Warning: " + LOCAL_DIR + " has enough free space for update " +
170 activeUpdate.name + ", but is not writable");
176 getUpdateDir: function dp_getUpdateDir(persistent, subdir, multiple) {
177 let defaultUpdateDir = this.getDefaultUpdateDir();
178 persistent.value = false;
180 let activeUpdate = Services.um.activeUpdate;
182 log("Warning: No active update found, using default update dir: " +
184 return defaultUpdateDir;
187 let selectedPatch = activeUpdate.selectedPatch;
188 if (!selectedPatch) {
189 log("Warning: No selected patch, using default update dir: " +
191 return defaultUpdateDir;
194 let requiredSpace = selectedPatch.size * multiple;
195 let updateDir = this.findUpdateDirWithFreeSpace(requiredSpace, subdir);
200 // If we've gotten this far, there isn't enough free space to download the patch
201 // on either external storage or /data/local. All we can do is report the
202 // error and let upstream code handle it more gracefully.
203 log("Error: No volume found with " + requiredSpace + " bytes for downloading"+
204 " update " + activeUpdate.name);
205 activeUpdate.errorCode = Cr.NS_ERROR_FILE_TOO_BIG;
209 createUpdatesDir: function dp_createUpdatesDir(root, subdir) {
210 let dir = Cc["@mozilla.org/file/local;1"]
211 .createInstance(Ci.nsILocalFile);
212 dir.initWithPath(root);
213 if (!dir.isWritable()) {
214 log("Error: " + dir.path + " isn't writable");
217 dir.appendRelativePath(subdir);
219 if (dir.isDirectory() && dir.isWritable()) {
222 // subdir is either a file or isn't writable. In either case we
224 log("Error: " + dir.path + " is a file or isn't writable");
227 // subdir doesn't exist, and the parent is writable, so try to
228 // create it. This can fail if a file named updates exists.
230 dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770);
232 // The create failed for some reason. We can't use it.
233 log("Error: " + dir.path + " unable to create directory");
239 getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
240 let path = gExtStorage;
245 if (Services.volumeService) {
246 let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
252 let dir = Cc["@mozilla.org/file/local;1"]
253 .createInstance(Ci.nsILocalFile)
254 dir.initWithPath(path);
256 if (!dir.exists() && path != LOCAL_DIR) {
257 // Fallback to LOCAL_DIR if we didn't fallback earlier
258 dir.initWithPath(LOCAL_DIR);
261 throw Cr.NS_ERROR_FILE_NOT_FOUND;
265 dir.appendRelativePath("updates");
270 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);