Classifier.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  1. //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "Classifier.h"
  6. #include "LookupCacheV4.h"
  7. #include "nsIPrefBranch.h"
  8. #include "nsIPrefService.h"
  9. #include "nsISimpleEnumerator.h"
  10. #include "nsIRandomGenerator.h"
  11. #include "nsIInputStream.h"
  12. #include "nsISeekableStream.h"
  13. #include "nsIFile.h"
  14. #include "nsNetCID.h"
  15. #include "nsPrintfCString.h"
  16. #include "nsThreadUtils.h"
  17. #include "mozilla/Logging.h"
  18. #include "mozilla/SyncRunnable.h"
  19. #include "mozilla/Base64.h"
  20. #include "mozilla/Unused.h"
  21. #include "mozilla/TypedEnumBits.h"
  22. #include "nsIUrlClassifierUtils.h"
  23. #include "nsUrlClassifierDBService.h"
  24. // MOZ_LOG=UrlClassifierDbService:5
  25. extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
  26. #define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
  27. #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
  28. #define STORE_DIRECTORY NS_LITERAL_CSTRING("safebrowsing")
  29. #define TO_DELETE_DIR_SUFFIX NS_LITERAL_CSTRING("-to_delete")
  30. #define BACKUP_DIR_SUFFIX NS_LITERAL_CSTRING("-backup")
  31. #define METADATA_SUFFIX NS_LITERAL_CSTRING(".metadata")
  32. namespace mozilla {
  33. namespace safebrowsing {
  34. namespace {
  35. // A scoped-clearer for nsTArray<TableUpdate*>.
  36. // The owning elements will be deleted and the array itself
  37. // will be cleared on exiting the scope.
  38. class ScopedUpdatesClearer {
  39. public:
  40. explicit ScopedUpdatesClearer(nsTArray<TableUpdate*> *aUpdates)
  41. : mUpdatesArrayRef(aUpdates)
  42. {
  43. for (auto update : *aUpdates) {
  44. mUpdatesPointerHolder.AppendElement(update);
  45. }
  46. }
  47. ~ScopedUpdatesClearer()
  48. {
  49. mUpdatesArrayRef->Clear();
  50. }
  51. private:
  52. nsTArray<TableUpdate*>* mUpdatesArrayRef;
  53. nsTArray<nsAutoPtr<TableUpdate>> mUpdatesPointerHolder;
  54. };
  55. } // End of unnamed namespace.
  56. void
  57. Classifier::SplitTables(const nsACString& str, nsTArray<nsCString>& tables)
  58. {
  59. tables.Clear();
  60. nsACString::const_iterator begin, iter, end;
  61. str.BeginReading(begin);
  62. str.EndReading(end);
  63. while (begin != end) {
  64. iter = begin;
  65. FindCharInReadable(',', iter, end);
  66. nsDependentCSubstring table = Substring(begin,iter);
  67. if (!table.IsEmpty()) {
  68. tables.AppendElement(Substring(begin, iter));
  69. }
  70. begin = iter;
  71. if (begin != end) {
  72. begin++;
  73. }
  74. }
  75. }
  76. nsresult
  77. Classifier::GetPrivateStoreDirectory(nsIFile* aRootStoreDirectory,
  78. const nsACString& aTableName,
  79. const nsACString& aProvider,
  80. nsIFile** aPrivateStoreDirectory)
  81. {
  82. NS_ENSURE_ARG_POINTER(aPrivateStoreDirectory);
  83. if (!StringEndsWith(aTableName, NS_LITERAL_CSTRING("-proto"))) {
  84. // Only V4 table names (ends with '-proto') would be stored
  85. // to per-provider sub-directory.
  86. nsCOMPtr<nsIFile>(aRootStoreDirectory).forget(aPrivateStoreDirectory);
  87. return NS_OK;
  88. }
  89. if (aProvider.IsEmpty()) {
  90. // When failing to get provider, just store in the root folder.
  91. nsCOMPtr<nsIFile>(aRootStoreDirectory).forget(aPrivateStoreDirectory);
  92. return NS_OK;
  93. }
  94. nsCOMPtr<nsIFile> providerDirectory;
  95. // Clone first since we are gonna create a new directory.
  96. nsresult rv = aRootStoreDirectory->Clone(getter_AddRefs(providerDirectory));
  97. NS_ENSURE_SUCCESS(rv, rv);
  98. // Append the provider name to the root store directory.
  99. rv = providerDirectory->AppendNative(aProvider);
  100. NS_ENSURE_SUCCESS(rv, rv);
  101. // Ensure existence of the provider directory.
  102. bool dirExists;
  103. rv = providerDirectory->Exists(&dirExists);
  104. NS_ENSURE_SUCCESS(rv, rv);
  105. if (!dirExists) {
  106. LOG(("Creating private directory for %s", nsCString(aTableName).get()));
  107. rv = providerDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
  108. NS_ENSURE_SUCCESS(rv, rv);
  109. providerDirectory.forget(aPrivateStoreDirectory);
  110. return rv;
  111. }
  112. // Store directory exists. Check if it's a directory.
  113. bool isDir;
  114. rv = providerDirectory->IsDirectory(&isDir);
  115. NS_ENSURE_SUCCESS(rv, rv);
  116. if (!isDir) {
  117. return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  118. }
  119. providerDirectory.forget(aPrivateStoreDirectory);
  120. return NS_OK;
  121. }
  122. Classifier::Classifier()
  123. {
  124. }
  125. Classifier::~Classifier()
  126. {
  127. Close();
  128. }
  129. nsresult
  130. Classifier::SetupPathNames()
  131. {
  132. // Get the root directory where to store all the databases.
  133. nsresult rv = mCacheDirectory->Clone(getter_AddRefs(mRootStoreDirectory));
  134. NS_ENSURE_SUCCESS(rv, rv);
  135. rv = mRootStoreDirectory->AppendNative(STORE_DIRECTORY);
  136. NS_ENSURE_SUCCESS(rv, rv);
  137. // Make sure LookupCaches (which are persistent and survive updates)
  138. // are reading/writing in the right place. We will be moving their
  139. // files "underneath" them during backup/restore.
  140. for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
  141. mLookupCaches[i]->UpdateRootDirHandle(mRootStoreDirectory);
  142. }
  143. // Directory where to move a backup before an update.
  144. rv = mCacheDirectory->Clone(getter_AddRefs(mBackupDirectory));
  145. NS_ENSURE_SUCCESS(rv, rv);
  146. rv = mBackupDirectory->AppendNative(STORE_DIRECTORY + BACKUP_DIR_SUFFIX);
  147. NS_ENSURE_SUCCESS(rv, rv);
  148. // Directory where to move the backup so we can atomically
  149. // delete (really move) it.
  150. rv = mCacheDirectory->Clone(getter_AddRefs(mToDeleteDirectory));
  151. NS_ENSURE_SUCCESS(rv, rv);
  152. rv = mToDeleteDirectory->AppendNative(STORE_DIRECTORY + TO_DELETE_DIR_SUFFIX);
  153. NS_ENSURE_SUCCESS(rv, rv);
  154. return NS_OK;
  155. }
  156. nsresult
  157. Classifier::CreateStoreDirectory()
  158. {
  159. // Ensure the safebrowsing directory exists.
  160. bool storeExists;
  161. nsresult rv = mRootStoreDirectory->Exists(&storeExists);
  162. NS_ENSURE_SUCCESS(rv, rv);
  163. if (!storeExists) {
  164. rv = mRootStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
  165. NS_ENSURE_SUCCESS(rv, rv);
  166. } else {
  167. bool storeIsDir;
  168. rv = mRootStoreDirectory->IsDirectory(&storeIsDir);
  169. NS_ENSURE_SUCCESS(rv, rv);
  170. if (!storeIsDir)
  171. return NS_ERROR_FILE_DESTINATION_NOT_DIR;
  172. }
  173. return NS_OK;
  174. }
  175. nsresult
  176. Classifier::Open(nsIFile& aCacheDirectory)
  177. {
  178. // Remember the Local profile directory.
  179. nsresult rv = aCacheDirectory.Clone(getter_AddRefs(mCacheDirectory));
  180. NS_ENSURE_SUCCESS(rv, rv);
  181. // Create the handles to the update and backup directories.
  182. rv = SetupPathNames();
  183. NS_ENSURE_SUCCESS(rv, rv);
  184. // Clean up any to-delete directories that haven't been deleted yet.
  185. rv = CleanToDelete();
  186. NS_ENSURE_SUCCESS(rv, rv);
  187. // Check whether we have an incomplete update and recover from the
  188. // backup if so.
  189. rv = RecoverBackups();
  190. NS_ENSURE_SUCCESS(rv, rv);
  191. // Make sure the main store directory exists.
  192. rv = CreateStoreDirectory();
  193. NS_ENSURE_SUCCESS(rv, rv);
  194. mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
  195. NS_ENSURE_SUCCESS(rv, rv);
  196. // Build the list of know urlclassifier lists
  197. // XXX: Disk IO potentially on the main thread during startup
  198. RegenActiveTables();
  199. return NS_OK;
  200. }
  201. void
  202. Classifier::Close()
  203. {
  204. DropStores();
  205. }
  206. void
  207. Classifier::Reset()
  208. {
  209. DropStores();
  210. mRootStoreDirectory->Remove(true);
  211. mBackupDirectory->Remove(true);
  212. mToDeleteDirectory->Remove(true);
  213. CreateStoreDirectory();
  214. mTableFreshness.Clear();
  215. RegenActiveTables();
  216. }
  217. void
  218. Classifier::ResetTables(ClearType aType, const nsTArray<nsCString>& aTables)
  219. {
  220. for (uint32_t i = 0; i < aTables.Length(); i++) {
  221. LOG(("Resetting table: %s", aTables[i].get()));
  222. // Spoil this table by marking it as no known freshness
  223. mTableFreshness.Remove(aTables[i]);
  224. LookupCache *cache = GetLookupCache(aTables[i]);
  225. if (cache) {
  226. // Remove any cached Completes for this table if clear type is Clear_Cache
  227. if (aType == Clear_Cache) {
  228. cache->ClearCache();
  229. } else {
  230. cache->ClearAll();
  231. }
  232. }
  233. }
  234. // Clear on-disk database if clear type is Clear_All
  235. if (aType == Clear_All) {
  236. DeleteTables(mRootStoreDirectory, aTables);
  237. RegenActiveTables();
  238. }
  239. }
  240. void
  241. Classifier::DeleteTables(nsIFile* aDirectory, const nsTArray<nsCString>& aTables)
  242. {
  243. nsCOMPtr<nsISimpleEnumerator> entries;
  244. nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
  245. NS_ENSURE_SUCCESS_VOID(rv);
  246. bool hasMore;
  247. while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
  248. nsCOMPtr<nsISupports> supports;
  249. rv = entries->GetNext(getter_AddRefs(supports));
  250. NS_ENSURE_SUCCESS_VOID(rv);
  251. nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
  252. NS_ENSURE_TRUE_VOID(file);
  253. // If |file| is a directory, recurse to find its entries as well.
  254. bool isDirectory;
  255. if (NS_FAILED(file->IsDirectory(&isDirectory))) {
  256. continue;
  257. }
  258. if (isDirectory) {
  259. DeleteTables(file, aTables);
  260. continue;
  261. }
  262. nsCString leafName;
  263. rv = file->GetNativeLeafName(leafName);
  264. NS_ENSURE_SUCCESS_VOID(rv);
  265. leafName.Truncate(leafName.RFind("."));
  266. if (aTables.Contains(leafName)) {
  267. if (NS_FAILED(file->Remove(false))) {
  268. NS_WARNING(nsPrintfCString("Fail to remove file %s from the disk",
  269. leafName.get()).get());
  270. }
  271. }
  272. }
  273. NS_ENSURE_SUCCESS_VOID(rv);
  274. }
  275. void
  276. Classifier::AbortUpdateAndReset(const nsCString& aTable)
  277. {
  278. // We don't need to reset while shutting down. It will only slow us down.
  279. if (nsUrlClassifierDBService::ShutdownHasStarted()) {
  280. return;
  281. }
  282. LOG(("Abort updating table %s.", aTable.get()));
  283. // ResetTables will clear both in-memory & on-disk data.
  284. ResetTables(Clear_All, nsTArray<nsCString> { aTable });
  285. // Remove the backup and delete directory since we are aborting
  286. // from an update.
  287. Unused << RemoveBackupTables();
  288. Unused << CleanToDelete();
  289. }
  290. void
  291. Classifier::TableRequest(nsACString& aResult)
  292. {
  293. // Generating v2 table info.
  294. nsTArray<nsCString> tables;
  295. ActiveTables(tables);
  296. for (uint32_t i = 0; i < tables.Length(); i++) {
  297. HashStore store(tables[i], GetProvider(tables[i]), mRootStoreDirectory);
  298. nsresult rv = store.Open();
  299. if (NS_FAILED(rv))
  300. continue;
  301. aResult.Append(store.TableName());
  302. aResult.Append(';');
  303. ChunkSet &adds = store.AddChunks();
  304. ChunkSet &subs = store.SubChunks();
  305. if (adds.Length() > 0) {
  306. aResult.AppendLiteral("a:");
  307. nsAutoCString addList;
  308. adds.Serialize(addList);
  309. aResult.Append(addList);
  310. }
  311. if (subs.Length() > 0) {
  312. if (adds.Length() > 0)
  313. aResult.Append(':');
  314. aResult.AppendLiteral("s:");
  315. nsAutoCString subList;
  316. subs.Serialize(subList);
  317. aResult.Append(subList);
  318. }
  319. aResult.Append('\n');
  320. }
  321. // Load meta data from *.metadata files in the root directory.
  322. // Specifically for v4 tables.
  323. nsCString metadata;
  324. nsresult rv = LoadMetadata(mRootStoreDirectory, metadata);
  325. NS_ENSURE_SUCCESS_VOID(rv);
  326. aResult.Append(metadata);
  327. }
  328. // This is used to record the matching statistics for v2 and v4.
  329. enum class PrefixMatch : uint8_t {
  330. eNoMatch = 0x00,
  331. eMatchV2Prefix = 0x01,
  332. eMatchV4Prefix = 0x02,
  333. eMatchBoth = eMatchV2Prefix | eMatchV4Prefix
  334. };
  335. MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(PrefixMatch)
  336. nsresult
  337. Classifier::Check(const nsACString& aSpec,
  338. const nsACString& aTables,
  339. uint32_t aFreshnessGuarantee,
  340. LookupResultArray& aResults)
  341. {
  342. // Get the set of fragments based on the url. This is necessary because we
  343. // only look up at most 5 URLs per aSpec, even if aSpec has more than 5
  344. // components.
  345. nsTArray<nsCString> fragments;
  346. nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments);
  347. NS_ENSURE_SUCCESS(rv, rv);
  348. nsTArray<nsCString> activeTables;
  349. SplitTables(aTables, activeTables);
  350. nsTArray<LookupCache*> cacheArray;
  351. for (uint32_t i = 0; i < activeTables.Length(); i++) {
  352. LOG(("Checking table %s", activeTables[i].get()));
  353. LookupCache *cache = GetLookupCache(activeTables[i]);
  354. if (cache) {
  355. cacheArray.AppendElement(cache);
  356. } else {
  357. return NS_ERROR_FAILURE;
  358. }
  359. }
  360. PrefixMatch matchingStatistics = PrefixMatch::eNoMatch;
  361. // Now check each lookup fragment against the entries in the DB.
  362. for (uint32_t i = 0; i < fragments.Length(); i++) {
  363. Completion lookupHash;
  364. lookupHash.FromPlaintext(fragments[i], mCryptoHash);
  365. if (LOG_ENABLED()) {
  366. nsAutoCString checking;
  367. lookupHash.ToHexString(checking);
  368. LOG(("Checking fragment %s, hash %s (%X)", fragments[i].get(),
  369. checking.get(), lookupHash.ToUint32()));
  370. }
  371. for (uint32_t i = 0; i < cacheArray.Length(); i++) {
  372. LookupCache *cache = cacheArray[i];
  373. bool has, complete;
  374. if (LookupCache::Cast<LookupCacheV4>(cache)) {
  375. // TODO Bug 1312339 Return length in LookupCache.Has and support
  376. // VariableLengthPrefix in LookupResultArray
  377. rv = cache->Has(lookupHash, &has, &complete);
  378. if (NS_FAILED(rv)) {
  379. LOG(("Failed to lookup fragment %s V4", fragments[i].get()));
  380. }
  381. if (has) {
  382. matchingStatistics |= PrefixMatch::eMatchV4Prefix;
  383. // TODO: Bug 1311935 - Implement Safe Browsing v4 caching
  384. // Should check cache expired
  385. }
  386. continue;
  387. }
  388. rv = cache->Has(lookupHash, &has, &complete);
  389. NS_ENSURE_SUCCESS(rv, rv);
  390. if (has) {
  391. LookupResult *result = aResults.AppendElement();
  392. if (!result)
  393. return NS_ERROR_OUT_OF_MEMORY;
  394. int64_t age;
  395. bool found = mTableFreshness.Get(cache->TableName(), &age);
  396. if (!found) {
  397. age = 24 * 60 * 60; // just a large number
  398. } else {
  399. int64_t now = (PR_Now() / PR_USEC_PER_SEC);
  400. age = now - age;
  401. }
  402. LOG(("Found a result in %s: %s (Age: %Lds)",
  403. cache->TableName().get(),
  404. complete ? "complete." : "Not complete.",
  405. age));
  406. result->hash.complete = lookupHash;
  407. result->mComplete = complete;
  408. result->mFresh = (age < aFreshnessGuarantee);
  409. result->mTableName.Assign(cache->TableName());
  410. matchingStatistics |= PrefixMatch::eMatchV2Prefix;
  411. }
  412. }
  413. }
  414. return NS_OK;
  415. }
  416. nsresult
  417. Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates)
  418. {
  419. PRIntervalTime clockStart = 0;
  420. if (LOG_ENABLED()) {
  421. clockStart = PR_IntervalNow();
  422. }
  423. nsresult rv;
  424. {
  425. ScopedUpdatesClearer scopedUpdatesClearer(aUpdates);
  426. LOG(("Backup before update."));
  427. rv = BackupTables();
  428. NS_ENSURE_SUCCESS(rv, rv);
  429. LOG(("Applying %d table updates.", aUpdates->Length()));
  430. for (uint32_t i = 0; i < aUpdates->Length(); i++) {
  431. // Previous UpdateHashStore() may have consumed this update..
  432. if ((*aUpdates)[i]) {
  433. // Run all updates for one table
  434. nsCString updateTable(aUpdates->ElementAt(i)->TableName());
  435. if (TableUpdate::Cast<TableUpdateV2>((*aUpdates)[i])) {
  436. rv = UpdateHashStore(aUpdates, updateTable);
  437. } else {
  438. rv = UpdateTableV4(aUpdates, updateTable);
  439. }
  440. if (NS_FAILED(rv)) {
  441. if (rv != NS_ERROR_OUT_OF_MEMORY) {
  442. #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
  443. DumpFailedUpdate();
  444. #endif
  445. AbortUpdateAndReset(updateTable);
  446. }
  447. return rv;
  448. }
  449. }
  450. }
  451. } // End of scopedUpdatesClearer scope.
  452. rv = RegenActiveTables();
  453. NS_ENSURE_SUCCESS(rv, rv);
  454. LOG(("Cleaning up backups."));
  455. // Move the backup directory away (signaling the transaction finished
  456. // successfully). This is atomic.
  457. rv = RemoveBackupTables();
  458. NS_ENSURE_SUCCESS(rv, rv);
  459. // Do the actual deletion of the backup files.
  460. rv = CleanToDelete();
  461. NS_ENSURE_SUCCESS(rv, rv);
  462. LOG(("Done applying updates."));
  463. if (LOG_ENABLED()) {
  464. PRIntervalTime clockEnd = PR_IntervalNow();
  465. LOG(("update took %dms\n",
  466. PR_IntervalToMilliseconds(clockEnd - clockStart)));
  467. }
  468. return NS_OK;
  469. }
  470. nsresult
  471. Classifier::ApplyFullHashes(nsTArray<TableUpdate*>* aUpdates)
  472. {
  473. LOG(("Applying %d table gethashes.", aUpdates->Length()));
  474. ScopedUpdatesClearer scopedUpdatesClearer(aUpdates);
  475. for (uint32_t i = 0; i < aUpdates->Length(); i++) {
  476. TableUpdate *update = aUpdates->ElementAt(i);
  477. nsresult rv = UpdateCache(update);
  478. NS_ENSURE_SUCCESS(rv, rv);
  479. aUpdates->ElementAt(i) = nullptr;
  480. }
  481. return NS_OK;
  482. }
  483. int64_t
  484. Classifier::GetLastUpdateTime(const nsACString& aTableName)
  485. {
  486. int64_t age;
  487. bool found = mTableFreshness.Get(aTableName, &age);
  488. return found ? (age * PR_MSEC_PER_SEC) : 0;
  489. }
  490. void
  491. Classifier::SetLastUpdateTime(const nsACString &aTable,
  492. uint64_t updateTime)
  493. {
  494. LOG(("Marking table %s as last updated on %u",
  495. PromiseFlatCString(aTable).get(), updateTime));
  496. mTableFreshness.Put(aTable, updateTime / PR_MSEC_PER_SEC);
  497. }
  498. void
  499. Classifier::DropStores()
  500. {
  501. for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
  502. delete mLookupCaches[i];
  503. }
  504. mLookupCaches.Clear();
  505. }
  506. nsresult
  507. Classifier::RegenActiveTables()
  508. {
  509. mActiveTablesCache.Clear();
  510. nsTArray<nsCString> foundTables;
  511. ScanStoreDir(foundTables);
  512. for (uint32_t i = 0; i < foundTables.Length(); i++) {
  513. nsCString table(foundTables[i]);
  514. HashStore store(table, GetProvider(table), mRootStoreDirectory);
  515. nsresult rv = store.Open();
  516. if (NS_FAILED(rv))
  517. continue;
  518. LookupCache *lookupCache = GetLookupCache(store.TableName());
  519. if (!lookupCache) {
  520. continue;
  521. }
  522. if (!lookupCache->IsPrimed())
  523. continue;
  524. const ChunkSet &adds = store.AddChunks();
  525. const ChunkSet &subs = store.SubChunks();
  526. if (adds.Length() == 0 && subs.Length() == 0)
  527. continue;
  528. LOG(("Active table: %s", store.TableName().get()));
  529. mActiveTablesCache.AppendElement(store.TableName());
  530. }
  531. return NS_OK;
  532. }
  533. nsresult
  534. Classifier::ScanStoreDir(nsTArray<nsCString>& aTables)
  535. {
  536. nsCOMPtr<nsISimpleEnumerator> entries;
  537. nsresult rv = mRootStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries));
  538. NS_ENSURE_SUCCESS(rv, rv);
  539. bool hasMore;
  540. while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
  541. nsCOMPtr<nsISupports> supports;
  542. rv = entries->GetNext(getter_AddRefs(supports));
  543. NS_ENSURE_SUCCESS(rv, rv);
  544. nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
  545. nsCString leafName;
  546. rv = file->GetNativeLeafName(leafName);
  547. NS_ENSURE_SUCCESS(rv, rv);
  548. nsCString suffix(NS_LITERAL_CSTRING(".sbstore"));
  549. int32_t dot = leafName.RFind(suffix, 0);
  550. if (dot != -1) {
  551. leafName.Cut(dot, suffix.Length());
  552. aTables.AppendElement(leafName);
  553. }
  554. }
  555. NS_ENSURE_SUCCESS(rv, rv);
  556. return NS_OK;
  557. }
  558. nsresult
  559. Classifier::ActiveTables(nsTArray<nsCString>& aTables)
  560. {
  561. aTables = mActiveTablesCache;
  562. return NS_OK;
  563. }
  564. nsresult
  565. Classifier::CleanToDelete()
  566. {
  567. bool exists;
  568. nsresult rv = mToDeleteDirectory->Exists(&exists);
  569. NS_ENSURE_SUCCESS(rv, rv);
  570. if (exists) {
  571. rv = mToDeleteDirectory->Remove(true);
  572. NS_ENSURE_SUCCESS(rv, rv);
  573. }
  574. return NS_OK;
  575. }
  576. #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
  577. already_AddRefed<nsIFile>
  578. Classifier::GetFailedUpdateDirectroy()
  579. {
  580. nsCString failedUpdatekDirName = STORE_DIRECTORY + nsCString("-failedupdate");
  581. nsCOMPtr<nsIFile> failedUpdatekDirectory;
  582. if (NS_FAILED(mCacheDirectory->Clone(getter_AddRefs(failedUpdatekDirectory))) ||
  583. NS_FAILED(failedUpdatekDirectory->AppendNative(failedUpdatekDirName))) {
  584. LOG(("Failed to init failedUpdatekDirectory."));
  585. return nullptr;
  586. }
  587. return failedUpdatekDirectory.forget();
  588. }
  589. nsresult
  590. Classifier::DumpRawTableUpdates(const nsACString& aRawUpdates)
  591. {
  592. // DumpFailedUpdate() MUST be called first to create the
  593. // "failed update" directory
  594. LOG(("Dumping raw table updates..."));
  595. nsCOMPtr<nsIFile> failedUpdatekDirectory = GetFailedUpdateDirectroy();
  596. // Create tableupdate.bin and dump raw table update data.
  597. nsCOMPtr<nsIFile> rawTableUpdatesFile;
  598. nsCOMPtr<nsIOutputStream> outputStream;
  599. if (NS_FAILED(failedUpdatekDirectory->Clone(getter_AddRefs(rawTableUpdatesFile))) ||
  600. NS_FAILED(rawTableUpdatesFile->AppendNative(nsCString("tableupdates.bin"))) ||
  601. NS_FAILED(NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
  602. rawTableUpdatesFile,
  603. PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE))) {
  604. LOG(("Failed to create file to dump raw table updates."));
  605. return NS_ERROR_FAILURE;
  606. }
  607. // Write out the data.
  608. uint32_t written;
  609. nsresult rv = outputStream->Write(aRawUpdates.BeginReading(),
  610. aRawUpdates.Length(), &written);
  611. NS_ENSURE_SUCCESS(rv, rv);
  612. NS_ENSURE_TRUE(written == aRawUpdates.Length(), NS_ERROR_FAILURE);
  613. return rv;
  614. }
  615. nsresult
  616. Classifier::DumpFailedUpdate()
  617. {
  618. LOG(("Dumping failed update..."));
  619. nsCOMPtr<nsIFile> failedUpdatekDirectory = GetFailedUpdateDirectroy();
  620. // Remove the "failed update" directory no matter it exists or not.
  621. // Failure is fine because the directory may not exist.
  622. failedUpdatekDirectory->Remove(true);
  623. nsCString failedUpdatekDirName;
  624. nsresult rv = failedUpdatekDirectory->GetNativeLeafName(failedUpdatekDirName);
  625. NS_ENSURE_SUCCESS(rv, rv);
  626. // Move backup to a clean "failed update" directory.
  627. nsCOMPtr<nsIFile> backupDirectory;
  628. if (NS_FAILED(mBackupDirectory->Clone(getter_AddRefs(backupDirectory))) ||
  629. NS_FAILED(backupDirectory->MoveToNative(nullptr, failedUpdatekDirName))) {
  630. LOG(("Failed to move backup to the \"failed update\" directory %s",
  631. failedUpdatekDirName.get()));
  632. return NS_ERROR_FAILURE;
  633. }
  634. return rv;
  635. }
  636. #endif // MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
  637. nsresult
  638. Classifier::BackupTables()
  639. {
  640. // We have to work in reverse here: first move the normal directory
  641. // away to be the backup directory, then copy the files over
  642. // to the normal directory. This ensures that if we crash the backup
  643. // dir always has a valid, complete copy, instead of a partial one,
  644. // because that's the one we will copy over the normal store dir.
  645. nsCString backupDirName;
  646. nsresult rv = mBackupDirectory->GetNativeLeafName(backupDirName);
  647. NS_ENSURE_SUCCESS(rv, rv);
  648. nsCString storeDirName;
  649. rv = mRootStoreDirectory->GetNativeLeafName(storeDirName);
  650. NS_ENSURE_SUCCESS(rv, rv);
  651. rv = mRootStoreDirectory->MoveToNative(nullptr, backupDirName);
  652. NS_ENSURE_SUCCESS(rv, rv);
  653. rv = mRootStoreDirectory->CopyToNative(nullptr, storeDirName);
  654. NS_ENSURE_SUCCESS(rv, rv);
  655. // We moved some things to new places, so move the handles around, too.
  656. rv = SetupPathNames();
  657. NS_ENSURE_SUCCESS(rv, rv);
  658. return NS_OK;
  659. }
  660. nsresult
  661. Classifier::RemoveBackupTables()
  662. {
  663. nsCString toDeleteName;
  664. nsresult rv = mToDeleteDirectory->GetNativeLeafName(toDeleteName);
  665. NS_ENSURE_SUCCESS(rv, rv);
  666. rv = mBackupDirectory->MoveToNative(nullptr, toDeleteName);
  667. NS_ENSURE_SUCCESS(rv, rv);
  668. // mBackupDirectory now points to toDelete, fix that up.
  669. rv = SetupPathNames();
  670. NS_ENSURE_SUCCESS(rv, rv);
  671. return NS_OK;
  672. }
  673. nsresult
  674. Classifier::RecoverBackups()
  675. {
  676. bool backupExists;
  677. nsresult rv = mBackupDirectory->Exists(&backupExists);
  678. NS_ENSURE_SUCCESS(rv, rv);
  679. if (backupExists) {
  680. // Remove the safebrowsing dir if it exists
  681. nsCString storeDirName;
  682. rv = mRootStoreDirectory->GetNativeLeafName(storeDirName);
  683. NS_ENSURE_SUCCESS(rv, rv);
  684. bool storeExists;
  685. rv = mRootStoreDirectory->Exists(&storeExists);
  686. NS_ENSURE_SUCCESS(rv, rv);
  687. if (storeExists) {
  688. rv = mRootStoreDirectory->Remove(true);
  689. NS_ENSURE_SUCCESS(rv, rv);
  690. }
  691. // Move the backup to the store location
  692. rv = mBackupDirectory->MoveToNative(nullptr, storeDirName);
  693. NS_ENSURE_SUCCESS(rv, rv);
  694. // mBackupDirectory now points to storeDir, fix up.
  695. rv = SetupPathNames();
  696. NS_ENSURE_SUCCESS(rv, rv);
  697. }
  698. return NS_OK;
  699. }
  700. bool
  701. Classifier::CheckValidUpdate(nsTArray<TableUpdate*>* aUpdates,
  702. const nsACString& aTable)
  703. {
  704. // take the quick exit if there is no valid update for us
  705. // (common case)
  706. uint32_t validupdates = 0;
  707. for (uint32_t i = 0; i < aUpdates->Length(); i++) {
  708. TableUpdate *update = aUpdates->ElementAt(i);
  709. if (!update || !update->TableName().Equals(aTable))
  710. continue;
  711. if (update->Empty()) {
  712. aUpdates->ElementAt(i) = nullptr;
  713. continue;
  714. }
  715. validupdates++;
  716. }
  717. if (!validupdates) {
  718. // This can happen if the update was only valid for one table.
  719. return false;
  720. }
  721. return true;
  722. }
  723. nsCString
  724. Classifier::GetProvider(const nsACString& aTableName)
  725. {
  726. nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
  727. do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
  728. nsCString provider;
  729. nsresult rv = urlUtil->GetProvider(aTableName, provider);
  730. return NS_SUCCEEDED(rv) ? provider : EmptyCString();
  731. }
  732. /*
  733. * This will consume+delete updates from the passed nsTArray.
  734. */
  735. nsresult
  736. Classifier::UpdateHashStore(nsTArray<TableUpdate*>* aUpdates,
  737. const nsACString& aTable)
  738. {
  739. if (nsUrlClassifierDBService::ShutdownHasStarted()) {
  740. return NS_ERROR_ABORT;
  741. }
  742. LOG(("Classifier::UpdateHashStore(%s)", PromiseFlatCString(aTable).get()));
  743. HashStore store(aTable, GetProvider(aTable), mRootStoreDirectory);
  744. if (!CheckValidUpdate(aUpdates, store.TableName())) {
  745. return NS_OK;
  746. }
  747. nsresult rv = store.Open();
  748. NS_ENSURE_SUCCESS(rv, rv);
  749. rv = store.BeginUpdate();
  750. NS_ENSURE_SUCCESS(rv, rv);
  751. // Read the part of the store that is (only) in the cache
  752. LookupCacheV2* lookupCache =
  753. LookupCache::Cast<LookupCacheV2>(GetLookupCache(store.TableName()));
  754. if (!lookupCache) {
  755. return NS_ERROR_FAILURE;
  756. }
  757. // Clear cache when update
  758. lookupCache->ClearCache();
  759. FallibleTArray<uint32_t> AddPrefixHashes;
  760. rv = lookupCache->GetPrefixes(AddPrefixHashes);
  761. NS_ENSURE_SUCCESS(rv, rv);
  762. rv = store.AugmentAdds(AddPrefixHashes);
  763. NS_ENSURE_SUCCESS(rv, rv);
  764. AddPrefixHashes.Clear();
  765. uint32_t applied = 0;
  766. for (uint32_t i = 0; i < aUpdates->Length(); i++) {
  767. TableUpdate *update = aUpdates->ElementAt(i);
  768. if (!update || !update->TableName().Equals(store.TableName()))
  769. continue;
  770. rv = store.ApplyUpdate(*update);
  771. NS_ENSURE_SUCCESS(rv, rv);
  772. applied++;
  773. auto updateV2 = TableUpdate::Cast<TableUpdateV2>(update);
  774. if (updateV2) {
  775. LOG(("Applied update to table %s:", store.TableName().get()));
  776. LOG((" %d add chunks", updateV2->AddChunks().Length()));
  777. LOG((" %d add prefixes", updateV2->AddPrefixes().Length()));
  778. LOG((" %d add completions", updateV2->AddCompletes().Length()));
  779. LOG((" %d sub chunks", updateV2->SubChunks().Length()));
  780. LOG((" %d sub prefixes", updateV2->SubPrefixes().Length()));
  781. LOG((" %d sub completions", updateV2->SubCompletes().Length()));
  782. LOG((" %d add expirations", updateV2->AddExpirations().Length()));
  783. LOG((" %d sub expirations", updateV2->SubExpirations().Length()));
  784. }
  785. aUpdates->ElementAt(i) = nullptr;
  786. }
  787. LOG(("Applied %d update(s) to %s.", applied, store.TableName().get()));
  788. rv = store.Rebuild();
  789. NS_ENSURE_SUCCESS(rv, rv);
  790. LOG(("Table %s now has:", store.TableName().get()));
  791. LOG((" %d add chunks", store.AddChunks().Length()));
  792. LOG((" %d add prefixes", store.AddPrefixes().Length()));
  793. LOG((" %d add completions", store.AddCompletes().Length()));
  794. LOG((" %d sub chunks", store.SubChunks().Length()));
  795. LOG((" %d sub prefixes", store.SubPrefixes().Length()));
  796. LOG((" %d sub completions", store.SubCompletes().Length()));
  797. rv = store.WriteFile();
  798. NS_ENSURE_SUCCESS(rv, rv);
  799. // At this point the store is updated and written out to disk, but
  800. // the data is still in memory. Build our quick-lookup table here.
  801. rv = lookupCache->Build(store.AddPrefixes(), store.AddCompletes());
  802. NS_ENSURE_SUCCESS(rv, rv);
  803. #if defined(DEBUG)
  804. lookupCache->DumpCompletions();
  805. #endif
  806. rv = lookupCache->WriteFile();
  807. NS_ENSURE_SUCCESS(rv, rv);
  808. int64_t now = (PR_Now() / PR_USEC_PER_SEC);
  809. LOG(("Successfully updated %s", store.TableName().get()));
  810. mTableFreshness.Put(store.TableName(), now);
  811. return NS_OK;
  812. }
  813. nsresult
  814. Classifier::UpdateTableV4(nsTArray<TableUpdate*>* aUpdates,
  815. const nsACString& aTable)
  816. {
  817. if (nsUrlClassifierDBService::ShutdownHasStarted()) {
  818. return NS_ERROR_ABORT;
  819. }
  820. LOG(("Classifier::UpdateTableV4(%s)", PromiseFlatCString(aTable).get()));
  821. if (!CheckValidUpdate(aUpdates, aTable)) {
  822. return NS_OK;
  823. }
  824. LookupCacheV4* lookupCache =
  825. LookupCache::Cast<LookupCacheV4>(GetLookupCache(aTable));
  826. if (!lookupCache) {
  827. return NS_ERROR_FAILURE;
  828. }
  829. nsresult rv = NS_OK;
  830. // If there are multiple updates for the same table, prefixes1 & prefixes2
  831. // will act as input and output in turn to reduce memory copy overhead.
  832. PrefixStringMap prefixes1, prefixes2;
  833. PrefixStringMap* input = &prefixes1;
  834. PrefixStringMap* output = &prefixes2;
  835. TableUpdateV4* lastAppliedUpdate = nullptr;
  836. for (uint32_t i = 0; i < aUpdates->Length(); i++) {
  837. TableUpdate *update = aUpdates->ElementAt(i);
  838. if (!update || !update->TableName().Equals(aTable)) {
  839. continue;
  840. }
  841. auto updateV4 = TableUpdate::Cast<TableUpdateV4>(update);
  842. NS_ENSURE_TRUE(updateV4, NS_ERROR_FAILURE);
  843. if (updateV4->IsFullUpdate()) {
  844. input->Clear();
  845. output->Clear();
  846. rv = lookupCache->ApplyUpdate(updateV4, *input, *output);
  847. if (NS_FAILED(rv)) {
  848. return rv;
  849. }
  850. } else {
  851. // If both prefix sets are empty, this means we are doing a partial update
  852. // without a prior full/partial update in the loop. In this case we should
  853. // get prefixes from the lookup cache first.
  854. if (prefixes1.IsEmpty() && prefixes2.IsEmpty()) {
  855. lookupCache->GetPrefixes(prefixes1);
  856. } else {
  857. MOZ_ASSERT(prefixes1.IsEmpty() ^ prefixes2.IsEmpty());
  858. // When there are multiple partial updates, input should always point
  859. // to the non-empty prefix set(filled by previous full/partial update).
  860. // output should always point to the empty prefix set.
  861. input = prefixes1.IsEmpty() ? &prefixes2 : &prefixes1;
  862. output = prefixes1.IsEmpty() ? &prefixes1 : &prefixes2;
  863. }
  864. rv = lookupCache->ApplyUpdate(updateV4, *input, *output);
  865. if (NS_FAILED(rv)) {
  866. return rv;
  867. }
  868. input->Clear();
  869. }
  870. // Keep track of the last applied update.
  871. lastAppliedUpdate = updateV4;
  872. aUpdates->ElementAt(i) = nullptr;
  873. }
  874. rv = lookupCache->Build(*output);
  875. NS_ENSURE_SUCCESS(rv, rv);
  876. rv = lookupCache->WriteFile();
  877. NS_ENSURE_SUCCESS(rv, rv);
  878. if (lastAppliedUpdate) {
  879. LOG(("Write meta data of the last applied update."));
  880. rv = lookupCache->WriteMetadata(lastAppliedUpdate);
  881. NS_ENSURE_SUCCESS(rv, rv);
  882. }
  883. int64_t now = (PR_Now() / PR_USEC_PER_SEC);
  884. LOG(("Successfully updated %s\n", PromiseFlatCString(aTable).get()));
  885. mTableFreshness.Put(aTable, now);
  886. return NS_OK;
  887. }
  888. nsresult
  889. Classifier::UpdateCache(TableUpdate* aUpdate)
  890. {
  891. if (!aUpdate) {
  892. return NS_OK;
  893. }
  894. nsAutoCString table(aUpdate->TableName());
  895. LOG(("Classifier::UpdateCache(%s)", table.get()));
  896. LookupCache *lookupCache = GetLookupCache(table);
  897. if (!lookupCache) {
  898. return NS_ERROR_FAILURE;
  899. }
  900. auto updateV2 = TableUpdate::Cast<TableUpdateV2>(aUpdate);
  901. lookupCache->AddCompletionsToCache(updateV2->AddCompletes());
  902. #if defined(DEBUG)
  903. lookupCache->DumpCache();
  904. #endif
  905. return NS_OK;
  906. }
  907. LookupCache *
  908. Classifier::GetLookupCache(const nsACString& aTable)
  909. {
  910. for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
  911. if (mLookupCaches[i]->TableName().Equals(aTable)) {
  912. return mLookupCaches[i];
  913. }
  914. }
  915. // TODO : Bug 1302600, It would be better if we have a more general non-main
  916. // thread method to convert table name to protocol version. Currently
  917. // we can only know this by checking if the table name ends with '-proto'.
  918. UniquePtr<LookupCache> cache;
  919. nsCString provider = GetProvider(aTable);
  920. if (StringEndsWith(aTable, NS_LITERAL_CSTRING("-proto"))) {
  921. cache = MakeUnique<LookupCacheV4>(aTable, provider, mRootStoreDirectory);
  922. } else {
  923. cache = MakeUnique<LookupCacheV2>(aTable, provider, mRootStoreDirectory);
  924. }
  925. nsresult rv = cache->Init();
  926. if (NS_FAILED(rv)) {
  927. return nullptr;
  928. }
  929. rv = cache->Open();
  930. if (NS_FAILED(rv)) {
  931. if (rv == NS_ERROR_FILE_CORRUPTED) {
  932. Reset();
  933. }
  934. return nullptr;
  935. }
  936. mLookupCaches.AppendElement(cache.get());
  937. return cache.release();
  938. }
  939. nsresult
  940. Classifier::ReadNoiseEntries(const Prefix& aPrefix,
  941. const nsACString& aTableName,
  942. uint32_t aCount,
  943. PrefixArray* aNoiseEntries)
  944. {
  945. // TODO : Bug 1297962, support adding noise for v4
  946. LookupCacheV2 *cache = static_cast<LookupCacheV2*>(GetLookupCache(aTableName));
  947. if (!cache) {
  948. return NS_ERROR_FAILURE;
  949. }
  950. FallibleTArray<uint32_t> prefixes;
  951. nsresult rv = cache->GetPrefixes(prefixes);
  952. NS_ENSURE_SUCCESS(rv, rv);
  953. size_t idx = prefixes.BinaryIndexOf(aPrefix.ToUint32());
  954. if (idx == nsTArray<uint32_t>::NoIndex) {
  955. NS_WARNING("Could not find prefix in PrefixSet during noise lookup");
  956. return NS_ERROR_FAILURE;
  957. }
  958. idx -= idx % aCount;
  959. for (size_t i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) {
  960. Prefix newPref;
  961. newPref.FromUint32(prefixes[idx+i]);
  962. if (newPref != aPrefix) {
  963. aNoiseEntries->AppendElement(newPref);
  964. }
  965. }
  966. return NS_OK;
  967. }
  968. nsresult
  969. Classifier::LoadMetadata(nsIFile* aDirectory, nsACString& aResult)
  970. {
  971. nsCOMPtr<nsISimpleEnumerator> entries;
  972. nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
  973. NS_ENSURE_SUCCESS(rv, rv);
  974. NS_ENSURE_ARG_POINTER(entries);
  975. bool hasMore;
  976. while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
  977. nsCOMPtr<nsISupports> supports;
  978. rv = entries->GetNext(getter_AddRefs(supports));
  979. NS_ENSURE_SUCCESS(rv, rv);
  980. nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
  981. // If |file| is a directory, recurse to find its entries as well.
  982. bool isDirectory;
  983. if (NS_FAILED(file->IsDirectory(&isDirectory))) {
  984. continue;
  985. }
  986. if (isDirectory) {
  987. LoadMetadata(file, aResult);
  988. continue;
  989. }
  990. // Truncate file extension to get the table name.
  991. nsCString tableName;
  992. rv = file->GetNativeLeafName(tableName);
  993. NS_ENSURE_SUCCESS(rv, rv);
  994. int32_t dot = tableName.RFind(METADATA_SUFFIX, 0);
  995. if (dot == -1) {
  996. continue;
  997. }
  998. tableName.Cut(dot, METADATA_SUFFIX.Length());
  999. LookupCacheV4* lookupCache =
  1000. LookupCache::Cast<LookupCacheV4>(GetLookupCache(tableName));
  1001. if (!lookupCache) {
  1002. continue;
  1003. }
  1004. nsCString state;
  1005. nsCString checksum;
  1006. rv = lookupCache->LoadMetadata(state, checksum);
  1007. if (NS_FAILED(rv)) {
  1008. LOG(("Failed to get metadata for table %s", tableName.get()));
  1009. continue;
  1010. }
  1011. // The state might include '\n' so that we have to encode.
  1012. nsAutoCString stateBase64;
  1013. rv = Base64Encode(state, stateBase64);
  1014. NS_ENSURE_SUCCESS(rv, rv);
  1015. nsAutoCString checksumBase64;
  1016. rv = Base64Encode(checksum, checksumBase64);
  1017. NS_ENSURE_SUCCESS(rv, rv);
  1018. LOG(("Appending state '%s' and checksum '%s' for table %s",
  1019. stateBase64.get(), checksumBase64.get(), tableName.get()));
  1020. aResult.AppendPrintf("%s;%s:%s\n", tableName.get(),
  1021. stateBase64.get(),
  1022. checksumBase64.get());
  1023. }
  1024. return rv;
  1025. }
  1026. } // namespace safebrowsing
  1027. } // namespace mozilla