Bug 785124 - Pt 2 - JS changes to updater to allow storing update.mar to sdcard. r=marshall

From 93958fee051e9355930edba538eabeb91f4b442d Mon Sep 17 00:00:00 2001
 sdcard and lock sdcard while in use
---
 b2g/components/DirectoryProvider.js       |  154 ++++++++++++++++++++++++++---
 b2g/components/UpdatePrompt.js            |   16 ++-
 toolkit/mozapps/update/nsUpdateService.js |  124 ++++++++++++++++++++++-
 3 files changed, 276 insertions(+), 18 deletions(-)
* * *
Fix log stmt
This commit is contained in:
Dave Hylands 2012-12-14 16:05:39 -08:00
Родитель e458463bf7
Коммит a4b95968d1
3 изменённых файлов: 274 добавлений и 18 удалений

Просмотреть файл

@ -11,12 +11,27 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
const UPDATE_ARCHIVE_DIR = "UpdArchD"
const LOCAL_DIR = "/data/local";
XPCOMUtils.defineLazyServiceGetter(Services, "env",
"@mozilla.org/process/environment;1",
"nsIEnvironment");
XPCOMUtils.defineLazyServiceGetter(Services, "volumeService",
"@mozilla.org/telephony/volume-service;1",
"nsIVolumeService");
XPCOMUtils.defineLazyGetter(this, "gExtStorage", function dp_gExtStorage() {
return Services.env.get("EXTERNAL_STORAGE");
});
const VERBOSE = 1;
let log =
VERBOSE ?
function log_dump(msg) { dump("DirectoryProvider: " + msg + "\n"); } :
function log_noop(msg) { };
function DirectoryProvider() {
}
@ -35,36 +50,148 @@ DirectoryProvider.prototype = {
file.initWithPath(LOCAL_DIR);
persistent.value = true;
return file;
} else if (prop == "coreAppsDir") {
}
if (prop == "coreAppsDir") {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
file.initWithPath("/system/b2g");
persistent.value = true;
return file;
} else if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
return this.getOSUpdateApplyToDir(persistent);
}
if (prop == XRE_OS_UPDATE_APPLY_TO_DIR ||
prop == UPDATE_ARCHIVE_DIR) {
let file = this.getUpdateDir(persistent);
return file;
}
#endif
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.getVolumeByPath(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) {
if (!Services.volumeService) {
return this.createUpdatesDir(LOCAL_DIR);
}
let activeUpdate = Services.um.activeUpdate;
if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
let extUpdateDir = this.createUpdatesDir(gExtStorage);
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);
if (localUpdateDir !== null) {
return localUpdateDir;
}
log("Warning: " + LOCAL_DIR + " has enough free space for update " +
activeUpdate.name + ", but is not writable");
}
return null;
},
getOSUpdateApplyToDir: function dp_getOSUpdateApplyToDir(persistent) {
// TODO add logic to check available storage space,
// and iterate through pref(s) to find alternative dirs if
// necessary.
getUpdateDir: function dp_getUpdateDir(persistent) {
let defaultUpdateDir = this.getDefaultUpdateDir();
persistent.value = false;
let path = Services.env.get("EXTERNAL_STORAGE");
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 * 2;
let updateDir = this.findUpdateDirWithFreeSpace(requiredSpace, persistent);
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);
throw Cr.NS_ERROR_FILE_TOO_BIG;
},
createUpdatesDir: function dp_createUpdatesDir(root) {
let dir = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
dir.initWithPath(root);
if (!dir.isWritable()) {
return null;
}
dir.appendRelativePath("updates/0");
if (dir.exists()) {
if (dir.isDirectory() && dir.isWritable()) {
return dir;
}
// updates/0 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;
}
// updates/0 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, 0770);
} 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_PATH;
path = LOCAL_DIR;
}
if (Services.volumeService) {
let extVolume = Services.volumeService.getVolumeByPath(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_PATH) {
// Fallback to LOCAL_PATH if we didn't fallback earlier
dir.initWithPath(LOCAL_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;
@ -72,7 +199,6 @@ DirectoryProvider.prototype = {
}
dir.appendRelativePath("updates");
persistent.value = false;
return dir;
}
};

Просмотреть файл

@ -22,6 +22,9 @@ const PREF_APPLY_PROMPT_TIMEOUT = "b2g.update.apply-prompt-timeout";
const PREF_APPLY_IDLE_TIMEOUT = "b2g.update.apply-idle-timeout";
const NETWORK_ERROR_OFFLINE = 111;
const FILE_ERROR_TOO_BIG = 112;
const STATE_DOWNLOADING = 'downloading';
XPCOMUtils.defineLazyServiceGetter(Services, "aus",
"@mozilla.org/updates/update-service;1",
@ -272,8 +275,17 @@ UpdatePrompt.prototype = {
}
}
Services.aus.downloadUpdate(aUpdate, true);
Services.aus.addDownloadListener(this);
let status = Services.aus.downloadUpdate(aUpdate, true);
if (status == STATE_DOWNLOADING) {
Services.aus.addDownloadListener(this);
return;
}
log("Error downloading update " + aUpdate.name + ": " + aUpdate.errorCode);
if (aUpdate.errorCode == FILE_ERROR_TOO_BIG) {
aUpdate.statusText = "file-too-big";
}
this.showUpdateError(aUpdate);
},
handleDownloadCancel: function UP_handleDownloadCancel() {

Просмотреть файл

@ -74,10 +74,18 @@ const KEY_GRED = "GreD";
#define USE_UPDROOT
#endif
#ifdef MOZ_WIDGET_GONK
#define USE_UPDATE_ARCHIVE_DIR
#endif
#ifdef USE_UPDROOT
const KEY_UPDROOT = "UpdRootD";
#endif
#ifdef USE_UPDATE_ARCHIVE_DIR
const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD"
#endif
#ifdef XP_WIN
#define SKIP_STAGE_UPDATES_TEST
#elifdef MOZ_WIDGET_GONK
@ -99,7 +107,8 @@ const FILE_UPDATE_ARCHIVE = "update.apk";
#else
const FILE_UPDATE_ARCHIVE = "update.mar";
#endif
const FILE_UPDATE_LOG = "update.log"
const FILE_UPDATE_LINK = "update.link";
const FILE_UPDATE_LOG = "update.log";
const FILE_UPDATES_DB = "updates.xml";
const FILE_UPDATE_ACTIVE = "active-update.xml";
const FILE_PERMS_TEST = "update.test";
@ -155,6 +164,7 @@ const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
const NETWORK_ERROR_OFFLINE = 111;
const FILE_ERROR_TOO_BIG = 112;
const DOWNLOAD_CHUNK_SIZE = 300000; // bytes
const DOWNLOAD_BACKGROUND_INTERVAL = 600; // seconds
@ -174,6 +184,12 @@ const DEFAULT_SOCKET_MAX_ERRORS = 10;
const DEFAULT_UPDATE_RETRY_TIMEOUT = 2000;
var gLocale = null;
#ifdef MOZ_B2G
var gVolumeMountLock = null;
XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
return Services.env.get("EXTERNAL_STORAGE");
});
#endif
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
"resource://gre/modules/UpdateChannel.jsm");
@ -731,6 +747,74 @@ function writeStatusFile(dir, state) {
writeStringToFile(statusFile, state);
}
/**
* Reads the link file from the update.link file in the
* specified directory.
* @param dir
* The dir to look for an update.link file in
* @return The contents of the update.link file.
*/
function readLinkFile(dir) {
var linkFile = dir.clone();
linkFile.append(FILE_UPDATE_LINK);
var link = readStringFromFile(linkFile) || STATE_NONE;
LOG("readLinkFile - link: " + link + ", path: " + linkFile.path);
return status;
}
/**
* Creates a link file, which allows the actual patch to live in
* a directory different from the update directory.
* @param dir
* The patch directory where the update.link file
* should be written.
* @param patchFile
* The fully qualified filename of the patchfile.
*/
function writeLinkFile(dir, patchFile) {
var linkFile = dir.clone();
linkFile.append(FILE_UPDATE_LINK);
writeStringToFile(linkFile, patchFile.path);
#ifdef MOZ_B2G
if (patchFile.path.indexOf(gExtStorage) == 0) {
// The patchfile is being stored on external storage. Try to lock it
// so that it doesn't get shared with the PC while we're downloading
// to it.
acquireSDCardMountLock();
}
#endif
}
/**
* Acquires a VolumeMountLock for the sdcard volume.
*
* This prevents the SDCard from being shared with the PC while
* we're downloading the update.
*/
function acquireSDCardMountLock() {
#ifdef MOZ_B2G
let volsvc = Cc["@mozilla.org/telephony/volume-service;1"].
getService(Ci.nsIVolumeService);
if (volsvc) {
gSDCardMountLock = volsvc.createMountLock("sdcard");
}
#endif
}
/**
* Releases any SDCard mount lock that we might have.
*
* This once again allows the SDCard to be shared with the PC.
*/
function releaseSDCardMountLock() {
#ifdef MOZ_B2G
if (gSDCardMountLock) {
gSDCardMountLock.unlock();
gSDCardMountLock = null;
}
#endif
}
/**
* Determines if the service should be used to attempt an update
* or not. For now this is only when PREF_APP_UPDATE_SERVICE_ENABLED
@ -899,6 +983,7 @@ function cleanUpUpdatesDir(aBackgroundUpdate) {
LOG("cleanUpUpdatesDir - failed to remove file " + f.path);
}
}
releaseSDCardMountLock();
}
/**
@ -2798,6 +2883,8 @@ UpdateManager.prototype = {
var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
createInstance(Ci.nsIUpdatePrompt);
prompter.showUpdateDownloaded(update, true);
} else {
releaseSDCardMountLock();
}
},
@ -3146,6 +3233,7 @@ Downloader.prototype = {
if (this._request && this._request instanceof Ci.nsIRequest) {
this._request.cancel(cancelError);
}
releaseSDCardMountLock();
},
/**
@ -3316,6 +3404,28 @@ Downloader.prototype = {
return this._request != null;
},
/**
* Get the nsIFile to use for downloading the active update's selected patch
*/
_getUpdateArchiveFile: function Downloader__getUpdateArchiveFile() {
var updateArchive;
#ifdef USE_UPDATE_ARCHIVE_DIR
try {
updateArchive = FileUtils.getDir(KEY_UPDATE_ARCHIVE_DIR, [], true);
} catch (e) {
if (e == Cr.NS_ERROR_FILE_TOO_BIG) {
this._update.errorCode = FILE_ERROR_TOO_BIG;
}
return null;
}
#else
updateArchive = getUpdatesDir().clone();
#endif
updateArchive.append(FILE_UPDATE_ARCHIVE);
return updateArchive;
},
/**
* Download and stage the given update.
* @param update
@ -3339,8 +3449,16 @@ Downloader.prototype = {
}
this.isCompleteUpdate = this._patch.type == "complete";
var patchFile = updateDir.clone();
patchFile.append(FILE_UPDATE_ARCHIVE);
var patchFile = this._getUpdateArchiveFile();
if (!patchFile) {
return STATE_NONE;
}
if (patchFile.path.indexOf(updateDir.path) != 0) {
// The patchFile is in a directory which is different from the
// updateDir, create a link file.
writeLinkFile(updateDir, patchFile);
}
var uri = Services.io.newURI(this._patch.URL, null, null);