Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(114)

Side by Side Diff: webkit/fileapi/obfuscated_name_map.cc

Issue 6286038: Add initial code to do filename munging in the FileSystem.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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/obfuscated_name_map.h"
6
7 #if defined(OS_WIN)
8 #include <errno.h>
9 #endif
10
11 #include "base/file_util.h"
12 #include "base/format_macros.h"
13 #include "base/logging.h"
14 #include "base/stringprintf.h"
15 #include "base/sys_string_conversions.h"
16
17 namespace fileapi {
18
19 ObfuscatedNameMap::ObfuscatedNameMap()
20 : directory_(),
21 map_valid_(false),
22 map_next_valid_(false) {
23 }
24
25 void ObfuscatedNameMap::Init(const FilePath& directory) {
26 Rollback();
27 map_valid_ = false;
28 map_.clear();
29 map_next_valid_ = false;
30 map_next_.clear();
31 directory_ = directory;
32 }
33
34 PlatformFileError ObfuscatedNameMap::ClearNameFromObfuscatedName(
35 StringType obfuscated_name,
36 StringType* clear_name) {
37 CHECK(clear_name);
38 PlatformFileError error = LoadFromFileIfNeeded();
39 if (base::PLATFORM_FILE_OK != error)
40 return error;
41 MapType::iterator found = map_.find(obfuscated_name);
42 if (found == map_.end())
43 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
44 *clear_name = found->second;
45 return base::PLATFORM_FILE_OK;
46 }
47
48 StringType ObfuscatedNameMap::ObfuscatedNameFromClearName(
49 StringType clear_name) {
50 // FIXME: This is a hack to get around the fact that PRIxS produces an 8-bit
51 // string on all platforms, and the macro that would fix that gets evaluated
52 // *before* PRIxS does, so it can't be used.
53 FilePath temp;
54 return base::StringPrintf(
55 temp.AppendASCII("%08" PRIxS).BaseName().value().c_str(),
56 hash(FilePath(clear_name)));
57 }
58
59 PlatformFileError ObfuscatedNameMap::PrepareAdd(
60 const StringType& clear_name, const StringType& obfuscated_name) {
61 PlatformFileError error = InitMapNext();
62 if (base::PLATFORM_FILE_OK != error)
63 return error;
64 #ifndef NDEBUG
65 StringType test_name(ObfuscatedNameFromClearName(clear_name));
66 CHECK(test_name == obfuscated_name);
67 #endif
68 if (map_next_.find(obfuscated_name) != map_next_.end())
69 return base::PLATFORM_FILE_ERROR_EXISTS;
70 map_next_[obfuscated_name] = clear_name;
71 return WriteToNextFile();
72 }
73
74 PlatformFileError ObfuscatedNameMap::PrepareDelete(
75 const StringType& clear_name, const StringType& obfuscated_name) {
76 PlatformFileError error = InitMapNext();
77 if (base::PLATFORM_FILE_OK != error)
78 return error;
79 #ifndef NDEBUG
80 StringType test_name(ObfuscatedNameFromClearName(clear_name));
81 CHECK(test_name == obfuscated_name);
82 #endif
83
84 MapType::iterator found = map_next_.find(obfuscated_name);
85 if (found == map_next_.end())
86 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
87 map_next_.erase(found);
88 return WriteToNextFile();
89 }
90
91 PlatformFileError ObfuscatedNameMap::PrepareMove(
92 const StringType& old_clear_name,
93 const StringType& old_obfuscated_name,
94 const StringType& new_clear_name,
95 const StringType& new_obfuscated_name) {
96 PlatformFileError error = InitMapNext();
97 if (base::PLATFORM_FILE_OK != error)
98 return error;
99 #ifndef NDEBUG
100 StringType test_name(ObfuscatedNameFromClearName(old_clear_name));
101 CHECK(test_name == old_obfuscated_name);
102 test_name = ObfuscatedNameFromClearName(new_clear_name);
103 CHECK(test_name == new_obfuscated_name);
104 #endif
105 if (map_next_.find(new_obfuscated_name) != map_next_.end())
106 return base::PLATFORM_FILE_ERROR_EXISTS;
107 MapType::iterator old_found = map_next_.find(old_obfuscated_name);
108 if (old_found == map_next_.end())
109 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
110 map_next_.erase(old_found);
111 map_next_[new_obfuscated_name] = new_clear_name;
112 return WriteToNextFile();
113 }
114
115 PlatformFileError ObfuscatedNameMap::Commit() {
116 CHECK(map_next_valid_);
117 if (file_util::Move(GetDictionaryNextLocation(), GetDictionaryLocation())) {
118 map_.swap(map_next_);
119 map_valid_ = map_next_valid_;
120 map_next_valid_ = false;
121 map_next_.clear();
122 return base::PLATFORM_FILE_OK;
123 } else {
124 return base::PLATFORM_FILE_ERROR_FAILED;
125 }
126 }
127
128 void ObfuscatedNameMap::Rollback() {
129 if (map_next_valid_) {
130 map_next_valid_ = false;
131 map_next_.clear();
132 // The file may or may not exist; we delete it and ignore errors.
133 file_util::Delete(GetDictionaryNextLocation(), false);
134 }
135 }
136
137 size_t ObfuscatedNameMap::hash(const FilePath& f) {
138 // TODO(ericu): This is temporary--find a real SHA1 implementation or the
139 // like somewhere in our existing codebase.
140 #if defined(COMPILER_GCC)
141 struct __gnu__cxx::hash<FilePath> gcc_hash;
Dai Mikurube (google.com) 2011/02/22 02:57:35 s/__gnu__cxx/__gnu_cxx/
142 return gcc_hash(f);
143 #elif defined(COMPILER_MSVC)
144 return stdext::hash_value(f);
145 #else
146 #error // Must define hashing function
147 #endif
148 }
149
150 FilePath ObfuscatedNameMap::GetDictionaryLocation() {
151 return directory_.Append(StringType(DICTIONARY_NAME));
152 }
153
154 FilePath ObfuscatedNameMap::GetDictionaryNextLocation() {
155 return directory_.Append(StringType(DICTIONARY_NEXT_NAME));
156 }
157
158 PlatformFileError ObfuscatedNameMap::LoadFromFileIfNeeded() {
159 if (map_valid_)
160 return base::PLATFORM_FILE_OK;
161
162 PlatformFileError error = RecoverIfNeeded();
163 if (base::PLATFORM_FILE_OK != error)
164 return error;
165
166 if (!file_util::PathExists(GetDictionaryLocation())) {
167 map_valid_ = true; // It's just empty.
168 return base::PLATFORM_FILE_OK;
169 }
170
171 std::string contents;
172 if (!file_util::ReadFileToString(GetDictionaryLocation(), &contents))
173 return base::PLATFORM_FILE_ERROR_FAILED;
174 StringType data;
175 #if defined(OS_POSIX)
176 data = contents;
177 #elif defined(OS_WIN)
178 errno_t local_errno;
179 size_t size = 0;
180
181 local_errno = mbstowcs_s(&size, NULL, 0, contents.c_str(), 0);
182 // FIXME: If either of these two happen, the dictionary is corrupt or
183 // otherwise unusable. We should probably log an error and continue, but I
184 // won't do that yet.
185 // Max entries + entry length + a little padding.
186 if (local_errno || size > 4000 * (256 + 120))
187 return base::PLATFORM_FILE_ERROR_FAILED;
188 scoped_ptr<wchar_t> buffer(new wchar_t[size]);
189 local_errno =
190 mbstowcs_s(&size, buffer.get(), size, contents.c_str(), size - 1);
191 if (local_errno) {
192 return base::PLATFORM_FILE_ERROR_FAILED; // This should never happen.
193 }
194 data = buffer.get();
195 #else
196 #error // Must define the appropriate conversion here.
197 #endif
198 LoadMapFromString(&map_, data);
199 return base::PLATFORM_FILE_OK;
200 }
201
202 void ObfuscatedNameMap::LoadMapFromString(
203 MapType* map, const StringType& serialized) {
204 // Format: ([a-z0-0]+:[$legal_set_of_filename_chars]+:\n)*
205 // Empty is legal; it's easier than deleting the dict when we remove the
206 // last file from the dir, probably?
207 // Note that ':' is the separator *and* the terminator [along with the
208 // newline], since a newline could legally be in the filename.
209 StringType hash, name;
210 MapType temp_map;
211 size_t i;
212 for (i = 0; i < serialized.length(); ++i) {
213 size_t hash_start = i;
214 i = serialized.find(SEPARATOR, i);
215 if (i == StringType::npos)
216 break;
217 size_t hash_end = i;
218 size_t name_start = i + SEPARATOR_LENGTH;
219 size_t name_end = serialized.find(TERMINATOR, name_start);
220 if (name_end == StringType::npos)
221 break; // TODO(ericu): Log an error here, but continue?
222 i = name_end + TERMINATOR_LENGTH;
223 hash = serialized.substr(hash_start, hash_end - hash_start);
224 name = serialized.substr(name_start, name_end - name_start);
225 temp_map[hash] = name;
226 }
227 // if (i != serialized.length())
228 // ; // TODO(ericu): Log an error here, but continue?
229 map->swap(temp_map);
230 }
231
232 StringType ObfuscatedNameMap::ConvertMapToString(const MapType& map) {
233 StringType serialized;
234 for (MapType::const_iterator iter = map.begin();
235 iter != map.end(); ++iter) {
236 serialized += iter->first + SEPARATOR + iter->second + TERMINATOR;
237 }
238 return serialized;
239 }
240
241 PlatformFileError ObfuscatedNameMap::InitMapNext() {
242 CHECK(!map_next_valid_);
243 PlatformFileError error = LoadFromFileIfNeeded();
244 if (base::PLATFORM_FILE_OK != error)
245 return error;
246 map_next_ = map_;
247 map_next_valid_ = true;
248 return base::PLATFORM_FILE_OK;
249 }
250
251 PlatformFileError ObfuscatedNameMap::RecoverIfNeeded() {
252 // TODO(ericu): if DICT_NEXT doesn't exist, return base::PLATFORM_FILE_OK.
253 // TODO(ericu): read map_ from DICT, map_next from DICT_NEXT.
254 // TODO(ericu): find the *one* element that differs.
255 // TODO(ericu): if it's not exactly one, goto handle_error
256 // TODO(ericu):
257 // if it's a deletion:
258 // if the file's gone, commit the dict, else revert the dict
259 // else if it's an addition:
260 // if the file's there, commit the dict, else revert the dict
261 // else // it's a move:
262 // if the old file's gone and the new file's there, commit the dict.
263 // if the old file's there and the new file's not, revert the dict.
264 // else goto handle_error
265 // return base::PLATFORM_FILE_OK;
266 // handle_error: either return an error here, or revert the dict so that
267 // progress can be made; it's a policy decision.
268 // either way, make sure that map_valid_ is valid.
269 //
270 return base::PLATFORM_FILE_OK;
271 }
272
273 PlatformFileError ObfuscatedNameMap::WriteToNextFile() {
274 StringType contents = ConvertMapToString(map_next_);
275 std::string data;
276 #if defined(OS_POSIX)
277 data.swap(contents);
278 #elif defined(OS_WIN)
279 data = base::SysWideToUTF8(contents);
280 #else
281 #error // Must define the appropriate conversion here.
282 #endif
283 size_t bytes_written = 0;
284 FilePath location = GetDictionaryNextLocation();
285 while (bytes_written < data.length()) {
286 int write_response = file_util::WriteFile(
287 location, data.c_str() + bytes_written, data.length());
288 if (write_response <= 0) {
289 if (bytes_written)
290 file_util::Delete(GetDictionaryNextLocation(), false /* recursive */);
291 return base::PLATFORM_FILE_ERROR_FAILED;
292 }
293 bytes_written += write_response;
294 }
295 return base::PLATFORM_FILE_OK;
296 }
297
298 const StringType::value_type ObfuscatedNameMap::DICTIONARY_NAME[] =
299 FILE_PATH_LITERAL("dict");
300 const StringType::value_type ObfuscatedNameMap::DICTIONARY_NEXT_NAME[] =
301 FILE_PATH_LITERAL("dict_next");
302 const StringType::value_type ObfuscatedNameMap::SEPARATOR[] =
303 FILE_PATH_LITERAL(":");
304 const size_t ObfuscatedNameMap::SEPARATOR_LENGTH = 1;
305 const StringType::value_type ObfuscatedNameMap::TERMINATOR[] =
306 FILE_PATH_LITERAL(":\n");
307 const size_t ObfuscatedNameMap::TERMINATOR_LENGTH = 2;
308
309 } // namespace fileapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698