OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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/fileapi/sandbox_mount_point_provider.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/message_loop.h" |
| 9 #include "base/message_loop_proxy.h" |
| 10 #include "base/rand_util.h" |
| 11 #include "base/scoped_callback_factory.h" |
| 12 #include "base/scoped_ptr.h" |
| 13 #include "base/string_util.h" |
| 14 #include "base/stringprintf.h" |
| 15 #include "base/utf_string_conversions.h" |
| 16 #include "googleurl/src/gurl.h" |
| 17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h" |
| 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" |
| 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" |
| 20 #include "webkit/fileapi/file_system_path_manager.h" |
| 21 #include "webkit/fileapi/file_system_util.h" |
| 22 #include "webkit/glue/webkit_glue.h" |
| 23 |
| 24 namespace { |
| 25 |
| 26 static const FilePath::CharType kFileSystemUniqueNamePrefix[] = |
| 27 FILE_PATH_LITERAL("chrome-"); |
| 28 static const int kFileSystemUniqueLength = 16; |
| 29 static const unsigned kFileSystemUniqueDirectoryNameLength = |
| 30 kFileSystemUniqueLength + arraysize(kFileSystemUniqueNamePrefix) - 1; |
| 31 |
| 32 // Restricted names. |
| 33 // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions |
| 34 static const char* const kRestrictedNames[] = { |
| 35 "con", "prn", "aux", "nul", |
| 36 "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", |
| 37 "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", |
| 38 }; |
| 39 |
| 40 // Restricted chars. |
| 41 static const FilePath::CharType kRestrictedChars[] = { |
| 42 '/', '\\', '<', '>', ':', '?', '*', '"', '|', |
| 43 }; |
| 44 |
| 45 inline std::string FilePathStringToASCII( |
| 46 const FilePath::StringType& path_string) { |
| 47 #if defined(OS_WIN) |
| 48 return WideToASCII(path_string); |
| 49 #elif defined(OS_POSIX) |
| 50 return path_string; |
| 51 #endif |
| 52 } |
| 53 |
| 54 FilePath::StringType CreateUniqueDirectoryName(const GURL& origin_url) { |
| 55 // This can be anything but need to be unpredictable. |
| 56 static const FilePath::CharType letters[] = FILE_PATH_LITERAL( |
| 57 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); |
| 58 FilePath::StringType unique(kFileSystemUniqueNamePrefix); |
| 59 for (int i = 0; i < kFileSystemUniqueLength; ++i) |
| 60 unique += letters[base::RandInt(0, arraysize(letters) - 2)]; |
| 61 return unique; |
| 62 } |
| 63 |
| 64 bool ReadOriginDirectory(const FilePath& base_path, |
| 65 const GURL& origin_url, |
| 66 FilePath* unique) { |
| 67 file_util::FileEnumerator file_enum( |
| 68 base_path, false /* recursive */, |
| 69 file_util::FileEnumerator::DIRECTORIES, |
| 70 FilePath::StringType(kFileSystemUniqueNamePrefix) + |
| 71 FILE_PATH_LITERAL("*")); |
| 72 FilePath current; |
| 73 bool found = false; |
| 74 while (!(current = file_enum.Next()).empty()) { |
| 75 if (current.BaseName().value().length() != |
| 76 kFileSystemUniqueDirectoryNameLength) |
| 77 continue; |
| 78 if (found) { |
| 79 // TODO(kinuko): Should notify the user to ask for some action. |
| 80 LOG(WARNING) << "Unexpectedly found more than one FileSystem " |
| 81 << "directories for " << origin_url; |
| 82 return false; |
| 83 } |
| 84 found = true; |
| 85 *unique = current; |
| 86 } |
| 87 return !unique->empty(); |
| 88 } |
| 89 |
| 90 FilePath GetFileSystemRootPathOnFileThreadHelper( |
| 91 const GURL& origin_url, const FilePath &origin_base_path, bool create) { |
| 92 FilePath root; |
| 93 if (ReadOriginDirectory(origin_base_path, origin_url, &root)) |
| 94 return root; |
| 95 |
| 96 if (!create) |
| 97 return FilePath(); |
| 98 |
| 99 // Creates the root directory. |
| 100 root = origin_base_path.Append(CreateUniqueDirectoryName(origin_url)); |
| 101 if (!file_util::CreateDirectory(root)) |
| 102 return FilePath(); |
| 103 |
| 104 return root; |
| 105 } |
| 106 |
| 107 } // anonymous namespace |
| 108 |
| 109 namespace fileapi { |
| 110 |
| 111 const FilePath::CharType SandboxMountPointProvider::kFileSystemDirectory[] = |
| 112 FILE_PATH_LITERAL("FileSystem"); |
| 113 |
| 114 const char SandboxMountPointProvider::kPersistentName[] = "Persistent"; |
| 115 const char SandboxMountPointProvider::kTemporaryName[] = "Temporary"; |
| 116 |
| 117 SandboxMountPointProvider::SandboxMountPointProvider( |
| 118 FileSystemPathManager* path_manager, |
| 119 scoped_refptr<base::MessageLoopProxy> file_message_loop, |
| 120 const FilePath& profile_path) |
| 121 : path_manager_(path_manager), |
| 122 file_message_loop_(file_message_loop), |
| 123 base_path_(profile_path.Append(kFileSystemDirectory)) { |
| 124 } |
| 125 |
| 126 bool SandboxMountPointProvider::IsAccessAllowed(const GURL& origin_url) { |
| 127 // We essentially depend on quota to do our access controls. |
| 128 return path_manager_->IsAllowedScheme(origin_url); |
| 129 } |
| 130 |
| 131 class SandboxMountPointProvider::GetFileSystemRootPathTask |
| 132 : public base::RefCountedThreadSafe< |
| 133 SandboxMountPointProvider::GetFileSystemRootPathTask> { |
| 134 public: |
| 135 GetFileSystemRootPathTask( |
| 136 scoped_refptr<base::MessageLoopProxy> file_message_loop, |
| 137 const std::string& name, |
| 138 FileSystemPathManager::GetRootPathCallback* callback) |
| 139 : file_message_loop_(file_message_loop), |
| 140 origin_message_loop_proxy_( |
| 141 base::MessageLoopProxy::CreateForCurrentThread()), |
| 142 name_(name), |
| 143 callback_(callback) { |
| 144 } |
| 145 |
| 146 void Start(const GURL& origin_url, |
| 147 const FilePath& origin_base_path, |
| 148 bool create) { |
| 149 file_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 150 &GetFileSystemRootPathTask::GetFileSystemRootPathOnFileThread, |
| 151 origin_url, origin_base_path, create)); |
| 152 } |
| 153 |
| 154 private: |
| 155 void GetFileSystemRootPathOnFileThread( |
| 156 const GURL& origin_url, |
| 157 const FilePath& origin_base_path, |
| 158 bool create) { |
| 159 DispatchCallbackOnCallerThread( |
| 160 GetFileSystemRootPathOnFileThreadHelper( |
| 161 origin_url, origin_base_path, create)); |
| 162 } |
| 163 |
| 164 void DispatchCallbackOnCallerThread(const FilePath& root_path) { |
| 165 origin_message_loop_proxy_->PostTask(FROM_HERE, |
| 166 NewRunnableMethod(this, &GetFileSystemRootPathTask::DispatchCallback, |
| 167 root_path)); |
| 168 } |
| 169 |
| 170 void DispatchCallback(const FilePath& root_path) { |
| 171 callback_->Run(!root_path.empty(), root_path, name_); |
| 172 callback_.reset(); |
| 173 } |
| 174 |
| 175 scoped_refptr<base::MessageLoopProxy> file_message_loop_; |
| 176 scoped_refptr<base::MessageLoopProxy> origin_message_loop_proxy_; |
| 177 std::string name_; |
| 178 scoped_ptr<FileSystemPathManager::GetRootPathCallback> callback_; |
| 179 }; |
| 180 |
| 181 bool SandboxMountPointProvider::IsRestrictedFileName(const FilePath& filename) |
| 182 const { |
| 183 if (filename.value().empty()) |
| 184 return false; |
| 185 |
| 186 if (IsWhitespace(filename.value()[filename.value().size() - 1]) || |
| 187 filename.value()[filename.value().size() - 1] == '.') |
| 188 return true; |
| 189 |
| 190 std::string filename_lower = StringToLowerASCII( |
| 191 FilePathStringToASCII(filename.value())); |
| 192 |
| 193 for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) { |
| 194 // Exact match. |
| 195 if (filename_lower == kRestrictedNames[i]) |
| 196 return true; |
| 197 // Starts with "RESTRICTED_NAME.". |
| 198 if (filename_lower.find(std::string(kRestrictedNames[i]) + ".") == 0) |
| 199 return true; |
| 200 } |
| 201 |
| 202 for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) { |
| 203 if (filename.value().find(kRestrictedChars[i]) != |
| 204 FilePath::StringType::npos) |
| 205 return true; |
| 206 } |
| 207 |
| 208 return false; |
| 209 } |
| 210 |
| 211 void SandboxMountPointProvider::GetFileSystemRootPath( |
| 212 const GURL& origin_url, fileapi::FileSystemType type, |
| 213 bool create, FileSystemPathManager::GetRootPathCallback* callback_ptr) { |
| 214 scoped_ptr<FileSystemPathManager::GetRootPathCallback> callback(callback_ptr); |
| 215 std::string name; |
| 216 FilePath origin_base_path; |
| 217 |
| 218 if (!GetOriginBasePathAndName(origin_url, &origin_base_path, type, &name)) { |
| 219 callback->Run(false, FilePath(), std::string()); |
| 220 return; |
| 221 } |
| 222 |
| 223 scoped_refptr<GetFileSystemRootPathTask> task( |
| 224 new GetFileSystemRootPathTask(file_message_loop_, |
| 225 name, |
| 226 callback.release())); |
| 227 task->Start(origin_url, origin_base_path, create); |
| 228 }; |
| 229 |
| 230 FilePath SandboxMountPointProvider::GetFileSystemRootPathOnFileThread( |
| 231 const GURL& origin_url, FileSystemType type, bool create) { |
| 232 FilePath origin_base_path; |
| 233 if (!GetOriginBasePathAndName(origin_url, &origin_base_path, type, NULL)) { |
| 234 return FilePath(); |
| 235 } |
| 236 return GetFileSystemRootPathOnFileThreadHelper( |
| 237 origin_url, origin_base_path, create); |
| 238 } |
| 239 |
| 240 // static |
| 241 std::string SandboxMountPointProvider::GetOriginIdentifierFromURL( |
| 242 const GURL& url) { |
| 243 WebKit::WebSecurityOrigin web_security_origin = |
| 244 WebKit::WebSecurityOrigin::createFromString(UTF8ToUTF16(url.spec())); |
| 245 return web_security_origin.databaseIdentifier().utf8(); |
| 246 } |
| 247 |
| 248 // static |
| 249 FilePath SandboxMountPointProvider::GetFileSystemBaseDirectoryForOriginAndType( |
| 250 const FilePath& base_path, const std::string& origin_identifier, |
| 251 fileapi::FileSystemType type) { |
| 252 if (origin_identifier.empty()) |
| 253 return FilePath(); |
| 254 std::string type_string = |
| 255 FileSystemPathManager::GetFileSystemTypeString(type); |
| 256 if (type_string.empty()) { |
| 257 LOG(WARNING) << "Unknown filesystem type is requested:" << type; |
| 258 return FilePath(); |
| 259 } |
| 260 return base_path.AppendASCII(origin_identifier) |
| 261 .AppendASCII(type_string); |
| 262 } |
| 263 |
| 264 SandboxMountPointProvider::OriginEnumerator::OriginEnumerator( |
| 265 const FilePath& base_path) |
| 266 : enumerator_(base_path, false /* recursive */, |
| 267 file_util::FileEnumerator::DIRECTORIES) { |
| 268 } |
| 269 |
| 270 std::string SandboxMountPointProvider::OriginEnumerator::Next() { |
| 271 current_ = enumerator_.Next(); |
| 272 return FilePathStringToASCII(current_.BaseName().value()); |
| 273 } |
| 274 |
| 275 bool SandboxMountPointProvider::OriginEnumerator::HasTemporary() { |
| 276 return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( |
| 277 SandboxMountPointProvider::kTemporaryName)); |
| 278 } |
| 279 |
| 280 bool SandboxMountPointProvider::OriginEnumerator::HasPersistent() { |
| 281 return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( |
| 282 SandboxMountPointProvider::kPersistentName)); |
| 283 } |
| 284 |
| 285 bool SandboxMountPointProvider::GetOriginBasePathAndName( |
| 286 const GURL& origin_url, |
| 287 FilePath* origin_base_path, |
| 288 FileSystemType type, |
| 289 std::string* name) { |
| 290 |
| 291 if (path_manager_->is_incognito()) |
| 292 // TODO(kinuko): return an isolated temporary directory. |
| 293 return false; |
| 294 |
| 295 if (!path_manager_->IsAllowedScheme(origin_url)) |
| 296 return false; |
| 297 |
| 298 std::string origin_identifier = GetOriginIdentifierFromURL(origin_url); |
| 299 *origin_base_path = GetFileSystemBaseDirectoryForOriginAndType( |
| 300 base_path(), origin_identifier, type); |
| 301 if (origin_base_path->empty()) |
| 302 return false; |
| 303 |
| 304 std::string type_string = |
| 305 FileSystemPathManager::GetFileSystemTypeString(type); |
| 306 DCHECK(!type_string.empty()); |
| 307 if (name) |
| 308 *name = origin_identifier + ":" + type_string; |
| 309 return true; |
| 310 } |
| 311 |
| 312 } // namespace fileapi |
OLD | NEW |