1 package storage
2
3
4
5
6 import (
7 "encoding/xml"
8 "errors"
9 "fmt"
10 "io"
11 "net/http"
12 "net/url"
13 "time"
14 )
15
16
17
18
19
20 type GetPageRangesResponse struct {
21 XMLName xml.Name `xml:"PageList"`
22 PageList []PageRange `xml:"PageRange"`
23 }
24
25
26
27
28
29 type PageRange struct {
30 Start int64 `xml:"Start"`
31 End int64 `xml:"End"`
32 }
33
34 var (
35 errBlobCopyAborted = errors.New("storage: blob copy is aborted")
36 errBlobCopyIDMismatch = errors.New("storage: blob copy id is a mismatch")
37 )
38
39
40 type PutPageOptions struct {
41 Timeout uint
42 LeaseID string `header:"x-ms-lease-id"`
43 IfSequenceNumberLessThanOrEqualTo *int `header:"x-ms-if-sequence-number-le"`
44 IfSequenceNumberLessThan *int `header:"x-ms-if-sequence-number-lt"`
45 IfSequenceNumberEqualTo *int `header:"x-ms-if-sequence-number-eq"`
46 IfModifiedSince *time.Time `header:"If-Modified-Since"`
47 IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
48 IfMatch string `header:"If-Match"`
49 IfNoneMatch string `header:"If-None-Match"`
50 RequestID string `header:"x-ms-client-request-id"`
51 }
52
53
54
55
56
57
58 func (b *Blob) WriteRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
59 if bytes == nil {
60 return errors.New("bytes cannot be nil")
61 }
62 return b.modifyRange(blobRange, bytes, options)
63 }
64
65
66
67
68
69
70 func (b *Blob) ClearRange(blobRange BlobRange, options *PutPageOptions) error {
71 return b.modifyRange(blobRange, nil, options)
72 }
73
74 func (b *Blob) modifyRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
75 if blobRange.End < blobRange.Start {
76 return errors.New("the value for rangeEnd must be greater than or equal to rangeStart")
77 }
78 if blobRange.Start%512 != 0 {
79 return errors.New("the value for rangeStart must be a multiple of 512")
80 }
81 if blobRange.End%512 != 511 {
82 return errors.New("the value for rangeEnd must be a multiple of 512 - 1")
83 }
84
85 params := url.Values{"comp": {"page"}}
86
87
88 write := "clear"
89 var cl uint64
90
91
92 if bytes != nil {
93 write = "update"
94 cl = (blobRange.End - blobRange.Start) + 1
95 }
96
97 headers := b.Container.bsc.client.getStandardHeaders()
98 headers["x-ms-blob-type"] = string(BlobTypePage)
99 headers["x-ms-page-write"] = write
100 headers["x-ms-range"] = blobRange.String()
101 headers["Content-Length"] = fmt.Sprintf("%v", cl)
102
103 if options != nil {
104 params = addTimeout(params, options.Timeout)
105 headers = mergeHeaders(headers, headersFromStruct(*options))
106 }
107 uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
108
109 resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, bytes, b.Container.bsc.auth)
110 if err != nil {
111 return err
112 }
113 defer drainRespBody(resp)
114 return checkRespCode(resp, []int{http.StatusCreated})
115 }
116
117
118 type GetPageRangesOptions struct {
119 Timeout uint
120 Snapshot *time.Time
121 PreviousSnapshot *time.Time
122 Range *BlobRange
123 LeaseID string `header:"x-ms-lease-id"`
124 RequestID string `header:"x-ms-client-request-id"`
125 }
126
127
128
129
130 func (b *Blob) GetPageRanges(options *GetPageRangesOptions) (GetPageRangesResponse, error) {
131 params := url.Values{"comp": {"pagelist"}}
132 headers := b.Container.bsc.client.getStandardHeaders()
133
134 if options != nil {
135 params = addTimeout(params, options.Timeout)
136 params = addSnapshot(params, options.Snapshot)
137 if options.PreviousSnapshot != nil {
138 params.Add("prevsnapshot", timeRFC3339Formatted(*options.PreviousSnapshot))
139 }
140 if options.Range != nil {
141 headers["Range"] = options.Range.String()
142 }
143 headers = mergeHeaders(headers, headersFromStruct(*options))
144 }
145 uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
146
147 var out GetPageRangesResponse
148 resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
149 if err != nil {
150 return out, err
151 }
152 defer drainRespBody(resp)
153
154 if err = checkRespCode(resp, []int{http.StatusOK}); err != nil {
155 return out, err
156 }
157 err = xmlUnmarshal(resp.Body, &out)
158 return out, err
159 }
160
161
162
163
164
165
166
167
168 func (b *Blob) PutPageBlob(options *PutBlobOptions) error {
169 if b.Properties.ContentLength%512 != 0 {
170 return errors.New("Content length must be aligned to a 512-byte boundary")
171 }
172
173 params := url.Values{}
174 headers := b.Container.bsc.client.getStandardHeaders()
175 headers["x-ms-blob-type"] = string(BlobTypePage)
176 headers["x-ms-blob-content-length"] = fmt.Sprintf("%v", b.Properties.ContentLength)
177 headers["x-ms-blob-sequence-number"] = fmt.Sprintf("%v", b.Properties.SequenceNumber)
178 headers = mergeHeaders(headers, headersFromStruct(b.Properties))
179 headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
180
181 if options != nil {
182 params = addTimeout(params, options.Timeout)
183 headers = mergeHeaders(headers, headersFromStruct(*options))
184 }
185 uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
186
187 resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
188 if err != nil {
189 return err
190 }
191 return b.respondCreation(resp, BlobTypePage)
192 }
193
View as plain text