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

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
16 namespace obfuscated_file_util {
17
18 ObfuscatedNameMap::ObfuscatedNameMap()
19 : directory_()
20 , map_valid_(false)
21 , map_next_valid_(false) {
michaeln 2011/02/11 00:03:34 Massage the style here to chrome-land style :) Or
ericu 2011/02/11 01:57:47 No, that's an accidental webkit-style leak.
22 }
23
24 void ObfuscatedNameMap::Init(const FilePath& directory) {
25 RollBack();
26 map_valid_ = false;
27 map_.clear();
28 map_next_valid_ = false;
29 map_next_.clear();
30 directory_ = directory;
31 }
32
33 PlatformFileError ObfuscatedNameMap::ClearNameFromObfuscatedName(
34 StringType obfuscated_name,
35 StringType* clear_name) {
36 CHECK(clear_name);
37 {
michaeln 2011/02/11 00:03:34 is the extra scope here intentional?
ericu 2011/02/11 01:57:47 It was just to limit the scope of error, but that
38 PlatformFileError error = LoadFromFile();
39 if (base::PLATFORM_FILE_OK != error)
40 return error;
41 }
42 MapType::iterator found = map_.find(obfuscated_name);
43 if (found == map_.end())
44 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
45 *clear_name = found->second;
46 return base::PLATFORM_FILE_OK;
47 }
48
49 StringType ObfuscatedNameMap::ObfuscatedNameFromClearName(
50 StringType clear_name) {
51 // FIXME: This is a hack to get around the fact that PRIxS produces an 8-bit
52 // string on all platforms, and the macro that would fix that gets evaluated
53 // *before* PRIxS does, so it can't be used.
54 FilePath temp;
55 return base::StringPrintf(
56 temp.AppendASCII("%08" PRIxS).BaseName().value().c_str(),
57 hash(FilePath(clear_name)));
58 }
59
60 PlatformFileError ObfuscatedNameMap::PrepareAdd(
61 const StringType& clear_name, const StringType& obfuscated_name) {
62 PlatformFileError error = InitDictNext();
63 if (base::PLATFORM_FILE_OK != error)
64 return error;
65 #ifndef NDEBUG
66 StringType test_name(ObfuscatedNameFromClearName(clear_name));
67 CHECK(test_name == obfuscated_name);
68 #endif
69 if (map_next_.find(obfuscated_name) != map_next_.end())
70 return base::PLATFORM_FILE_ERROR_EXISTS;
71 map_next_[obfuscated_name] = clear_name;
72 return WriteToNextFile();
73 }
74
75 PlatformFileError ObfuscatedNameMap::PrepareDelete(
76 const StringType& clear_name, const StringType& obfuscated_name) {
77 PlatformFileError error = InitDictNext();
78 if (base::PLATFORM_FILE_OK != error)
79 return error;
80 #ifndef NDEBUG
81 StringType test_name(ObfuscatedNameFromClearName(clear_name));
82 CHECK(test_name == obfuscated_name);
83 #endif
84
85 MapType::iterator found = map_next_.find(obfuscated_name);
86 if (found == map_next_.end())
87 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
88 map_next_.erase(found);
89 return WriteToNextFile();
90 }
91
92 PlatformFileError ObfuscatedNameMap::PrepareMove(
93 const StringType& old_clear_name,
94 const StringType& old_obfuscated_name,
95 const StringType& new_clear_name,
96 const StringType& new_obfuscated_name) {
97 PlatformFileError error = InitDictNext();
98 if (base::PLATFORM_FILE_OK != error)
99 return error;
100 #ifndef NDEBUG
101 StringType test_name(ObfuscatedNameFromClearName(old_clear_name));
102 CHECK(test_name == old_obfuscated_name);
103 test_name = ObfuscatedNameFromClearName(new_clear_name);
104 CHECK(test_name == new_obfuscated_name);
105 #endif
106 if (map_next_.find(new_obfuscated_name) != map_next_.end())
107 return base::PLATFORM_FILE_ERROR_EXISTS;
108 MapType::iterator old_found = map_next_.find(old_obfuscated_name);
109 if (old_found == map_next_.end())
110 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
111 map_next_.erase(old_found);
112 map_next_[new_obfuscated_name] = new_clear_name;
113 return WriteToNextFile();
114 }
115
116 PlatformFileError ObfuscatedNameMap::Commit() {
117 CHECK(map_next_valid_);
118 if (file_util::Move(GetDictionaryNextLocation(), GetDictionaryLocation())) {
119 map_ = map_next_;
michaeln 2011/02/11 00:03:34 i wonder if you can use the swap method here? ma
ericu 2011/02/11 01:57:47 Yup.
120 map_valid_ = map_next_valid_;
121 map_next_valid_ = false;
122 map_next_.clear();
123 return base::PLATFORM_FILE_OK;
124 } else {
125 return base::PLATFORM_FILE_ERROR_FAILED;
126 }
127 }
128
129 void ObfuscatedNameMap::RollBack() {
130 if (map_next_valid_) {
131 map_next_valid_ = false;
132 map_next_.clear();
133 // The file may or may not exist; we delete it and ignore errors.
134 file_util::Delete(GetDictionaryNextLocation(), false);
135 }
136 }
137
138 size_t ObfuscatedNameMap::hash(const FilePath& f) {
139 // TODO(ericu): This is temporary--find a real SHA1 implementation or the
140 // like somewhere in our existing codebase.
141 #if defined(COMPILER_GCC)
142 struct __gnu__cxx::hash<FilePath> gcc_hash;
143 return gcc_hash(f);
144 #elif defined(COMPILER_MSVC)
145 return stdext::hash_value(f);
146 #else
147 #error // Must define hashing function
148 #endif
149 }
150
151 FilePath ObfuscatedNameMap::GetDictionaryLocation() {
152 return directory_.Append(FilePath(DICTIONARY_NAME));
153 }
154
155 FilePath ObfuscatedNameMap::GetDictionaryNextLocation() {
156 return directory_.Append(FilePath(DICTIONARY_NEXT_NAME));
157 }
158
159 PlatformFileError ObfuscatedNameMap::LoadFromFile() {
michaeln 2011/02/11 00:03:34 maybe rename the method to make it more clear that
ericu 2011/02/11 01:57:47 Will do.
160 if (map_valid_)
161 return base::PLATFORM_FILE_OK;
162
163 PlatformFileError error = RecoverIfNeeded();
164 if (base::PLATFORM_FILE_OK != error)
165 return error;
166
167 if (!file_util::PathExists(GetDictionaryLocation())) {
168 map_valid_ = true; // It's just empty.
169 return base::PLATFORM_FILE_OK;
170 }
171
172 std::string contents;
173 if (!file_util::ReadFileToString(GetDictionaryLocation(), &contents))
174 return base::PLATFORM_FILE_ERROR_FAILED;
175 StringType data;
176 #if defined(OS_POSIX)
177 data = contents;
178 #elif defined(OS_WIN)
179 errno_t local_errno;
180 size_t size = 0;
181
182 local_errno = mbstowcs_s(&size, NULL, 0, contents.c_str(), 0);
183 // FIXME: If either of these two happen, the dictionary is corrupt or
184 // otherwise unusable. We should probably log an error and continue, but I
185 // won't do that yet.
186 // Max entries + entry length + a little padding.
187 if (local_errno || size > 4000 * (256 + 120))
188 return base::PLATFORM_FILE_ERROR_FAILED;
189 scoped_ptr<wchar_t> buffer(new wchar_t[size]);
190 local_errno =
191 mbstowcs_s(&size, buffer.get(), size, contents.c_str(), size - 1);
192 if (local_errno) {
193 return base::PLATFORM_FILE_ERROR_FAILED; // This should never happen.
194 }
195 data = buffer.get();
196 #else
197 #error // Must define the appropriate conversion here.
198 #endif
199 LoadMapFromString(&map_, data);
200 return base::PLATFORM_FILE_OK;
201 }
202
203 void ObfuscatedNameMap::LoadMapFromString(
204 MapType* map, const StringType& serialized) {
205 // Format: ([a-z0-0]+:[$legal_set_of_filename_chars]+:\n)*
206 // Empty is legal; it's easier than deleting the dict when we remove the
207 // last file from the dir, probably?
208 // Note that ':' is the separator *and* the terminator [along with the
209 // newline], since a newline could legally be in the filename.
210 StringType hash, name;
211 MapType temp_map;
212 size_t i;
213 for (i = 0; i < serialized.length(); ++i) {
214 size_t hash_start = i;
215 i = serialized.find(SEPARATOR, i);
216 if (i == StringType::npos)
217 break;
218 size_t hash_end = i;
219 size_t name_start = i + SEPARATOR.length();
220 size_t name_end = serialized.find(TERMINATOR, name_start);
221 if (name_end == StringType::npos)
222 break; // TODO(ericu): Log an error here, but continue?
223 i = name_end + TERMINATOR.length();
224 hash = serialized.substr(hash_start, hash_end - hash_start);
225 name = serialized.substr(name_start, name_end - name_start);
226 temp_map[hash] = name;
227 }
228 // if (i != serialized.length())
229 // ; // TODO(ericu): Log an error here, but continue?
230 map->swap(temp_map);
231 }
232
233 StringType ObfuscatedNameMap::ConvertMapToString(const MapType& map) {
234 StringType serialized;
235 for (MapType::const_iterator iter = map.begin();
236 iter != map.end(); ++iter) {
237 serialized += iter->first + SEPARATOR + iter->second + TERMINATOR;
238 }
239 return serialized;
240 }
241
242 PlatformFileError ObfuscatedNameMap::InitDictNext() {
michaeln 2011/02/11 00:03:34 InitNextMap()? This is the only place the term 'di
ericu 2011/02/11 01:57:47 I'll try to clean up the map vs. dict terminology.
243 CHECK(!map_next_valid_);
244 if (!map_valid_) {
michaeln 2011/02/11 00:03:34 Given how LoadFromFile works, i don't think you ne
ericu 2011/02/11 01:57:47 You mean just always call it, since it's lazy? Ye
245 PlatformFileError error = LoadFromFile();
246 if (base::PLATFORM_FILE_OK != error)
247 return error;
248 }
249 map_next_ = map_;
250 map_next_valid_ = true;
251 return base::PLATFORM_FILE_OK;
252 }
253
254 PlatformFileError ObfuscatedNameMap::RecoverIfNeeded() {
255 // TODO(ericu): if DICT_NEXT doesn't exist, return base::PLATFORM_FILE_OK.
256 // TODO(ericu): read map_ from DICT, map_next from DICT_NEXT.
257 // TODO(ericu): find the *one* element that differs.
258 // TODO(ericu): if it's not exactly one, goto handle_error
259 // TODO(ericu):
260 // if it's a deletion:
261 // if the file's gone, commit the dict, else revert the dict
262 // else if it's an addition:
263 // if the file's there, commit the dict, else revert the dict
264 // else // it's a move:
265 // if the old file's gone and the new file's there, commit the dict.
266 // if the old file's there and the new file's not, revert the dict.
267 // else goto handle_error
268 // return base::PLATFORM_FILE_OK;
269 // handle_error: either return an error here, or revert the dict so that
270 // progress can be made; it's a policy decision.
271 // either way, make sure that map_valid_ is valid.
272 //
273 return base::PLATFORM_FILE_OK;
274 }
275
276 PlatformFileError ObfuscatedNameMap::WriteToNextFile() {
277 StringType contents = ConvertMapToString(map_next_);
278 std::string data;
279 #if defined(OS_POSIX)
280 data = contents;
michaeln 2011/02/11 00:03:34 another potential swap() callsite?
ericu 2011/02/11 01:57:47 Yup.
281 #elif defined(OS_WIN)
282 static const size_t BUFFER_SIZE = 1024;
283 char buffer[BUFFER_SIZE];
284 errno_t error;
285 size_t offset = 0;
286 size_t converted;
287
288 while (offset < contents.length()) {
289 error = wcstombs_s(&converted, buffer, BUFFER_SIZE, contents.c_str() +
michaeln 2011/02/11 00:03:34 is there's no util that can do this conversion for
ericu 2011/02/11 01:57:47 I'm not sure that base is the place, given that I'
290 offset, _TRUNCATE);
291 if (error && error != STRUNCATE) {
292 return base::PLATFORM_FILE_ERROR_FAILED;
293 }
294 offset += converted;
295 data += buffer;
296 }
297 CHECK_EQ(contents.length(), data.length());
298 #else
299 #error // Must define the appropriate conversion here.
300 #endif
301 size_t bytes_written = 0;
302 FilePath location = GetDictionaryNextLocation();
303 while (bytes_written < data.length()) {
304 int write_response = file_util::WriteFile(
305 location, data.c_str() + bytes_written, data.length());
306 if (write_response <= 0) {
307 if (bytes_written)
308 file_util::Delete(GetDictionaryNextLocation(), false /* recursive */);
309 return base::PLATFORM_FILE_ERROR_FAILED;
310 }
311 bytes_written += write_response;
312 }
313 return base::PLATFORM_FILE_OK;
314 }
315
316 const StringType ObfuscatedNameMap::DICTIONARY_NAME =
michaeln 2011/02/11 00:03:34 Since StringType is a class with a constructor, i
ericu 2011/02/11 01:57:47 Good point. Converted to StringType::value_type.
317 FILE_PATH_LITERAL("dict");
318 const StringType ObfuscatedNameMap::DICTIONARY_NEXT_NAME =
319 FILE_PATH_LITERAL("dict_next");
320 const StringType ObfuscatedNameMap::SEPARATOR =
321 FILE_PATH_LITERAL(":");
322 const StringType ObfuscatedNameMap::TERMINATOR =
323 FILE_PATH_LITERAL(":\n");
324
325 } // namespace obfuscated_file_util
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698