OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/fileapi/file_system_path_manager.h" | 5 #include "webkit/fileapi/file_system_path_manager.h" |
6 | 6 |
7 #include "base/rand_util.h" | 7 #include "base/rand_util.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
11 #include "base/scoped_callback_factory.h" | 11 #include "base/scoped_callback_factory.h" |
12 #include "base/scoped_ptr.h" | 12 #include "base/scoped_ptr.h" |
13 #include "base/stringprintf.h" | 13 #include "base/stringprintf.h" |
14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
15 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
16 #include "googleurl/src/gurl.h" | 16 #include "googleurl/src/gurl.h" |
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h" | 17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h" |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystem.h" | 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystem.h" |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" |
kinuko
2011/03/14 11:03:57
line 17,19 not needed
ericu
2011/03/15 02:43:11
Done.
| |
20 #include "webkit/fileapi/file_system_util.h" | |
21 #include "webkit/fileapi/sandbox_mount_point_provider.h" | |
20 #include "webkit/glue/webkit_glue.h" | 22 #include "webkit/glue/webkit_glue.h" |
21 | 23 |
22 // We use some of WebKit types for conversions between origin identifiers | 24 // We use some of WebKit types for conversions between origin identifiers |
23 // and origin URLs. | 25 // and origin URLs. |
24 using WebKit::WebFileSystem; | 26 using WebKit::WebFileSystem; |
25 using WebKit::WebSecurityOrigin; | 27 using WebKit::WebSecurityOrigin; |
26 using WebKit::WebString; | 28 using WebKit::WebString; |
kinuko
2011/03/14 11:03:57
line 27,28 not needed
ericu
2011/03/15 02:43:11
Done.
| |
27 | 29 |
28 using base::PlatformFileError; | 30 using base::PlatformFileError; |
29 | 31 |
30 namespace fileapi { | |
31 | |
32 const FilePath::CharType FileSystemPathManager::kFileSystemDirectory[] = | |
33 FILE_PATH_LITERAL("FileSystem"); | |
34 | |
35 const char FileSystemPathManager::kPersistentName[] = "Persistent"; | |
36 const char FileSystemPathManager::kTemporaryName[] = "Temporary"; | |
37 | |
38 static const FilePath::CharType kFileSystemUniqueNamePrefix[] = | |
39 FILE_PATH_LITERAL("chrome-"); | |
40 static const int kFileSystemUniqueLength = 16; | |
41 static const unsigned kFileSystemUniqueDirectoryNameLength = | |
42 kFileSystemUniqueLength + arraysize(kFileSystemUniqueNamePrefix) - 1; | |
43 | |
44 namespace { | |
45 | |
46 // Restricted names. | |
47 // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions | |
48 static const char* const kRestrictedNames[] = { | |
49 "con", "prn", "aux", "nul", | |
50 "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", | |
51 "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", | |
52 }; | |
53 | |
54 // Restricted chars. | |
55 static const FilePath::CharType kRestrictedChars[] = { | |
56 '/', '\\', '<', '>', ':', '?', '*', '"', '|', | |
57 }; | |
58 | |
59 inline std::string FilePathStringToASCII( | |
60 const FilePath::StringType& path_string) { | |
61 #if defined(OS_WIN) | |
62 return WideToASCII(path_string); | |
63 #elif defined(OS_POSIX) | |
64 return path_string; | |
65 #endif | |
66 } | |
67 | |
68 FilePath::StringType CreateUniqueDirectoryName(const GURL& origin_url) { | |
69 // This can be anything but need to be unpredictable. | |
70 static const FilePath::CharType letters[] = FILE_PATH_LITERAL( | |
71 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); | |
72 FilePath::StringType unique(kFileSystemUniqueNamePrefix); | |
73 for (int i = 0; i < kFileSystemUniqueLength; ++i) | |
74 unique += letters[base::RandInt(0, arraysize(letters) - 2)]; | |
75 return unique; | |
76 } | |
77 | |
78 static const char kExtensionScheme[] = "chrome-extension"; | 32 static const char kExtensionScheme[] = "chrome-extension"; |
79 | 33 |
80 } // anonymous namespace | 34 namespace fileapi { |
81 | |
82 class FileSystemPathManager::GetFileSystemRootPathTask | |
83 : public base::RefCountedThreadSafe< | |
84 FileSystemPathManager::GetFileSystemRootPathTask> { | |
85 public: | |
86 GetFileSystemRootPathTask( | |
87 scoped_refptr<base::MessageLoopProxy> file_message_loop, | |
88 const std::string& name, | |
89 FileSystemPathManager::GetRootPathCallback* callback) | |
90 : file_message_loop_(file_message_loop), | |
91 origin_message_loop_proxy_( | |
92 base::MessageLoopProxy::CreateForCurrentThread()), | |
93 name_(name), | |
94 callback_(callback) { | |
95 } | |
96 | |
97 void Start(const GURL& origin_url, | |
98 const FilePath& origin_base_path, | |
99 bool create) { | |
100 file_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | |
101 &GetFileSystemRootPathTask::GetFileSystemRootPathOnFileThread, | |
102 origin_url, origin_base_path, create)); | |
103 } | |
104 | |
105 private: | |
106 void GetFileSystemRootPathOnFileThread( | |
107 const GURL& origin_url, | |
108 const FilePath& base_path, | |
109 bool create) { | |
110 FilePath root; | |
111 if (ReadOriginDirectory(base_path, origin_url, &root)) { | |
112 DispatchCallbackOnCallerThread(root); | |
113 return; | |
114 } | |
115 | |
116 if (!create) { | |
117 DispatchCallbackOnCallerThread(FilePath()); | |
118 return; | |
119 } | |
120 | |
121 // Creates the root directory. | |
122 root = base_path.Append(CreateUniqueDirectoryName(origin_url)); | |
123 if (!file_util::CreateDirectory(root)) { | |
124 DispatchCallbackOnCallerThread(FilePath()); | |
125 return; | |
126 } | |
127 DispatchCallbackOnCallerThread(root); | |
128 } | |
129 | |
130 bool ReadOriginDirectory(const FilePath& base_path, | |
131 const GURL& origin_url, | |
132 FilePath* unique) { | |
133 file_util::FileEnumerator file_enum( | |
134 base_path, false /* recursive */, | |
135 file_util::FileEnumerator::DIRECTORIES, | |
136 FilePath::StringType(kFileSystemUniqueNamePrefix) + | |
137 FILE_PATH_LITERAL("*")); | |
138 FilePath current; | |
139 bool found = false; | |
140 while (!(current = file_enum.Next()).empty()) { | |
141 if (current.BaseName().value().length() != | |
142 kFileSystemUniqueDirectoryNameLength) | |
143 continue; | |
144 if (found) { | |
145 // TODO(kinuko): Should notify the user to ask for some action. | |
146 LOG(WARNING) << "Unexpectedly found more than one FileSystem " | |
147 << "directories for " << origin_url; | |
148 return false; | |
149 } | |
150 found = true; | |
151 *unique = current; | |
152 } | |
153 return !unique->empty(); | |
154 } | |
155 | |
156 void DispatchCallbackOnCallerThread(const FilePath& root_path) { | |
157 origin_message_loop_proxy_->PostTask(FROM_HERE, | |
158 NewRunnableMethod(this, &GetFileSystemRootPathTask::DispatchCallback, | |
159 root_path)); | |
160 } | |
161 | |
162 void DispatchCallback(const FilePath& root_path) { | |
163 callback_->Run(!root_path.empty(), root_path, name_); | |
164 callback_.reset(); | |
165 } | |
166 | |
167 scoped_refptr<base::MessageLoopProxy> file_message_loop_; | |
168 scoped_refptr<base::MessageLoopProxy> origin_message_loop_proxy_; | |
169 std::string name_; | |
170 scoped_ptr<FileSystemPathManager::GetRootPathCallback> callback_; | |
171 }; | |
172 | 35 |
173 FileSystemPathManager::FileSystemPathManager( | 36 FileSystemPathManager::FileSystemPathManager( |
174 scoped_refptr<base::MessageLoopProxy> file_message_loop, | 37 scoped_refptr<base::MessageLoopProxy> file_message_loop, |
175 const FilePath& profile_path, | 38 const FilePath& profile_path, |
176 bool is_incognito, | 39 bool is_incognito, |
177 bool allow_file_access_from_files) | 40 bool allow_file_access_from_files) |
178 : file_message_loop_(file_message_loop), | 41 : is_incognito_(is_incognito), |
179 base_path_(profile_path.Append(kFileSystemDirectory)), | 42 allow_file_access_from_files_(allow_file_access_from_files), |
180 is_incognito_(is_incognito), | 43 sandbox_provider_( |
181 allow_file_access_from_files_(allow_file_access_from_files) { | 44 new SandboxMountPointProvider( |
45 ALLOW_THIS_IN_INITIALIZER_LIST(this), | |
46 file_message_loop, | |
47 profile_path)) { | |
182 } | 48 } |
183 | 49 |
184 FileSystemPathManager::~FileSystemPathManager() {} | 50 FileSystemPathManager::~FileSystemPathManager() {} |
185 | 51 |
186 void FileSystemPathManager::GetFileSystemRootPath( | 52 void FileSystemPathManager::GetFileSystemRootPath( |
187 const GURL& origin_url, fileapi::FileSystemType type, | 53 const GURL& origin_url, fileapi::FileSystemType type, |
188 bool create, GetRootPathCallback* callback_ptr) { | 54 bool create, GetRootPathCallback* callback_ptr) { |
189 scoped_ptr<GetRootPathCallback> callback(callback_ptr); | 55 |
190 if (is_incognito_) { | 56 switch (type) { |
191 // TODO(kinuko): return an isolated temporary directory. | 57 case kFileSystemTypeTemporary: |
192 callback->Run(false, FilePath(), std::string()); | 58 case kFileSystemTypePersistent: |
193 return; | 59 sandbox_provider_->GetFileSystemRootPath( |
60 origin_url, type, create, callback_ptr); | |
61 break; | |
62 case kFileSystemTypeUnknown: | |
63 default: | |
64 NOTREACHED(); | |
65 callback_ptr->Run(false, FilePath(), std::string()); | |
194 } | 66 } |
67 } | |
195 | 68 |
196 if (!IsAllowedScheme(origin_url)) { | 69 FilePath FileSystemPathManager::GetFileSystemRootPathOnFileThread( |
197 callback->Run(false, FilePath(), std::string()); | 70 const GURL& origin_url, FileSystemType type, bool create) { |
198 return; | 71 switch (type) { |
72 case kFileSystemTypeTemporary: | |
73 case kFileSystemTypePersistent: | |
74 return sandbox_provider_->GetFileSystemRootPathOnFileThread( | |
75 origin_url, type, create); | |
76 break; | |
77 case kFileSystemTypeUnknown: | |
78 default: | |
79 NOTREACHED(); | |
80 return FilePath(); | |
199 } | 81 } |
200 | |
201 std::string origin_identifier = GetOriginIdentifierFromURL(origin_url); | |
202 FilePath origin_base_path = GetFileSystemBaseDirectoryForOriginAndType( | |
203 base_path(), origin_identifier, type); | |
204 if (origin_base_path.empty()) { | |
205 callback->Run(false, FilePath(), std::string()); | |
206 return; | |
207 } | |
208 | |
209 std::string type_string = GetFileSystemTypeString(type); | |
210 DCHECK(!type_string.empty()); | |
211 | |
212 scoped_refptr<GetFileSystemRootPathTask> task( | |
213 new GetFileSystemRootPathTask(file_message_loop_, | |
214 origin_identifier + ":" + type_string, | |
215 callback.release())); | |
216 task->Start(origin_url, origin_base_path, create); | |
217 } | 82 } |
218 | 83 |
219 bool FileSystemPathManager::CrackFileSystemPath( | 84 bool FileSystemPathManager::CrackFileSystemPath( |
220 const FilePath& path, GURL* origin_url, FileSystemType* type, | 85 const FilePath& path, GURL* origin_url, FileSystemType* type, |
221 FilePath* virtual_path) const { | 86 FilePath* virtual_path) const { |
222 // Any paths that includes parent references are considered invalid. | 87 // TODO(ericu): |
223 if (path.ReferencesParent()) | 88 // Paths come in here [for now] as a URL, followed by a virtual path in |
89 // platform format. For example, on Windows, this will look like | |
90 // filesystem:http://www.example.com/temporary/\path\to\file.txt. | |
91 // A potentially dangerous malicious path on Windows might look like: | |
92 // filesystem:http://www.example.com/temporary/foo/../../\path\to\file.txt. | |
93 // This code is ugly, but will get cleaned up as we fix the calling side. | |
94 // We should be passing these to WebKit as string, not FilePath, for ease of | |
95 // manipulation, or possibly as GURL/KURL. | |
96 | |
97 std::string path_as_string; | |
98 #ifdef OS_WIN | |
99 path_as_string = WideToUTF8(path.value()); | |
100 #else | |
101 path_as_string = path.value(); | |
102 #endif | |
103 GURL path_as_url(path_as_string); | |
104 | |
105 FilePath local_path; | |
106 GURL local_url; | |
107 FileSystemType local_type; | |
108 if (!CrackFileSystemURL(path_as_url, &local_url, &local_type, &local_path)) | |
224 return false; | 109 return false; |
225 | 110 |
226 // The path should be a child of the profile FileSystem path. | 111 #if defined(FILE_PATH_USES_WIN_SEPARATORS) |
227 FilePath relative; | 112 // TODO(ericu): This puts the separators back to windows-standard; they come |
228 if (!base_path_.AppendRelativePath(path, &relative)) | 113 // out of the above code as '/' no matter the platform. Long-term, we'll |
229 return false; | 114 // want to let the underlying FileSystemFileUtil implementation do this part, |
115 // since they won't all need it. | |
116 local_path = local_path.NormalizeWindowsPathSeparators(); | |
117 #endif | |
230 | 118 |
231 // The relative path from the profile FileSystem path should contain | 119 // Any paths that include parent references are considered invalid. |
232 // at least three components, one for storage identifier, one for type | 120 // These should have been taken care of in CrackFileSystemURL. |
233 // and one for the 'unique' part. | 121 DCHECK(!local_path.ReferencesParent()); |
kinuko
2011/03/14 11:03:57
Can't we get rid of these checks (line 111 to 121)
ericu
2011/03/15 02:43:11
111->117 fixes up separators that are the wrong wa
kinuko
2011/03/16 19:33:38
Ok... can we add some comments about that? (A sho
ericu
2011/03/16 23:47:31
Added a small addendum to the upper comment in thi
| |
234 std::vector<FilePath::StringType> components; | |
235 relative.GetComponents(&components); | |
236 if (components.size() < 3) | |
237 return false; | |
238 | 122 |
239 // The second component of the relative path to the root directory | 123 // The given |local_path| seems valid. Populates the |origin_url|, |type| |
240 // must be kPersistent or kTemporary. | |
241 if (!IsStringASCII(components[1])) | |
242 return false; | |
243 | |
244 std::string ascii_type_component = FilePathStringToASCII(components[1]); | |
245 FileSystemType cracked_type = kFileSystemTypeUnknown; | |
246 if (ascii_type_component == kPersistentName) | |
247 cracked_type = kFileSystemTypePersistent; | |
248 else if (ascii_type_component == kTemporaryName) | |
249 cracked_type = kFileSystemTypeTemporary; | |
250 else | |
251 return false; | |
252 | |
253 DCHECK(cracked_type != kFileSystemTypeUnknown); | |
254 | |
255 // The given |path| seems valid. Populates the |origin_url|, |type| | |
256 // and |virtual_path| if they are given. | 124 // and |virtual_path| if they are given. |
257 | 125 |
258 if (origin_url) { | 126 if (origin_url) { |
259 WebSecurityOrigin web_security_origin = | 127 *origin_url = local_url; |
260 WebSecurityOrigin::createFromDatabaseIdentifier( | |
261 webkit_glue::FilePathStringToWebString(components[0])); | |
262 *origin_url = GURL(web_security_origin.toString()); | |
263 | |
264 // We need this work-around for file:/// URIs as | |
265 // createFromDatabaseIdentifier returns empty origin_url for them. | |
266 if (allow_file_access_from_files_ && origin_url->spec().empty() && | |
267 components[0].find(FILE_PATH_LITERAL("file")) == 0) | |
268 *origin_url = GURL("file:///"); | |
269 } | 128 } |
270 | 129 |
271 if (type) | 130 if (type) |
272 *type = cracked_type; | 131 *type = local_type; |
273 | 132 |
274 if (virtual_path) { | 133 if (virtual_path) { |
275 virtual_path->clear(); | 134 *virtual_path = local_path; |
276 for (size_t i = 3; i < components.size(); ++i) | |
277 *virtual_path = virtual_path->Append(components[i]); | |
278 } | 135 } |
279 | 136 |
280 return true; | 137 return true; |
281 } | 138 } |
282 | 139 |
283 bool FileSystemPathManager::IsAllowedScheme(const GURL& url) const { | 140 bool FileSystemPathManager::IsAllowedScheme(const GURL& url) const { |
284 // Basically we only accept http or https. We allow file:// URLs | 141 // Basically we only accept http or https. We allow file:// URLs |
285 // only if --allow-file-access-from-files flag is given. | 142 // only if --allow-file-access-from-files flag is given. |
286 return url.SchemeIs("http") || url.SchemeIs("https") || | 143 return url.SchemeIs("http") || url.SchemeIs("https") || |
287 url.SchemeIs(kExtensionScheme) || | 144 url.SchemeIs(kExtensionScheme) || |
288 (url.SchemeIsFile() && allow_file_access_from_files_); | 145 (url.SchemeIsFile() && allow_file_access_from_files_); |
289 } | 146 } |
290 | 147 |
291 // static | 148 // static |
292 bool FileSystemPathManager::IsRestrictedFileName(const FilePath& filename) { | |
293 if (filename.value().empty()) | |
294 return false; | |
295 | |
296 if (IsWhitespace(filename.value()[filename.value().size() - 1]) || | |
297 filename.value()[filename.value().size() - 1] == '.') | |
298 return true; | |
299 | |
300 std::string filename_lower = StringToLowerASCII( | |
301 FilePathStringToASCII(filename.value())); | |
302 | |
303 for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) { | |
304 // Exact match. | |
305 if (filename_lower == kRestrictedNames[i]) | |
306 return true; | |
307 // Starts with "RESTRICTED_NAME.". | |
308 if (filename_lower.find(std::string(kRestrictedNames[i]) + ".") == 0) | |
309 return true; | |
310 } | |
311 | |
312 for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) { | |
313 if (filename.value().find(kRestrictedChars[i]) != | |
314 FilePath::StringType::npos) | |
315 return true; | |
316 } | |
317 | |
318 return false; | |
319 } | |
320 | |
321 // static | |
322 std::string FileSystemPathManager::GetFileSystemTypeString( | 149 std::string FileSystemPathManager::GetFileSystemTypeString( |
323 fileapi::FileSystemType type) { | 150 fileapi::FileSystemType type) { |
324 if (type == fileapi::kFileSystemTypeTemporary) | 151 if (type == fileapi::kFileSystemTypeTemporary) |
325 return fileapi::FileSystemPathManager::kTemporaryName; | 152 return fileapi::SandboxMountPointProvider::kTemporaryName; |
326 else if (type == fileapi::kFileSystemTypePersistent) | 153 else if (type == fileapi::kFileSystemTypePersistent) |
327 return fileapi::FileSystemPathManager::kPersistentName; | 154 return fileapi::SandboxMountPointProvider::kPersistentName; |
328 return std::string(); | 155 return std::string(); |
329 } | 156 } |
330 | 157 |
331 // static | 158 // Checks if a given |name| contains any restricted names/chars in it. |
332 std::string FileSystemPathManager::GetOriginIdentifierFromURL( | 159 bool FileSystemPathManager::IsRestrictedFileName( |
333 const GURL& url) { | 160 FileSystemType type, const FilePath& filename) { |
334 WebKit::WebSecurityOrigin web_security_origin = | 161 switch (type) { |
335 WebKit::WebSecurityOrigin::createFromString(UTF8ToUTF16(url.spec())); | 162 case kFileSystemTypeTemporary: |
336 return web_security_origin.databaseIdentifier().utf8(); | 163 case kFileSystemTypePersistent: |
337 } | 164 return sandbox_provider_->IsRestrictedFileName(filename); |
338 | 165 case kFileSystemTypeUnknown: |
339 // static | 166 default: |
340 FilePath FileSystemPathManager::GetFileSystemBaseDirectoryForOriginAndType( | 167 NOTREACHED(); |
341 const FilePath& base_path, const std::string& origin_identifier, | 168 return true; |
342 fileapi::FileSystemType type) { | |
343 if (origin_identifier.empty()) | |
344 return FilePath(); | |
345 std::string type_string = GetFileSystemTypeString(type); | |
346 if (type_string.empty()) { | |
347 LOG(WARNING) << "Unknown filesystem type is requested:" << type; | |
348 return FilePath(); | |
349 } | 169 } |
350 return base_path.AppendASCII(origin_identifier) | |
351 .AppendASCII(type_string); | |
352 } | |
353 | |
354 FileSystemPathManager::OriginEnumerator::OriginEnumerator( | |
355 const FilePath& base_path) | |
356 : enumerator_(base_path, false /* recursive */, | |
357 file_util::FileEnumerator::DIRECTORIES) { | |
358 } | |
359 | |
360 std::string FileSystemPathManager::OriginEnumerator::Next() { | |
361 current_ = enumerator_.Next(); | |
362 return FilePathStringToASCII(current_.BaseName().value()); | |
363 } | |
364 | |
365 bool FileSystemPathManager::OriginEnumerator::HasTemporary() { | |
366 return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( | |
367 FileSystemPathManager::kTemporaryName)); | |
368 } | |
369 | |
370 bool FileSystemPathManager::OriginEnumerator::HasPersistent() { | |
371 return !current_.empty() && file_util::DirectoryExists(current_.AppendASCII( | |
372 FileSystemPathManager::kPersistentName)); | |
373 } | 170 } |
374 | 171 |
375 } // namespace fileapi | 172 } // namespace fileapi |
376 | 173 |
377 COMPILE_ASSERT(int(WebFileSystem::TypeTemporary) == \ | 174 COMPILE_ASSERT(int(WebFileSystem::TypeTemporary) == \ |
378 int(fileapi::kFileSystemTypeTemporary), mismatching_enums); | 175 int(fileapi::kFileSystemTypeTemporary), mismatching_enums); |
379 COMPILE_ASSERT(int(WebFileSystem::TypePersistent) == \ | 176 COMPILE_ASSERT(int(WebFileSystem::TypePersistent) == \ |
380 int(fileapi::kFileSystemTypePersistent), mismatching_enums); | 177 int(fileapi::kFileSystemTypePersistent), mismatching_enums); |
OLD | NEW |