1package azblob 2 3import ( 4 "context" 5 "fmt" 6 "io" 7 "net/url" 8 "strconv" 9 10 "github.com/Azure/azure-pipeline-go/pipeline" 11) 12 13const ( 14 // PageBlobPageBytes indicates the number of bytes in a page (512). 15 PageBlobPageBytes = 512 16 17 // PageBlobMaxUploadPagesBytes indicates the maximum number of bytes that can be sent in a call to PutPage. 18 PageBlobMaxUploadPagesBytes = 4 * 1024 * 1024 // 4MB 19) 20 21// PageBlobURL defines a set of operations applicable to page blobs. 22type PageBlobURL struct { 23 BlobURL 24 pbClient pageBlobClient 25} 26 27// NewPageBlobURL creates a PageBlobURL object using the specified URL and request policy pipeline. 28func NewPageBlobURL(url url.URL, p pipeline.Pipeline) PageBlobURL { 29 blobClient := newBlobClient(url, p) 30 pbClient := newPageBlobClient(url, p) 31 return PageBlobURL{BlobURL: BlobURL{blobClient: blobClient}, pbClient: pbClient} 32} 33 34// WithPipeline creates a new PageBlobURL object identical to the source but with the specific request policy pipeline. 35func (pb PageBlobURL) WithPipeline(p pipeline.Pipeline) PageBlobURL { 36 return NewPageBlobURL(pb.blobClient.URL(), p) 37} 38 39// WithSnapshot creates a new PageBlobURL object identical to the source but with the specified snapshot timestamp. 40// Pass "" to remove the snapshot returning a URL to the base blob. 41func (pb PageBlobURL) WithSnapshot(snapshot string) PageBlobURL { 42 p := NewBlobURLParts(pb.URL()) 43 p.Snapshot = snapshot 44 return NewPageBlobURL(p.URL(), pb.blobClient.Pipeline()) 45} 46 47// WithVersionID creates a new PageBlobURL object identical to the source but with the specified snapshot timestamp. 48// Pass "" to remove the snapshot returning a URL to the base blob. 49func (pb PageBlobURL) WithVersionID(versionId string) PageBlobURL { 50 p := NewBlobURLParts(pb.URL()) 51 p.VersionID = versionId 52 return NewPageBlobURL(p.URL(), pb.blobClient.Pipeline()) 53} 54 55func (pb PageBlobURL) GetAccountInfo(ctx context.Context) (*BlobGetAccountInfoResponse, error) { 56 return pb.blobClient.GetAccountInfo(ctx) 57} 58 59// Create creates a page blob of the specified length. Call PutPage to upload data to a page blob. 60// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob. 61func (pb PageBlobURL) Create(ctx context.Context, size int64, sequenceNumber int64, h BlobHTTPHeaders, metadata Metadata, ac BlobAccessConditions, tier PremiumPageBlobAccessTierType, blobTagsMap BlobTagsMap, cpk ClientProvidedKeyOptions) (*PageBlobCreateResponse, error) { 62 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 63 blobTagsString := SerializeBlobTagsHeader(blobTagsMap) 64 return pb.pbClient.Create(ctx, 0, size, nil, tier, 65 &h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5, &h.CacheControl, 66 metadata, ac.LeaseAccessConditions.pointers(), &h.ContentDisposition, 67 cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK-V 68 cpk.EncryptionScope, // CPK-N 69 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 70 nil, // Blob tags 71 &sequenceNumber, nil, 72 blobTagsString, // Blob tags 73 ) 74} 75 76// UploadPages writes 1 or more pages to the page blob. The start offset and the stream size must be a multiple of 512 bytes. 77// This method panics if the stream is not at position 0. 78// Note that the http client closes the body stream after the request is sent to the service. 79// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page. 80func (pb PageBlobURL) UploadPages(ctx context.Context, offset int64, body io.ReadSeeker, ac PageBlobAccessConditions, transactionalMD5 []byte, cpk ClientProvidedKeyOptions) (*PageBlobUploadPagesResponse, error) { 81 count, err := validateSeekableStreamAt0AndGetCount(body) 82 if err != nil { 83 return nil, err 84 } 85 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 86 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.SequenceNumberAccessConditions.pointers() 87 return pb.pbClient.UploadPages(ctx, body, count, transactionalMD5, nil, nil, 88 PageRange{Start: offset, End: offset + count - 1}.pointers(), 89 ac.LeaseAccessConditions.pointers(), 90 cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK 91 cpk.EncryptionScope, // CPK-N 92 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual, 93 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 94 nil, // Blob ifTags 95 nil) 96} 97 98// UploadPagesFromURL copies 1 or more pages from a source URL to the page blob. 99// The sourceOffset specifies the start offset of source data to copy from. 100// The destOffset specifies the start offset of data in page blob will be written to. 101// The count must be a multiple of 512 bytes. 102// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page-from-url. 103func (pb PageBlobURL) UploadPagesFromURL(ctx context.Context, sourceURL url.URL, sourceOffset int64, destOffset int64, count int64, transactionalMD5 []byte, destinationAccessConditions PageBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions, cpk ClientProvidedKeyOptions) (*PageBlobUploadPagesFromURLResponse, error) { 104 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := destinationAccessConditions.ModifiedAccessConditions.pointers() 105 sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers() 106 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := destinationAccessConditions.SequenceNumberAccessConditions.pointers() 107 return pb.pbClient.UploadPagesFromURL(ctx, sourceURL.String(), *PageRange{Start: sourceOffset, End: sourceOffset + count - 1}.pointers(), 0, 108 *PageRange{Start: destOffset, End: destOffset + count - 1}.pointers(), transactionalMD5, nil, nil, 109 cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK-V 110 cpk.EncryptionScope, // CPK-N 111 destinationAccessConditions.LeaseAccessConditions.pointers(), 112 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual, 113 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 114 nil, // Blob ifTags 115 sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil) 116} 117 118// ClearPages frees the specified pages from the page blob. 119// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page. 120func (pb PageBlobURL) ClearPages(ctx context.Context, offset int64, count int64, ac PageBlobAccessConditions, cpk ClientProvidedKeyOptions) (*PageBlobClearPagesResponse, error) { 121 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 122 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.SequenceNumberAccessConditions.pointers() 123 return pb.pbClient.ClearPages(ctx, 0, nil, 124 PageRange{Start: offset, End: offset + count - 1}.pointers(), 125 ac.LeaseAccessConditions.pointers(), 126 cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK 127 cpk.EncryptionScope, // CPK-N 128 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, 129 ifSequenceNumberEqual, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil, nil) 130} 131 132// GetPageRanges returns the list of valid page ranges for a page blob or snapshot of a page blob. 133// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges. 134func (pb PageBlobURL) GetPageRanges(ctx context.Context, offset int64, count int64, ac BlobAccessConditions) (*PageList, error) { 135 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 136 return pb.pbClient.GetPageRanges(ctx, nil, nil, 137 httpRange{offset: offset, count: count}.pointers(), 138 ac.LeaseAccessConditions.pointers(), 139 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 140 nil, // Blob ifTags 141 nil) 142} 143 144// GetManagedDiskPageRangesDiff gets the collection of page ranges that differ between a specified snapshot and this page blob representing managed disk. 145// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges. 146func (pb PageBlobURL) GetManagedDiskPageRangesDiff(ctx context.Context, offset int64, count int64, prevSnapshot *string, prevSnapshotURL *string, ac BlobAccessConditions) (*PageList, error) { 147 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 148 149 return pb.pbClient.GetPageRangesDiff(ctx, nil, nil, prevSnapshot, 150 prevSnapshotURL, // Get managed disk diff 151 httpRange{offset: offset, count: count}.pointers(), 152 ac.LeaseAccessConditions.pointers(), 153 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 154 nil, // Blob ifTags 155 nil) 156} 157 158// GetPageRangesDiff gets the collection of page ranges that differ between a specified snapshot and this page blob. 159// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges. 160func (pb PageBlobURL) GetPageRangesDiff(ctx context.Context, offset int64, count int64, prevSnapshot string, ac BlobAccessConditions) (*PageList, error) { 161 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 162 return pb.pbClient.GetPageRangesDiff(ctx, nil, nil, &prevSnapshot, 163 nil, // Get managed disk diff 164 httpRange{offset: offset, count: count}.pointers(), 165 ac.LeaseAccessConditions.pointers(), 166 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 167 nil, // Blob ifTags 168 nil) 169} 170 171// Resize resizes the page blob to the specified size (which must be a multiple of 512). 172// For more information, see https://docs.microsoft.com/rest/api/storageservices/set-blob-properties. 173func (pb PageBlobURL) Resize(ctx context.Context, size int64, ac BlobAccessConditions, cpk ClientProvidedKeyOptions) (*PageBlobResizeResponse, error) { 174 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 175 return pb.pbClient.Resize(ctx, size, nil, ac.LeaseAccessConditions.pointers(), 176 cpk.EncryptionKey, cpk.EncryptionKeySha256, cpk.EncryptionAlgorithm, // CPK 177 cpk.EncryptionScope, // CPK-N 178 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil, nil) 179} 180 181// UpdateSequenceNumber sets the page blob's sequence number. 182func (pb PageBlobURL) UpdateSequenceNumber(ctx context.Context, action SequenceNumberActionType, sequenceNumber int64, 183 ac BlobAccessConditions) (*PageBlobUpdateSequenceNumberResponse, error) { 184 sn := &sequenceNumber 185 if action == SequenceNumberActionIncrement { 186 sn = nil 187 } 188 ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.ModifiedAccessConditions.pointers() 189 return pb.pbClient.UpdateSequenceNumber(ctx, action, nil, 190 ac.LeaseAccessConditions.pointers(), ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, 191 nil, sn, nil) 192} 193 194// StartCopyIncremental begins an operation to start an incremental copy from one page blob's snapshot to this page blob. 195// The snapshot is copied such that only the differential changes between the previously copied snapshot are transferred to the destination. 196// The copied snapshots are complete copies of the original snapshot and can be read or copied from as usual. 197// For more information, see https://docs.microsoft.com/rest/api/storageservices/incremental-copy-blob and 198// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/incremental-snapshots. 199func (pb PageBlobURL) StartCopyIncremental(ctx context.Context, source url.URL, snapshot string, ac BlobAccessConditions) (*PageBlobCopyIncrementalResponse, error) { 200 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 201 qp := source.Query() 202 qp.Set("snapshot", snapshot) 203 source.RawQuery = qp.Encode() 204 return pb.pbClient.CopyIncremental(ctx, source.String(), nil, 205 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil, nil) 206} 207 208func (pr PageRange) pointers() *string { 209 endOffset := strconv.FormatInt(int64(pr.End), 10) 210 asString := fmt.Sprintf("bytes=%v-%s", pr.Start, endOffset) 211 return &asString 212} 213 214type PageBlobAccessConditions struct { 215 ModifiedAccessConditions 216 LeaseAccessConditions 217 SequenceNumberAccessConditions 218} 219 220// SequenceNumberAccessConditions identifies page blob-specific access conditions which you optionally set. 221type SequenceNumberAccessConditions struct { 222 // IfSequenceNumberLessThan ensures that the page blob operation succeeds 223 // only if the blob's sequence number is less than a value. 224 // IfSequenceNumberLessThan=0 means no 'IfSequenceNumberLessThan' header specified. 225 // IfSequenceNumberLessThan>0 means 'IfSequenceNumberLessThan' header specified with its value 226 // IfSequenceNumberLessThan==-1 means 'IfSequenceNumberLessThan' header specified with a value of 0 227 IfSequenceNumberLessThan int64 228 229 // IfSequenceNumberLessThanOrEqual ensures that the page blob operation succeeds 230 // only if the blob's sequence number is less than or equal to a value. 231 // IfSequenceNumberLessThanOrEqual=0 means no 'IfSequenceNumberLessThanOrEqual' header specified. 232 // IfSequenceNumberLessThanOrEqual>0 means 'IfSequenceNumberLessThanOrEqual' header specified with its value 233 // IfSequenceNumberLessThanOrEqual=-1 means 'IfSequenceNumberLessThanOrEqual' header specified with a value of 0 234 IfSequenceNumberLessThanOrEqual int64 235 236 // IfSequenceNumberEqual ensures that the page blob operation succeeds 237 // only if the blob's sequence number is equal to a value. 238 // IfSequenceNumberEqual=0 means no 'IfSequenceNumberEqual' header specified. 239 // IfSequenceNumberEqual>0 means 'IfSequenceNumberEqual' header specified with its value 240 // IfSequenceNumberEqual=-1 means 'IfSequenceNumberEqual' header specified with a value of 0 241 IfSequenceNumberEqual int64 242} 243 244// pointers is for internal infrastructure. It returns the fields as pointers. 245func (ac SequenceNumberAccessConditions) pointers() (snltoe *int64, snlt *int64, sne *int64) { 246 var zero int64 // Defaults to 0 247 switch ac.IfSequenceNumberLessThan { 248 case -1: 249 snlt = &zero 250 case 0: 251 snlt = nil 252 default: 253 snlt = &ac.IfSequenceNumberLessThan 254 } 255 256 switch ac.IfSequenceNumberLessThanOrEqual { 257 case -1: 258 snltoe = &zero 259 case 0: 260 snltoe = nil 261 default: 262 snltoe = &ac.IfSequenceNumberLessThanOrEqual 263 } 264 switch ac.IfSequenceNumberEqual { 265 case -1: 266 sne = &zero 267 case 0: 268 sne = nil 269 default: 270 sne = &ac.IfSequenceNumberEqual 271 } 272 return 273} 274