blob: 0fd0d7903cb0224da50ed7295e4ef4b540d80432 [file] [log] [blame]
Ben Chancb517732012-04-11 17:00:00 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Ben Chan8dcede82011-07-25 20:56:13 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ben Chan5ccd9fe2013-11-13 18:28:27 -08005#include "cros-disks/archive_manager.h"
Ben Chan8dcede82011-07-25 20:56:13 -07006
Ben Chan19c112c2011-08-24 15:17:22 -07007#include <linux/capability.h>
8
Ben Chan97e20d42014-02-05 18:38:07 -08009#include <base/files/file_path.h>
Ben Chancd8fda42014-09-05 08:21:06 -070010#include <base/files/file_util.h>
Ben Chan8dcede82011-07-25 20:56:13 -070011#include <base/logging.h>
Ben Chan97e20d42014-02-05 18:38:07 -080012#include <base/strings/string_util.h>
13#include <base/strings/stringprintf.h>
Ben Chan16d85862013-05-28 20:30:30 -070014#include <chromeos/cryptohome.h>
Ben Chan8dcede82011-07-25 20:56:13 -070015
Ben Chanbe2b4a72011-11-08 13:42:23 -080016#include "cros-disks/metrics.h"
Ben Chan5ccd9fe2013-11-13 18:28:27 -080017#include "cros-disks/mount_info.h"
18#include "cros-disks/mount_options.h"
Ben Chan8dcede82011-07-25 20:56:13 -070019#include "cros-disks/platform.h"
Ben Chan5ccd9fe2013-11-13 18:28:27 -080020#include "cros-disks/sandboxed_process.h"
21#include "cros-disks/system_mounter.h"
Ben Chan8dcede82011-07-25 20:56:13 -070022
Ben Chan2e8aa6f2013-02-15 11:40:04 -080023using base::FilePath;
Ben Chancb517732012-04-11 17:00:00 -070024using std::map;
Ben Chan8dcede82011-07-25 20:56:13 -070025using std::string;
26using std::vector;
27
Ben Chan460439f2011-09-13 09:16:28 -070028namespace {
Ben Chan8dcede82011-07-25 20:56:13 -070029
30// Mapping from a base path to its corresponding path inside the AVFS mount.
31struct AVFSPathMapping {
32 const char* const base_path;
33 const char* const avfs_path;
34};
35
Ben Chan19c112c2011-08-24 15:17:22 -070036// Process capabilities required by the avfsd process:
37// CAP_SYS_ADMIN for mounting/unmounting filesystem
Ben Chan460439f2011-09-13 09:16:28 -070038const uint64_t kAVFSMountProgramCapabilities = 1 << CAP_SYS_ADMIN;
Ben Chan19c112c2011-08-24 15:17:22 -070039
Ben Chancf06ac82011-10-20 16:42:21 -070040const char kAVFSMountGroup[] = "chronos-access";
41const char kAVFSMountUser[] = "avfs";
Ben Chan98f8ae02011-10-04 16:34:34 -070042// TODO(wad,benchan): Revisit the location of policy files once more system
43// daemons are sandboxed with seccomp filters.
44const char kAVFSSeccompFilterPolicyFile[] =
45 "/opt/google/cros-disks/avfsd-seccomp.policy";
Ben Chan460439f2011-09-13 09:16:28 -070046const char kAVFSMountProgram[] = "/usr/bin/avfsd";
Ben Chan7f8dcb62014-06-25 23:11:53 -070047const char kAVFSRootDirectory[] = "/run/avfsroot";
48const char kAVFSLogFile[] = "/run/avfsroot/avfs.log";
49const char kAVFSMediaDirectory[] = "/run/avfsroot/media";
50const char kAVFSUsersDirectory[] = "/run/avfsroot/users";
Ben Chan460439f2011-09-13 09:16:28 -070051const char kMediaDirectory[] = "/media";
Ben Chan16d85862013-05-28 20:30:30 -070052const char kUserRootDirectory[] = "/home/chronos";
Ben Chan460439f2011-09-13 09:16:28 -070053const AVFSPathMapping kAVFSPathMapping[] = {
Ben Chan16d85862013-05-28 20:30:30 -070054 { kMediaDirectory, kAVFSMediaDirectory },
55 { kUserRootDirectory, kAVFSUsersDirectory },
Ben Chan8dcede82011-07-25 20:56:13 -070056};
57
Ben Chan460439f2011-09-13 09:16:28 -070058} // namespace
59
60namespace cros_disks {
61
Ben Chan8dcede82011-07-25 20:56:13 -070062ArchiveManager::ArchiveManager(const string& mount_root,
Ben Chanbe2b4a72011-11-08 13:42:23 -080063 Platform* platform,
64 Metrics* metrics)
65 : MountManager(mount_root, platform, metrics),
Ben Chan98f8ae02011-10-04 16:34:34 -070066 avfs_started_(false) {
Ben Chan8dcede82011-07-25 20:56:13 -070067}
68
Ben Chana4c75062011-11-11 09:47:55 -080069ArchiveManager::~ArchiveManager() {
Ben Chan8f513762011-11-14 12:44:42 -080070 // StopAVFS() unmounts all mounted archives as well as AVFS mount points.
71 StopAVFS();
Ben Chana4c75062011-11-11 09:47:55 -080072}
73
Ben Chan8dcede82011-07-25 20:56:13 -070074bool ArchiveManager::Initialize() {
75 RegisterDefaultFileExtensions();
76 return MountManager::Initialize();
77}
78
Ben Chanb3bf8d12013-04-23 13:57:55 -070079bool ArchiveManager::StopSession() {
Ben Chan98f8ae02011-10-04 16:34:34 -070080 return StopAVFS();
Ben Chan8dcede82011-07-25 20:56:13 -070081}
82
83bool ArchiveManager::CanMount(const string& source_path) const {
84 // The following paths can be mounted:
Ben Chan16d85862013-05-28 20:30:30 -070085 // /home/chronos/u-<user-id>/Downloads/...<file>
86 // /home/chronos/u-<user-id>/GCache/...<file>
Ben Chan8dcede82011-07-25 20:56:13 -070087 // /media/<dir>/<dir>/...<file>
Ben Chan16d85862013-05-28 20:30:30 -070088 //
Ben Chan8dcede82011-07-25 20:56:13 -070089 FilePath file_path(source_path);
Ben Chan16d85862013-05-28 20:30:30 -070090 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 Chanc480c402014-07-30 11:26:07 -070098 (StartsWithASCII(components[3], "u-", false) &&
99 chromeos::cryptohome::home::IsSanitizedUserName(
100 components[3].substr(2))) &&
Ben Chan16d85862013-05-28 20:30:30 -0700101 (components[4] == "Downloads" || components[4] == "GCache")) {
102 return true;
103 }
104 }
Ben Chan8dcede82011-07-25 20:56:13 -0700105
106 if (FilePath(kMediaDirectory).IsParent(file_path)) {
107 vector<FilePath::StringType> components;
108 file_path.StripTrailingSeparators().GetComponents(&components);
Ben Chan16d85862013-05-28 20:30:30 -0700109 // 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 Chan8dcede82011-07-25 20:56:13 -0700114 return true;
115 }
116 return false;
117}
118
119MountErrorType ArchiveManager::DoMount(const string& source_path,
Ben Chancb517732012-04-11 17:00:00 -0700120 const string& source_format,
Ben Chan8dcede82011-07-25 20:56:13 -0700121 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 Chancb517732012-04-11 17:00:00 -0700126 string extension = GetFileExtension(source_format);
127 if (extension.empty())
128 extension = GetFileExtension(source_path);
129
Ben Chan930f18d2011-11-21 09:18:50 -0800130 metrics()->RecordArchiveType(extension);
Ben Chanbe2b4a72011-11-08 13:42:23 -0800131
Ben Chancb517732012-04-11 17:00:00 -0700132 string avfs_path = GetAVFSPath(source_path, extension);
Ben Chancf06ac82011-10-20 16:42:21 -0700133 if (avfs_path.empty()) {
Ben Chan8dcede82011-07-25 20:56:13 -0700134 LOG(ERROR) << "Path '" << source_path << "' is not a supported archive";
Ben Chanfcb2fc02011-11-21 09:44:07 -0800135 return MOUNT_ERROR_UNSUPPORTED_ARCHIVE;
Ben Chan8dcede82011-07-25 20:56:13 -0700136 }
137
Ben Chan98f8ae02011-10-04 16:34:34 -0700138 if (!StartAVFS()) {
139 LOG(ERROR) << "Failed to start AVFS mounts.";
Ben Chanfcb2fc02011-11-21 09:44:07 -0800140 return MOUNT_ERROR_INTERNAL;
Ben Chan98f8ae02011-10-04 16:34:34 -0700141 }
142
Ben Chan8dcede82011-07-25 20:56:13 -0700143 // 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 Chan40351632012-02-17 14:36:38 -0800150
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 Chan8dcede82011-07-25 20:56:13 -0700156}
157
158MountErrorType 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 Chan40351632012-02-17 14:36:38 -0800162 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 Chan8dcede82011-07-25 20:56:13 -0700168}
169
170string 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 Chan930f18d2011-11-21 09:18:50 -0800173 return FilePath(mount_root()).Append(base_name).value();
Ben Chan8dcede82011-07-25 20:56:13 -0700174}
175
Ben Chan8dcede82011-07-25 20:56:13 -0700176void ArchiveManager::RegisterDefaultFileExtensions() {
177 // TODO(benchan): Perhaps these settings can be read from a config file.
Ben Chan68e325a2012-02-14 22:35:15 -0800178
179 // zip
Ben Chancb517732012-04-11 17:00:00 -0700180 RegisterFileExtension("zip", "#uzip");
Ben Chan68e325a2012-02-14 22:35:15 -0800181 // tar
Ben Chancb517732012-04-11 17:00:00 -0700182 RegisterFileExtension("tar", "#utar");
Ben Chan68e325a2012-02-14 22:35:15 -0800183 // All variants of bzip2-compessed tar
Ben Chancb517732012-04-11 17:00:00 -0700184 RegisterFileExtension("tar.bz2", "#ubz2#utar");
185 RegisterFileExtension("tbz", "#ubz2#utar");
186 RegisterFileExtension("tbz2", "#ubz2#utar");
Ben Chan68e325a2012-02-14 22:35:15 -0800187 // All variants of gzip-compessed tar
Ben Chancb517732012-04-11 17:00:00 -0700188 RegisterFileExtension("tar.gz", "#ugz#utar");
189 RegisterFileExtension("tgz", "#ugz#utar");
Ben Chand7ff95f2012-02-16 13:41:14 -0800190 // rar
Ben Chancb517732012-04-11 17:00:00 -0700191 RegisterFileExtension("rar", "#urar");
Ben Chan8dcede82011-07-25 20:56:13 -0700192}
193
Ben Chancb517732012-04-11 17:00:00 -0700194void ArchiveManager::RegisterFileExtension(const string& extension,
195 const string& avfs_handler) {
196 extension_handlers_[extension] = avfs_handler;
Ben Chan8dcede82011-07-25 20:56:13 -0700197}
198
Ben Chanbe2b4a72011-11-08 13:42:23 -0800199string ArchiveManager::GetFileExtension(const string& path) const {
Ben Chan8dcede82011-07-25 20:56:13 -0700200 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 Chane1ca84e2014-09-05 08:20:59 -0700205 base::StringToLowerASCII(&extension);
Ben Chan8dcede82011-07-25 20:56:13 -0700206 }
Ben Chanbe2b4a72011-11-08 13:42:23 -0800207 return extension;
208}
Ben Chan8dcede82011-07-25 20:56:13 -0700209
Ben Chancb517732012-04-11 17:00:00 -0700210string ArchiveManager::GetAVFSPath(const string& path,
211 const string& extension) const {
Ben Chan40351632012-02-17 14:36:38 -0800212 // 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 Chan7f8dcb62014-06-25 23:11:53 -0700215 // "/run/avfsroot/media/layer2.zip#/test/doc/layer1.zip#"
Ben Chan40351632012-02-17 14:36:38 -0800216 // instead of the "nested" form, such as
Ben Chan7f8dcb62014-06-25 23:11:53 -0700217 // "/run/avfsroot/media/archive/layer2.zip/test/doc/layer1.zip#"
Ben Chan40351632012-02-17 14:36:38 -0800218 // where "/media/archive/layer2.zip" is a mount point to the virtual
Ben Chan7f8dcb62014-06-25 23:11:53 -0700219 // path "/run/avfsroot/media/layer2.zip#".
Ben Chan40351632012-02-17 14:36:38 -0800220 //
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 Chan7f8dcb62014-06-25 23:11:53 -0700230 // path "/run/avfsroot/media/layer2.zip#" within the AVFS mount.
Ben Chan40351632012-02-17 14:36:38 -0800231 // The following code should return the virtual path of |path| as
Ben Chan7f8dcb62014-06-25 23:11:53 -0700232 // "/run/avfsroot/media/layer2.zip#/test/doc/layer1.zip#".
Ben Chancb517732012-04-11 17:00:00 -0700233 map<string, string>::const_iterator handler_iterator =
234 extension_handlers_.find(extension);
235 if (handler_iterator == extension_handlers_.end())
236 return string();
237
Ben Chanbe2b4a72011-11-08 13:42:23 -0800238 FilePath file_path(path);
Ben Chan40351632012-02-17 14:36:38 -0800239 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 Chancb517732012-04-11 17:00:00 -0700249 return avfs_path.value() + handler_iterator->second;
Ben Chan40351632012-02-17 14:36:38 -0800250 }
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 Chan930f18d2011-11-21 09:18:50 -0800258 for (size_t i = 0; i < arraysize(kAVFSPathMapping); ++i) {
Ben Chanbe2b4a72011-11-08 13:42:23 -0800259 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 Chancb517732012-04-11 17:00:00 -0700262 return avfs_path.value() + handler_iterator->second;
Ben Chan8dcede82011-07-25 20:56:13 -0700263 }
264 }
265 return string();
266}
267
Ben Chan98f8ae02011-10-04 16:34:34 -0700268bool ArchiveManager::StartAVFS() {
269 if (avfs_started_)
270 return true;
271
Ben Chancf06ac82011-10-20 16:42:21 -0700272 uid_t user_id;
273 gid_t group_id;
Ben Chan930f18d2011-11-21 09:18:50 -0800274 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 Chan98f8ae02011-10-04 16:34:34 -0700279 return false;
280 }
281
Ben Chane9a28872011-12-19 18:08:37 -0800282 // 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 Chan98f8ae02011-10-04 16:34:34 -0700290 avfs_started_ = true;
Ben Chan930f18d2011-11-21 09:18:50 -0800291 for (size_t i = 0; i < arraysize(kAVFSPathMapping); ++i) {
Ben Chancb517732012-04-11 17:00:00 -0700292 bool base_path_exists =
Ben Chan97e20d42014-02-05 18:38:07 -0800293 base::PathExists(FilePath(kAVFSPathMapping[i].base_path));
Ben Chan98f8ae02011-10-04 16:34:34 -0700294 const string& avfs_path = kAVFSPathMapping[i].avfs_path;
Ben Chancb517732012-04-11 17:00:00 -0700295 if (!base_path_exists ||
296 !platform()->CreateDirectory(avfs_path) ||
Ben Chan930f18d2011-11-21 09:18:50 -0800297 !platform()->SetOwnership(avfs_path, user_id, group_id) ||
298 !platform()->SetPermissions(avfs_path, S_IRWXU) ||
Ben Chan98f8ae02011-10-04 16:34:34 -0700299 !MountAVFSPath(kAVFSPathMapping[i].base_path, avfs_path)) {
300 StopAVFS();
301 return false;
302 }
303 }
304 return true;
305}
306
307bool 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 Chan930f18d2011-11-21 09:18:50 -0800314 for (size_t i = 0; i < arraysize(kAVFSPathMapping); ++i) {
Ben Chan98f8ae02011-10-04 16:34:34 -0700315 const string& path = kAVFSPathMapping[i].avfs_path;
Ben Chan97e20d42014-02-05 18:38:07 -0800316 if (!base::PathExists(FilePath(path)))
Ben Chancb517732012-04-11 17:00:00 -0700317 continue;
318
Ben Chan930f18d2011-11-21 09:18:50 -0800319 if (!platform()->Unmount(path))
Ben Chan98f8ae02011-10-04 16:34:34 -0700320 all_unmounted = false;
Ben Chan930f18d2011-11-21 09:18:50 -0800321 platform()->RemoveEmptyDirectory(path);
Ben Chan98f8ae02011-10-04 16:34:34 -0700322 }
Ben Chan930f18d2011-11-21 09:18:50 -0800323 platform()->RemoveEmptyDirectory(kAVFSRootDirectory);
Ben Chan98f8ae02011-10-04 16:34:34 -0700324 return all_unmounted;
325}
326
Ben Chan8dcede82011-07-25 20:56:13 -0700327bool 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 Chancf06ac82011-10-20 16:42:21 -0700338 uid_t user_id;
339 gid_t group_id;
Ben Chan44e7ea62014-08-29 18:13:37 -0700340 if (!platform()->GetUserAndGroupId(kAVFSMountUser, &user_id, nullptr) ||
Ben Chan930f18d2011-11-21 09:18:50 -0800341 !platform()->GetGroupId(kAVFSMountGroup, &group_id)) {
Ben Chancf06ac82011-10-20 16:42:21 -0700342 return false;
343 }
344
Ben Chan19c112c2011-08-24 15:17:22 -0700345 SandboxedProcess mount_process;
346 mount_process.AddArgument(kAVFSMountProgram);
347 mount_process.AddArgument("-o");
Ben Chancf06ac82011-10-20 16:42:21 -0700348 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 Chan19c112c2011-08-24 15:17:22 -0700351 mount_process.AddArgument(avfs_path);
Ben Chan97e20d42014-02-05 18:38:07 -0800352 if (base::PathExists(FilePath(kAVFSSeccompFilterPolicyFile))) {
Ben Chan98f8ae02011-10-04 16:34:34 -0700353 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 Chancf06ac82011-10-20 16:42:21 -0700364 mount_process.SetUserId(user_id);
365 mount_process.SetGroupId(group_id);
Ben Chan8dcede82011-07-25 20:56:13 -0700366 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 Chan40351632012-02-17 14:36:38 -0800379void ArchiveManager::AddMountVirtualPath(const string& mount_path,
380 const string& virtual_path) {
381 virtual_paths_[mount_path] = virtual_path;
382}
383
384void ArchiveManager::RemoveMountVirtualPath(const string& mount_path) {
385 virtual_paths_.erase(mount_path);
386}
387
Ben Chan8dcede82011-07-25 20:56:13 -0700388} // namespace cros_disks