storj.io/uplink@v1.13.0/private/metaclient/objects.go (about) 1 // Copyright (C) 2019 Storj Labs, Inc. 2 // See LICENSE for copying information. 3 4 package metaclient 5 6 import ( 7 "context" 8 "crypto/rand" 9 "errors" 10 "strings" 11 "time" 12 13 "storj.io/common/base58" 14 "storj.io/common/encryption" 15 "storj.io/common/paths" 16 "storj.io/common/pb" 17 "storj.io/common/storj" 18 ) 19 20 var contentTypeKey = "content-type" 21 22 // Meta info about a segment. 23 type Meta struct { 24 Modified time.Time 25 Expiration time.Time 26 Size int64 27 Data []byte 28 } 29 30 // GetObjectIPs returns the IP addresses of the nodes which hold the object. 31 func (db *DB) GetObjectIPs(ctx context.Context, bucket Bucket, key string) (_ *GetObjectIPsResponse, err error) { 32 defer mon.Task()(&ctx)(&err) 33 34 if bucket.Name == "" { 35 return nil, ErrNoBucket.New("") 36 } 37 38 if key == "" { 39 return nil, ErrNoPath.New("") 40 } 41 42 encPath, err := encryption.EncryptPathWithStoreCipher(bucket.Name, paths.NewUnencrypted(key), db.encStore) 43 if err != nil { 44 return nil, err 45 } 46 47 return db.metainfo.GetObjectIPs(ctx, GetObjectIPsParams{ 48 Bucket: []byte(bucket.Name), 49 EncryptedObjectKey: []byte(encPath.Raw()), 50 }) 51 } 52 53 // CreateObject creates an uploading object and returns an interface for uploading Object information. 54 func (db *DB) CreateObject(ctx context.Context, bucket, key string, createInfo *CreateObject) (object *MutableObject, err error) { 55 defer mon.Task()(&ctx)(&err) 56 57 if bucket == "" { 58 return nil, ErrNoBucket.New("") 59 } 60 61 if key == "" { 62 return nil, ErrNoPath.New("") 63 } 64 65 info := Object{ 66 Bucket: Bucket{Name: bucket}, 67 Path: key, 68 } 69 70 if createInfo != nil { 71 info.Metadata = createInfo.Metadata 72 info.ContentType = createInfo.ContentType 73 info.Expires = createInfo.Expires 74 info.RedundancyScheme = createInfo.RedundancyScheme 75 info.EncryptionParameters = createInfo.EncryptionParameters 76 } 77 78 // TODO: autodetect content type from the path extension 79 // if info.ContentType == "" {} 80 81 return &MutableObject{ 82 info: info, 83 }, nil 84 } 85 86 // ModifyObject modifies a committed object. 87 func (db *DB) ModifyObject(ctx context.Context, bucket, key string) (object *MutableObject, err error) { 88 defer mon.Task()(&ctx)(&err) 89 return nil, errors.New("not implemented") 90 } 91 92 // UpdateObjectMetadata replaces the custom metadata for the object at the specific key with newMetadata. 93 // Any existing custom metadata will be deleted. 94 func (db *DB) UpdateObjectMetadata(ctx context.Context, bucket, key string, newMetadata map[string]string) (err error) { 95 defer mon.Task()(&ctx)(&err) 96 97 if bucket == "" { 98 return ErrNoBucket.New("") 99 } 100 101 if key == "" { 102 return ErrNoPath.New("") 103 } 104 105 encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore) 106 if err != nil { 107 return err 108 } 109 110 // TODO: check if we could avoid this round-trip to satellite 111 // At the moment, we need to get the object for two reason: 112 // 1. Retrieve the backward-compatibility metadata 113 // (max segment size and last segment size) 114 // and copy it to the new metadata. 115 // 2. Retrieve the object's encryption parameters 116 // and use them for encrypting the new metadata. 117 objectInfo, err := db.metainfo.GetObject(ctx, GetObjectParams{ 118 Bucket: []byte(bucket), 119 EncryptedObjectKey: []byte(encPath.Raw()), 120 RedundancySchemePerSegment: true, 121 }) 122 if err != nil { 123 return err 124 } 125 126 object, err := db.ObjectFromRawObjectItem(ctx, bucket, key, objectInfo) 127 if err != nil { 128 return err 129 } 130 131 metadataBytes, err := pb.Marshal(&pb.SerializableMeta{ 132 UserDefined: newMetadata, 133 }) 134 if err != nil { 135 return err 136 } 137 138 streamInfo, err := pb.Marshal(&pb.StreamInfo{ 139 SegmentsSize: object.FixedSegmentSize, 140 LastSegmentSize: object.LastSegment.Size, 141 Metadata: metadataBytes, 142 }) 143 if err != nil { 144 return err 145 } 146 147 derivedKey, err := encryption.DeriveContentKey(bucket, paths.NewUnencrypted(key), db.encStore) 148 if err != nil { 149 return err 150 } 151 152 var metadataKey storj.Key 153 // generate random key for encrypting the segment's content 154 _, err = rand.Read(metadataKey[:]) 155 if err != nil { 156 return err 157 } 158 159 var encryptedKeyNonce storj.Nonce 160 // generate random nonce for encrypting the metadata key 161 _, err = rand.Read(encryptedKeyNonce[:]) 162 if err != nil { 163 return err 164 } 165 166 encryptionParameters := objectInfo.EncryptionParameters 167 encryptedKey, err := encryption.EncryptKey(&metadataKey, encryptionParameters.CipherSuite, derivedKey, &encryptedKeyNonce) 168 if err != nil { 169 return err 170 } 171 172 // encrypt metadata with the content encryption key and zero nonce. 173 encryptedStreamInfo, err := encryption.Encrypt(streamInfo, encryptionParameters.CipherSuite, &metadataKey, &storj.Nonce{}) 174 if err != nil { 175 return err 176 } 177 178 // TODO should we commit StreamMeta or commit only encrypted StreamInfo 179 streamMetaBytes, err := pb.Marshal(&pb.StreamMeta{ 180 EncryptedStreamInfo: encryptedStreamInfo, 181 }) 182 if err != nil { 183 return err 184 } 185 186 return db.metainfo.UpdateObjectMetadata(ctx, UpdateObjectMetadataParams{ 187 Bucket: []byte(bucket), 188 EncryptedObjectKey: []byte(encPath.Raw()), 189 StreamID: object.Stream.ID, 190 EncryptedMetadata: streamMetaBytes, 191 EncryptedMetadataEncryptedKey: encryptedKey, 192 EncryptedMetadataNonce: encryptedKeyNonce, 193 }) 194 } 195 196 // DeleteObject deletes an object from database. 197 func (db *DB) DeleteObject(ctx context.Context, bucket, key string, version []byte) (_ Object, err error) { 198 defer mon.Task()(&ctx)(&err) 199 200 if bucket == "" { 201 return Object{}, ErrNoBucket.New("") 202 } 203 204 if len(key) == 0 { 205 return Object{}, ErrNoPath.New("") 206 } 207 208 encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore) 209 if err != nil { 210 return Object{}, err 211 } 212 213 object, err := db.metainfo.BeginDeleteObject(ctx, BeginDeleteObjectParams{ 214 Bucket: []byte(bucket), 215 EncryptedObjectKey: []byte(encPath.Raw()), 216 Version: version, 217 }) 218 if err != nil { 219 return Object{}, err 220 } 221 222 return db.ObjectFromRawObjectItem(ctx, bucket, key, object) 223 } 224 225 // ModifyPendingObject creates an interface for updating a partially uploaded object. 226 func (db *DB) ModifyPendingObject(ctx context.Context, bucket, key string) (object *MutableObject, err error) { 227 defer mon.Task()(&ctx)(&err) 228 return nil, errors.New("not implemented") 229 } 230 231 // ListPendingObjects lists pending objects in bucket based on the ListOptions. 232 func (db *DB) ListPendingObjects(ctx context.Context, bucket string, options ListOptions) (list ObjectList, err error) { 233 defer mon.Task()(&ctx)(&err) 234 return ObjectList{}, errors.New("not implemented") 235 } 236 237 // ListPendingObjectStreams lists streams for a specific pending object key. 238 func (db *DB) ListPendingObjectStreams(ctx context.Context, bucket string, options ListOptions) (list ObjectList, err error) { 239 defer mon.Task()(&ctx)(&err) 240 241 if bucket == "" { 242 return ObjectList{}, ErrNoBucket.New("") 243 } 244 245 var startAfter string 246 switch options.Direction { 247 // TODO for now we are supporting only After 248 // case Forward: 249 // // forward lists forwards from cursor, including cursor 250 // startAfter = keyBefore(options.Cursor) 251 case After: 252 // after lists forwards from cursor, without cursor 253 startAfter = options.Cursor 254 default: 255 return ObjectList{}, errClass.New("invalid direction %d", options.Direction) 256 } 257 258 pi, err := encryption.GetPrefixInfo(bucket, paths.NewUnencrypted(options.Prefix), db.encStore) 259 if err != nil { 260 return ObjectList{}, errClass.Wrap(err) 261 } 262 263 resp, err := db.metainfo.ListPendingObjectStreams(ctx, ListPendingObjectStreamsParams{ 264 Bucket: []byte(bucket), 265 EncryptedObjectKey: []byte(pi.PathEnc.Raw()), 266 EncryptedCursor: []byte(startAfter), 267 Limit: int32(options.Limit), 268 }) 269 if err != nil { 270 return ObjectList{}, errClass.Wrap(err) 271 } 272 273 objectsList, err := db.pendingObjectsFromRawObjectList(ctx, resp.Items, pi, startAfter) 274 if err != nil { 275 return ObjectList{}, errClass.Wrap(err) 276 } 277 278 return ObjectList{ 279 Bucket: bucket, 280 Prefix: options.Prefix, 281 More: resp.More, 282 Items: objectsList, 283 }, nil 284 } 285 286 func (db *DB) pendingObjectsFromRawObjectList(ctx context.Context, items []RawObjectListItem, pi *encryption.PrefixInfo, startAfter string) (objectList []Object, err error) { 287 objectList = make([]Object, 0, len(items)) 288 289 for _, item := range items { 290 stream, streamMeta, err := db.typedDecryptStreamInfo(ctx, pi.Bucket, pi.PathUnenc, 291 item.EncryptedMetadata, 292 item.EncryptedMetadataEncryptedKey, 293 item.EncryptedMetadataNonce, 294 ) 295 if err != nil { 296 // skip items that cannot be decrypted 297 if encryption.ErrDecryptFailed.Has(err) { 298 continue 299 } 300 return nil, errClass.Wrap(err) 301 } 302 303 object, err := db.objectFromRawObjectListItem(pi.Bucket, pi.PathUnenc.Raw(), item, stream, streamMeta) 304 if err != nil { 305 return nil, errClass.Wrap(err) 306 } 307 308 objectList = append(objectList, object) 309 } 310 311 return objectList, nil 312 } 313 314 // ListObjects lists objects in bucket based on the ListOptions. 315 func (db *DB) ListObjects(ctx context.Context, bucket string, options ListOptions) (list ObjectList, err error) { 316 defer mon.Task()(&ctx)(&err) 317 318 if bucket == "" { 319 return ObjectList{}, ErrNoBucket.New("") 320 } 321 322 if options.Prefix != "" && !strings.HasSuffix(options.Prefix, "/") { 323 return ObjectList{}, errClass.New("prefix should end with slash") 324 } 325 326 var startAfter string 327 switch options.Direction { 328 // TODO for now we are supporting only After 329 // case Forward: 330 // // forward lists forwards from cursor, including cursor 331 // startAfter = keyBefore(options.Cursor) 332 case After: 333 // after lists forwards from cursor, without cursor 334 startAfter = options.Cursor 335 default: 336 return ObjectList{}, errClass.New("invalid direction %d", options.Direction) 337 } 338 339 // TODO: we should let libuplink users be able to determine what metadata fields they request as well 340 // metaFlags := meta.All 341 // if db.pathCipher(bucket) == EncNull || db.pathCipher(bucket) == EncNullBase64URL { 342 // metaFlags = meta.None 343 // } 344 345 // TODO use flags with listing 346 // if metaFlags&meta.Size != 0 { 347 // Calculating the stream's size require also the user-defined metadata, 348 // where stream store keeps info about the number of segments and their size. 349 // metaFlags |= meta.UserDefined 350 // } 351 352 pi, err := encryption.GetPrefixInfo(bucket, paths.NewUnencrypted(options.Prefix), db.encStore) 353 if err != nil { 354 return ObjectList{}, errClass.Wrap(err) 355 } 356 357 startAfter, err = encryption.EncryptPathRaw(startAfter, pi.Cipher, &pi.ParentKey) 358 if err != nil { 359 return ObjectList{}, errClass.Wrap(err) 360 } 361 362 startAfterEnc := []byte(startAfter) 363 if len(options.CursorEnc) > 0 { 364 startAfterEnc = options.CursorEnc 365 } 366 versionCursor := options.VersionCursor 367 368 var m bool 369 var objectsList []Object 370 // Keep looking until we find an object we can decrypt or we run out of objects 371 for { 372 items, more, err := db.metainfo.ListObjects(ctx, ListObjectsParams{ 373 Bucket: []byte(bucket), 374 EncryptedPrefix: []byte(pi.ParentEnc.Raw()), 375 EncryptedCursor: startAfterEnc, 376 Limit: int32(options.Limit), 377 IncludeCustomMetadata: options.IncludeCustomMetadata, 378 IncludeSystemMetadata: options.IncludeSystemMetadata, 379 Recursive: options.Recursive, 380 Status: options.Status, 381 IncludeAllVersions: options.IncludeAllVersions, 382 VersionCursor: versionCursor, 383 }) 384 if err != nil { 385 return ObjectList{}, errClass.Wrap(err) 386 } 387 m = more 388 389 objectsList, err = db.objectsFromRawObjectList(ctx, items, pi) 390 if err != nil { 391 return ObjectList{}, errClass.Wrap(err) 392 } 393 394 if len(items) > 0 { 395 startAfterEnc = items[len(items)-1].EncryptedObjectKey 396 versionCursor = items[len(items)-1].Version 397 } 398 399 if len(objectsList) != 0 || !more { 400 break 401 } 402 } 403 404 return ObjectList{ 405 Bucket: bucket, 406 Prefix: options.Prefix, 407 More: m, 408 Items: objectsList, 409 Cursor: startAfterEnc, 410 VersionCursor: versionCursor, 411 }, nil 412 } 413 414 func (db *DB) objectsFromRawObjectList(ctx context.Context, items []RawObjectListItem, pi *encryption.PrefixInfo) (objectList []Object, err error) { 415 objectList = make([]Object, 0, len(items)) 416 417 for _, item := range items { 418 unencItem, err := encryption.DecryptPathRaw(string(item.EncryptedObjectKey), pi.Cipher, &pi.ParentKey) 419 if err != nil { 420 // skip items that cannot be decrypted 421 if encryption.ErrDecryptFailed.Has(err) { 422 continue 423 } 424 return nil, errClass.Wrap(err) 425 } 426 427 var unencKey paths.Unencrypted 428 if pi.ParentEnc.Valid() { 429 unencKey = paths.NewUnencrypted(pi.ParentUnenc.Raw() + "/" + unencItem) 430 } else { 431 unencKey = paths.NewUnencrypted(unencItem) 432 } 433 434 stream, streamMeta, err := db.typedDecryptStreamInfo(ctx, pi.Bucket, unencKey, 435 item.EncryptedMetadata, 436 item.EncryptedMetadataEncryptedKey, 437 item.EncryptedMetadataNonce, 438 ) 439 if err != nil { 440 // skip items that cannot be decrypted 441 if encryption.ErrDecryptFailed.Has(err) { 442 continue 443 } 444 return nil, errClass.Wrap(err) 445 } 446 447 object, err := db.objectFromRawObjectListItem(pi.Bucket, unencItem, item, stream, streamMeta) 448 if err != nil { 449 return nil, errClass.Wrap(err) 450 } 451 452 objectList = append(objectList, object) 453 } 454 455 return objectList, nil 456 } 457 458 // DownloadOptions contains additional options for downloading. 459 type DownloadOptions struct { 460 Range StreamRange 461 } 462 463 // DownloadInfo contains response for DownloadObject. 464 type DownloadInfo struct { 465 Object Object 466 EncPath paths.Encrypted 467 DownloadedSegments []DownloadSegmentWithRSResponse 468 ListSegments ListSegmentsResponse 469 Range StreamRange 470 } 471 472 // DownloadObject gets object information, lists segments and downloads the first segment. 473 func (db *DB) DownloadObject(ctx context.Context, bucket, key string, version []byte, options DownloadOptions) (info DownloadInfo, err error) { 474 defer mon.Task()(&ctx)(&err) 475 476 if bucket == "" { 477 return DownloadInfo{}, ErrNoBucket.New("") 478 } 479 if key == "" { 480 return DownloadInfo{}, ErrNoPath.New("") 481 } 482 483 encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore) 484 if err != nil { 485 return DownloadInfo{}, err 486 } 487 488 resp, err := db.metainfo.DownloadObject(ctx, DownloadObjectParams{ 489 Bucket: []byte(bucket), 490 EncryptedObjectKey: []byte(encPath.Raw()), 491 Version: version, 492 Range: options.Range, 493 }) 494 if err != nil { 495 return DownloadInfo{}, err 496 } 497 498 info, err = db.newDownloadInfo(ctx, bucket, key, encPath, resp, options.Range) 499 if err != nil { 500 return DownloadInfo{}, err 501 } 502 503 for info.ListSegments.More { 504 var cursor SegmentPosition 505 if len(info.ListSegments.Items) > 0 { 506 last := info.ListSegments.Items[len(info.ListSegments.Items)-1] 507 cursor = last.Position 508 } 509 510 result, err := db.ListSegments(ctx, ListSegmentsParams{ 511 StreamID: info.Object.Stream.ID, 512 Cursor: cursor, 513 Range: info.Range, 514 }) 515 if err != nil { 516 return DownloadInfo{}, err 517 } 518 519 info.ListSegments.Items = append(info.ListSegments.Items, result.Items...) 520 info.ListSegments.More = result.More 521 } 522 523 return info, nil 524 } 525 526 func (db *DB) newDownloadInfo(ctx context.Context, bucket, key string, encPath paths.Encrypted, response DownloadObjectResponse, streamRange StreamRange) (DownloadInfo, error) { 527 object, err := db.ObjectFromRawObjectItem(ctx, bucket, key, response.Object) 528 if err != nil { 529 return DownloadInfo{}, err 530 } 531 532 return DownloadInfo{ 533 Object: object, 534 EncPath: encPath, 535 DownloadedSegments: response.DownloadedSegments, 536 ListSegments: response.ListSegments, 537 Range: streamRange.Normalize(object.Size), 538 }, nil 539 } 540 541 // ListSegments returns paged segments list. 542 func (db *DB) ListSegments(ctx context.Context, params ListSegmentsParams) (response ListSegmentsResponse, err error) { 543 result, err := db.metainfo.ListSegments(ctx, params) 544 if err != nil { 545 return ListSegmentsResponse{}, err 546 } 547 return result, nil 548 } 549 550 // GetObject returns information about an object. 551 func (db *DB) GetObject(ctx context.Context, bucket, key string, version []byte) (info Object, err error) { 552 defer mon.Task()(&ctx)(&err) 553 554 if bucket == "" { 555 return Object{}, ErrNoBucket.New("") 556 } 557 558 if key == "" { 559 return Object{}, ErrNoPath.New("") 560 } 561 562 encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore) 563 if err != nil { 564 return Object{}, err 565 } 566 567 objectInfo, err := db.metainfo.GetObject(ctx, GetObjectParams{ 568 Bucket: []byte(bucket), 569 EncryptedObjectKey: []byte(encPath.Raw()), 570 Version: version, 571 RedundancySchemePerSegment: true, 572 }) 573 if err != nil { 574 return Object{}, err 575 } 576 577 return db.ObjectFromRawObjectItem(ctx, bucket, key, objectInfo) 578 } 579 580 // CommitObject commits an object. 581 func (db *DB) CommitObject(ctx context.Context, bucket, key, uploadID string, customMetadata map[string]string, encryptionParameters storj.EncryptionParameters) (info Object, err error) { 582 defer mon.Task()(&ctx)(&err) 583 584 switch { 585 case bucket == "": 586 return Object{}, ErrNoBucket.New("") 587 case key == "": 588 return Object{}, ErrNoPath.New("") 589 case uploadID == "": 590 return Object{}, ErrUploadIDInvalid.New("") 591 } 592 593 decodedStreamID, version, err := base58.CheckDecode(uploadID) 594 if err != nil || version != 1 { 595 return Object{}, ErrUploadIDInvalid.New("") 596 } 597 598 id, err := storj.StreamIDFromBytes(decodedStreamID) 599 if err != nil { 600 return Object{}, err 601 } 602 603 commitObjParams, err := db.fillMetadata(bucket, key, id, customMetadata, encryptionParameters) 604 if err != nil { 605 return Object{}, err 606 } 607 608 response, err := db.metainfo.CommitObjectWithResponse(ctx, commitObjParams) 609 if err != nil { 610 return Object{}, err 611 } 612 613 return db.ObjectFromRawObjectItem(ctx, bucket, key, response.Object) 614 } 615 616 func (db *DB) fillMetadata(bucket, key string, id storj.StreamID, metadata map[string]string, encryptionParameters storj.EncryptionParameters) (CommitObjectParams, error) { 617 commitObjParams := CommitObjectParams{StreamID: id} 618 if len(metadata) == 0 { 619 return commitObjParams, nil 620 } 621 622 clone := make(map[string]string, len(metadata)) 623 for k, v := range metadata { 624 clone[k] = v 625 } 626 627 metadataBytes, err := pb.Marshal(&pb.SerializableMeta{ 628 UserDefined: clone, 629 }) 630 if err != nil { 631 return CommitObjectParams{}, err 632 } 633 634 streamInfo, err := pb.Marshal(&pb.StreamInfo{ 635 Metadata: metadataBytes, 636 }) 637 if err != nil { 638 return CommitObjectParams{}, err 639 } 640 641 derivedKey, err := encryption.DeriveContentKey(bucket, paths.NewUnencrypted(key), db.encStore) 642 if err != nil { 643 return CommitObjectParams{}, err 644 } 645 646 var metadataKey storj.Key 647 // generate random key for encrypting the segment's content 648 _, err = rand.Read(metadataKey[:]) 649 if err != nil { 650 return CommitObjectParams{}, err 651 } 652 653 var encryptedKeyNonce storj.Nonce 654 // generate random nonce for encrypting the metadata key 655 _, err = rand.Read(encryptedKeyNonce[:]) 656 if err != nil { 657 return CommitObjectParams{}, err 658 } 659 660 encryptedKey, err := encryption.EncryptKey(&metadataKey, encryptionParameters.CipherSuite, derivedKey, &encryptedKeyNonce) 661 if err != nil { 662 return CommitObjectParams{}, err 663 } 664 665 // encrypt metadata with the content encryption key and zero nonce. 666 encryptedStreamInfo, err := encryption.Encrypt(streamInfo, encryptionParameters.CipherSuite, &metadataKey, &storj.Nonce{}) 667 if err != nil { 668 return CommitObjectParams{}, err 669 } 670 671 // TODO should we commit StreamMeta or commit only encrypted StreamInfo 672 streamMetaBytes, err := pb.Marshal(&pb.StreamMeta{ 673 EncryptedStreamInfo: encryptedStreamInfo, 674 }) 675 if err != nil { 676 return CommitObjectParams{}, err 677 } 678 679 commitObjParams.EncryptedMetadataEncryptedKey = encryptedKey 680 commitObjParams.EncryptedMetadataNonce = encryptedKeyNonce 681 commitObjParams.EncryptedMetadata = streamMetaBytes 682 683 return commitObjParams, nil 684 } 685 686 // ObjectFromRawObjectItem converts RawObjectItem into storj.Object struct. 687 func (db *DB) ObjectFromRawObjectItem(ctx context.Context, bucket, key string, objectInfo RawObjectItem) (Object, error) { 688 if objectInfo.Bucket == "" { // zero objectInfo 689 return Object{}, nil 690 } 691 692 object := Object{ 693 Version: version(objectInfo.Status, objectInfo.Version), 694 Bucket: Bucket{Name: bucket}, 695 Path: key, 696 IsPrefix: objectInfo.IsPrefix(), 697 IsDeleteMarker: objectInfo.IsDeleteMarker(), 698 699 Created: objectInfo.Created, // TODO: use correct field 700 Modified: objectInfo.Created, // TODO: use correct field 701 Expires: objectInfo.Expires, // TODO: use correct field 702 703 Retention: objectInfo.Retention, 704 705 Stream: Stream{ 706 ID: objectInfo.StreamID, 707 708 Size: objectInfo.PlainSize, 709 710 RedundancyScheme: objectInfo.RedundancyScheme, 711 EncryptionParameters: objectInfo.EncryptionParameters, 712 }, 713 } 714 715 streamInfo, streamMeta, err := db.typedDecryptStreamInfo(ctx, bucket, paths.NewUnencrypted(key), 716 objectInfo.EncryptedMetadata, 717 objectInfo.EncryptedMetadataEncryptedKey, 718 objectInfo.EncryptedMetadataNonce, 719 ) 720 if err != nil { 721 return Object{}, err 722 } 723 724 if object.Stream.EncryptionParameters.CipherSuite == storj.EncUnspecified { 725 object.Stream.EncryptionParameters = storj.EncryptionParameters{ 726 CipherSuite: storj.CipherSuite(streamMeta.EncryptionType), 727 BlockSize: streamMeta.EncryptionBlockSize, 728 } 729 } 730 if streamMeta.LastSegmentMeta != nil { 731 var nonce storj.Nonce 732 copy(nonce[:], streamMeta.LastSegmentMeta.KeyNonce) 733 734 object.Stream.LastSegment = LastSegment{ 735 EncryptedKeyNonce: nonce, 736 EncryptedKey: streamMeta.LastSegmentMeta.EncryptedKey, 737 } 738 } 739 740 err = updateObjectWithStream(&object, streamInfo, streamMeta) 741 if err != nil { 742 return Object{}, err 743 } 744 745 return object, nil 746 } 747 748 func (db *DB) objectFromRawObjectListItem(bucket string, path storj.Path, listItem RawObjectListItem, stream *pb.StreamInfo, streamMeta pb.StreamMeta) (Object, error) { 749 object := Object{ 750 Version: listItem.Version, 751 Bucket: Bucket{Name: bucket}, 752 Path: path, 753 IsPrefix: listItem.IsPrefix, 754 IsDeleteMarker: listItem.IsDeleteMarker(), 755 756 Created: listItem.CreatedAt, // TODO: use correct field 757 Modified: listItem.CreatedAt, // TODO: use correct field 758 Expires: listItem.ExpiresAt, 759 760 Stream: Stream{ 761 Size: listItem.PlainSize, 762 }, 763 } 764 765 object.Stream.ID = listItem.StreamID 766 767 err := updateObjectWithStream(&object, stream, streamMeta) 768 if err != nil { 769 return Object{}, err 770 } 771 772 return object, nil 773 } 774 775 func updateObjectWithStream(object *Object, stream *pb.StreamInfo, streamMeta pb.StreamMeta) error { 776 if stream == nil { 777 return nil 778 } 779 780 serializableMeta := pb.SerializableMeta{} 781 err := pb.Unmarshal(stream.Metadata, &serializableMeta) 782 if err != nil { 783 return err 784 } 785 786 // ensure that the map is not nil 787 if serializableMeta.UserDefined == nil { 788 serializableMeta.UserDefined = map[string]string{} 789 } 790 791 _, found := serializableMeta.UserDefined[contentTypeKey] 792 if !found && serializableMeta.ContentType != "" { 793 serializableMeta.UserDefined[contentTypeKey] = serializableMeta.ContentType 794 } 795 796 segmentCount := streamMeta.NumberOfSegments 797 object.Metadata = serializableMeta.UserDefined 798 799 if object.Stream.Size == 0 { 800 object.Stream.Size = ((segmentCount - 1) * stream.SegmentsSize) + stream.LastSegmentSize 801 } 802 object.Stream.SegmentCount = segmentCount 803 object.Stream.FixedSegmentSize = stream.SegmentsSize 804 object.Stream.LastSegment.Size = stream.LastSegmentSize 805 806 return nil 807 } 808 809 // MutableObject is for creating an object stream. 810 type MutableObject struct { 811 info Object 812 } 813 814 // Info gets the current information about the object. 815 func (object *MutableObject) Info() Object { return object.info } 816 817 // CreateStream creates a new stream for the object. 818 func (object *MutableObject) CreateStream(ctx context.Context) (_ *MutableStream, err error) { 819 defer mon.Task()(&ctx)(&err) 820 return &MutableStream{ 821 info: object.info, 822 }, nil 823 } 824 825 // CreateDynamicStream creates a new dynamic stream for the object. 826 func (object *MutableObject) CreateDynamicStream(ctx context.Context, metadata SerializableMeta, expires time.Time) (_ *MutableStream, err error) { 827 defer mon.Task()(&ctx)(&err) 828 return &MutableStream{ 829 info: object.info, 830 831 dynamic: true, 832 dynamicMetadata: metadata, 833 dynamicExpires: expires, 834 }, nil 835 } 836 837 // typedDecryptStreamInfo decrypts stream info. 838 func (db *DB) typedDecryptStreamInfo(ctx context.Context, bucket string, unencryptedKey paths.Unencrypted, 839 streamMetaBytes, metadataKey []byte, metadataNonce storj.Nonce) (_ *pb.StreamInfo, _ pb.StreamMeta, err error) { 840 defer mon.Task()(&ctx)(&err) 841 842 streamMeta := pb.StreamMeta{} 843 err = pb.Unmarshal(streamMetaBytes, &streamMeta) 844 if err != nil { 845 return nil, pb.StreamMeta{}, err 846 } 847 848 if db.encStore.EncryptionBypass { 849 return nil, streamMeta, nil 850 } 851 852 derivedKey, err := encryption.DeriveContentKey(bucket, unencryptedKey, db.encStore) 853 if err != nil { 854 return nil, pb.StreamMeta{}, err 855 } 856 857 cipher := storj.CipherSuite(streamMeta.EncryptionType) 858 encryptedKey, keyNonce := getEncryptedKeyAndNonce(metadataKey, metadataNonce, streamMeta.LastSegmentMeta) 859 contentKey, err := encryption.DecryptKey(encryptedKey, cipher, derivedKey, keyNonce) 860 if err != nil { 861 return nil, pb.StreamMeta{}, err 862 } 863 864 // decrypt metadata with the content encryption key and zero nonce 865 streamInfo, err := encryption.Decrypt(streamMeta.EncryptedStreamInfo, cipher, contentKey, &storj.Nonce{}) 866 if err != nil { 867 return nil, pb.StreamMeta{}, err 868 } 869 870 var stream pb.StreamInfo 871 if err := pb.Unmarshal(streamInfo, &stream); err != nil { 872 return nil, pb.StreamMeta{}, err 873 } 874 875 return &stream, streamMeta, nil 876 } 877 878 // getEncryptedKeyAndNonce returns key and nonce directly if exists, otherwise try to get them from SegmentMeta. 879 func getEncryptedKeyAndNonce(metadataKey []byte, metadataNonce storj.Nonce, m *pb.SegmentMeta) (storj.EncryptedPrivateKey, *storj.Nonce) { 880 if len(metadataKey) > 0 { 881 return storj.EncryptedPrivateKey(metadataKey), &metadataNonce 882 } 883 884 if m == nil { 885 return nil, nil 886 } 887 888 var nonce storj.Nonce 889 copy(nonce[:], m.KeyNonce) 890 891 return m.EncryptedKey, &nonce 892 } 893 894 func version(status int32, version []byte) []byte { 895 if status == int32(pb.Object_COMMITTED_VERSIONED) || status == int32(pb.Object_DELETE_MARKER_VERSIONED) { 896 return version 897 } 898 return nil 899 }