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