pageblob.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package storage
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "encoding/xml"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "net/http"
  21. "net/url"
  22. "time"
  23. )
  24. // GetPageRangesResponse contains the response fields from
  25. // Get Page Ranges call.
  26. //
  27. // See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
  28. type GetPageRangesResponse struct {
  29. XMLName xml.Name `xml:"PageList"`
  30. PageList []PageRange `xml:"PageRange"`
  31. }
  32. // PageRange contains information about a page of a page blob from
  33. // Get Pages Range call.
  34. //
  35. // See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
  36. type PageRange struct {
  37. Start int64 `xml:"Start"`
  38. End int64 `xml:"End"`
  39. }
  40. var (
  41. errBlobCopyAborted = errors.New("storage: blob copy is aborted")
  42. errBlobCopyIDMismatch = errors.New("storage: blob copy id is a mismatch")
  43. )
  44. // PutPageOptions includes the options for a put page operation
  45. type PutPageOptions struct {
  46. Timeout uint
  47. LeaseID string `header:"x-ms-lease-id"`
  48. IfSequenceNumberLessThanOrEqualTo *int `header:"x-ms-if-sequence-number-le"`
  49. IfSequenceNumberLessThan *int `header:"x-ms-if-sequence-number-lt"`
  50. IfSequenceNumberEqualTo *int `header:"x-ms-if-sequence-number-eq"`
  51. IfModifiedSince *time.Time `header:"If-Modified-Since"`
  52. IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
  53. IfMatch string `header:"If-Match"`
  54. IfNoneMatch string `header:"If-None-Match"`
  55. RequestID string `header:"x-ms-client-request-id"`
  56. }
  57. // WriteRange writes a range of pages to a page blob.
  58. // Ranges must be aligned with 512-byte boundaries and chunk must be of size
  59. // multiplies by 512.
  60. //
  61. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Page
  62. func (b *Blob) WriteRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
  63. if bytes == nil {
  64. return errors.New("bytes cannot be nil")
  65. }
  66. return b.modifyRange(blobRange, bytes, options)
  67. }
  68. // ClearRange clears the given range in a page blob.
  69. // Ranges must be aligned with 512-byte boundaries and chunk must be of size
  70. // multiplies by 512.
  71. //
  72. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Page
  73. func (b *Blob) ClearRange(blobRange BlobRange, options *PutPageOptions) error {
  74. return b.modifyRange(blobRange, nil, options)
  75. }
  76. func (b *Blob) modifyRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
  77. if blobRange.End < blobRange.Start {
  78. return errors.New("the value for rangeEnd must be greater than or equal to rangeStart")
  79. }
  80. if blobRange.Start%512 != 0 {
  81. return errors.New("the value for rangeStart must be a multiple of 512")
  82. }
  83. if blobRange.End%512 != 511 {
  84. return errors.New("the value for rangeEnd must be a multiple of 512 - 1")
  85. }
  86. params := url.Values{"comp": {"page"}}
  87. // default to clear
  88. write := "clear"
  89. var cl uint64
  90. // if bytes is not nil then this is an update operation
  91. if bytes != nil {
  92. write = "update"
  93. cl = (blobRange.End - blobRange.Start) + 1
  94. }
  95. headers := b.Container.bsc.client.getStandardHeaders()
  96. headers["x-ms-blob-type"] = string(BlobTypePage)
  97. headers["x-ms-page-write"] = write
  98. headers["x-ms-range"] = blobRange.String()
  99. headers["Content-Length"] = fmt.Sprintf("%v", cl)
  100. if options != nil {
  101. params = addTimeout(params, options.Timeout)
  102. headers = mergeHeaders(headers, headersFromStruct(*options))
  103. }
  104. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
  105. resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, bytes, b.Container.bsc.auth)
  106. if err != nil {
  107. return err
  108. }
  109. defer drainRespBody(resp)
  110. return checkRespCode(resp, []int{http.StatusCreated})
  111. }
  112. // GetPageRangesOptions includes the options for a get page ranges operation
  113. type GetPageRangesOptions struct {
  114. Timeout uint
  115. Snapshot *time.Time
  116. PreviousSnapshot *time.Time
  117. Range *BlobRange
  118. LeaseID string `header:"x-ms-lease-id"`
  119. RequestID string `header:"x-ms-client-request-id"`
  120. }
  121. // GetPageRanges returns the list of valid page ranges for a page blob.
  122. //
  123. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Page-Ranges
  124. func (b *Blob) GetPageRanges(options *GetPageRangesOptions) (GetPageRangesResponse, error) {
  125. params := url.Values{"comp": {"pagelist"}}
  126. headers := b.Container.bsc.client.getStandardHeaders()
  127. if options != nil {
  128. params = addTimeout(params, options.Timeout)
  129. params = addSnapshot(params, options.Snapshot)
  130. if options.PreviousSnapshot != nil {
  131. params.Add("prevsnapshot", timeRFC3339Formatted(*options.PreviousSnapshot))
  132. }
  133. if options.Range != nil {
  134. headers["Range"] = options.Range.String()
  135. }
  136. headers = mergeHeaders(headers, headersFromStruct(*options))
  137. }
  138. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
  139. var out GetPageRangesResponse
  140. resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
  141. if err != nil {
  142. return out, err
  143. }
  144. defer drainRespBody(resp)
  145. if err = checkRespCode(resp, []int{http.StatusOK}); err != nil {
  146. return out, err
  147. }
  148. err = xmlUnmarshal(resp.Body, &out)
  149. return out, err
  150. }
  151. // PutPageBlob initializes an empty page blob with specified name and maximum
  152. // size in bytes (size must be aligned to a 512-byte boundary). A page blob must
  153. // be created using this method before writing pages.
  154. //
  155. // See CreateBlockBlobFromReader for more info on creating blobs.
  156. //
  157. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
  158. func (b *Blob) PutPageBlob(options *PutBlobOptions) error {
  159. if b.Properties.ContentLength%512 != 0 {
  160. return errors.New("Content length must be aligned to a 512-byte boundary")
  161. }
  162. params := url.Values{}
  163. headers := b.Container.bsc.client.getStandardHeaders()
  164. headers["x-ms-blob-type"] = string(BlobTypePage)
  165. headers["x-ms-blob-content-length"] = fmt.Sprintf("%v", b.Properties.ContentLength)
  166. headers["x-ms-blob-sequence-number"] = fmt.Sprintf("%v", b.Properties.SequenceNumber)
  167. headers = mergeHeaders(headers, headersFromStruct(b.Properties))
  168. headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
  169. if options != nil {
  170. params = addTimeout(params, options.Timeout)
  171. headers = mergeHeaders(headers, headersFromStruct(*options))
  172. }
  173. uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
  174. resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
  175. if err != nil {
  176. return err
  177. }
  178. return b.respondCreation(resp, BlobTypePage)
  179. }