url_page_blob.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package azblob
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/url"
  7. "strconv"
  8. "time"
  9. "github.com/Azure/azure-pipeline-go/pipeline"
  10. )
  11. const (
  12. // PageBlobPageBytes indicates the number of bytes in a page (512).
  13. PageBlobPageBytes = 512
  14. // PageBlobMaxPutPagesBytes indicates the maximum number of bytes that can be sent in a call to PutPage.
  15. PageBlobMaxPutPagesBytes = 4 * 1024 * 1024 // 4MB
  16. )
  17. // PageBlobURL defines a set of operations applicable to page blobs.
  18. type PageBlobURL struct {
  19. BlobURL
  20. pbClient pageBlobsClient
  21. }
  22. // NewPageBlobURL creates a PageBlobURL object using the specified URL and request policy pipeline.
  23. func NewPageBlobURL(url url.URL, p pipeline.Pipeline) PageBlobURL {
  24. if p == nil {
  25. panic("p can't be nil")
  26. }
  27. blobClient := newBlobsClient(url, p)
  28. pbClient := newPageBlobsClient(url, p)
  29. return PageBlobURL{BlobURL: BlobURL{blobClient: blobClient}, pbClient: pbClient}
  30. }
  31. // WithPipeline creates a new PageBlobURL object identical to the source but with the specific request policy pipeline.
  32. func (pb PageBlobURL) WithPipeline(p pipeline.Pipeline) PageBlobURL {
  33. return NewPageBlobURL(pb.blobClient.URL(), p)
  34. }
  35. // WithSnapshot creates a new PageBlobURL object identical to the source but with the specified snapshot timestamp.
  36. // Pass time.Time{} to remove the snapshot returning a URL to the base blob.
  37. func (pb PageBlobURL) WithSnapshot(snapshot time.Time) PageBlobURL {
  38. p := NewBlobURLParts(pb.URL())
  39. p.Snapshot = snapshot
  40. return NewPageBlobURL(p.URL(), pb.blobClient.Pipeline())
  41. }
  42. // Create creates a page blob of the specified length. Call PutPage to upload data data to a page blob.
  43. // For more information, see https://docs.microsoft.com/rest/api/storageservices/put-blob.
  44. func (pb PageBlobURL) Create(ctx context.Context, size int64, sequenceNumber int64, h BlobHTTPHeaders, metadata Metadata, ac BlobAccessConditions) (*BlobsPutResponse, error) {
  45. if sequenceNumber < 0 {
  46. panic("sequenceNumber must be greater than or equal to 0")
  47. }
  48. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
  49. return pb.blobClient.Put(ctx, BlobPageBlob, nil, nil, nil,
  50. &h.ContentType, &h.ContentEncoding, &h.ContentLanguage, h.contentMD5Pointer(), &h.CacheControl,
  51. metadata, ac.LeaseAccessConditions.pointers(),
  52. &h.ContentDisposition, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, &size, &sequenceNumber, nil)
  53. }
  54. // PutPages writes 1 or more pages to the page blob. The start and end offsets must be a multiple of 512.
  55. // Note that the http client closes the body stream after the request is sent to the service.
  56. // For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page.
  57. func (pb PageBlobURL) PutPages(ctx context.Context, pr PageRange, body io.ReadSeeker, ac BlobAccessConditions) (*PageBlobsPutPageResponse, error) {
  58. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
  59. ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.PageBlobAccessConditions.pointers()
  60. return pb.pbClient.PutPage(ctx, PageWriteUpdate, body, nil, pr.pointers(), ac.LeaseAccessConditions.pointers(),
  61. ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual,
  62. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
  63. }
  64. // ClearPages frees the specified pages from the page blob.
  65. // For more information, see https://docs.microsoft.com/rest/api/storageservices/put-page.
  66. func (pb PageBlobURL) ClearPages(ctx context.Context, pr PageRange, ac BlobAccessConditions) (*PageBlobsPutPageResponse, error) {
  67. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
  68. ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan, ifSequenceNumberEqual := ac.PageBlobAccessConditions.pointers()
  69. return pb.pbClient.PutPage(ctx, PageWriteClear, nil, nil, pr.pointers(), ac.LeaseAccessConditions.pointers(),
  70. ifSequenceNumberLessThanOrEqual, ifSequenceNumberLessThan,
  71. ifSequenceNumberEqual, ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
  72. }
  73. // GetPageRanges returns the list of valid page ranges for a page blob or snapshot of a page blob.
  74. // For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges.
  75. func (pb PageBlobURL) GetPageRanges(ctx context.Context, br BlobRange, ac BlobAccessConditions) (*PageList, error) {
  76. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
  77. return pb.pbClient.GetPageRanges(ctx, nil, nil, nil, br.pointers(), ac.LeaseAccessConditions.pointers(),
  78. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
  79. }
  80. // GetPageRangesDiff gets the collection of page ranges that differ between a specified snapshot and this page blob.
  81. // For more information, see https://docs.microsoft.com/rest/api/storageservices/get-page-ranges.
  82. func (pb PageBlobURL) GetPageRangesDiff(ctx context.Context, br BlobRange, prevSnapshot time.Time, ac BlobAccessConditions) (*PageList, error) {
  83. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
  84. return pb.pbClient.GetPageRanges(ctx, nil, nil, &prevSnapshot, br.pointers(),
  85. ac.LeaseAccessConditions.pointers(), ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
  86. }
  87. // Resize resizes the page blob to the specified size (which must be a multiple of 512).
  88. // For more information, see https://docs.microsoft.com/rest/api/storageservices/set-blob-properties.
  89. func (pb PageBlobURL) Resize(ctx context.Context, size int64, ac BlobAccessConditions) (*BlobsSetPropertiesResponse, error) {
  90. if size%PageBlobPageBytes != 0 {
  91. panic("Size must be a multiple of PageBlobPageBytes (512)")
  92. }
  93. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
  94. return pb.blobClient.SetProperties(ctx, nil, nil, nil, nil, nil, nil, ac.LeaseAccessConditions.pointers(),
  95. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil, &size, SequenceNumberActionNone, nil, nil)
  96. }
  97. // SetSequenceNumber sets the page blob's sequence number.
  98. func (pb PageBlobURL) SetSequenceNumber(ctx context.Context, action SequenceNumberActionType, sequenceNumber int64,
  99. h BlobHTTPHeaders, ac BlobAccessConditions) (*BlobsSetPropertiesResponse, error) {
  100. if sequenceNumber < 0 {
  101. panic("sequenceNumber must be greater than or equal to 0")
  102. }
  103. sn := &sequenceNumber
  104. if action == SequenceNumberActionIncrement {
  105. sn = nil
  106. }
  107. ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch := ac.HTTPAccessConditions.pointers()
  108. return pb.blobClient.SetProperties(ctx, nil, &h.CacheControl, &h.ContentType, h.contentMD5Pointer(), &h.ContentEncoding, &h.ContentLanguage,
  109. ac.LeaseAccessConditions.pointers(),
  110. ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch, &h.ContentDisposition, nil, action, sn, nil)
  111. }
  112. // StartIncrementalCopy begins an operation to start an incremental copy from one page blob's snapshot to this page blob.
  113. // The snapshot is copied such that only the differential changes between the previously copied snapshot are transferred to the destination.
  114. // The copied snapshots are complete copies of the original snapshot and can be read or copied from as usual.
  115. // For more information, see https://docs.microsoft.com/rest/api/storageservices/incremental-copy-blob and
  116. // https://docs.microsoft.com/en-us/azure/virtual-machines/windows/incremental-snapshots.
  117. func (pb PageBlobURL) StartIncrementalCopy(ctx context.Context, source url.URL, snapshot time.Time, ac BlobAccessConditions) (*PageBlobsIncrementalCopyResponse, error) {
  118. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag := ac.HTTPAccessConditions.pointers()
  119. qp := source.Query()
  120. qp.Set("snapshot", snapshot.Format(snapshotTimeFormat))
  121. source.RawQuery = qp.Encode()
  122. return pb.pbClient.IncrementalCopy(ctx, source.String(), nil, nil,
  123. ifModifiedSince, ifUnmodifiedSince, ifMatchETag, ifNoneMatchETag, nil)
  124. }
  125. func (pr PageRange) pointers() *string {
  126. if pr.Start < 0 {
  127. panic("PageRange's Start value must be greater than or equal to 0")
  128. }
  129. if pr.End <= 0 {
  130. panic("PageRange's End value must be greater than 0")
  131. }
  132. if pr.Start%512 != 0 {
  133. panic("PageRange's Start value must be a multiple of 512")
  134. }
  135. if pr.End%512 != 511 {
  136. panic("PageRange's End value must be 1 less than a multiple of 512")
  137. }
  138. if pr.End <= pr.Start {
  139. panic("PageRange's End value must be after the start")
  140. }
  141. endOffset := strconv.FormatInt(int64(pr.End), 10)
  142. asString := fmt.Sprintf("bytes=%v-%s", pr.Start, endOffset)
  143. return &asString
  144. }
  145. // PageBlobAccessConditions identifies page blob-specific access conditions which you optionally set.
  146. type PageBlobAccessConditions struct {
  147. // IfSequenceNumberLessThan ensures that the page blob operation succeeds
  148. // only if the blob's sequence number is less than a value.
  149. // IfSequenceNumberLessThan=0 means no 'IfSequenceNumberLessThan' header specified.
  150. // IfSequenceNumberLessThan>0 means 'IfSequenceNumberLessThan' header specified with its value
  151. // IfSequenceNumberLessThan==-1 means 'IfSequenceNumberLessThan' header specified with a value of 0
  152. IfSequenceNumberLessThan int32
  153. // IfSequenceNumberLessThanOrEqual ensures that the page blob operation succeeds
  154. // only if the blob's sequence number is less than or equal to a value.
  155. // IfSequenceNumberLessThanOrEqual=0 means no 'IfSequenceNumberLessThanOrEqual' header specified.
  156. // IfSequenceNumberLessThanOrEqual>0 means 'IfSequenceNumberLessThanOrEqual' header specified with its value
  157. // IfSequenceNumberLessThanOrEqual=-1 means 'IfSequenceNumberLessThanOrEqual' header specified with a value of 0
  158. IfSequenceNumberLessThanOrEqual int32
  159. // IfSequenceNumberEqual ensures that the page blob operation succeeds
  160. // only if the blob's sequence number is equal to a value.
  161. // IfSequenceNumberEqual=0 means no 'IfSequenceNumberEqual' header specified.
  162. // IfSequenceNumberEqual>0 means 'IfSequenceNumberEqual' header specified with its value
  163. // IfSequenceNumberEqual=-1 means 'IfSequenceNumberEqual' header specified with a value of 0
  164. IfSequenceNumberEqual int32
  165. }
  166. // pointers is for internal infrastructure. It returns the fields as pointers.
  167. func (ac PageBlobAccessConditions) pointers() (snltoe *int32, snlt *int32, sne *int32) {
  168. if ac.IfSequenceNumberLessThan < -1 {
  169. panic("Ifsequencenumberlessthan can't be less than -1")
  170. }
  171. if ac.IfSequenceNumberLessThanOrEqual < -1 {
  172. panic("IfSequenceNumberLessThanOrEqual can't be less than -1")
  173. }
  174. if ac.IfSequenceNumberEqual < -1 {
  175. panic("IfSequenceNumberEqual can't be less than -1")
  176. }
  177. var zero int32 // Defaults to 0
  178. switch ac.IfSequenceNumberLessThan {
  179. case -1:
  180. snlt = &zero
  181. case 0:
  182. snlt = nil
  183. default:
  184. snlt = &ac.IfSequenceNumberLessThan
  185. }
  186. switch ac.IfSequenceNumberLessThanOrEqual {
  187. case -1:
  188. snltoe = &zero
  189. case 0:
  190. snltoe = nil
  191. default:
  192. snltoe = &ac.IfSequenceNumberLessThanOrEqual
  193. }
  194. switch ac.IfSequenceNumberEqual {
  195. case -1:
  196. sne = &zero
  197. case 0:
  198. sne = nil
  199. default:
  200. sne = &ac.IfSequenceNumberEqual
  201. }
  202. return
  203. }