204 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
| package storage
 | |
| 
 | |
| // Copyright 2017 Microsoft Corporation
 | |
| //
 | |
| //  Licensed under the Apache License, Version 2.0 (the "License");
 | |
| //  you may not use this file except in compliance with the License.
 | |
| //  You may obtain a copy of the License at
 | |
| //
 | |
| //      http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| //  Unless required by applicable law or agreed to in writing, software
 | |
| //  distributed under the License is distributed on an "AS IS" BASIS,
 | |
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| //  See the License for the specific language governing permissions and
 | |
| //  limitations under the License.
 | |
| 
 | |
| import (
 | |
| 	"encoding/xml"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // GetPageRangesResponse contains the response fields from
 | |
| // Get Page Ranges call.
 | |
| //
 | |
| // See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
 | |
| type GetPageRangesResponse struct {
 | |
| 	XMLName  xml.Name    `xml:"PageList"`
 | |
| 	PageList []PageRange `xml:"PageRange"`
 | |
| }
 | |
| 
 | |
| // PageRange contains information about a page of a page blob from
 | |
| // Get Pages Range call.
 | |
| //
 | |
| // See https://msdn.microsoft.com/en-us/library/azure/ee691973.aspx
 | |
| type PageRange struct {
 | |
| 	Start int64 `xml:"Start"`
 | |
| 	End   int64 `xml:"End"`
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	errBlobCopyAborted    = errors.New("storage: blob copy is aborted")
 | |
| 	errBlobCopyIDMismatch = errors.New("storage: blob copy id is a mismatch")
 | |
| )
 | |
| 
 | |
| // PutPageOptions includes the options for a put page operation
 | |
| type PutPageOptions struct {
 | |
| 	Timeout                           uint
 | |
| 	LeaseID                           string     `header:"x-ms-lease-id"`
 | |
| 	IfSequenceNumberLessThanOrEqualTo *int       `header:"x-ms-if-sequence-number-le"`
 | |
| 	IfSequenceNumberLessThan          *int       `header:"x-ms-if-sequence-number-lt"`
 | |
| 	IfSequenceNumberEqualTo           *int       `header:"x-ms-if-sequence-number-eq"`
 | |
| 	IfModifiedSince                   *time.Time `header:"If-Modified-Since"`
 | |
| 	IfUnmodifiedSince                 *time.Time `header:"If-Unmodified-Since"`
 | |
| 	IfMatch                           string     `header:"If-Match"`
 | |
| 	IfNoneMatch                       string     `header:"If-None-Match"`
 | |
| 	RequestID                         string     `header:"x-ms-client-request-id"`
 | |
| }
 | |
| 
 | |
| // WriteRange writes a range of pages to a page blob.
 | |
| // Ranges must be aligned with 512-byte boundaries and chunk must be of size
 | |
| // multiplies by 512.
 | |
| //
 | |
| // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Page
 | |
| func (b *Blob) WriteRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
 | |
| 	if bytes == nil {
 | |
| 		return errors.New("bytes cannot be nil")
 | |
| 	}
 | |
| 	return b.modifyRange(blobRange, bytes, options)
 | |
| }
 | |
| 
 | |
| // ClearRange clears the given range in a page blob.
 | |
| // Ranges must be aligned with 512-byte boundaries and chunk must be of size
 | |
| // multiplies by 512.
 | |
| //
 | |
| // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Page
 | |
| func (b *Blob) ClearRange(blobRange BlobRange, options *PutPageOptions) error {
 | |
| 	return b.modifyRange(blobRange, nil, options)
 | |
| }
 | |
| 
 | |
| func (b *Blob) modifyRange(blobRange BlobRange, bytes io.Reader, options *PutPageOptions) error {
 | |
| 	if blobRange.End < blobRange.Start {
 | |
| 		return errors.New("the value for rangeEnd must be greater than or equal to rangeStart")
 | |
| 	}
 | |
| 	if blobRange.Start%512 != 0 {
 | |
| 		return errors.New("the value for rangeStart must be a multiple of 512")
 | |
| 	}
 | |
| 	if blobRange.End%512 != 511 {
 | |
| 		return errors.New("the value for rangeEnd must be a multiple of 512 - 1")
 | |
| 	}
 | |
| 
 | |
| 	params := url.Values{"comp": {"page"}}
 | |
| 
 | |
| 	// default to clear
 | |
| 	write := "clear"
 | |
| 	var cl uint64
 | |
| 
 | |
| 	// if bytes is not nil then this is an update operation
 | |
| 	if bytes != nil {
 | |
| 		write = "update"
 | |
| 		cl = (blobRange.End - blobRange.Start) + 1
 | |
| 	}
 | |
| 
 | |
| 	headers := b.Container.bsc.client.getStandardHeaders()
 | |
| 	headers["x-ms-blob-type"] = string(BlobTypePage)
 | |
| 	headers["x-ms-page-write"] = write
 | |
| 	headers["x-ms-range"] = blobRange.String()
 | |
| 	headers["Content-Length"] = fmt.Sprintf("%v", cl)
 | |
| 
 | |
| 	if options != nil {
 | |
| 		params = addTimeout(params, options.Timeout)
 | |
| 		headers = mergeHeaders(headers, headersFromStruct(*options))
 | |
| 	}
 | |
| 	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
 | |
| 
 | |
| 	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, bytes, b.Container.bsc.auth)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer drainRespBody(resp)
 | |
