Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 5 | #include "cros-disks/archive_manager.h" |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 6 | |
Ben Chan | 19c112c | 2011-08-24 15:17:22 -0700 | [diff] [blame] | 7 | #include <linux/capability.h> |
| 8 | |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 9 | #include <base/files/file_path.h> |
Ben Chan | cd8fda4 | 2014-09-05 08:21:06 -0700 | [diff] [blame^] | 10 | #include <base/files/file_util.h> |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 11 | #include <base/logging.h> |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 12 | #include <base/strings/string_util.h> |
| 13 | #include <base/strings/stringprintf.h> |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 14 | #include <chromeos/cryptohome.h> |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 15 | |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 16 | #include "cros-disks/metrics.h" |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 17 | #include "cros-disks/mount_info.h" |
| 18 | #include "cros-disks/mount_options.h" |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 19 | #include "cros-disks/platform.h" |
Ben Chan | 5ccd9fe | 2013-11-13 18:28:27 -0800 | [diff] [blame] | 20 | #include "cros-disks/sandboxed_process.h" |
| 21 | #include "cros-disks/system_mounter.h" |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 22 | |
Ben Chan | 2e8aa6f | 2013-02-15 11:40:04 -0800 | [diff] [blame] | 23 | using base::FilePath; |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 24 | using std::map; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 25 | using std::string; |
| 26 | using std::vector; |
| 27 | |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 28 | namespace { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 29 | |
| 30 | // Mapping from a base path to its corresponding path inside the AVFS mount. |
| 31 | struct AVFSPathMapping { |
| 32 | const char* const base_path; |
| 33 | const char* const avfs_path; |
| 34 | }; |
| 35 | |
Ben Chan | 19c112c | 2011-08-24 15:17:22 -0700 | [diff] [blame] | 36 | // Process capabilities required by the avfsd process: |
| 37 | // CAP_SYS_ADMIN for mounting/unmounting filesystem |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 38 | const uint64_t kAVFSMountProgramCapabilities = 1 << CAP_SYS_ADMIN; |
Ben Chan | 19c112c | 2011-08-24 15:17:22 -0700 | [diff] [blame] | 39 | |
Ben Chan | cf06ac8 | 2011-10-20 16:42:21 -0700 | [diff] [blame] | 40 | const char kAVFSMountGroup[] = "chronos-access"; |
| 41 | const char kAVFSMountUser[] = "avfs"; |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 42 | // TODO(wad,benchan): Revisit the location of policy files once more system |
| 43 | // daemons are sandboxed with seccomp filters. |
| 44 | const char kAVFSSeccompFilterPolicyFile[] = |
| 45 | "/opt/google/cros-disks/avfsd-seccomp.policy"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 46 | const char kAVFSMountProgram[] = "/usr/bin/avfsd"; |
Ben Chan | 7f8dcb6 | 2014-06-25 23:11:53 -0700 | [diff] [blame] | 47 | const char kAVFSRootDirectory[] = "/run/avfsroot"; |
| 48 | const char kAVFSLogFile[] = "/run/avfsroot/avfs.log"; |
| 49 | const char kAVFSMediaDirectory[] = "/run/avfsroot/media"; |
| 50 | const char kAVFSUsersDirectory[] = "/run/avfsroot/users"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 51 | const char kMediaDirectory[] = "/media"; |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 52 | const char kUserRootDirectory[] = "/home/chronos"; |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 53 | const AVFSPathMapping kAVFSPathMapping[] = { |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 54 | { kMediaDirectory, kAVFSMediaDirectory }, |
| 55 | { kUserRootDirectory, kAVFSUsersDirectory }, |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 56 | }; |
| 57 | |
Ben Chan | 460439f | 2011-09-13 09:16:28 -0700 | [diff] [blame] | 58 | } // namespace |
| 59 | |
| 60 | namespace cros_disks { |
| 61 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 62 | ArchiveManager::ArchiveManager(const string& mount_root, |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 63 | Platform* platform, |
| 64 | Metrics* metrics) |
| 65 | : MountManager(mount_root, platform, metrics), |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 66 | avfs_started_(false) { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 67 | } |
| 68 | |
Ben Chan | a4c7506 | 2011-11-11 09:47:55 -0800 | [diff] [blame] | 69 | ArchiveManager::~ArchiveManager() { |
Ben Chan | 8f51376 | 2011-11-14 12:44:42 -0800 | [diff] [blame] | 70 | // StopAVFS() unmounts all mounted archives as well as AVFS mount points. |
| 71 | StopAVFS(); |
Ben Chan | a4c7506 | 2011-11-11 09:47:55 -0800 | [diff] [blame] | 72 | } |
| 73 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 74 | bool ArchiveManager::Initialize() { |
| 75 | RegisterDefaultFileExtensions(); |
| 76 | return MountManager::Initialize(); |
| 77 | } |
| 78 | |
Ben Chan | b3bf8d1 | 2013-04-23 13:57:55 -0700 | [diff] [blame] | 79 | bool ArchiveManager::StopSession() { |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 80 | return StopAVFS(); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | bool ArchiveManager::CanMount(const string& source_path) const { |
| 84 | // The following paths can be mounted: |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 85 | // /home/chronos/u-<user-id>/Downloads/...<file> |
| 86 | // /home/chronos/u-<user-id>/GCache/...<file> |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 87 | // /media/<dir>/<dir>/...<file> |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 88 | // |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 89 | FilePath file_path(source_path); |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 90 | if (FilePath(kUserRootDirectory).IsParent(file_path)) { |
| 91 | vector<FilePath::StringType> components; |
| 92 | file_path.StripTrailingSeparators().GetComponents(&components); |
| 93 | // The file path of an archive file under a user's Downloads or GCache |
| 94 | // directory path is split into the following components: |
| 95 | // '/', 'home', 'chronos', 'u-<userid>', 'Downloads', ..., 'doc.zip' |
| 96 | // '/', 'home', 'chronos', 'u-<userid>', 'GCache', ..., 'doc.zip' |
| 97 | if (components.size() > 5 && |
Ben Chan | c480c40 | 2014-07-30 11:26:07 -0700 | [diff] [blame] | 98 | (StartsWithASCII(components[3], "u-", false) && |
| 99 | chromeos::cryptohome::home::IsSanitizedUserName( |
| 100 | components[3].substr(2))) && |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 101 | (components[4] == "Downloads" || components[4] == "GCache")) { |
| 102 | return true; |
| 103 | } |
| 104 | } |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 105 | |
| 106 | if (FilePath(kMediaDirectory).IsParent(file_path)) { |
| 107 | vector<FilePath::StringType> components; |
| 108 | file_path.StripTrailingSeparators().GetComponents(&components); |
Ben Chan | 16d8586 | 2013-05-28 20:30:30 -0700 | [diff] [blame] | 109 | // A mount directory is always created under /media/<sub type>/<mount dir>, |
| 110 | // so the file path of an archive file under a mount directory is split |
| 111 | // into more than 4 components: |
| 112 | // '/', 'media', 'removable', 'usb', ..., 'doc.zip' |
| 113 | if (components.size() > 4) |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 114 | return true; |
| 115 | } |
| 116 | return false; |
| 117 | } |
| 118 | |
| 119 | MountErrorType ArchiveManager::DoMount(const string& source_path, |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 120 | const string& source_format, |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 121 | const vector<string>& options, |
| 122 | const string& mount_path) { |
| 123 | CHECK(!source_path.empty()) << "Invalid source path argument"; |
| 124 | CHECK(!mount_path.empty()) << "Invalid mount path argument"; |
| 125 | |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 126 | string extension = GetFileExtension(source_format); |
| 127 | if (extension.empty()) |
| 128 | extension = GetFileExtension(source_path); |
| 129 | |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 130 | metrics()->RecordArchiveType(extension); |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 131 | |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 132 | string avfs_path = GetAVFSPath(source_path, extension); |
Ben Chan | cf06ac8 | 2011-10-20 16:42:21 -0700 | [diff] [blame] | 133 | if (avfs_path.empty()) { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 134 | LOG(ERROR) << "Path '" << source_path << "' is not a supported archive"; |
Ben Chan | fcb2fc0 | 2011-11-21 09:44:07 -0800 | [diff] [blame] | 135 | return MOUNT_ERROR_UNSUPPORTED_ARCHIVE; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 136 | } |
| 137 | |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 138 | if (!StartAVFS()) { |
| 139 | LOG(ERROR) << "Failed to start AVFS mounts."; |
Ben Chan | fcb2fc0 | 2011-11-21 09:44:07 -0800 | [diff] [blame] | 140 | return MOUNT_ERROR_INTERNAL; |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 141 | } |
| 142 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 143 | // Perform a bind mount from the archive path under the AVFS mount |
| 144 | // to /media/archive/<archive name>. |
| 145 | vector<string> extended_options = options; |
| 146 | extended_options.push_back(MountOptions::kOptionBind); |
| 147 | MountOptions mount_options; |
| 148 | mount_options.Initialize(extended_options, false, "", ""); |
| 149 | SystemMounter mounter(avfs_path, mount_path, "", mount_options); |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 150 | |
| 151 | MountErrorType error_type = mounter.Mount(); |
| 152 | if (error_type == MOUNT_ERROR_NONE) { |
| 153 | AddMountVirtualPath(mount_path, avfs_path); |
| 154 | } |
| 155 | return error_type; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | MountErrorType ArchiveManager::DoUnmount(const string& path, |
| 159 | const vector<string>& options) { |
| 160 | CHECK(!path.empty()) << "Invalid path argument"; |
| 161 | // TODO(benchan): Extract error from low-level unmount operation. |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 162 | if (platform()->Unmount(path)) { |
| 163 | // DoUnmount() is always called with |path| being the mount path. |
| 164 | RemoveMountVirtualPath(path); |
| 165 | return MOUNT_ERROR_NONE; |
| 166 | } |
| 167 | return MOUNT_ERROR_UNKNOWN; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | string ArchiveManager::SuggestMountPath(const string& source_path) const { |
| 171 | // Use the archive name to name the mount directory. |
| 172 | FilePath base_name = FilePath(source_path).BaseName(); |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 173 | return FilePath(mount_root()).Append(base_name).value(); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 174 | } |
| 175 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 176 | void ArchiveManager::RegisterDefaultFileExtensions() { |
| 177 | // TODO(benchan): Perhaps these settings can be read from a config file. |
Ben Chan | 68e325a | 2012-02-14 22:35:15 -0800 | [diff] [blame] | 178 | |
| 179 | // zip |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 180 | RegisterFileExtension("zip", "#uzip"); |
Ben Chan | 68e325a | 2012-02-14 22:35:15 -0800 | [diff] [blame] | 181 | // tar |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 182 | RegisterFileExtension("tar", "#utar"); |
Ben Chan | 68e325a | 2012-02-14 22:35:15 -0800 | [diff] [blame] | 183 | // All variants of bzip2-compessed tar |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 184 | RegisterFileExtension("tar.bz2", "#ubz2#utar"); |
| 185 | RegisterFileExtension("tbz", "#ubz2#utar"); |
| 186 | RegisterFileExtension("tbz2", "#ubz2#utar"); |
Ben Chan | 68e325a | 2012-02-14 22:35:15 -0800 | [diff] [blame] | 187 | // All variants of gzip-compessed tar |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 188 | RegisterFileExtension("tar.gz", "#ugz#utar"); |
| 189 | RegisterFileExtension("tgz", "#ugz#utar"); |
Ben Chan | d7ff95f | 2012-02-16 13:41:14 -0800 | [diff] [blame] | 190 | // rar |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 191 | RegisterFileExtension("rar", "#urar"); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 192 | } |
| 193 | |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 194 | void ArchiveManager::RegisterFileExtension(const string& extension, |
| 195 | const string& avfs_handler) { |
| 196 | extension_handlers_[extension] = avfs_handler; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 197 | } |
| 198 | |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 199 | string ArchiveManager::GetFileExtension(const string& path) const { |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 200 | FilePath file_path(path); |
| 201 | string extension = file_path.Extension(); |
| 202 | if (!extension.empty()) { |
| 203 | // Strip the leading dot and convert the extension to lower case. |
| 204 | extension.erase(0, 1); |
Ben Chan | e1ca84e | 2014-09-05 08:20:59 -0700 | [diff] [blame] | 205 | base::StringToLowerASCII(&extension); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 206 | } |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 207 | return extension; |
| 208 | } |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 209 | |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 210 | string ArchiveManager::GetAVFSPath(const string& path, |
| 211 | const string& extension) const { |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 212 | // When mounting an archive within another mounted archive, we need to |
| 213 | // resolve the virtual path of the inner archive to the "unfolded" |
| 214 | // form within the AVFS mount, such as |
Ben Chan | 7f8dcb6 | 2014-06-25 23:11:53 -0700 | [diff] [blame] | 215 | // "/run/avfsroot/media/layer2.zip#/test/doc/layer1.zip#" |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 216 | // instead of the "nested" form, such as |
Ben Chan | 7f8dcb6 | 2014-06-25 23:11:53 -0700 | [diff] [blame] | 217 | // "/run/avfsroot/media/archive/layer2.zip/test/doc/layer1.zip#" |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 218 | // where "/media/archive/layer2.zip" is a mount point to the virtual |
Ben Chan | 7f8dcb6 | 2014-06-25 23:11:53 -0700 | [diff] [blame] | 219 | // path "/run/avfsroot/media/layer2.zip#". |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 220 | // |
| 221 | // Mounting the inner archive using the nested form may cause problems |
| 222 | // reading files from the inner archive. To avoid that, we first try to |
| 223 | // find the longest parent path of |path| that is an existing mount |
| 224 | // point to a virtual path within the AVFS mount. If such a parent path |
| 225 | // is found, we construct the virtual path of |path| within the AVFS |
| 226 | // mount as a subpath of its parent's virtual path. |
| 227 | // |
| 228 | // e.g. Given |path| is "/media/archive/layer2.zip/test/doc/layer1.zip", |
| 229 | // and "/media/archive/layer2.zip" is a mount point to the virtual |
Ben Chan | 7f8dcb6 | 2014-06-25 23:11:53 -0700 | [diff] [blame] | 230 | // path "/run/avfsroot/media/layer2.zip#" within the AVFS mount. |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 231 | // The following code should return the virtual path of |path| as |
Ben Chan | 7f8dcb6 | 2014-06-25 23:11:53 -0700 | [diff] [blame] | 232 | // "/run/avfsroot/media/layer2.zip#/test/doc/layer1.zip#". |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 233 | map<string, string>::const_iterator handler_iterator = |
| 234 | extension_handlers_.find(extension); |
| 235 | if (handler_iterator == extension_handlers_.end()) |
| 236 | return string(); |
| 237 | |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 238 | FilePath file_path(path); |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 239 | FilePath current_path = file_path.DirName(); |
| 240 | FilePath parent_path = current_path.DirName(); |
| 241 | while (current_path != parent_path) { // Search till the root |
| 242 | VirtualPathMap::const_iterator path_iterator = |
| 243 | virtual_paths_.find(current_path.value()); |
| 244 | if (path_iterator != virtual_paths_.end()) { |
| 245 | FilePath avfs_path(path_iterator->second); |
| 246 | // As current_path is a parent of file_path, AppendRelativePath() |
| 247 | // should return true here. |
| 248 | CHECK(current_path.AppendRelativePath(file_path, &avfs_path)); |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 249 | return avfs_path.value() + handler_iterator->second; |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 250 | } |
| 251 | current_path = parent_path; |
| 252 | parent_path = parent_path.DirName(); |
| 253 | } |
| 254 | |
| 255 | // If no parent path is a mounted via AVFS, we are not mounting a nested |
| 256 | // archive and thus construct the virtual path of the archive based on a |
| 257 | // corresponding AVFS mount path. |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 258 | for (size_t i = 0; i < arraysize(kAVFSPathMapping); ++i) { |
Ben Chan | be2b4a7 | 2011-11-08 13:42:23 -0800 | [diff] [blame] | 259 | FilePath base_path(kAVFSPathMapping[i].base_path); |
| 260 | FilePath avfs_path(kAVFSPathMapping[i].avfs_path); |
| 261 | if (base_path.AppendRelativePath(file_path, &avfs_path)) { |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 262 | return avfs_path.value() + handler_iterator->second; |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 263 | } |
| 264 | } |
| 265 | return string(); |
| 266 | } |
| 267 | |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 268 | bool ArchiveManager::StartAVFS() { |
| 269 | if (avfs_started_) |
| 270 | return true; |
| 271 | |
Ben Chan | cf06ac8 | 2011-10-20 16:42:21 -0700 | [diff] [blame] | 272 | uid_t user_id; |
| 273 | gid_t group_id; |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 274 | if (!platform()->GetUserAndGroupId(kAVFSMountUser, &user_id, &group_id) || |
| 275 | !platform()->CreateDirectory(kAVFSRootDirectory) || |
| 276 | !platform()->SetOwnership(kAVFSRootDirectory, user_id, group_id) || |
| 277 | !platform()->SetPermissions(kAVFSRootDirectory, S_IRWXU)) { |
| 278 | platform()->RemoveEmptyDirectory(kAVFSRootDirectory); |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 279 | return false; |
| 280 | } |
| 281 | |
Ben Chan | e9a2887 | 2011-12-19 18:08:37 -0800 | [diff] [blame] | 282 | // Set the AVFS_LOGFILE environment variable so that the AVFS daemon |
| 283 | // writes log messages to a file instead of syslog. Otherwise, writing |
| 284 | // to syslog may trigger the socket/connect/send system calls, which are |
| 285 | // disabled by the seccomp filters policy file. This only affects the |
| 286 | // child processes spawned by cros-disks and does not persist after |
| 287 | // cros-disks restarts. |
| 288 | setenv("AVFS_LOGFILE", kAVFSLogFile, 1); |
| 289 | |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 290 | avfs_started_ = true; |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 291 | for (size_t i = 0; i < arraysize(kAVFSPathMapping); ++i) { |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 292 | bool base_path_exists = |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 293 | base::PathExists(FilePath(kAVFSPathMapping[i].base_path)); |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 294 | const string& avfs_path = kAVFSPathMapping[i].avfs_path; |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 295 | if (!base_path_exists || |
| 296 | !platform()->CreateDirectory(avfs_path) || |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 297 | !platform()->SetOwnership(avfs_path, user_id, group_id) || |
| 298 | !platform()->SetPermissions(avfs_path, S_IRWXU) || |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 299 | !MountAVFSPath(kAVFSPathMapping[i].base_path, avfs_path)) { |
| 300 | StopAVFS(); |
| 301 | return false; |
| 302 | } |
| 303 | } |
| 304 | return true; |
| 305 | } |
| 306 | |
| 307 | bool ArchiveManager::StopAVFS() { |
| 308 | if (!avfs_started_) |
| 309 | return true; |
| 310 | |
| 311 | avfs_started_ = false; |
| 312 | // Unmounts all mounted archives before unmounting AVFS mounts. |
| 313 | bool all_unmounted = UnmountAll(); |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 314 | for (size_t i = 0; i < arraysize(kAVFSPathMapping); ++i) { |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 315 | const string& path = kAVFSPathMapping[i].avfs_path; |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 316 | if (!base::PathExists(FilePath(path))) |
Ben Chan | cb51773 | 2012-04-11 17:00:00 -0700 | [diff] [blame] | 317 | continue; |
| 318 | |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 319 | if (!platform()->Unmount(path)) |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 320 | all_unmounted = false; |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 321 | platform()->RemoveEmptyDirectory(path); |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 322 | } |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 323 | platform()->RemoveEmptyDirectory(kAVFSRootDirectory); |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 324 | return all_unmounted; |
| 325 | } |
| 326 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 327 | bool ArchiveManager::MountAVFSPath(const string& base_path, |
| 328 | const string& avfs_path) const { |
| 329 | MountInfo mount_info; |
| 330 | if (!mount_info.RetrieveFromCurrentProcess()) |
| 331 | return false; |
| 332 | |
| 333 | if (mount_info.HasMountPath(avfs_path)) { |
| 334 | LOG(WARNING) << "Path '" << avfs_path << "' is already mounted."; |
| 335 | return false; |
| 336 | } |
| 337 | |
Ben Chan | cf06ac8 | 2011-10-20 16:42:21 -0700 | [diff] [blame] | 338 | uid_t user_id; |
| 339 | gid_t group_id; |
Ben Chan | 44e7ea6 | 2014-08-29 18:13:37 -0700 | [diff] [blame] | 340 | if (!platform()->GetUserAndGroupId(kAVFSMountUser, &user_id, nullptr) || |
Ben Chan | 930f18d | 2011-11-21 09:18:50 -0800 | [diff] [blame] | 341 | !platform()->GetGroupId(kAVFSMountGroup, &group_id)) { |
Ben Chan | cf06ac8 | 2011-10-20 16:42:21 -0700 | [diff] [blame] | 342 | return false; |
| 343 | } |
| 344 | |
Ben Chan | 19c112c | 2011-08-24 15:17:22 -0700 | [diff] [blame] | 345 | SandboxedProcess mount_process; |
| 346 | mount_process.AddArgument(kAVFSMountProgram); |
| 347 | mount_process.AddArgument("-o"); |
Ben Chan | cf06ac8 | 2011-10-20 16:42:21 -0700 | [diff] [blame] | 348 | mount_process.AddArgument(base::StringPrintf( |
| 349 | "ro,nodev,noexec,nosuid,allow_other,user=%s,modules=subdir,subdir=%s", |
| 350 | kAVFSMountUser, base_path.c_str())); |
Ben Chan | 19c112c | 2011-08-24 15:17:22 -0700 | [diff] [blame] | 351 | mount_process.AddArgument(avfs_path); |
Ben Chan | 97e20d4 | 2014-02-05 18:38:07 -0800 | [diff] [blame] | 352 | if (base::PathExists(FilePath(kAVFSSeccompFilterPolicyFile))) { |
Ben Chan | 98f8ae0 | 2011-10-04 16:34:34 -0700 | [diff] [blame] | 353 | mount_process.LoadSeccompFilterPolicy(kAVFSSeccompFilterPolicyFile); |
| 354 | } else { |
| 355 | // TODO(benchan): Remove this fallback mechanism once we have policy files |
| 356 | // for all supported platforms. |
| 357 | LOG(WARNING) << "Seccomp filter policy '" << kAVFSSeccompFilterPolicyFile |
| 358 | << "' not found. Use POSIX capabilities mechanism instead"; |
| 359 | mount_process.SetCapabilities(kAVFSMountProgramCapabilities); |
| 360 | } |
| 361 | // TODO(benchan): Enable PID and VFS namespace. |
| 362 | // TODO(wad,ellyjones,benchan): Enable network namespace once libminijail |
| 363 | // supports it. |
Ben Chan | cf06ac8 | 2011-10-20 16:42:21 -0700 | [diff] [blame] | 364 | mount_process.SetUserId(user_id); |
| 365 | mount_process.SetGroupId(group_id); |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 366 | if (mount_process.Run() != 0 || |
| 367 | !mount_info.RetrieveFromCurrentProcess() || |
| 368 | !mount_info.HasMountPath(avfs_path)) { |
| 369 | LOG(WARNING) << "Failed to mount '" << base_path << "' to '" |
| 370 | << avfs_path << "' via AVFS"; |
| 371 | return false; |
| 372 | } |
| 373 | |
| 374 | LOG(INFO) << "Mounted '" << base_path << "' to '" << avfs_path |
| 375 | << "' via AVFS"; |
| 376 | return true; |
| 377 | } |
| 378 | |
Ben Chan | 4035163 | 2012-02-17 14:36:38 -0800 | [diff] [blame] | 379 | void ArchiveManager::AddMountVirtualPath(const string& mount_path, |
| 380 | const string& virtual_path) { |
| 381 | virtual_paths_[mount_path] = virtual_path; |
| 382 | } |
| 383 | |
| 384 | void ArchiveManager::RemoveMountVirtualPath(const string& mount_path) { |
| 385 | virtual_paths_.erase(mount_path); |
| 386 | } |
| 387 | |
Ben Chan | 8dcede8 | 2011-07-25 20:56:13 -0700 | [diff] [blame] | 388 | } // namespace cros_disks |