| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "sync/internal_api/public/attachments/on_disk_attachment_store.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <memory> | |
| 10 #include <string> | |
| 11 #include <utility> | |
| 12 | |
| 13 #include "base/bind.h" | |
| 14 #include "base/callback.h" | |
| 15 #include "base/location.h" | |
| 16 #include "base/metrics/histogram.h" | |
| 17 #include "base/sequenced_task_runner.h" | |
| 18 #include "sync/internal_api/attachments/proto/attachment_store.pb.h" | |
| 19 #include "sync/internal_api/public/attachments/attachment_util.h" | |
| 20 #include "sync/protocol/attachments.pb.h" | |
| 21 #include "third_party/leveldatabase/env_chromium.h" | |
| 22 #include "third_party/leveldatabase/src/include/leveldb/db.h" | |
| 23 #include "third_party/leveldatabase/src/include/leveldb/options.h" | |
| 24 #include "third_party/leveldatabase/src/include/leveldb/slice.h" | |
| 25 #include "third_party/leveldatabase/src/include/leveldb/status.h" | |
| 26 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
| 27 | |
| 28 namespace syncer { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 // Prefix for records containing attachment data. | |
| 33 const char kDataPrefix[] = "data-"; | |
| 34 | |
| 35 // Prefix for records containing attachment metadata. | |
| 36 const char kMetadataPrefix[] = "metadata-"; | |
| 37 | |
| 38 const char kDatabaseMetadataKey[] = "database-metadata"; | |
| 39 | |
| 40 const int32_t kCurrentSchemaVersion = 1; | |
| 41 | |
| 42 const base::FilePath::CharType kLeveldbDirectory[] = | |
| 43 FILE_PATH_LITERAL("leveldb"); | |
| 44 | |
| 45 // Converts syncer::AttachmentStore::Component values into | |
| 46 // attachment_store_pb::RecordMetadata::Component. | |
| 47 attachment_store_pb::RecordMetadata::Component ComponentToProto( | |
| 48 syncer::AttachmentStore::Component component) { | |
| 49 switch (component) { | |
| 50 case AttachmentStore::MODEL_TYPE: | |
| 51 return attachment_store_pb::RecordMetadata::MODEL_TYPE; | |
| 52 case AttachmentStore::SYNC: | |
| 53 return attachment_store_pb::RecordMetadata::SYNC; | |
| 54 } | |
| 55 NOTREACHED(); | |
| 56 return attachment_store_pb::RecordMetadata::UNKNOWN; | |
| 57 } | |
| 58 | |
| 59 leveldb::WriteOptions MakeWriteOptions() { | |
| 60 leveldb::WriteOptions write_options; | |
| 61 write_options.sync = true; | |
| 62 return write_options; | |
| 63 } | |
| 64 | |
| 65 leveldb::ReadOptions MakeNonCachingReadOptions() { | |
| 66 leveldb::ReadOptions read_options; | |
| 67 read_options.fill_cache = false; | |
| 68 read_options.verify_checksums = true; | |
| 69 return read_options; | |
| 70 } | |
| 71 | |
| 72 leveldb::ReadOptions MakeCachingReadOptions() { | |
| 73 leveldb::ReadOptions read_options; | |
| 74 read_options.fill_cache = true; | |
| 75 read_options.verify_checksums = true; | |
| 76 return read_options; | |
| 77 } | |
| 78 | |
| 79 leveldb::Status ReadStoreMetadata( | |
| 80 leveldb::DB* db, | |
| 81 attachment_store_pb::StoreMetadata* metadata) { | |
| 82 std::string data_str; | |
| 83 | |
| 84 leveldb::Status status = | |
| 85 db->Get(MakeCachingReadOptions(), kDatabaseMetadataKey, &data_str); | |
| 86 if (!status.ok()) | |
| 87 return status; | |
| 88 if (!metadata->ParseFromString(data_str)) | |
| 89 return leveldb::Status::Corruption("Metadata record corruption"); | |
| 90 return leveldb::Status::OK(); | |
| 91 } | |
| 92 | |
| 93 leveldb::Status WriteStoreMetadata( | |
| 94 leveldb::DB* db, | |
| 95 const attachment_store_pb::StoreMetadata& metadata) { | |
| 96 std::string data_str; | |
| 97 | |
| 98 metadata.SerializeToString(&data_str); | |
| 99 return db->Put(MakeWriteOptions(), kDatabaseMetadataKey, data_str); | |
| 100 } | |
| 101 | |
| 102 // Adds reference to component into RecordMetadata::component set. | |
| 103 // Returns true if record_metadata was modified and needs to be written to disk. | |
| 104 bool SetReferenceInRecordMetadata( | |
| 105 attachment_store_pb::RecordMetadata* record_metadata, | |
| 106 attachment_store_pb::RecordMetadata::Component proto_component) { | |
| 107 DCHECK(record_metadata); | |
| 108 for (const int component : record_metadata->component()) { | |
| 109 if (component == proto_component) | |
| 110 return false; | |
| 111 } | |
| 112 record_metadata->add_component(proto_component); | |
| 113 return true; | |
| 114 } | |
| 115 | |
| 116 // Drops reference to component from RecordMetadata::component set. | |
| 117 // Returns true if record_metadata was modified and needs to be written to disk. | |
| 118 bool DropReferenceInRecordMetadata( | |
| 119 attachment_store_pb::RecordMetadata* record_metadata, | |
| 120 attachment_store_pb::RecordMetadata::Component proto_component) { | |
| 121 DCHECK(record_metadata); | |
| 122 bool component_removed = false; | |
| 123 ::google::protobuf::RepeatedField<int>* mutable_components = | |
| 124 record_metadata->mutable_component(); | |
| 125 for (int i = 0; i < mutable_components->size();) { | |
| 126 if (mutable_components->Get(i) == proto_component) { | |
| 127 if (i < mutable_components->size() - 1) { | |
| 128 // Don't swap last element with itself. | |
| 129 mutable_components->SwapElements(i, mutable_components->size() - 1); | |
| 130 } | |
| 131 mutable_components->RemoveLast(); | |
| 132 component_removed = true; | |
| 133 } else { | |
| 134 ++i; | |
| 135 } | |
| 136 } | |
| 137 return component_removed; | |
| 138 } | |
| 139 | |
| 140 bool AttachmentHasReferenceFromComponent( | |
| 141 const attachment_store_pb::RecordMetadata& record_metadata, | |
| 142 attachment_store_pb::RecordMetadata::Component proto_component) { | |
| 143 for (const auto& reference_component : record_metadata.component()) { | |
| 144 if (reference_component == proto_component) { | |
| 145 return true; | |
| 146 } | |
| 147 } | |
| 148 return false; | |
| 149 } | |
| 150 | |
| 151 } // namespace | |
| 152 | |
| 153 OnDiskAttachmentStore::OnDiskAttachmentStore( | |
| 154 const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner, | |
| 155 const base::FilePath& path) | |
| 156 : AttachmentStoreBackend(callback_task_runner), path_(path) { | |
| 157 } | |
| 158 | |
| 159 OnDiskAttachmentStore::~OnDiskAttachmentStore() { | |
| 160 } | |
| 161 | |
| 162 void OnDiskAttachmentStore::Init( | |
| 163 const AttachmentStore::InitCallback& callback) { | |
| 164 DCHECK(CalledOnValidThread()); | |
| 165 AttachmentStore::Result result_code = OpenOrCreate(path_); | |
| 166 UMA_HISTOGRAM_ENUMERATION("Sync.Attachments.StoreInitResult", result_code, | |
| 167 AttachmentStore::RESULT_SIZE); | |
| 168 PostCallback(base::Bind(callback, result_code)); | |
| 169 } | |
| 170 | |
| 171 void OnDiskAttachmentStore::Read( | |
| 172 AttachmentStore::Component component, | |
| 173 const AttachmentIdList& ids, | |
| 174 const AttachmentStore::ReadCallback& callback) { | |
| 175 DCHECK(CalledOnValidThread()); | |
| 176 std::unique_ptr<AttachmentMap> result_map(new AttachmentMap()); | |
| 177 std::unique_ptr<AttachmentIdList> unavailable_attachments( | |
| 178 new AttachmentIdList()); | |
| 179 | |
| 180 AttachmentStore::Result result_code = | |
| 181 AttachmentStore::STORE_INITIALIZATION_FAILED; | |
| 182 | |
| 183 if (db_) { | |
| 184 result_code = AttachmentStore::SUCCESS; | |
| 185 for (const auto& id : ids) { | |
| 186 std::unique_ptr<Attachment> attachment; | |
| 187 attachment = ReadSingleAttachment(id, component); | |
| 188 if (attachment) { | |
| 189 result_map->insert(std::make_pair(id, *attachment)); | |
| 190 } else { | |
| 191 unavailable_attachments->push_back(id); | |
| 192 } | |
| 193 } | |
| 194 result_code = unavailable_attachments->empty() | |
| 195 ? AttachmentStore::SUCCESS | |
| 196 : AttachmentStore::UNSPECIFIED_ERROR; | |
| 197 } else { | |
| 198 *unavailable_attachments = ids; | |
| 199 } | |
| 200 | |
| 201 PostCallback(base::Bind(callback, result_code, base::Passed(&result_map), | |
| 202 base::Passed(&unavailable_attachments))); | |
| 203 } | |
| 204 | |
| 205 void OnDiskAttachmentStore::Write( | |
| 206 AttachmentStore::Component component, | |
| 207 const AttachmentList& attachments, | |
| 208 const AttachmentStore::WriteCallback& callback) { | |
| 209 DCHECK(CalledOnValidThread()); | |
| 210 AttachmentStore::Result result_code = | |
| 211 AttachmentStore::STORE_INITIALIZATION_FAILED; | |
| 212 | |
| 213 if (db_) { | |
| 214 result_code = AttachmentStore::SUCCESS; | |
| 215 AttachmentList::const_iterator iter = attachments.begin(); | |
| 216 const AttachmentList::const_iterator end = attachments.end(); | |
| 217 for (; iter != end; ++iter) { | |
| 218 if (!WriteSingleAttachment(*iter, component)) | |
| 219 result_code = AttachmentStore::UNSPECIFIED_ERROR; | |
| 220 } | |
| 221 } | |
| 222 PostCallback(base::Bind(callback, result_code)); | |
| 223 } | |
| 224 | |
| 225 void OnDiskAttachmentStore::SetReference(AttachmentStore::Component component, | |
| 226 const AttachmentIdList& ids) { | |
| 227 DCHECK(CalledOnValidThread()); | |
| 228 if (!db_) | |
| 229 return; | |
| 230 attachment_store_pb::RecordMetadata::Component proto_component = | |
| 231 ComponentToProto(component); | |
| 232 for (const auto& id : ids) { | |
| 233 attachment_store_pb::RecordMetadata record_metadata; | |
| 234 if (!ReadSingleRecordMetadata(id, &record_metadata)) | |
| 235 continue; | |
| 236 if (SetReferenceInRecordMetadata(&record_metadata, proto_component)) | |
| 237 WriteSingleRecordMetadata(id, record_metadata); | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 void OnDiskAttachmentStore::DropReference( | |
| 242 AttachmentStore::Component component, | |
| 243 const AttachmentIdList& ids, | |
| 244 const AttachmentStore::DropCallback& callback) { | |
| 245 DCHECK(CalledOnValidThread()); | |
| 246 AttachmentStore::Result result_code = | |
| 247 AttachmentStore::STORE_INITIALIZATION_FAILED; | |
| 248 if (db_) { | |
| 249 attachment_store_pb::RecordMetadata::Component proto_component = | |
| 250 ComponentToProto(component); | |
| 251 result_code = AttachmentStore::SUCCESS; | |
| 252 leveldb::WriteOptions write_options = MakeWriteOptions(); | |
| 253 for (const auto& id : ids) { | |
| 254 attachment_store_pb::RecordMetadata record_metadata; | |
| 255 if (!ReadSingleRecordMetadata(id, &record_metadata)) | |
| 256 continue; // Record not found. | |
| 257 if (!DropReferenceInRecordMetadata(&record_metadata, proto_component)) | |
| 258 continue; // Component is not in components set. Metadata was not | |
| 259 // updated. | |
| 260 if (record_metadata.component_size() == 0) { | |
| 261 // Last reference dropped. Need to delete attachment. | |
| 262 leveldb::WriteBatch write_batch; | |
| 263 write_batch.Delete(MakeDataKeyFromAttachmentId(id)); | |
| 264 write_batch.Delete(MakeMetadataKeyFromAttachmentId(id)); | |
| 265 | |
| 266 leveldb::Status status = db_->Write(write_options, &write_batch); | |
| 267 if (!status.ok()) { | |
| 268 // DB::Delete doesn't check if record exists, it returns ok just like | |
| 269 // AttachmentStore::Drop should. | |
| 270 DVLOG(1) << "DB::Write failed: status=" << status.ToString(); | |
| 271 result_code = AttachmentStore::UNSPECIFIED_ERROR; | |
| 272 } | |
| 273 } else { | |
| 274 WriteSingleRecordMetadata(id, record_metadata); | |
| 275 } | |
| 276 } | |
| 277 } | |
| 278 PostCallback(base::Bind(callback, result_code)); | |
| 279 } | |
| 280 | |
| 281 void OnDiskAttachmentStore::ReadMetadataById( | |
| 282 AttachmentStore::Component component, | |
| 283 const AttachmentIdList& ids, | |
| 284 const AttachmentStore::ReadMetadataCallback& callback) { | |
| 285 DCHECK(CalledOnValidThread()); | |
| 286 AttachmentStore::Result result_code = | |
| 287 AttachmentStore::STORE_INITIALIZATION_FAILED; | |
| 288 std::unique_ptr<AttachmentMetadataList> metadata_list( | |
| 289 new AttachmentMetadataList()); | |
| 290 if (db_) { | |
| 291 result_code = AttachmentStore::SUCCESS; | |
| 292 for (const auto& id : ids) { | |
| 293 attachment_store_pb::RecordMetadata record_metadata; | |
| 294 if (!ReadSingleRecordMetadata(id, &record_metadata)) { | |
| 295 result_code = AttachmentStore::UNSPECIFIED_ERROR; | |
| 296 continue; | |
| 297 } | |
| 298 if (!AttachmentHasReferenceFromComponent(record_metadata, | |
| 299 ComponentToProto(component))) { | |
| 300 result_code = AttachmentStore::UNSPECIFIED_ERROR; | |
| 301 continue; | |
| 302 } | |
| 303 metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); | |
| 304 } | |
| 305 } | |
| 306 PostCallback(base::Bind(callback, result_code, base::Passed(&metadata_list))); | |
| 307 } | |
| 308 | |
| 309 void OnDiskAttachmentStore::ReadMetadata( | |
| 310 AttachmentStore::Component component, | |
| 311 const AttachmentStore::ReadMetadataCallback& callback) { | |
| 312 DCHECK(CalledOnValidThread()); | |
| 313 AttachmentStore::Result result_code = | |
| 314 AttachmentStore::STORE_INITIALIZATION_FAILED; | |
| 315 std::unique_ptr<AttachmentMetadataList> metadata_list( | |
| 316 new AttachmentMetadataList()); | |
| 317 | |
| 318 if (db_) { | |
| 319 attachment_store_pb::RecordMetadata::Component proto_component = | |
| 320 ComponentToProto(component); | |
| 321 result_code = AttachmentStore::SUCCESS; | |
| 322 std::unique_ptr<leveldb::Iterator> db_iterator( | |
| 323 db_->NewIterator(MakeNonCachingReadOptions())); | |
| 324 DCHECK(db_iterator); | |
| 325 for (db_iterator->Seek(kMetadataPrefix); db_iterator->Valid(); | |
| 326 db_iterator->Next()) { | |
| 327 leveldb::Slice key = db_iterator->key(); | |
| 328 if (!key.starts_with(kMetadataPrefix)) { | |
| 329 break; | |
| 330 } | |
| 331 // Make AttachmentId from levelDB key. | |
| 332 key.remove_prefix(strlen(kMetadataPrefix)); | |
| 333 sync_pb::AttachmentIdProto id_proto; | |
| 334 id_proto.set_unique_id(key.ToString()); | |
| 335 AttachmentId id = AttachmentId::CreateFromProto(id_proto); | |
| 336 // Parse metadata record. | |
| 337 attachment_store_pb::RecordMetadata record_metadata; | |
| 338 if (!record_metadata.ParseFromString(db_iterator->value().ToString())) { | |
| 339 DVLOG(1) << "RecordMetadata::ParseFromString failed"; | |
| 340 result_code = AttachmentStore::UNSPECIFIED_ERROR; | |
| 341 continue; | |
| 342 } | |
| 343 DCHECK_GT(record_metadata.component_size(), 0); | |
| 344 if (AttachmentHasReferenceFromComponent(record_metadata, proto_component)) | |
| 345 metadata_list->push_back(MakeAttachmentMetadata(id, record_metadata)); | |
| 346 } | |
| 347 | |
| 348 if (!db_iterator->status().ok()) { | |
| 349 DVLOG(1) << "DB Iterator failed: status=" | |
| 350 << db_iterator->status().ToString(); | |
| 351 result_code = AttachmentStore::UNSPECIFIED_ERROR; | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 PostCallback(base::Bind(callback, result_code, base::Passed(&metadata_list))); | |
| 356 } | |
| 357 | |
| 358 AttachmentStore::Result OnDiskAttachmentStore::OpenOrCreate( | |
| 359 const base::FilePath& path) { | |
| 360 DCHECK(!db_); | |
| 361 base::FilePath leveldb_path = path.Append(kLeveldbDirectory); | |
| 362 | |
| 363 leveldb::DB* db_raw; | |
| 364 std::unique_ptr<leveldb::DB> db; | |
| 365 leveldb::Options options; | |
| 366 options.create_if_missing = true; | |
| 367 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue; | |
| 368 // TODO(pavely): crbug/424287 Consider adding info_log, block_cache and | |
| 369 // filter_policy to options. | |
| 370 leveldb::Status status = | |
| 371 leveldb::DB::Open(options, leveldb_path.AsUTF8Unsafe(), &db_raw); | |
| 372 if (!status.ok()) { | |
| 373 DVLOG(1) << "DB::Open failed: status=" << status.ToString() | |
| 374 << ", path=" << path.AsUTF8Unsafe(); | |
| 375 return AttachmentStore::UNSPECIFIED_ERROR; | |
| 376 } | |
| 377 | |
| 378 db.reset(db_raw); | |
| 379 | |
| 380 attachment_store_pb::StoreMetadata metadata; | |
| 381 status = ReadStoreMetadata(db.get(), &metadata); | |
| 382 if (!status.ok() && !status.IsNotFound()) { | |
| 383 DVLOG(1) << "ReadStoreMetadata failed: status=" << status.ToString(); | |
| 384 return AttachmentStore::UNSPECIFIED_ERROR; | |
| 385 } | |
| 386 if (status.IsNotFound()) { | |
| 387 // Brand new database. | |
| 388 metadata.set_schema_version(kCurrentSchemaVersion); | |
| 389 status = WriteStoreMetadata(db.get(), metadata); | |
| 390 if (!status.ok()) { | |
| 391 DVLOG(1) << "WriteStoreMetadata failed: status=" << status.ToString(); | |
| 392 return AttachmentStore::UNSPECIFIED_ERROR; | |
| 393 } | |
| 394 } | |
| 395 DCHECK(status.ok()); | |
| 396 | |
| 397 // Upgrade code goes here. | |
| 398 | |
| 399 if (metadata.schema_version() != kCurrentSchemaVersion) { | |
| 400 DVLOG(1) << "Unknown schema version: " << metadata.schema_version(); | |
| 401 return AttachmentStore::UNSPECIFIED_ERROR; | |
| 402 } | |
| 403 | |
| 404 db_ = std::move(db); | |
| 405 return AttachmentStore::SUCCESS; | |
| 406 } | |
| 407 | |
| 408 std::unique_ptr<Attachment> OnDiskAttachmentStore::ReadSingleAttachment( | |
| 409 const AttachmentId& attachment_id, | |
| 410 AttachmentStore::Component component) { | |
| 411 std::unique_ptr<Attachment> attachment; | |
| 412 attachment_store_pb::RecordMetadata record_metadata; | |
| 413 if (!ReadSingleRecordMetadata(attachment_id, &record_metadata)) { | |
| 414 return attachment; | |
| 415 } | |
| 416 if (!AttachmentHasReferenceFromComponent(record_metadata, | |
| 417 ComponentToProto(component))) | |
| 418 return attachment; | |
| 419 | |
| 420 const std::string key = MakeDataKeyFromAttachmentId(attachment_id); | |
| 421 std::string data_str; | |
| 422 leveldb::Status status = db_->Get( | |
| 423 MakeNonCachingReadOptions(), key, &data_str); | |
| 424 if (!status.ok()) { | |
| 425 DVLOG(1) << "DB::Get for data failed: status=" << status.ToString(); | |
| 426 return attachment; | |
| 427 } | |
| 428 scoped_refptr<base::RefCountedMemory> data = | |
| 429 base::RefCountedString::TakeString(&data_str); | |
| 430 uint32_t crc32c = ComputeCrc32c(data); | |
| 431 if (record_metadata.has_crc32c()) { | |
| 432 if (record_metadata.crc32c() != crc32c) { | |
| 433 DVLOG(1) << "Attachment crc32c does not match value read from store"; | |
| 434 return attachment; | |
| 435 } | |
| 436 if (record_metadata.crc32c() != attachment_id.GetCrc32c()) { | |
| 437 DVLOG(1) << "Attachment crc32c does not match value in AttachmentId"; | |
| 438 return attachment; | |
| 439 } | |
| 440 } | |
| 441 attachment.reset( | |
| 442 new Attachment(Attachment::CreateFromParts(attachment_id, data))); | |
| 443 return attachment; | |
| 444 } | |
| 445 | |
| 446 bool OnDiskAttachmentStore::WriteSingleAttachment( | |
| 447 const Attachment& attachment, | |
| 448 AttachmentStore::Component component) { | |
| 449 const std::string metadata_key = | |
| 450 MakeMetadataKeyFromAttachmentId(attachment.GetId()); | |
| 451 const std::string data_key = MakeDataKeyFromAttachmentId(attachment.GetId()); | |
| 452 | |
| 453 std::string metadata_str; | |
| 454 leveldb::Status status = | |
| 455 db_->Get(MakeCachingReadOptions(), metadata_key, &metadata_str); | |
| 456 if (status.ok()) { | |
| 457 // Entry exists, don't overwrite. | |
| 458 return true; | |
| 459 } else if (!status.IsNotFound()) { | |
| 460 // Entry exists but failed to read. | |
| 461 DVLOG(1) << "DB::Get failed: status=" << status.ToString(); | |
| 462 return false; | |
| 463 } | |
| 464 DCHECK(status.IsNotFound()); | |
| 465 | |
| 466 leveldb::WriteBatch write_batch; | |
| 467 // Write metadata. | |
| 468 attachment_store_pb::RecordMetadata metadata; | |
| 469 metadata.set_attachment_size(attachment.GetData()->size()); | |
| 470 metadata.set_crc32c(attachment.GetCrc32c()); | |
| 471 SetReferenceInRecordMetadata(&metadata, ComponentToProto(component)); | |
| 472 metadata_str = metadata.SerializeAsString(); | |
| 473 write_batch.Put(metadata_key, metadata_str); | |
| 474 // Write data. | |
| 475 scoped_refptr<base::RefCountedMemory> data = attachment.GetData(); | |
| 476 leveldb::Slice data_slice(data->front_as<char>(), data->size()); | |
| 477 write_batch.Put(data_key, data_slice); | |
| 478 | |
| 479 status = db_->Write(MakeWriteOptions(), &write_batch); | |
| 480 if (!status.ok()) { | |
| 481 // Failed to write. | |
| 482 DVLOG(1) << "DB::Write failed: status=" << status.ToString(); | |
| 483 return false; | |
| 484 } | |
| 485 return true; | |
| 486 } | |
| 487 | |
| 488 bool OnDiskAttachmentStore::ReadSingleRecordMetadata( | |
| 489 const AttachmentId& attachment_id, | |
| 490 attachment_store_pb::RecordMetadata* record_metadata) { | |
| 491 DCHECK(record_metadata); | |
| 492 const std::string metadata_key = | |
| 493 MakeMetadataKeyFromAttachmentId(attachment_id); | |
| 494 std::string metadata_str; | |
| 495 leveldb::Status status = | |
| 496 db_->Get(MakeCachingReadOptions(), metadata_key, &metadata_str); | |
| 497 if (!status.ok()) { | |
| 498 DVLOG(1) << "DB::Get for metadata failed: status=" << status.ToString(); | |
| 499 return false; | |
| 500 } | |
| 501 if (!record_metadata->ParseFromString(metadata_str)) { | |
| 502 DVLOG(1) << "RecordMetadata::ParseFromString failed"; | |
| 503 return false; | |
| 504 } | |
| 505 DCHECK_GT(record_metadata->component_size(), 0); | |
| 506 return true; | |
| 507 } | |
| 508 | |
| 509 bool OnDiskAttachmentStore::WriteSingleRecordMetadata( | |
| 510 const AttachmentId& attachment_id, | |
| 511 const attachment_store_pb::RecordMetadata& record_metadata) { | |
| 512 const std::string metadata_key = | |
| 513 MakeMetadataKeyFromAttachmentId(attachment_id); | |
| 514 std::string metadata_str; | |
| 515 metadata_str = record_metadata.SerializeAsString(); | |
| 516 leveldb::Status status = | |
| 517 db_->Put(MakeWriteOptions(), metadata_key, metadata_str); | |
| 518 if (!status.ok()) { | |
| 519 DVLOG(1) << "DB::Put failed: status=" << status.ToString(); | |
| 520 return false; | |
| 521 } | |
| 522 return true; | |
| 523 } | |
| 524 | |
| 525 std::string OnDiskAttachmentStore::MakeDataKeyFromAttachmentId( | |
| 526 const AttachmentId& attachment_id) { | |
| 527 std::string key = kDataPrefix + attachment_id.GetProto().unique_id(); | |
| 528 return key; | |
| 529 } | |
| 530 | |
| 531 std::string OnDiskAttachmentStore::MakeMetadataKeyFromAttachmentId( | |
| 532 const AttachmentId& attachment_id) { | |
| 533 std::string key = kMetadataPrefix + attachment_id.GetProto().unique_id(); | |
| 534 return key; | |
| 535 } | |
| 536 | |
| 537 AttachmentMetadata OnDiskAttachmentStore::MakeAttachmentMetadata( | |
| 538 const AttachmentId& attachment_id, | |
| 539 const attachment_store_pb::RecordMetadata& record_metadata) { | |
| 540 return AttachmentMetadata(attachment_id, record_metadata.attachment_size()); | |
| 541 } | |
| 542 | |
| 543 } // namespace syncer | |
| OLD | NEW |