Chromium Code Reviews
|
| 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/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 | |
| OLD | NEW |