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 // PageBlobMaxPutPagesBytes 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// Create creates a page blob of the specified length. Call PutPage to upload data data to a page blob. 48// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob. 49func (pb PageBlobURL) Create(ctx context.Context, size int64, sequenceNumber int64, h BlobHTTPHeaders, metadata Metadata, ac BlobAccessConditions) (*PageBlobCreateResponse, error) { 50 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 51 return pb.pbClient.Create(ctx, 0, size, nil, 52 &h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.ContentMD5, &h.CacheControl, 53 metadata, ac.LeaseAccessConditions.pointers(), 54 &h.ContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, &sequenceNumber, nil) 55} 56 57// UploadPages writes 1 or more pages to the page blob. The start offset and the stream size must be a multiple of 512 bytes. 58// This method panics if the stream is not at position 0. 59// Note that the http client closes the body stream after the request is sent to the service. 60// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page. 61func (pb PageBlobURL) UploadPages(ctx context.Context, offset int64, body io.ReadSeeker, ac PageBlobAccessConditions, transactionalMD5 []byte) (*PageBlobUploadPagesResponse, error) { 62 count, err := validateSeekableStreamAt0AndGetCount(body) 63 if err != nil { 64 return nil, err 65 } 66 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 67 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.SequenceNumberAccessConditions.pointers() 68 return pb.pbClient.UploadPages(ctx, body, count, transactionalMD5, nil, 69 PageRange{Start: offset, End: offset + count - 1}.pointers(), 70 ac.LeaseAccessConditions.pointers(), 71 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual, 72 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil) 73} 74 75// UploadPagesFromURL copies 1 or more pages from a source URL to the page blob. 76// The sourceOffset specifies the start offset of source data to copy from. 77// The destOffset specifies the start offset of data in page blob will be written to. 78// The count must be a multiple of 512 bytes. 79// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page-from-url. 80func (pb PageBlobURL) UploadPagesFromURL(ctx context.Context, sourceURL url.URL, sourceOffset int64, destOffset int64, count int64, transactionalMD5 []byte, destinationAccessConditions PageBlobAccessConditions, sourceAccessConditions ModifiedAccessConditions) (*PageBlobUploadPagesFromURLResponse, error) { 81 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := destinationAccessConditions.ModifiedAccessConditions.pointers() 82 sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag := sourceAccessConditions.pointers() 83 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := destinationAccessConditions.SequenceNumberAccessConditions.pointers() 84 return pb.pbClient.UploadPagesFromURL(ctx, sourceURL.String(), *PageRange{Start: sourceOffset, End: sourceOffset + count - 1}.pointers(), 0, 85 *PageRange{Start: destOffset, End: destOffset + count - 1}.pointers(), transactionalMD5, nil, destinationAccessConditions.LeaseAccessConditions.pointers(), 86 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual, 87 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, sourceIfModifiedSince, sourceIfUnmodifiedSince, sourceIfMatchETag, sourceIfNoneMatchETag, nil) 88} 89 90// ClearPages frees the specified pages from the page blob. 91// For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page. 92func (pb PageBlobURL) ClearPages(ctx context.Context, offset int64, count int64, ac PageBlobAccessConditions) (*PageBlobClearPagesResponse, error) { 93 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 94 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.SequenceNumberAccessConditions.pointers() 95 return pb.pbClient.ClearPages(ctx, 0, nil, 96 PageRange{Start: offset, End: offset + count - 1}.pointers(), 97 ac.LeaseAccessConditions.pointers(), 98 ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, 99 ifSequenceNumberEqual, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil) 100} 101 102// GetPageRanges returns the list of valid page ranges for a page blob or snapshot of a page blob. 103// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges. 104func (pb PageBlobURL) GetPageRanges(ctx context.Context, offset int64, count int64, ac BlobAccessConditions) (*PageList, error) { 105 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 106 return pb.pbClient.GetPageRanges(ctx, nil, nil, 107 httpRange{offset: offset, count: count}.pointers(), 108 ac.LeaseAccessConditions.pointers(), 109 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil) 110} 111 112// GetPageRangesDiff gets the collection of page ranges that differ between a specified snapshot and this page blob. 113// For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges. 114func (pb PageBlobURL) GetPageRangesDiff(ctx context.Context, offset int64, count int64, prevSnapshot string, ac BlobAccessConditions) (*PageList, error) { 115 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 116 return pb.pbClient.GetPageRangesDiff(ctx, nil, nil, &prevSnapshot, 117 httpRange{offset: offset, count: count}.pointers(), 118 ac.LeaseAccessConditions.pointers(), 119 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, 120 nil) 121} 122 123// Resize resizes the page blob to the specified size (which must be a multiple of 512). 124// For more information, see https://docs.microsoft.com/rest/api/storageservices/set-blob-properties. 125func (pb PageBlobURL) Resize(ctx context.Context, size int64, ac BlobAccessConditions) (*PageBlobResizeResponse, error) { 126 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 127 return pb.pbClient.Resize(ctx, size, nil, ac.LeaseAccessConditions.pointers(), 128 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil) 129} 130 131// SetSequenceNumber sets the page blob's sequence number. 132func (pb PageBlobURL) UpdateSequenceNumber(ctx context.Context, action SequenceNumberActionType, sequenceNumber int64, 133 ac BlobAccessConditions) (*PageBlobUpdateSequenceNumberResponse, error) { 134 sn := &sequenceNumber 135 if action == SequenceNumberActionIncrement { 136 sn = nil 137 } 138 ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.ModifiedAccessConditions.pointers() 139 return pb.pbClient.UpdateSequenceNumber(ctx, action, nil, 140 ac.LeaseAccessConditions.pointers(), ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, 141 sn, nil) 142} 143 144// StartIncrementalCopy begins an operation to start an incremental copy from one page blob's snapshot to this page blob. 145// The snapshot is copied such that only the differential changes between the previously copied snapshot are transferred to the destination. 146// The copied snapshots are complete copies of the original snapshot and can be read or copied from as usual. 147// For more information, see https://docs.microsoft.com/rest/api/storageservices/incremental-copy-blob and 148// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/incremental-snapshots. 149func (pb PageBlobURL) StartCopyIncremental(ctx context.Context, source url.URL, snapshot string, ac BlobAccessConditions) (*PageBlobCopyIncrementalResponse, error) { 150 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.ModifiedAccessConditions.pointers() 151 qp := source.Query() 152 qp.Set("snapshot", snapshot) 153 source.RawQuery = qp.Encode() 154 return pb.pbClient.CopyIncremental(ctx, source.String(), nil, 155 ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil) 156} 157 158func (pr PageRange) pointers() *string { 159 endOffset := strconv.FormatInt(int64(pr.End), 10) 160 asString := fmt.Sprintf("bytes=%v-%s", pr.Start, endOffset) 161 return &asString 162} 163 164type PageBlobAccessConditions struct { 165 ModifiedAccessConditions 166 LeaseAccessConditions 167 SequenceNumberAccessConditions 168} 169 170// SequenceNumberAccessConditions identifies page blob-specific access conditions which you optionally set. 171type SequenceNumberAccessConditions struct { 172 // IfSequenceNumberLessThan ensures that the page blob operation succeeds 173 // only if the blob's sequence number is less than a value. 174 // IfSequenceNumberLessThan=0 means no 'IfSequenceNumberLessThan' header specified. 175 // IfSequenceNumberLessThan>0 means 'IfSequenceNumberLessThan' header specified with its value 176 // IfSequenceNumberLessThan==-1 means 'IfSequenceNumberLessThan' header specified with a value of 0 177 IfSequenceNumberLessThan int64 178 179 // IfSequenceNumberLessThanOrEqual ensures that the page blob operation succeeds 180 // only if the blob's sequence number is less than or equal to a value. 181 // IfSequenceNumberLessThanOrEqual=0 means no 'IfSequenceNumberLessThanOrEqual' header specified. 182 // IfSequenceNumberLessThanOrEqual>0 means 'IfSequenceNumberLessThanOrEqual' header specified with its value 183 // IfSequenceNumberLessThanOrEqual=-1 means 'IfSequenceNumberLessThanOrEqual' header specified with a value of 0 184 IfSequenceNumberLessThanOrEqual int64 185 186 // IfSequenceNumberEqual ensures that the page blob operation succeeds 187 // only if the blob's sequence number is equal to a value. 188 // IfSequenceNumberEqual=0 means no 'IfSequenceNumberEqual' header specified. 189 // IfSequenceNumberEqual>0 means 'IfSequenceNumberEqual' header specified with its value 190 // IfSequenceNumberEqual=-1 means 'IfSequenceNumberEqual' header specified with a value of 0 191 IfSequenceNumberEqual int64 192} 193 194// pointers is for internal infrastructure. It returns the fields as pointers. 195func (ac SequenceNumberAccessConditions) pointers() (snltoe *int64, snlt *int64, sne *int64) { 196 var zero int64 // Defaults to 0 197 switch ac.IfSequenceNumberLessThan { 198 case -1: 199 snlt = &zero 200 case 0: 201 snlt = nil 202 default: 203 snlt = &ac.IfSequenceNumberLessThan 204 } 205 206 switch ac.IfSequenceNumberLessThanOrEqual { 207 case -1: 208 snltoe = &zero 209 case 0: 210 snltoe = nil 211 default: 212 snltoe = &ac.IfSequenceNumberLessThanOrEqual 213 } 214 switch ac.IfSequenceNumberEqual { 215 case -1: 216 sne = &zero 217 case 0: 218 sne = nil 219 default: 220 sne = &ac.IfSequenceNumberEqual 221 } 222 return 223} 224