diff options
author | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-28 17:33:23 +0000 |
---|---|---|
committer | mark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-28 17:33:23 +0000 |
commit | e01d28171595bd723e6fd9846a7783c037d882f1 (patch) | |
tree | fbdd334093ec9c297552615f68b7a3b6f333b409 /chrome/browser/mac | |
parent | fcad494579ca15abbb60a4c45ce068206b049299 (diff) | |
download | chromium_src-e01d28171595bd723e6fd9846a7783c037d882f1.zip chromium_src-e01d28171595bd723e6fd9846a7783c037d882f1.tar.gz chromium_src-e01d28171595bd723e6fd9846a7783c037d882f1.tar.bz2 |
Install from disk image: eject original disk image volume and trash disk image
file after installation.
BUG=87622
TEST=Install from disk image. Remove /Applications/Google Chrome.app (or
/Applications/Google Chrome Canary.app if using the Canary). Launch a
copy on a read-only disk image without dragging it off of the disk image.
At launch, a dialog box asking you to install should appear. Choose to
install. At the completion of installation, Chrome should relaunch from
its installed-on-disk location in /Applications, the disk image should
be unmounted and ejected, and the disk image file should be placed in the
Trash.
Review URL: http://codereview.chromium.org/7275009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90786 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/mac')
-rw-r--r-- | chrome/browser/mac/install_from_dmg.h | 9 | ||||
-rw-r--r-- | chrome/browser/mac/install_from_dmg.mm | 413 | ||||
-rw-r--r-- | chrome/browser/mac/relauncher.cc | 22 | ||||
-rw-r--r-- | chrome/browser/mac/relauncher.h | 21 |
4 files changed, 403 insertions, 62 deletions
diff --git a/chrome/browser/mac/install_from_dmg.h b/chrome/browser/mac/install_from_dmg.h index 34d66d5..449a785 100644 --- a/chrome/browser/mac/install_from_dmg.h +++ b/chrome/browser/mac/install_from_dmg.h @@ -6,10 +6,19 @@ #define CHROME_BROWSER_MAC_INSTALL_FROM_DMG_H_ #pragma once +#include <string> + // If the application is running from a read-only disk image, prompts the user // to install it to the hard drive. If the user approves, the application // will be installed and launched, and MaybeInstallFromDiskImage will return // true. In that case, the caller must exit expeditiously. bool MaybeInstallFromDiskImage(); +// Given a BSD device name of the form "diskN" or "diskNsM" as used by IOKit, +// where the device name corresponds to a disk image, unmounts all filesystems +// on that disk image ("diskN", even if "diskNsM" was supplied), "ejects" the +// disk image from the system, and places the disk image file into the Trash. +// If at any step an error occurs, further processing is aborted. +void EjectAndTrashDiskImage(const std::string& dmg_bsd_device_name); + #endif // CHROME_BROWSER_MAC_INSTALL_FROM_DMG_H_ diff --git a/chrome/browser/mac/install_from_dmg.mm b/chrome/browser/mac/install_from_dmg.mm index 356171b..c7474c9 100644 --- a/chrome/browser/mac/install_from_dmg.mm +++ b/chrome/browser/mac/install_from_dmg.mm @@ -8,17 +8,23 @@ #import <AppKit/AppKit.h> #include <CoreFoundation/CoreFoundation.h> #include <CoreServices/CoreServices.h> +#include <DiskArbitration/DiskArbitration.h> #include <IOKit/IOKitLib.h> +#include <stdlib.h> #include <string.h> #include <sys/param.h> #include <sys/mount.h> +#include "base/auto_reset.h" #include "base/basictypes.h" #include "base/command_line.h" #include "base/file_path.h" #include "base/logging.h" #import "base/mac/mac_util.h" +#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsautorelease_pool.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" #include "chrome/browser/mac/authorization_util.h" #include "chrome/browser/mac/scoped_authorizationref.h" #include "chrome/browser/mac/scoped_ioobject.h" @@ -41,9 +47,117 @@ namespace { +// Given an io_service_t (expected to be of class IOMedia), walks the ancestor +// chain, returning the closest ancestor that implements class IOHDIXHDDrive, +// if any. If no such ancestor is found, returns NULL. Following the "copy" +// rule, the caller assumes ownership of the returned value. +// +// Note that this looks for a class that inherits from IOHDIXHDDrive, but it +// will not likely find a concrete IOHDIXHDDrive. It will be +// IOHDIXHDDriveOutKernel for disk images mounted "out-of-kernel" or +// IOHDIXHDDriveInKernel for disk images mounted "in-kernel." Out-of-kernel is +// the default as of Mac OS X 10.5. See the documentation for "hdiutil attach +// -kernel" for more information. +io_service_t CopyHDIXDriveServiceForMedia(io_service_t media) { + const char disk_image_class[] = "IOHDIXHDDrive"; + + // This is highly unlikely. media as passed in is expected to be of class + // IOMedia. Since the media service's entire ancestor chain will be checked, + // though, check it as well. + if (IOObjectConformsTo(media, disk_image_class)) { + IOObjectRetain(media); + return media; + } + + io_iterator_t iterator_ref; + kern_return_t kr = + IORegistryEntryCreateIterator(media, + kIOServicePlane, + kIORegistryIterateRecursively | + kIORegistryIterateParents, + &iterator_ref); + if (kr != KERN_SUCCESS) { + LOG(ERROR) << "IORegistryEntryCreateIterator: " << kr; + return NULL; + } + ScopedIOObject<io_iterator_t> iterator(iterator_ref); + iterator_ref = NULL; + + // Look at each of the ancestor services, beginning with the parent, + // iterating all the way up to the device tree's root. If any ancestor + // service matches the class used for disk images, the media resides on a + // disk image, and the disk image file's path can be determined by examining + // the image-path property. + for (ScopedIOObject<io_service_t> ancestor(IOIteratorNext(iterator)); + ancestor; + ancestor.reset(IOIteratorNext(iterator))) { + if (IOObjectConformsTo(ancestor, disk_image_class)) { + return ancestor.release(); + } + } + + // The media does not reside on a disk image. + return NULL; +} + +// Given an io_service_t (expected to be of class IOMedia), determines whether +// that service is on a disk image. If it is, returns true. If image_path is +// present, it will be set to the pathname of the disk image file, encoded in +// filesystem encoding. +bool MediaResidesOnDiskImage(io_service_t media, std::string* image_path) { + if (image_path) { + image_path->clear(); + } + + ScopedIOObject<io_service_t> hdix_drive(CopyHDIXDriveServiceForMedia(media)); + if (!hdix_drive) { + return false; + } + + if (image_path) { + base::mac::ScopedCFTypeRef<CFTypeRef> image_path_cftyperef( + IORegistryEntryCreateCFProperty(hdix_drive, + CFSTR("image-path"), + NULL, + 0)); + if (!image_path_cftyperef) { + LOG(ERROR) << "IORegistryEntryCreateCFProperty"; + return true; + } + if (CFGetTypeID(image_path_cftyperef) != CFDataGetTypeID()) { + base::mac::ScopedCFTypeRef<CFStringRef> observed_type_cf( + CFCopyTypeIDDescription(CFGetTypeID(image_path_cftyperef))); + std::string observed_type; + if (observed_type_cf) { + observed_type.assign(", observed "); + observed_type.append(base::SysCFStringRefToUTF8(observed_type_cf)); + } + LOG(ERROR) << "image-path: expected CFData" << observed_type; + return true; + } + + CFDataRef image_path_data = static_cast<CFDataRef>( + image_path_cftyperef.get()); + CFIndex length = CFDataGetLength(image_path_data); + char* image_path_c = WriteInto(image_path, length + 1); + CFDataGetBytes(image_path_data, + CFRangeMake(0, length), + reinterpret_cast<UInt8*>(image_path_c)); + } + + return true; +} + // Returns true if |path| is located on a read-only filesystem of a disk -// image. Returns false if not, or in the event of an error. -bool IsPathOnReadOnlyDiskImage(const char path[]) { +// image. Returns false if not, or in the event of an error. If +// out_dmg_bsd_device_name is present, it will be set to the BSD device name +// for the disk image's device, in "diskNsM" form. +bool IsPathOnReadOnlyDiskImage(const char path[], + std::string* out_dmg_bsd_device_name) { + if (out_dmg_bsd_device_name) { + out_dmg_bsd_device_name->clear(); + } + struct statfs statfs_buf; if (statfs(path, &statfs_buf) != 0) { PLOG(ERROR) << "statfs " << path; @@ -63,7 +177,10 @@ bool IsPathOnReadOnlyDiskImage(const char path[]) { } // BSD names in IOKit don't include dev_root. - const char* bsd_device_name = statfs_buf.f_mntfromname + dev_root_length; + const char* dmg_bsd_device_name = statfs_buf.f_mntfromname + dev_root_length; + if (out_dmg_bsd_device_name) { + out_dmg_bsd_device_name->assign(dmg_bsd_device_name); + } const mach_port_t master_port = kIOMasterPortDefault; @@ -71,9 +188,9 @@ bool IsPathOnReadOnlyDiskImage(const char path[]) { // IOServiceGetMatchingServices will assume that reference. CFMutableDictionaryRef match_dict = IOBSDNameMatching(master_port, 0, - bsd_device_name); + dmg_bsd_device_name); if (!match_dict) { - LOG(ERROR) << "IOBSDNameMatching " << bsd_device_name; + LOG(ERROR) << "IOBSDNameMatching " << dmg_bsd_device_name; return false; } @@ -82,70 +199,37 @@ bool IsPathOnReadOnlyDiskImage(const char path[]) { match_dict, &iterator_ref); if (kr != KERN_SUCCESS) { - LOG(ERROR) << "IOServiceGetMatchingServices " << bsd_device_name - << ": kernel error " << kr; + LOG(ERROR) << "IOServiceGetMatchingServices: " << kr; return false; } ScopedIOObject<io_iterator_t> iterator(iterator_ref); iterator_ref = NULL; // There needs to be exactly one matching service. - ScopedIOObject<io_service_t> filesystem_service(IOIteratorNext(iterator)); - if (!filesystem_service) { - LOG(ERROR) << "IOIteratorNext " << bsd_device_name << ": no service"; + ScopedIOObject<io_service_t> media(IOIteratorNext(iterator)); + if (!media) { + LOG(ERROR) << "IOIteratorNext: no service"; return false; } ScopedIOObject<io_service_t> unexpected_service(IOIteratorNext(iterator)); if (unexpected_service) { - LOG(ERROR) << "IOIteratorNext " << bsd_device_name << ": too many services"; + LOG(ERROR) << "IOIteratorNext: too many services"; return false; } iterator.reset(); - const char disk_image_class[] = "IOHDIXController"; - - // This is highly unlikely. The filesystem service is expected to be of - // class IOMedia. Since the filesystem service's entire ancestor chain - // will be checked, though, check the filesystem service's class itself. - if (IOObjectConformsTo(filesystem_service, disk_image_class)) { - return true; - } - - kr = IORegistryEntryCreateIterator(filesystem_service, - kIOServicePlane, - kIORegistryIterateRecursively | - kIORegistryIterateParents, - &iterator_ref); - if (kr != KERN_SUCCESS) { - LOG(ERROR) << "IORegistryEntryCreateIterator " << bsd_device_name - << ": kernel error " << kr; - return false; - } - iterator.reset(iterator_ref); - iterator_ref = NULL; - - // Look at each of the filesystem service's ancestor services, beginning - // with the parent, iterating all the way up to the device tree's root. If - // any ancestor service matches the class used for disk images, the - // filesystem resides on a disk image. - for (ScopedIOObject<io_service_t> ancestor_service(IOIteratorNext(iterator)); - ancestor_service; - ancestor_service.reset(IOIteratorNext(iterator))) { - if (IOObjectConformsTo(ancestor_service, disk_image_class)) { - return true; - } - } - - // The filesystem does not reside on a disk image. - return false; + return MediaResidesOnDiskImage(media, NULL); } // Returns true if the application is located on a read-only filesystem of a -// disk image. Returns false if not, or in the event of an error. -bool IsAppRunningFromReadOnlyDiskImage() { +// disk image. Returns false if not, or in the event of an error. If +// dmg_bsd_device_name is present, it will be set to the BSD device name for +// the disk image's device, in "diskNsM" form. +bool IsAppRunningFromReadOnlyDiskImage(std::string* dmg_bsd_device_name) { return IsPathOnReadOnlyDiskImage( - [[[NSBundle mainBundle] bundlePath] fileSystemRepresentation]); + [[[NSBundle mainBundle] bundlePath] fileSystemRepresentation], + dmg_bsd_device_name); } // Shows a dialog asking the user whether or not to install from the disk @@ -259,8 +343,10 @@ bool InstallFromDiskImage(AuthorizationRef authorization_arg, // Launches the application at installed_path. The helper application // contained within install_path will be used for the relauncher process. This // keeps Launch Services from ever having to see or think about the helper -// application on the disk image. -bool LaunchInstalledApp(NSString* installed_path) { +// application on the disk image. The relauncher process will be asked to +// call EjectAndTrashDiskImage on dmg_bsd_device_name. +bool LaunchInstalledApp(NSString* installed_path, + const std::string& dmg_bsd_device_name) { FilePath browser_path([installed_path fileSystemRepresentation]); FilePath helper_path = browser_path.Append("Contents/Versions"); @@ -271,7 +357,16 @@ bool LaunchInstalledApp(NSString* installed_path) { CommandLine::ForCurrentProcess()->argv(); args[0] = browser_path.value(); - return mac_relauncher::RelaunchAppWithHelper(helper_path.value(), args); + std::vector<std::string> relauncher_args; + if (!dmg_bsd_device_name.empty()) { + std::string dmg_arg(mac_relauncher::kRelauncherDMGDeviceArg); + dmg_arg.append(dmg_bsd_device_name); + relauncher_args.push_back(dmg_arg); + } + + return mac_relauncher::RelaunchAppWithHelper(helper_path.value(), + relauncher_args, + args); } void ShowErrorDialog() { @@ -296,7 +391,8 @@ void ShowErrorDialog() { bool MaybeInstallFromDiskImage() { base::mac::ScopedNSAutoreleasePool autorelease_pool; - if (!IsAppRunningFromReadOnlyDiskImage()) { + std::string dmg_bsd_device_name; + if (!IsAppRunningFromReadOnlyDiskImage(&dmg_bsd_device_name)) { return false; } @@ -353,10 +449,217 @@ bool MaybeInstallFromDiskImage() { installer_path, source_path, target_path) || - !LaunchInstalledApp(target_path)) { + !LaunchInstalledApp(target_path, dmg_bsd_device_name)) { ShowErrorDialog(); return false; } return true; } + +namespace { + +// A simple scoper that calls DASessionScheduleWithRunLoop when created and +// DASessionUnscheduleFromRunLoop when destroyed. +class ScopedDASessionScheduleWithRunLoop { + public: + ScopedDASessionScheduleWithRunLoop(DASessionRef session, + CFRunLoopRef run_loop, + CFStringRef run_loop_mode) + : session_(session), + run_loop_(run_loop), + run_loop_mode_(run_loop_mode) { + DASessionScheduleWithRunLoop(session_, run_loop_, run_loop_mode_); + } + + ~ScopedDASessionScheduleWithRunLoop() { + DASessionUnscheduleFromRunLoop(session_, run_loop_, run_loop_mode_); + } + + private: + DASessionRef session_; + CFRunLoopRef run_loop_; + CFStringRef run_loop_mode_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDASessionScheduleWithRunLoop); +}; + +// A small structure used to ferry data between SynchronousDAOperation and +// SynchronousDACallbackAdapter. +struct SynchronousDACallbackData { + public: + SynchronousDACallbackData() + : callback_called(false), + run_loop_running(false) { + } + + base::mac::ScopedCFTypeRef<DADissenterRef> dissenter; + bool callback_called; + bool run_loop_running; + + private: + DISALLOW_COPY_AND_ASSIGN(SynchronousDACallbackData); +}; + +// The callback target for SynchronousDAOperation. Set the fields in +// SynchronousDACallbackData properly and then stops the run loop so that +// SynchronousDAOperation may proceed. +void SynchronousDACallbackAdapter(DADiskRef disk, + DADissenterRef dissenter, + void* context) { + SynchronousDACallbackData* callback_data = + static_cast<SynchronousDACallbackData*>(context); + callback_data->callback_called = true; + + if (dissenter) { + CFRetain(dissenter); + callback_data->dissenter.reset(dissenter); + } + + // Only stop the run loop if SynchronousDAOperation started it. Don't stop + // anything if this callback was reached synchronously from DADiskUnmount or + // DADiskEject. + if (callback_data->run_loop_running) { + CFRunLoopStop(CFRunLoopGetCurrent()); + } +} + +// Performs a DiskArbitration operation synchronously. After the operation is +// requested by SynchronousDADiskUnmount or SynchronousDADiskEject, those +// functions will call this one to run a run loop for a period of time, +// waiting for the callback to be called. When the callback is called, the +// run loop will be stopped, and this function will examine the result. If +// a dissenter prevented the operation from completing, or if the run loop +// timed out without the callback being called, this function will return +// false. When the callback completes successfully with no dissenters within +// the time allotted, this function returns true. This function requires that +// the DASession being used for the operation being performed has been added +// to the current run loop with DASessionScheduleWithRunLoop. +bool SynchronousDAOperation(const char* name, + SynchronousDACallbackData* callback_data) { + // The callback may already have been called synchronously. In that case, + // avoid spinning the run loop at all. + if (!callback_data->callback_called) { + const CFTimeInterval kOperationTimeoutSeconds = 15; + AutoReset<bool> running_reset(&callback_data->run_loop_running, true); + CFRunLoopRunInMode(kCFRunLoopDefaultMode, kOperationTimeoutSeconds, FALSE); + } + + if (!callback_data->callback_called) { + LOG(ERROR) << name << ": timed out"; + return false; + } else if (callback_data->dissenter) { + CFStringRef status_string_cf = + DADissenterGetStatusString(callback_data->dissenter); + std::string status_string; + if (status_string_cf) { + status_string.assign(" "); + status_string.append(base::SysCFStringRefToUTF8(status_string_cf)); + } + LOG(ERROR) << name << ": dissenter: " + << DADissenterGetStatus(callback_data->dissenter) + << status_string; + return false; + } + + return true; +} + +// Calls DADiskUnmount synchronously, returning the result. +bool SynchronousDADiskUnmount(DADiskRef disk, DADiskUnmountOptions options) { + SynchronousDACallbackData callback_data; + DADiskUnmount(disk, options, SynchronousDACallbackAdapter, &callback_data); + return SynchronousDAOperation("DADiskUnmount", &callback_data); +} + +// Calls DADiskEject synchronously, returning the result. +bool SynchronousDADiskEject(DADiskRef disk, DADiskEjectOptions options) { + SynchronousDACallbackData callback_data; + DADiskEject(disk, options, SynchronousDACallbackAdapter, &callback_data); + return SynchronousDAOperation("DADiskEject", &callback_data); +} + +} // namespace + +void EjectAndTrashDiskImage(const std::string& dmg_bsd_device_name) { + base::mac::ScopedCFTypeRef<DASessionRef> session(DASessionCreate(NULL)); + if (!session.get()) { + LOG(ERROR) << "DASessionCreate"; + return; + } + + base::mac::ScopedCFTypeRef<DADiskRef> disk( + DADiskCreateFromBSDName(NULL, session, dmg_bsd_device_name.c_str())); + if (!disk.get()) { + LOG(ERROR) << "DADiskCreateFromBSDName"; + return; + } + + // dmg_bsd_device_name may only refer to part of the disk: it may be a + // single filesystem on a larger disk. Use the "whole disk" object to + // be able to unmount all mounted filesystems from the disk image, and eject + // the image. This is harmless if dmg_bsd_device_name already referred to a + // "whole disk." + disk.reset(DADiskCopyWholeDisk(disk)); + if (!disk.get()) { + LOG(ERROR) << "DADiskCopyWholeDisk"; + return; + } + + ScopedIOObject<io_service_t> media(DADiskCopyIOMedia(disk)); + if (!media.get()) { + LOG(ERROR) << "DADiskCopyIOMedia"; + return; + } + + // Make sure the device is a disk image, and get the path to its disk image + // file. + std::string disk_image_path; + if (!MediaResidesOnDiskImage(media, &disk_image_path)) { + LOG(ERROR) << "MediaResidesOnDiskImage"; + return; + } + + // SynchronousDADiskUnmount and SynchronousDADiskEject require that the + // session be scheduled with the current run loop. + ScopedDASessionScheduleWithRunLoop session_run_loop(session, + CFRunLoopGetCurrent(), + kCFRunLoopCommonModes); + + if (!SynchronousDADiskUnmount(disk, kDADiskUnmountOptionWhole)) { + LOG(ERROR) << "SynchronousDADiskUnmount"; + return; + } + + if (!SynchronousDADiskEject(disk, kDADiskEjectOptionDefault)) { + LOG(ERROR) << "SynchronousDADiskEject"; + return; + } + + char* disk_image_path_in_trash_c; + OSStatus status = FSPathMoveObjectToTrashSync(disk_image_path.c_str(), + &disk_image_path_in_trash_c, + kFSFileOperationDefaultOptions); + if (status != noErr) { + LOG(ERROR) << "FSPathMoveObjectToTrashSync: " << status; + return; + } + + // FSPathMoveObjectToTrashSync alone doesn't result in the Trash icon in the + // Dock indicating that any garbage has been placed within it. Using the + // trash path that FSPathMoveObjectToTrashSync claims to have used, call + // FNNotifyByPath to fatten up the icon. + FilePath disk_image_path_in_trash(disk_image_path_in_trash_c); + free(disk_image_path_in_trash_c); + + FilePath trash_path = disk_image_path_in_trash.DirName(); + const UInt8* trash_path_u8 = reinterpret_cast<const UInt8*>( + trash_path.value().c_str()); + status = FNNotifyByPath(trash_path_u8, + kFNDirectoryModifiedMessage, + kNilOptions); + if (status != noErr) { + LOG(ERROR) << "FNNotifyByPath: " << status; + return; + } +} diff --git a/chrome/browser/mac/relauncher.cc b/chrome/browser/mac/relauncher.cc index afa99fe..9594403 100644 --- a/chrome/browser/mac/relauncher.cc +++ b/chrome/browser/mac/relauncher.cc @@ -26,6 +26,7 @@ #include "base/process_util.h" #include "base/stringprintf.h" #include "base/sys_string_conversions.h" +#include "chrome/browser/mac/install_from_dmg.h" #include "chrome/common/chrome_switches.h" #include "content/common/content_paths.h" #include "content/common/content_switches.h" @@ -42,6 +43,8 @@ namespace mac_relauncher { +const char* const kRelauncherDMGDeviceArg = "--dmg-device="; + namespace { // The "magic" file descriptor that the relauncher process' write side of the @@ -91,10 +94,12 @@ bool RelaunchApp(const std::vector<std::string>& args) { return false; } - return RelaunchAppWithHelper(child_path.value(), args); + std::vector<std::string> relauncher_args; + return RelaunchAppWithHelper(child_path.value(), relauncher_args, args); } bool RelaunchAppWithHelper(const std::string& helper, + const std::vector<std::string>& relauncher_args, const std::vector<std::string>& args) { std::vector<std::string> relaunch_args; relaunch_args.push_back(helper); @@ -106,6 +111,9 @@ bool RelaunchAppWithHelper(const std::string& helper, relaunch_args.push_back(kRelauncherBackgroundArg); } + relaunch_args.insert(relaunch_args.end(), + relauncher_args.begin(), relauncher_args.end()); + relaunch_args.push_back(kRelauncherArgSeparator); // When using the CommandLine interface, -psn_ may have been rewritten as @@ -295,6 +303,7 @@ int RelauncherMain(const MainFunctionParams& main_parameters) { // start it in the background. bool background = false; bool in_relaunch_args = false; + std::string dmg_bsd_device_name; bool seen_relaunch_executable = false; std::string relaunch_executable; const std::string relauncher_arg_separator(kRelauncherArgSeparator); @@ -311,6 +320,9 @@ int RelauncherMain(const MainFunctionParams& main_parameters) { in_relaunch_args = true; } else if (arg == kRelauncherBackgroundArg) { background = true; + } else if (arg.compare(0, strlen(kRelauncherDMGDeviceArg), + kRelauncherDMGDeviceArg) == 0) { + dmg_bsd_device_name.assign(arg.substr(strlen(kRelauncherDMGDeviceArg))); } } else { if (!seen_relaunch_executable) { @@ -360,6 +372,14 @@ int RelauncherMain(const MainFunctionParams& main_parameters) { return 1; } + // The application should have relaunched (or is in the process of + // relaunching). From this point on, only clean-up tasks should occur, and + // failures are tolerable. + + if (!dmg_bsd_device_name.empty()) { + EjectAndTrashDiskImage(dmg_bsd_device_name); + } + return 0; } diff --git a/chrome/browser/mac/relauncher.h b/chrome/browser/mac/relauncher.h index 3986c07..26ab08b 100644 --- a/chrome/browser/mac/relauncher.h +++ b/chrome/browser/mac/relauncher.h @@ -37,6 +37,12 @@ struct MainFunctionParams; namespace mac_relauncher { +// The relauncher process can unmount and eject a mounted disk image and move +// its disk image file to the trash. This argument may be supplied to +// RelaunchAppWithHelper to achieve this. The argument's value must be a BSD +// device name of the form "diskN" or "diskNsM". +extern const char* const kRelauncherDMGDeviceArg; + // Relaunches the application using the helper application associated with the // currently running instance of Chrome in the parent browser process as the // executable for the relauncher process. |args| is an argv-style vector of @@ -51,13 +57,16 @@ namespace mac_relauncher { bool RelaunchApp(const std::vector<std::string>& args); // Identical to RelaunchApp, but uses |helper| as the path to the relauncher -// process. Unlike args[0], |helper| must be a pathname to an executable file. -// The helper path given must be from the same version of Chrome as the -// running parent browser process, as there are no guarantees that the parent -// and relauncher processes from different versions will be able to -// communicate with one another. This variant can be useful to relaunch the -// same version of Chrome from another location, using that location's helper. +// process, and allows additional arguments to be supplied to the relauncher +// process in relauncher_args. Unlike args[0], |helper| must be a pathname to +// an executable file. The helper path given must be from the same version of +// Chrome as the running parent browser process, as there are no guarantees +// that the parent and relauncher processes from different versions will be +// able to communicate with one another. This variant can be useful to +// relaunch the same version of Chrome from another location, using that +// location's helper. bool RelaunchAppWithHelper(const std::string& helper, + const std::vector<std::string>& relauncher_args, const std::vector<std::string>& args); namespace internal { |