gecko-dev/b2g/components/DirectoryProvider.js

296 lines
10 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
const UPDATE_ARCHIVE_DIR = "UpdArchD"
const LOCAL_DIR = "/data/local";
const UPDATES_DIR = "updates/0";
const FOTA_DIR = "updates/fota";
const COREAPPSDIR_PREF = "b2g.coreappsdir"
XPCOMUtils.defineLazyServiceGetter(Services, "env",
"@mozilla.org/process/environment;1",
"nsIEnvironment");
XPCOMUtils.defineLazyServiceGetter(Services, "um",
"@mozilla.org/updates/update-manager;1",
"nsIUpdateManager");
XPCOMUtils.defineLazyServiceGetter(Services, "volumeService",
"@mozilla.org/telephony/volume-service;1",
"nsIVolumeService");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
XPCOMUtils.defineLazyGetter(this, "gExtStorage", function dp_gExtStorage() {
return Services.env.get("EXTERNAL_STORAGE");
});
// This exists to mark the affected code for bug 828858.
const gUseSDCard = true;
const VERBOSE = 1;
var log =
VERBOSE ?
function log_dump(msg) { dump("DirectoryProvider: " + msg + "\n"); } :
function log_noop(msg) { };
function DirectoryProvider() {
}
DirectoryProvider.prototype = {
classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(DirectoryProvider),
_profD: null,
getFile: function(prop, persistent) {
if (AppConstants.platform === "gonk") {
return this.getFileOnGonk(prop, persistent);
}
return this.getFileNotGonk(prop, persistent);
},
getFileOnGonk: function(prop, persistent) {
let localProps = ["cachePDir", "webappsDir", "PrefD", "indexedDBPDir",
"permissionDBPDir", "UpdRootD"];
if (localProps.indexOf(prop) != -1) {
let file = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile)
file.initWithPath(LOCAL_DIR);
persistent.value = true;
return file;
}
if (prop == "ProfD") {
let dir = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
dir.initWithPath(LOCAL_DIR+"/tests/profile");
if (dir.exists()) {
persistent.value = true;
return dir;
}
}
if (prop == "coreAppsDir") {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
file.initWithPath("/system/b2g");
persistent.value = true;
return file;
}
if (prop == UPDATE_ARCHIVE_DIR) {
// getUpdateDir will set persistent to false since it may toggle between
// /data/local/ and /mnt/sdcard based on free space and/or availability
// of the sdcard.
// before download, check if free space is 2.1 times of update.mar
return this.getUpdateDir(persistent, UPDATES_DIR, 2.1);
}
if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
// getUpdateDir will set persistent to false since it may toggle between
// /data/local/ and /mnt/sdcard based on free space and/or availability
// of the sdcard.
// before apply, check if free space is 1.1 times of update.mar
return this.getUpdateDir(persistent, FOTA_DIR, 1.1);
}
return null;
},
getFileNotGonk: function(prop, persistent) {
// In desktop builds, coreAppsDir is the same as the profile
// directory unless otherwise specified. We just need to get the
// path from the parent, and it is then used to build
// jar:remoteopenfile:// uris.
if (prop == "coreAppsDir") {
let coreAppsDirPref;
try {
coreAppsDirPref = Services.prefs.getCharPref(COREAPPSDIR_PREF);
} catch (e) {
// coreAppsDirPref may not exist if we're on an older version
// of gaia, so just fail silently.
}
let appsDir;
// If pref doesn't exist or isn't set, default to old value
if (!coreAppsDirPref || coreAppsDirPref == "") {
appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
appsDir.append("webapps");
} else {
appsDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
appsDir.initWithPath(coreAppsDirPref);
}
persistent.value = true;
return appsDir;
} else if (prop == "ProfD") {
let inParent = Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
if (inParent) {
// Just bail out to use the default from toolkit.
return null;
}
if (!this._profD) {
this._profD = cpmm.sendSyncMessage("getProfD", {})[0];
}
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(this._profD);
persistent.value = true;
return file;
}
return null;
},
// The VolumeService only exists on the device, and not on desktop
volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
if (!volumePath) {
return false;
}
if (!Services.volumeService) {
return false;
}
let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
return false;
}
let stat = volume.getStats();
if (!stat) {
return false;
}
return requiredSpace <= stat.freeBytes;
},
findUpdateDirWithFreeSpace: function dp_findUpdateDirWithFreeSpace(requiredSpace, subdir) {
if (!Services.volumeService) {
return this.createUpdatesDir(LOCAL_DIR, subdir);
}
let activeUpdate = Services.um.activeUpdate;
if (gUseSDCard) {
if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
let extUpdateDir = this.createUpdatesDir(gExtStorage, subdir);
if (extUpdateDir !== null) {
return extUpdateDir;
}
log("Warning: " + gExtStorage + " has enough free space for update " +
activeUpdate.name + ", but is not writable");
}
}
if (this.volumeHasFreeSpace(LOCAL_DIR, requiredSpace)) {
let localUpdateDir = this.createUpdatesDir(LOCAL_DIR, subdir);
if (localUpdateDir !== null) {
return localUpdateDir;
}
log("Warning: " + LOCAL_DIR + " has enough free space for update " +
activeUpdate.name + ", but is not writable");
}
return null;
},
getUpdateDir: function dp_getUpdateDir(persistent, subdir, multiple) {
let defaultUpdateDir = this.getDefaultUpdateDir();
persistent.value = false;
let activeUpdate = Services.um.activeUpdate;
if (!activeUpdate) {
log("Warning: No active update found, using default update dir: " +
defaultUpdateDir);
return defaultUpdateDir;
}
let selectedPatch = activeUpdate.selectedPatch;
if (!selectedPatch) {
log("Warning: No selected patch, using default update dir: " +
defaultUpdateDir);
return defaultUpdateDir;
}
let requiredSpace = selectedPatch.size * multiple;
let updateDir = this.findUpdateDirWithFreeSpace(requiredSpace, subdir);
if (updateDir) {
return updateDir;
}
// If we've gotten this far, there isn't enough free space to download the patch
// on either external storage or /data/local. All we can do is report the
// error and let upstream code handle it more gracefully.
log("Error: No volume found with " + requiredSpace + " bytes for downloading"+
" update " + activeUpdate.name);
activeUpdate.errorCode = Cr.NS_ERROR_FILE_TOO_BIG;
return null;
},
createUpdatesDir: function dp_createUpdatesDir(root, subdir) {
let dir = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
dir.initWithPath(root);
if (!dir.isWritable()) {
log("Error: " + dir.path + " isn't writable");
return null;
}
dir.appendRelativePath(subdir);
if (dir.exists()) {
if (dir.isDirectory() && dir.isWritable()) {
return dir;
}
// subdir is either a file or isn't writable. In either case we
// can't use it.
log("Error: " + dir.path + " is a file or isn't writable");
return null;
}
// subdir doesn't exist, and the parent is writable, so try to
// create it. This can fail if a file named updates exists.
try {
dir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0770', 8));
} catch (e) {
// The create failed for some reason. We can't use it.
log("Error: " + dir.path + " unable to create directory");
return null;
}
return dir;
},
getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
let path = gExtStorage;
if (!path) {
path = LOCAL_DIR;
}
if (Services.volumeService) {
let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
if (!extVolume) {
path = LOCAL_DIR;
}
}
let dir = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile)
dir.initWithPath(path);
if (!dir.exists() && path != LOCAL_DIR) {
// Fallback to LOCAL_DIR if we didn't fallback earlier
dir.initWithPath(LOCAL_DIR);
if (!dir.exists()) {
throw Cr.NS_ERROR_FILE_NOT_FOUND;
}
}
dir.appendRelativePath("updates");
return dir;
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);