ProductUpgrader: platform specific upgrade directories#1224
Conversation
|
@jamill Thanks for the detailed PR description! It is truly enlightening all the things that are going on here and how we need to change things for Mac. |
37c5dea to
5c744fa
Compare
|
I reviewed the description/design and it looks good to me! Your model works for protecting the Also, there are some places in your PR description where you have |
alameenshah
left a comment
There was a problem hiding this comment.
The approach looks good. Couple of things about Protected Data that can be potentially optimized on the Mac.
Is Protected Data directory really needed on a Mac? Alternate options to consider -
-
The pattern of copying upgrader tool to a temporary directory on Windows exists on the premise that assemblies that are open get locked and an upgrade installer cannot overwrite them (as long as they remain open). By copying upgrader tool to a temp directory and shutting down running instances of gvfs, we ensure that none of the installed gvfs assemblies remain open during upgrade. However, on the Mac, this does not apply - it is possible to overwrite/delete an binary that is open & running. So
/usr/local/vfsforgit/gvfs upgradecan run and upgrade itself. -
Upgrade Download & Unpack (the upgrade payload). Can we utilize the fact that our installers are code signed using Microsoft certificate, to avoid the need for additional protection? On the newest Macs, Apple installer will not let user install an unsigned installer package, unless
-allowUntrustedcommand line option is specified. This should enable us to download the installer payload in an unprotected temp directory.
|
One more thought about using |
|
Thanks for the feedback!
Good question. As you pointed out, locked files are not an issue on Mac, but I was not sure if there could be other problems. For instance, could an operation cause an assembly load after we have overwritten the original application directory, and result in an "unexpected" assembly being found?
Another good point - this will help with the installer(s) themselves, but there could be scripts or other artifacts included with the installer package. @nickgra -
Good point. We had an assumption that the user would not tweak this part of their system. We could include an additional check that the system directories are ACL'ed as we expect, and handle the cases where it isn't (maybe refuse to install?) |
c4549b0 to
4bfd666
Compare
d7f39bd to
cfed1bd
Compare
bc76497 to
b412425
Compare
kewillford
left a comment
There was a problem hiding this comment.
I think we need to come up with the right terminology for these and make sure it is consistent throughout the code base or things get confusing.
| { | ||
| this.Config = upgraderConfig; | ||
|
|
||
| string upgradesDirectoryPath = ProductUpgraderInfo.GetUpgradesDirectoryPath(); |
There was a problem hiding this comment.
TODO: follow up on why we were creating this directory, and see if we still need to create a directory here.
There was a problem hiding this comment.
I'm not sure if something else would have created the directory by the time RecordHighestAvailableVersion runs, but it looks like if the directory did not exist for RecordHighestAvailableVersion the call to WriteAllText in RecordHighestAvailableVersion would throw a DirectoryNotFoundException.
There might be other methods that GetUpgradesDirectoryPath that similarly assume the directory already exists.
c70aaf7 to
a194f05
Compare
wilbaker
left a comment
There was a problem hiding this comment.
Overall I think the changes look good, just left some minor questions/comments
| { | ||
| this.Config = upgraderConfig; | ||
|
|
||
| string upgradesDirectoryPath = ProductUpgraderInfo.GetUpgradesDirectoryPath(); |
There was a problem hiding this comment.
I'm not sure if something else would have created the directory by the time RecordHighestAvailableVersion runs, but it looks like if the directory did not exist for RecordHighestAvailableVersion the call to WriteAllText in RecordHighestAvailableVersion would throw a DirectoryNotFoundException.
There might be other methods that GetUpgradesDirectoryPath that similarly assume the directory already exists.
| ProductUpgraderInfo.DownloadDirectory); | ||
| } | ||
|
|
||
| public static string GetHighestAvailableVersionDirectory() |
There was a problem hiding this comment.
Can/should this method be removed and replaced with calls to GVFSPlatform.Instance.GetUpgradeHighestAvailableVersionDirectory() directly?
| ProductUpgraderInfo.ApplicationDirectory); | ||
| } | ||
|
|
||
| public static string GetParentLogDirectoryPath() |
There was a problem hiding this comment.
Can/should this method be removed and replaced with calls to GVFSPlatform.Instance.GetUpgradeLogDirectoryParentDirectory() directly?
| { | ||
| get | ||
| { | ||
| return Path.Combine(this.Constants.GVFSBinDirectoryPath, LocalGVFSConfig.FileName); |
There was a problem hiding this comment.
Note: I am not an expert on how file permissions work on Mac, so there might be no issues here!
Does sudo care at all about what group a user is in? I'm wondering if two users are modifying /usr/local/vfsforgit/gvfs.config is there any risk that one of them will block the other from being able to make further changes (using sudo)?
Similarly, is there any chance that one user's changes might prevent another users from being able to read the file (without using sudo)?
There was a problem hiding this comment.
Great question(s)!
Root is its own account, so when running a command via sudo, root will be the owner of this file. The mode bits on the file will be such that root is the only account that can modify the file, while it will be readable by everyone. If two different users attempt to modify this file via a sudo command, it will be the root account that is making changes.
/cc @jeffhostetler to check my understanding :)
7950398 to
c299152
Compare
nickgra
left a comment
There was a problem hiding this comment.
This is looking good to me, I agree with Kevin and William on the naming suggestions to ensure that we're using the same terminology throughout the codebase.
kewillford
left a comment
There was a problem hiding this comment.
Looks good to me. Just a couple of naming things.
The config file location currently exists in the same directory as the GVFS service data directory. On Mac, the GVFS Service directory is per user, and not per machine. Even if the service is per user on Mac, we still would like the config to be per machine. This change enables the GVFS config file to exist at a location independent of the GVFS Service data directory. On Windows, the location will not change, but on macOS, it will now exist in the GVFS binary location.
c299152 to
96a51c1
Compare
Different platform have different structures for where the upgrade application and logs need to go. This change introduces the architecture for making this platform specific. Description of different components involved in upgrade and how they interact with different platforms. --- Component | description ----------------------- | ----------- Upgrader Application | Temporary copy of upgrader application upgrade main application gvfs.config | config file for upgrade related settings Upgrade Package Download | Location to download upgrade package Upgrader Package Unpack | Location to unpack the downloaded update package Upgrader Logs | Directory to write upgrade logs to Upgrade Available File | File indicating whether an upgrade is available Component | data scope | Windows | Mac ------------------------ | ------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------ Upgrader Application | Protected | ProgramData\GVFS\GVFS.Upgrade\Tools | /usr/local/vfsforgit_upgrader/Tools gvfs.config | Protected | ProgramData\GVFS\gvfs.config | /usr/local/vfsforgit/gvfs.config Upgrade Package Download | Protected | ProgramData\GVFS\GVFS.Upgrade\Download | /usr/local/vfsforgit_upgrader/Download Upgrader Package Unpack | Protected | ProgramData\GVFS\GVFS.Upgrade\Download\InstallerTemp | /usr/local/vfsforgit_upgrader/Download/InstallerTemp Upgrader Logs | Writable by user account | ProgramData\GVFS\GVFS.Upgrade\UpgraderLogs | ~/Library/Application Support/GVFS/GVFS.Upgrade/UpgraderLogs Upgrade Available File | Written by GVFS.Service process | ProgramData\GVFS\GVFS.Upgrade\Tools\HighestAvailableVersion | ~/Library/Application Support/GVFS/GVFS.Upgrade/HighestAvailableVersion
96a51c1 to
0e5fb4a
Compare
Different platforms have different file system layouts / capabilities. The existing upgrade logic is Windows centric and does not match the behaviors of other systems.
Upgrade components that interact with the fileystem:
Windows: protected
Mac: writable by user account
Data Scopes
This is not generally applicable across platforms, but is a model for how things are set up on Mac.
~/Library/Application Support/GVFS/GVFS.Upgrade
Motivation
On Windows, the upgrade related directories are located under the "service" level directory structure, and is system scoped and requires elevated permissions to modify (except for specific exemptions). The base directory is
ProgramData\GVFS\GVFS.Upgrade. We have to tweak the permissions on the UpgraderLogs directory in order to allow non-elevated access, as non-elevated processes need to be able to write logs here.On Mac, the root of the "service" data directories is a user scoped, and not system scoped. As the upgrade flow uses ACLs to protect the data, we need to find another directory.
This is a proposed change to enable VFS for Git to account for the platform differences.
Mac File System Hierarchy
There are several candidates for where we can place "protected" data, including /usr/local, or /var. The proposed location for the Upgrader app and related data is:
/usr/local/vfsforgit_upgrader, but I could see putting the data (and / or) application in/varas well.