| 	return checkRespCode(resp, []int{http.StatusCreated})
 | |
| }
 | |
| 
 | |
| // GetPageRangesOptions includes the options for a get page ranges operation
 | |
| type GetPageRangesOptions struct {
 | |
| 	Timeout          uint
 | |
| 	Snapshot         *time.Time
 | |
| 	PreviousSnapshot *time.Time
 | |
| 	Range            *BlobRange
 | |
| 	LeaseID          string `header:"x-ms-lease-id"`
 | |
| 	RequestID        string `header:"x-ms-client-request-id"`
 | |
| }
 | |
| 
 | |
| // GetPageRanges returns the list of valid page ranges for a page blob.
 | |
| //
 | |
| // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Get-Page-Ranges
 | |
| func (b *Blob) GetPageRanges(options *GetPageRangesOptions) (GetPageRangesResponse, error) {
 | |
| 	params := url.Values{"comp": {"pagelist"}}
 | |
| 	headers := b.Container.bsc.client.getStandardHeaders()
 | |
| 
 | |
| 	if options != nil {
 | |
| 		params = addTimeout(params, options.Timeout)
 | |
| 		params = addSnapshot(params, options.Snapshot)
 | |
| 		if options.PreviousSnapshot != nil {
 | |
| 			params.Add("prevsnapshot", timeRFC3339Formatted(*options.PreviousSnapshot))
 | |
| 		}
 | |
| 		if options.Range != nil {
 | |
| 			headers["Range"] = options.Range.String()
 | |
| 		}
 | |
| 		headers = mergeHeaders(headers, headersFromStruct(*options))
 | |
| 	}
 | |
| 	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
 | |
| 
 | |
| 	var out GetPageRangesResponse
 | |
| 	resp, err := b.Container.bsc.client.exec(http.MethodGet, uri, headers, nil, b.Container.bsc.auth)
 | |
| 	if err != nil {
 | |
| 		return out, err
 | |
| 	}
 | |
| 	defer drainRespBody(resp)
 | |
| 
 | |
| 	if err = checkRespCode(resp, []int{http.StatusOK}); err != nil {
 | |
| 		return out, err
 | |
| 	}
 | |
| 	err = xmlUnmarshal(resp.Body, &out)
 | |
| 	return out, err
 | |
| }
 | |
| 
 | |
| // PutPageBlob initializes an empty page blob with specified name and maximum
 | |
| // size in bytes (size must be aligned to a 512-byte boundary). A page blob must
 | |
| // be created using this method before writing pages.
 | |
| //
 | |
| // See CreateBlockBlobFromReader for more info on creating blobs.
 | |
| //
 | |
| // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Put-Blob
 | |
| func (b *Blob) PutPageBlob(options *PutBlobOptions) error {
 | |
| 	if b.Properties.ContentLength%512 != 0 {
 | |
| 		return errors.New("Content length must be aligned to a 512-byte boundary")
 | |
| 	}
 | |
| 
 | |
| 	params := url.Values{}
 | |
| 	headers := b.Container.bsc.client.getStandardHeaders()
 | |
| 	headers["x-ms-blob-type"] = string(BlobTypePage)
 | |
| 	headers["x-ms-blob-content-length"] = fmt.Sprintf("%v", b.Properties.ContentLength)
 | |
| 	headers["x-ms-blob-sequence-number"] = fmt.Sprintf("%v", b.Properties.SequenceNumber)
 | |
| 	headers = mergeHeaders(headers, headersFromStruct(b.Properties))
 | |
| 	headers = b.Container.bsc.client.addMetadataToHeaders(headers, b.Metadata)
 | |
| 
 | |
| 	if options != nil {
 | |
| 		params = addTimeout(params, options.Timeout)
 | |
| 		headers = mergeHeaders(headers, headersFromStruct(*options))
 | |
| 	}
 | |
| 	uri := b.Container.bsc.client.getEndpoint(blobServiceName, b.buildPath(), params)
 | |
| 
 | |
| 	resp, err := b.Container.bsc.client.exec(http.MethodPut, uri, headers, nil, b.Container.bsc.auth)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return b.respondCreation(resp, BlobTypePage)
 | |
| }
 |