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