mirror of https://github.com/minio/minio.git
				
				
				
			
		
			
	
	
		
			167 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			167 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | /* | ||
|  |  * Minio Cloud Storage, (C) 2016 Minio, Inc. | ||
|  |  * | ||
|  |  * 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. | ||
|  |  */ | ||
|  | 
 | ||
|  | package cmd | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"encoding/json" | ||
|  | 	"path" | ||
|  | 	"sort" | ||
|  | 	"sync" | ||
|  | 	"time" | ||
|  | ) | ||
|  | 
 | ||
|  | // A uploadInfo represents the s3 compatible spec.
 | ||
|  | type uploadInfo struct { | ||
|  | 	UploadID  string    `json:"uploadId"`  // UploadID for the active multipart upload.
 | ||
|  | 	Deleted   bool      `json:"deleted"`   // Currently unused, for future use.
 | ||
|  | 	Initiated time.Time `json:"initiated"` // Indicates when the uploadID was initiated.
 | ||
|  | } | ||
|  | 
 | ||
|  | // A uploadsV1 represents `uploads.json` metadata header.
 | ||
|  | type uploadsV1 struct { | ||
|  | 	Version string       `json:"version"`   // Version of the current `uploads.json`
 | ||
|  | 	Format  string       `json:"format"`    // Format of the current `uploads.json`
 | ||
|  | 	Uploads []uploadInfo `json:"uploadIds"` // Captures all the upload ids for a given object.
 | ||
|  | } | ||
|  | 
 | ||
|  | // byInitiatedTime is a collection satisfying sort.Interface.
 | ||
|  | type byInitiatedTime []uploadInfo | ||
|  | 
 | ||
|  | func (t byInitiatedTime) Len() int      { return len(t) } | ||
|  | func (t byInitiatedTime) Swap(i, j int) { t[i], t[j] = t[j], t[i] } | ||
|  | func (t byInitiatedTime) Less(i, j int) bool { | ||
|  | 	return t[i].Initiated.Before(t[j].Initiated) | ||
|  | } | ||
|  | 
 | ||
|  | // AddUploadID - adds a new upload id in order of its initiated time.
 | ||
|  | func (u *uploadsV1) AddUploadID(uploadID string, initiated time.Time) { | ||
|  | 	u.Uploads = append(u.Uploads, uploadInfo{ | ||
|  | 		UploadID:  uploadID, | ||
|  | 		Initiated: initiated, | ||
|  | 	}) | ||
|  | 	sort.Sort(byInitiatedTime(u.Uploads)) | ||
|  | } | ||
|  | 
 | ||
|  | // Index - returns the index of matching the upload id.
 | ||
|  | func (u uploadsV1) Index(uploadID string) int { | ||
|  | 	for i, u := range u.Uploads { | ||
|  | 		if u.UploadID == uploadID { | ||
|  | 			return i | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return -1 | ||
|  | } | ||
|  | 
 | ||
|  | // readUploadsJSON - get all the saved uploads JSON.
 | ||
|  | func readUploadsJSON(bucket, object string, disk StorageAPI) (uploadIDs uploadsV1, err error) { | ||
|  | 	uploadJSONPath := path.Join(mpartMetaPrefix, bucket, object, uploadsJSONFile) | ||
|  | 	// Reads entire `uploads.json`.
 | ||
|  | 	buf, err := disk.ReadAll(minioMetaBucket, uploadJSONPath) | ||
|  | 	if err != nil { | ||
|  | 		return uploadsV1{}, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Decode `uploads.json`.
 | ||
|  | 	if err = json.Unmarshal(buf, &uploadIDs); err != nil { | ||
|  | 		return uploadsV1{}, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Success.
 | ||
|  | 	return uploadIDs, nil | ||
|  | } | ||
|  | 
 | ||
|  | // newUploadsV1 - initialize new uploads v1.
 | ||
|  | func newUploadsV1(format string) uploadsV1 { | ||
|  | 	uploadIDs := uploadsV1{} | ||
|  | 	uploadIDs.Version = "1.0.0" // Should follow semantic versioning.
 | ||
|  | 	uploadIDs.Format = format | ||
|  | 	return uploadIDs | ||
|  | } | ||
|  | 
 | ||
|  | // Wrapper which removes all the uploaded parts.
 | ||
|  | func cleanupUploadedParts(bucket, object, uploadID string, storageDisks ...StorageAPI) error { | ||
|  | 	var errs = make([]error, len(storageDisks)) | ||
|  | 	var wg = &sync.WaitGroup{} | ||
|  | 
 | ||
|  | 	// Construct uploadIDPath.
 | ||
|  | 	uploadIDPath := path.Join(mpartMetaPrefix, bucket, object, uploadID) | ||
|  | 
 | ||
|  | 	// Cleanup uploadID for all disks.
 | ||
|  | 	for index, disk := range storageDisks { | ||
|  | 		if disk == nil { | ||
|  | 			errs[index] = errDiskNotFound | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		wg.Add(1) | ||
|  | 		// Cleanup each uploadID in a routine.
 | ||
|  | 		go func(index int, disk StorageAPI) { | ||
|  | 			defer wg.Done() | ||
|  | 			err := cleanupDir(disk, minioMetaBucket, uploadIDPath) | ||
|  | 			if err != nil { | ||
|  | 				errs[index] = err | ||
|  | 				return | ||
|  | 			} | ||
|  | 			errs[index] = nil | ||
|  | 		}(index, disk) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Wait for all the cleanups to finish.
 | ||
|  | 	wg.Wait() | ||
|  | 
 | ||
|  | 	// Return first error.
 | ||
|  | 	for _, err := range errs { | ||
|  | 		if err != nil { | ||
|  | 			return err | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // listMultipartUploadIDs - list all the upload ids from a marker up to 'count'.
 | ||
|  | func listMultipartUploadIDs(bucketName, objectName, uploadIDMarker string, count int, disk StorageAPI) ([]uploadMetadata, bool, error) { | ||
|  | 	var uploads []uploadMetadata | ||
|  | 	// Read `uploads.json`.
 | ||
|  | 	uploadsJSON, err := readUploadsJSON(bucketName, objectName, disk) | ||
|  | 	if err != nil { | ||
|  | 		return nil, false, err | ||
|  | 	} | ||
|  | 	index := 0 | ||
|  | 	if uploadIDMarker != "" { | ||
|  | 		for ; index < len(uploadsJSON.Uploads); index++ { | ||
|  | 			if uploadsJSON.Uploads[index].UploadID == uploadIDMarker { | ||
|  | 				// Skip the uploadID as it would already be listed in previous listing.
 | ||
|  | 				index++ | ||
|  | 				break | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 	for index < len(uploadsJSON.Uploads) { | ||
|  | 		uploads = append(uploads, uploadMetadata{ | ||
|  | 			Object:    objectName, | ||
|  | 			UploadID:  uploadsJSON.Uploads[index].UploadID, | ||
|  | 			Initiated: uploadsJSON.Uploads[index].Initiated, | ||
|  | 		}) | ||
|  | 		count-- | ||
|  | 		index++ | ||
|  | 		if count == 0 { | ||
|  | 			break | ||
|  | 		} | ||
|  | 	} | ||
|  | 	end := (index == len(uploadsJSON.Uploads)) | ||
|  | 	return uploads, end, nil | ||
|  | } |