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