1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "storage/browser/fileapi/external_mount_points.h" 6 7 #include "base/files/file_path.h" 8 #include "base/lazy_instance.h" 9 #include "base/path_service.h" 10 #include "base/stl_util.h" 11 #include "storage/browser/fileapi/file_system_url.h" 12 13 namespace { 14 15 // Normalizes file path so it has normalized separators and ends with exactly 16 // one separator. Paths have to be normalized this way for use in 17 // GetVirtualPath method. Separators cannot be completely stripped, or 18 // GetVirtualPath could not working in some edge cases. 19 // For example, /a/b/c(1)/d would be erroneously resolved as c/d if the 20 // following mount points were registered: "/a/b/c", "/a/b/c(1)". (Note: 21 // "/a/b/c" < "/a/b/c(1)" < "/a/b/c/"). 22 base::FilePath NormalizeFilePath(const base::FilePath& path) { 23 if (path.empty()) 24 return path; 25 26 base::FilePath::StringType path_str = path.StripTrailingSeparators().value(); 27 if (!base::FilePath::IsSeparator(path_str[path_str.length() - 1])) 28 path_str.append(FILE_PATH_LITERAL("/")); 29 30 return base::FilePath(path_str).NormalizePathSeparators(); 31 } 32 33 bool IsOverlappingMountPathForbidden(storage::FileSystemType type) { 34 return type != storage::kFileSystemTypeNativeMedia && 35 type != storage::kFileSystemTypeDeviceMedia; 36 } 37 38 // Wrapper around ref-counted ExternalMountPoints that will be used to lazily 39 // create and initialize LazyInstance system ExternalMountPoints. 40 class SystemMountPointsLazyWrapper { 41 public: 42 SystemMountPointsLazyWrapper() 43 : system_mount_points_(storage::ExternalMountPoints::CreateRefCounted()) { 44 } 45 46 ~SystemMountPointsLazyWrapper() {} 47 48 storage::ExternalMountPoints* get() { return system_mount_points_.get(); } 49 50 private: 51 scoped_refptr<storage::ExternalMountPoints> system_mount_points_; 52 }; 53 54 base::LazyInstance<SystemMountPointsLazyWrapper>::Leaky 55 g_external_mount_points = LAZY_INSTANCE_INITIALIZER; 56 57 } // namespace 58 59 namespace storage { 60 61 class ExternalMountPoints::Instance { 62 public: 63 Instance(FileSystemType type, 64 const base::FilePath& path, 65 const FileSystemMountOption& mount_option) 66 : type_(type), 67 path_(path.StripTrailingSeparators()), 68 mount_option_(mount_option) {} 69 ~Instance() {} 70 71 FileSystemType type() const { return type_; } 72 const base::FilePath& path() const { return path_; } 73 const FileSystemMountOption& mount_option() const { return mount_option_; } 74 75 private: 76 const FileSystemType type_; 77 const base::FilePath path_; 78 const FileSystemMountOption mount_option_; 79 80 DISALLOW_COPY_AND_ASSIGN(Instance); 81 }; 82 83 //-------------------------------------------------------------------------- 84 85 // static 86 ExternalMountPoints* ExternalMountPoints::GetSystemInstance() { 87 return g_external_mount_points.Pointer()->get(); 88 } 89 90 // static 91 scoped_refptr<ExternalMountPoints> ExternalMountPoints::CreateRefCounted() { 92 return new ExternalMountPoints(); 93 } 94 95 bool ExternalMountPoints::RegisterFileSystem( 96 const std::string& mount_name, 97 FileSystemType type, 98 const FileSystemMountOption& mount_option, 99 const base::FilePath& path_in) { 100 // COPY_SYNC_OPTION_SYNC is only applicable to native local file system. 101 DCHECK(type == kFileSystemTypeNativeLocal || 102 mount_option.copy_sync_option() != COPY_SYNC_OPTION_SYNC); 103 104 base::AutoLock locker(lock_); 105 106 base::FilePath path = NormalizeFilePath(path_in); 107 if (!ValidateNewMountPoint(mount_name, type, path)) 108 return false; 109 110 instance_map_[mount_name] = new Instance(type, path, mount_option); 111 if (!path.empty() && IsOverlappingMountPathForbidden(type)) 112 path_to_name_map_.insert(std::make_pair(path, mount_name)); 113 return true; 114 } 115 116 bool ExternalMountPoints::HandlesFileSystemMountType( 117 FileSystemType type) const { 118 return type == kFileSystemTypeExternal || 119 type == kFileSystemTypeNativeForPlatformApp; 120 } 121 122 bool ExternalMountPoints::RevokeFileSystem(const std::string& mount_name) { 123 base::AutoLock locker(lock_); 124 NameToInstance::iterator found = instance_map_.find(mount_name); 125 if (found == instance_map_.end()) 126 return false; 127 Instance* instance = found->second; 128 if (IsOverlappingMountPathForbidden(instance->type())) 129 path_to_name_map_.erase(NormalizeFilePath(instance->path())); 130 delete found->second; 131 instance_map_.erase(found); 132 return true; 133 } 134 135 bool ExternalMountPoints::GetRegisteredPath( 136 const std::string& filesystem_id, base::FilePath* path) const { 137 DCHECK(path); 138 base::AutoLock locker(lock_); 139 NameToInstance::const_iterator found = instance_map_.find(filesystem_id); 140 if (found == instance_map_.end()) 141 return false; 142 *path = found->second->path(); 143 return true; 144 } 145 146 bool ExternalMountPoints::CrackVirtualPath( 147 const base::FilePath& virtual_path, 148 std::string* mount_name, 149 FileSystemType* type, 150 std::string* cracked_id, 151 base::FilePath* path, 152 FileSystemMountOption* mount_option) const { 153 DCHECK(mount_name); 154 DCHECK(path); 155 156 // The path should not contain any '..' references. 157 if (virtual_path.ReferencesParent()) 158 return false; 159 160 // The virtual_path should comprise of <mount_name> and <relative_path> parts. 161 std::vector<base::FilePath::StringType> components; 162 virtual_path.GetComponents(&components); 163 if (components.size() < 1) 164 return false; 165 166 std::vector<base::FilePath::StringType>::iterator component_iter = 167 components.begin(); 168 std::string maybe_mount_name = 169 base::FilePath(*component_iter++).MaybeAsASCII(); 170 if (maybe_mount_name.empty()) 171 return false; 172 173 base::FilePath cracked_path; 174 { 175 base::AutoLock locker(lock_); 176 NameToInstance::const_iterator found_instance = 177 instance_map_.find(maybe_mount_name); 178 if (found_instance == instance_map_.end()) 179 return false; 180 181 *mount_name = maybe_mount_name; 182 const Instance* instance = found_instance->second; 183 if (type) 184 *type = instance->type(); 185 cracked_path = instance->path(); 186 *mount_option = instance->mount_option(); 187 } 188 189 for (; component_iter != components.end(); ++component_iter) 190 cracked_path = cracked_path.Append(*component_iter); 191 *path = cracked_path; 192 return true; 193 } 194 195 FileSystemURL ExternalMountPoints::CrackURL(const GURL& url) const { 196 FileSystemURL filesystem_url = FileSystemURL(url); 197 if (!filesystem_url.is_valid()) 198 return FileSystemURL(); 199 return CrackFileSystemURL(filesystem_url); 200 } 201 202 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL( 203 const GURL& origin, 204 FileSystemType type, 205 const base::FilePath& path) const { 206 return CrackFileSystemURL(FileSystemURL(origin, type, path)); 207 } 208 209 void ExternalMountPoints::AddMountPointInfosTo( 210 std::vector<MountPointInfo>* mount_points) const { 211 base::AutoLock locker(lock_); 212 DCHECK(mount_points); 213 for (NameToInstance::const_iterator iter = instance_map_.begin(); 214 iter != instance_map_.end(); ++iter) { 215 mount_points->push_back(MountPointInfo(iter->first, iter->second->path())); 216 } 217 } 218 219 bool ExternalMountPoints::GetVirtualPath(const base::FilePath& path_in, 220 base::FilePath* virtual_path) const { 221 DCHECK(virtual_path); 222 223 base::AutoLock locker(lock_); 224 225 base::FilePath path = NormalizeFilePath(path_in); 226 std::map<base::FilePath, std::string>::const_reverse_iterator iter( 227 path_to_name_map_.upper_bound(path)); 228 if (iter == path_to_name_map_.rend()) 229 return false; 230 231 *virtual_path = CreateVirtualRootPath(iter->second); 232 if (iter->first == path) 233 return true; 234 return iter->first.AppendRelativePath(path, virtual_path); 235 } 236 237 base::FilePath ExternalMountPoints::CreateVirtualRootPath( 238 const std::string& mount_name) const { 239 return base::FilePath().AppendASCII(mount_name); 240 } 241 242 FileSystemURL ExternalMountPoints::CreateExternalFileSystemURL( 243 const GURL& origin, 244 const std::string& mount_name, 245 const base::FilePath& path) const { 246 return CreateCrackedFileSystemURL( 247 origin, 248 storage::kFileSystemTypeExternal, 249 // Avoid using FilePath::Append as path may be an absolute path. 250 base::FilePath(CreateVirtualRootPath(mount_name).value() + 251 base::FilePath::kSeparators[0] + path.value())); 252 } 253 254 void ExternalMountPoints::RevokeAllFileSystems() { 255 NameToInstance instance_map_copy; 256 { 257 base::AutoLock locker(lock_); 258 instance_map_copy = instance_map_; 259 instance_map_.clear(); 260 path_to_name_map_.clear(); 261 } 262 STLDeleteContainerPairSecondPointers(instance_map_copy.begin(), 263 instance_map_copy.end()); 264 } 265 266 ExternalMountPoints::ExternalMountPoints() {} 267 268 ExternalMountPoints::~ExternalMountPoints() { 269 STLDeleteContainerPairSecondPointers(instance_map_.begin(), 270 instance_map_.end()); 271 } 272 273 FileSystemURL ExternalMountPoints::CrackFileSystemURL( 274 const FileSystemURL& url) const { 275 if (!HandlesFileSystemMountType(url.type())) 276 return FileSystemURL(); 277 278 base::FilePath virtual_path = url.path(); 279 if (url.type() == kFileSystemTypeNativeForPlatformApp) { 280 #if defined(OS_CHROMEOS) 281 // On Chrome OS, find a mount point and virtual path for the external fs. 282 if (!GetVirtualPath(url.path(), &virtual_path)) 283 return FileSystemURL(); 284 #else 285 // On other OS, it is simply a native local path. 286 return FileSystemURL( 287 url.origin(), url.mount_type(), url.virtual_path(), 288 url.mount_filesystem_id(), kFileSystemTypeNativeLocal, 289 url.path(), url.filesystem_id(), url.mount_option()); 290 #endif 291 } 292 293 std::string mount_name; 294 FileSystemType cracked_type; 295 std::string cracked_id; 296 base::FilePath cracked_path; 297 FileSystemMountOption cracked_mount_option; 298 299 if (!CrackVirtualPath(virtual_path, &mount_name, &cracked_type, 300 &cracked_id, &cracked_path, &cracked_mount_option)) { 301 return FileSystemURL(); 302 } 303 304 return FileSystemURL( 305 url.origin(), url.mount_type(), url.virtual_path(), 306 !url.filesystem_id().empty() ? url.filesystem_id() : mount_name, 307 cracked_type, cracked_path, 308 cracked_id.empty() ? mount_name : cracked_id, cracked_mount_option); 309 } 310 311 bool ExternalMountPoints::ValidateNewMountPoint(const std::string& mount_name, 312 FileSystemType type, 313 const base::FilePath& path) { 314 lock_.AssertAcquired(); 315 316 // Mount name must not be empty. 317 if (mount_name.empty()) 318 return false; 319 320 // Verify there is no registered mount point with the same name. 321 NameToInstance::iterator found = instance_map_.find(mount_name); 322 if (found != instance_map_.end()) 323 return false; 324 325 // Allow empty paths. 326 if (path.empty()) 327 return true; 328 329 // Verify path is legal. 330 if (path.ReferencesParent() || !path.IsAbsolute()) 331 return false; 332 333 if (IsOverlappingMountPathForbidden(type)) { 334 // Check there the new path does not overlap with one of the existing ones. 335 std::map<base::FilePath, std::string>::reverse_iterator potential_parent( 336 path_to_name_map_.upper_bound(path)); 337 if (potential_parent != path_to_name_map_.rend()) { 338 if (potential_parent->first == path || 339 potential_parent->first.IsParent(path)) { 340 return false; 341 } 342 } 343 344 std::map<base::FilePath, std::string>::iterator potential_child = 345 path_to_name_map_.upper_bound(path); 346 if (potential_child != path_to_name_map_.end()) { 347 if (potential_child->first == path || 348 path.IsParent(potential_child->first)) { 349 return false; 350 } 351 } 352 } 353 354 return true; 355 } 356 357 } // namespace storage 358