Merge pull request #1222 from harshavardhana/cleanup-fix

cleanup: Rename ObjectMetadata as ObjectInfo.
master
Anand Babu (AB) Periasamy 9 years ago
commit 3d8b9afa8f
  1. 12
      api-headers.go
  2. 12
      api-response.go
  3. 14
      bucket-handlers.go
  4. 53
      object-handlers.go
  5. 60
      pkg/fs/api_suite_nix_test.go
  6. 60
      pkg/fs/api_suite_windows_test.go
  7. 44
      pkg/fs/dir.go
  8. 10
      pkg/fs/fs-bucket-listobjects.go
  9. 29
      pkg/fs/fs-bucket.go
  10. 4
      pkg/fs/fs-bucket_test.go
  11. 25
      pkg/fs/fs-datatypes.go
  12. 46
      pkg/fs/fs-multipart.go
  13. 79
      pkg/fs/fs-object.go

@ -60,20 +60,20 @@ func encodeResponse(response interface{}) []byte {
} }
// Write object header // Write object header
func setObjectHeaders(w http.ResponseWriter, metadata fs.ObjectMetadata, contentRange *httpRange) { func setObjectHeaders(w http.ResponseWriter, objectInfo fs.ObjectInfo, contentRange *httpRange) {
// set common headers // set common headers
setCommonHeaders(w) setCommonHeaders(w)
// set object-related metadata headers // set object-related metadata headers
lastModified := metadata.LastModified.UTC().Format(http.TimeFormat) lastModified := objectInfo.ModifiedTime.UTC().Format(http.TimeFormat)
w.Header().Set("Last-Modified", lastModified) w.Header().Set("Last-Modified", lastModified)
w.Header().Set("Content-Type", metadata.ContentType) w.Header().Set("Content-Type", objectInfo.ContentType)
if metadata.MD5 != "" { if objectInfo.MD5Sum != "" {
w.Header().Set("ETag", "\""+metadata.MD5+"\"") w.Header().Set("ETag", "\""+objectInfo.MD5Sum+"\"")
} }
w.Header().Set("Content-Length", strconv.FormatInt(metadata.Size, 10)) w.Header().Set("Content-Length", strconv.FormatInt(objectInfo.Size, 10))
// for providing ranged content // for providing ranged content
if contentRange != nil { if contentRange != nil {

@ -247,7 +247,7 @@ func getLocation(r *http.Request) string {
// //
// output: // output:
// populated struct that can be serialized to match xml and json api spec output // populated struct that can be serialized to match xml and json api spec output
func generateListBucketsResponse(buckets []fs.BucketMetadata) ListBucketsResponse { func generateListBucketsResponse(buckets []fs.BucketInfo) ListBucketsResponse {
var listbuckets []Bucket var listbuckets []Bucket
var data = ListBucketsResponse{} var data = ListBucketsResponse{}
var owner = Owner{} var owner = Owner{}
@ -280,13 +280,13 @@ func generateListObjectsResponse(bucket, prefix, marker, delimiter string, maxKe
for _, object := range resp.Objects { for _, object := range resp.Objects {
var content = Object{} var content = Object{}
if object.Object == "" { if object.Name == "" {
continue continue
} }
content.Key = object.Object content.Key = object.Name
content.LastModified = object.LastModified.UTC().Format(timeFormatAMZ) content.LastModified = object.ModifiedTime.UTC().Format(timeFormatAMZ)
if object.MD5 != "" { if object.MD5Sum != "" {
content.ETag = "\"" + object.MD5 + "\"" content.ETag = "\"" + object.MD5Sum + "\""
} }
content.Size = object.Size content.Size = object.Size
content.StorageClass = "STANDARD" content.StorageClass = "STANDARD"

@ -101,9 +101,9 @@ func (api storageAPI) GetBucketLocationHandler(w http.ResponseWriter, r *http.Re
} }
} }
_, err := api.Filesystem.GetBucketMetadata(bucket) _, err := api.Filesystem.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err.Trace(), "GetBucketMetadata failed.", nil) errorIf(err.Trace(), "GetBucketInfo failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
case fs.BucketNotFound: case fs.BucketNotFound:
writeErrorResponse(w, r, ErrNoSuchBucket, r.URL.Path) writeErrorResponse(w, r, ErrNoSuchBucket, r.URL.Path)
@ -564,7 +564,7 @@ func (api storageAPI) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Req
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path)
return return
} }
metadata, err := api.Filesystem.CreateObject(bucket, object, "", -1, fileBody, nil) objectInfo, err := api.Filesystem.CreateObject(bucket, object, "", -1, fileBody, nil)
if err != nil { if err != nil {
errorIf(err.Trace(), "CreateObject failed.", nil) errorIf(err.Trace(), "CreateObject failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
@ -585,8 +585,8 @@ func (api storageAPI) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Req
} }
return return
} }
if metadata.MD5 != "" { if objectInfo.MD5Sum != "" {
w.Header().Set("ETag", "\""+metadata.MD5+"\"") w.Header().Set("ETag", "\""+objectInfo.MD5Sum+"\"")
} }
writeSuccessResponse(w, nil) writeSuccessResponse(w, nil)
} }
@ -613,9 +613,9 @@ func (api storageAPI) HeadBucketHandler(w http.ResponseWriter, r *http.Request)
} }
} }
_, err := api.Filesystem.GetBucketMetadata(bucket) _, err := api.Filesystem.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err.Trace(), "GetBucketMetadata failed.", nil) errorIf(err.Trace(), "GetBucketInfo failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
case fs.BucketNotFound: case fs.BucketNotFound:
writeErrorResponse(w, r, ErrNoSuchBucket, r.URL.Path) writeErrorResponse(w, r, ErrNoSuchBucket, r.URL.Path)

@ -79,7 +79,7 @@ func (api storageAPI) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
metadata, err := api.Filesystem.GetObjectMetadata(bucket, object) objectInfo, err := api.Filesystem.GetObjectInfo(bucket, object)
if err != nil { if err != nil {
errorIf(err.Trace(), "GetObject failed.", nil) errorIf(err.Trace(), "GetObject failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
@ -98,17 +98,18 @@ func (api storageAPI) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
} }
var hrange *httpRange var hrange *httpRange
hrange, err = getRequestedRange(r.Header.Get("Range"), metadata.Size) hrange, err = getRequestedRange(r.Header.Get("Range"), objectInfo.Size)
if err != nil { if err != nil {
writeErrorResponse(w, r, ErrInvalidRange, r.URL.Path) writeErrorResponse(w, r, ErrInvalidRange, r.URL.Path)
return return
} }
// Set standard object headers. // Set standard object headers.
setObjectHeaders(w, metadata, hrange) setObjectHeaders(w, objectInfo, hrange)
// Verify 'If-Modified-Since' and 'If-Unmodified-Since'. // Verify 'If-Modified-Since' and 'If-Unmodified-Since'.
if checkLastModified(w, r, metadata.LastModified) { lastModified := objectInfo.ModifiedTime
if checkLastModified(w, r, lastModified) {
return return
} }
// Verify 'If-Match' and 'If-None-Match'. // Verify 'If-Match' and 'If-None-Match'.
@ -236,8 +237,9 @@ func (api storageAPI) HeadObjectHandler(w http.ResponseWriter, r *http.Request)
return return
} }
metadata, err := api.Filesystem.GetObjectMetadata(bucket, object) objectInfo, err := api.Filesystem.GetObjectInfo(bucket, object)
if err != nil { if err != nil {
errorIf(err.Trace(bucket, object), "GetObjectInfo failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
case fs.BucketNameInvalid: case fs.BucketNameInvalid:
writeErrorResponse(w, r, ErrInvalidBucketName, r.URL.Path) writeErrorResponse(w, r, ErrInvalidBucketName, r.URL.Path)
@ -254,10 +256,11 @@ func (api storageAPI) HeadObjectHandler(w http.ResponseWriter, r *http.Request)
} }
// Set standard object headers. // Set standard object headers.
setObjectHeaders(w, metadata, nil) setObjectHeaders(w, objectInfo, nil)
// Verify 'If-Modified-Since' and 'If-Unmodified-Since'. // Verify 'If-Modified-Since' and 'If-Unmodified-Since'.
if checkLastModified(w, r, metadata.LastModified) { lastModified := objectInfo.ModifiedTime
if checkLastModified(w, r, lastModified) {
return return
} }
@ -333,9 +336,9 @@ func (api storageAPI) CopyObjectHandler(w http.ResponseWriter, r *http.Request)
return return
} }
metadata, err := api.Filesystem.GetObjectMetadata(sourceBucket, sourceObject) objectInfo, err := api.Filesystem.GetObjectInfo(sourceBucket, sourceObject)
if err != nil { if err != nil {
errorIf(err.Trace(), "GetObjectMetadata failed.", nil) errorIf(err.Trace(), "GetObjectInfo failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
case fs.BucketNameInvalid: case fs.BucketNameInvalid:
writeErrorResponse(w, r, ErrInvalidBucketName, objectSource) writeErrorResponse(w, r, ErrInvalidBucketName, objectSource)
@ -352,7 +355,7 @@ func (api storageAPI) CopyObjectHandler(w http.ResponseWriter, r *http.Request)
} }
/// maximum Upload size for object in a single CopyObject operation. /// maximum Upload size for object in a single CopyObject operation.
if isMaxObjectSize(metadata.Size) { if isMaxObjectSize(objectInfo.Size) {
writeErrorResponse(w, r, ErrEntityTooLarge, objectSource) writeErrorResponse(w, r, ErrEntityTooLarge, objectSource)
return return
} }
@ -370,12 +373,12 @@ func (api storageAPI) CopyObjectHandler(w http.ResponseWriter, r *http.Request)
}() }()
// Verify md5sum. // Verify md5sum.
expectedMD5Sum := metadata.MD5 expectedMD5Sum := objectInfo.MD5Sum
// Size of object. // Size of object.
size := metadata.Size size := objectInfo.Size
// Create the object. // Create the object.
metadata, err = api.Filesystem.CreateObject(bucket, object, expectedMD5Sum, size, reader, nil) objectInfo, err = api.Filesystem.CreateObject(bucket, object, expectedMD5Sum, size, reader, nil)
if err != nil { if err != nil {
errorIf(err.Trace(), "CreateObject failed.", nil) errorIf(err.Trace(), "CreateObject failed.", nil)
switch err.ToGoError().(type) { switch err.ToGoError().(type) {
@ -398,7 +401,7 @@ func (api storageAPI) CopyObjectHandler(w http.ResponseWriter, r *http.Request)
} }
return return
} }
response := generateCopyObjectResponse(metadata.MD5, metadata.LastModified) response := generateCopyObjectResponse(objectInfo.MD5Sum, objectInfo.ModifiedTime)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// write headers // write headers
setCommonHeaders(w) setCommonHeaders(w)
@ -440,7 +443,7 @@ func (api storageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
// Set http request for signature. // Set http request for signature.
auth := api.Signature.SetHTTPRequestToVerify(r) auth := api.Signature.SetHTTPRequestToVerify(r)
var metadata fs.ObjectMetadata var objectInfo fs.ObjectInfo
var err *probe.Error var err *probe.Error
switch getRequestAuthType(r) { switch getRequestAuthType(r) {
default: default:
@ -454,7 +457,7 @@ func (api storageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
// Create anonymous object. // Create anonymous object.
metadata, err = api.Filesystem.CreateObject(bucket, object, md5, size, r.Body, nil) objectInfo, err = api.Filesystem.CreateObject(bucket, object, md5, size, r.Body, nil)
case authTypePresigned: case authTypePresigned:
// For presigned requests verify them right here. // For presigned requests verify them right here.
var ok bool var ok bool
@ -469,10 +472,10 @@ func (api storageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
// Create presigned object. // Create presigned object.
metadata, err = api.Filesystem.CreateObject(bucket, object, md5, size, r.Body, nil) objectInfo, err = api.Filesystem.CreateObject(bucket, object, md5, size, r.Body, nil)
case authTypeSigned: case authTypeSigned:
// Create object. // Create object.
metadata, err = api.Filesystem.CreateObject(bucket, object, md5, size, r.Body, &auth) objectInfo, err = api.Filesystem.CreateObject(bucket, object, md5, size, r.Body, &auth)
} }
if err != nil { if err != nil {
errorIf(err.Trace(), "CreateObject failed.", nil) errorIf(err.Trace(), "CreateObject failed.", nil)
@ -498,8 +501,8 @@ func (api storageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
} }
return return
} }
if metadata.MD5 != "" { if objectInfo.MD5Sum != "" {
w.Header().Set("ETag", "\""+metadata.MD5+"\"") w.Header().Set("ETag", "\""+objectInfo.MD5Sum+"\"")
} }
writeSuccessResponse(w, nil) writeSuccessResponse(w, nil)
} }
@ -762,7 +765,7 @@ func (api storageAPI) CompleteMultipartUploadHandler(w http.ResponseWriter, r *h
// Set http request for signature. // Set http request for signature.
auth := api.Signature.SetHTTPRequestToVerify(r) auth := api.Signature.SetHTTPRequestToVerify(r)
var metadata fs.ObjectMetadata var objectInfo fs.ObjectInfo
var err *probe.Error var err *probe.Error
switch getRequestAuthType(r) { switch getRequestAuthType(r) {
default: default:
@ -776,7 +779,7 @@ func (api storageAPI) CompleteMultipartUploadHandler(w http.ResponseWriter, r *h
return return
} }
// Complete multipart upload anonymous. // Complete multipart upload anonymous.
metadata, err = api.Filesystem.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, r.Body, nil) objectInfo, err = api.Filesystem.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, r.Body, nil)
case authTypePresigned: case authTypePresigned:
// For presigned requests verify right here. // For presigned requests verify right here.
var ok bool var ok bool
@ -791,10 +794,10 @@ func (api storageAPI) CompleteMultipartUploadHandler(w http.ResponseWriter, r *h
return return
} }
// Complete multipart upload presigned. // Complete multipart upload presigned.
metadata, err = api.Filesystem.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, r.Body, nil) objectInfo, err = api.Filesystem.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, r.Body, nil)
case authTypeSigned: case authTypeSigned:
// Complete multipart upload. // Complete multipart upload.
metadata, err = api.Filesystem.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, r.Body, &auth) objectInfo, err = api.Filesystem.CompleteMultipartUpload(bucket, object, objectResourcesMetadata.UploadID, r.Body, &auth)
} }
if err != nil { if err != nil {
errorIf(err.Trace(), "CompleteMultipartUpload failed.", nil) errorIf(err.Trace(), "CompleteMultipartUpload failed.", nil)
@ -827,7 +830,7 @@ func (api storageAPI) CompleteMultipartUploadHandler(w http.ResponseWriter, r *h
// get object location. // get object location.
location := getLocation(r) location := getLocation(r)
// Generate complete multipart response. // Generate complete multipart response.
response := generateCompleteMultpartUploadResponse(bucket, object, location, metadata.MD5) response := generateCompleteMultpartUploadResponse(bucket, object, location, objectInfo.MD5Sum)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// write headers // write headers
setCommonHeaders(w) setCommonHeaders(w)

@ -86,9 +86,9 @@ func testMultipartObjectCreation(c *check.C, create func() Filesystem) {
} }
completedPartsBytes, e := xml.Marshal(completedParts) completedPartsBytes, e := xml.Marshal(completedParts)
c.Assert(e, check.IsNil) c.Assert(e, check.IsNil)
objectMetadata, err := fs.CompleteMultipartUpload("bucket", "key", uploadID, bytes.NewReader(completedPartsBytes), nil) objectInfo, err := fs.CompleteMultipartUpload("bucket", "key", uploadID, bytes.NewReader(completedPartsBytes), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objectMetadata.MD5, check.Equals, "9b7d6f13ba00e24d0b02de92e814891b-10") c.Assert(objectInfo.MD5Sum, check.Equals, "9b7d6f13ba00e24d0b02de92e814891b-10")
} }
func testMultipartObjectAbort(c *check.C, create func() Filesystem) { func testMultipartObjectAbort(c *check.C, create func() Filesystem) {
@ -141,9 +141,9 @@ func testMultipleObjectCreation(c *check.C, create func() Filesystem) {
key := "obj" + strconv.Itoa(i) key := "obj" + strconv.Itoa(i)
objects[key] = []byte(randomString) objects[key] = []byte(randomString)
objectMetadata, err := fs.CreateObject("bucket", key, expectedmd5Sum, int64(len(randomString)), bytes.NewBufferString(randomString), nil) objectInfo, err := fs.CreateObject("bucket", key, expectedmd5Sum, int64(len(randomString)), bytes.NewBufferString(randomString), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objectMetadata.MD5, check.Equals, expectedmd5Sumhex) c.Assert(objectInfo.MD5Sum, check.Equals, expectedmd5Sumhex)
} }
for key, value := range objects { for key, value := range objects {
@ -152,7 +152,7 @@ func testMultipleObjectCreation(c *check.C, create func() Filesystem) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(byteBuffer.Bytes(), check.DeepEquals, value) c.Assert(byteBuffer.Bytes(), check.DeepEquals, value)
metadata, err := fs.GetObjectMetadata("bucket", key) metadata, err := fs.GetObjectInfo("bucket", key)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(metadata.Size, check.Equals, int64(len(value))) c.Assert(metadata.Size, check.Equals, int64(len(value)))
} }
@ -200,11 +200,11 @@ func testPaging(c *check.C, create func() Filesystem) {
{ {
result, err = fs.ListObjects("bucket", "", "", "", 1000) result, err = fs.ListObjects("bucket", "", "", "", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
c.Assert(result.Objects[2].Object, check.Equals, "obj0") c.Assert(result.Objects[2].Name, check.Equals, "obj0")
c.Assert(result.Objects[3].Object, check.Equals, "obj1") c.Assert(result.Objects[3].Name, check.Equals, "obj1")
c.Assert(result.Objects[4].Object, check.Equals, "obj10") c.Assert(result.Objects[4].Name, check.Equals, "obj10")
} }
// check delimited results with delimiter and prefix // check delimited results with delimiter and prefix
@ -224,11 +224,11 @@ func testPaging(c *check.C, create func() Filesystem) {
{ {
result, err = fs.ListObjects("bucket", "", "", "/", 1000) result, err = fs.ListObjects("bucket", "", "", "/", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
c.Assert(result.Objects[2].Object, check.Equals, "obj0") c.Assert(result.Objects[2].Name, check.Equals, "obj0")
c.Assert(result.Objects[3].Object, check.Equals, "obj1") c.Assert(result.Objects[3].Name, check.Equals, "obj1")
c.Assert(result.Objects[4].Object, check.Equals, "obj10") c.Assert(result.Objects[4].Name, check.Equals, "obj10")
c.Assert(result.Prefixes[0], check.Equals, "this/") c.Assert(result.Prefixes[0], check.Equals, "this/")
} }
@ -236,26 +236,26 @@ func testPaging(c *check.C, create func() Filesystem) {
{ {
result, err = fs.ListObjects("bucket", "", "newPrefix", "", 3) result, err = fs.ListObjects("bucket", "", "newPrefix", "", 3)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix2")
c.Assert(result.Objects[1].Object, check.Equals, "obj0") c.Assert(result.Objects[1].Name, check.Equals, "obj0")
c.Assert(result.Objects[2].Object, check.Equals, "obj1") c.Assert(result.Objects[2].Name, check.Equals, "obj1")
} }
// check ordering of results with prefix // check ordering of results with prefix
{ {
result, err = fs.ListObjects("bucket", "obj", "", "", 1000) result, err = fs.ListObjects("bucket", "obj", "", "", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "obj0") c.Assert(result.Objects[0].Name, check.Equals, "obj0")
c.Assert(result.Objects[1].Object, check.Equals, "obj1") c.Assert(result.Objects[1].Name, check.Equals, "obj1")
c.Assert(result.Objects[2].Object, check.Equals, "obj10") c.Assert(result.Objects[2].Name, check.Equals, "obj10")
c.Assert(result.Objects[3].Object, check.Equals, "obj2") c.Assert(result.Objects[3].Name, check.Equals, "obj2")
c.Assert(result.Objects[4].Object, check.Equals, "obj3") c.Assert(result.Objects[4].Name, check.Equals, "obj3")
} }
// check ordering of results with prefix and no paging // check ordering of results with prefix and no paging
{ {
result, err = fs.ListObjects("bucket", "new", "", "", 5) result, err = fs.ListObjects("bucket", "new", "", "", 5)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
} }
} }
@ -268,9 +268,9 @@ func testObjectOverwriteWorks(c *check.C, create func() Filesystem) {
hasher1.Write([]byte("one")) hasher1.Write([]byte("one"))
md5Sum1 := base64.StdEncoding.EncodeToString(hasher1.Sum(nil)) md5Sum1 := base64.StdEncoding.EncodeToString(hasher1.Sum(nil))
md5Sum1hex := hex.EncodeToString(hasher1.Sum(nil)) md5Sum1hex := hex.EncodeToString(hasher1.Sum(nil))
objectMetadata, err := fs.CreateObject("bucket", "object", md5Sum1, int64(len("one")), bytes.NewBufferString("one"), nil) objectInfo, err := fs.CreateObject("bucket", "object", md5Sum1, int64(len("one")), bytes.NewBufferString("one"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(md5Sum1hex, check.Equals, objectMetadata.MD5) c.Assert(md5Sum1hex, check.Equals, objectInfo.MD5Sum)
hasher2 := md5.New() hasher2 := md5.New()
hasher2.Write([]byte("three")) hasher2.Write([]byte("three"))
@ -308,9 +308,9 @@ func testPutObjectInSubdir(c *check.C, create func() Filesystem) {
hasher.Write([]byte("hello world")) hasher.Write([]byte("hello world"))
md5Sum1 := base64.StdEncoding.EncodeToString(hasher.Sum(nil)) md5Sum1 := base64.StdEncoding.EncodeToString(hasher.Sum(nil))
md5Sum1hex := hex.EncodeToString(hasher.Sum(nil)) md5Sum1hex := hex.EncodeToString(hasher.Sum(nil))
objectMetadata, err := fs.CreateObject("bucket", "dir1/dir2/object", md5Sum1, int64(len("hello world")), bytes.NewBufferString("hello world"), nil) objectInfo, err := fs.CreateObject("bucket", "dir1/dir2/object", md5Sum1, int64(len("hello world")), bytes.NewBufferString("hello world"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objectMetadata.MD5, check.Equals, md5Sum1hex) c.Assert(objectInfo.MD5Sum, check.Equals, md5Sum1hex)
var bytesBuffer bytes.Buffer var bytesBuffer bytes.Buffer
length, err := fs.GetObject(&bytesBuffer, "bucket", "dir1/dir2/object", 0, 0) length, err := fs.GetObject(&bytesBuffer, "bucket", "dir1/dir2/object", 0, 0)
@ -437,7 +437,7 @@ func testDefaultContentType(c *check.C, create func() Filesystem) {
// test empty // test empty
_, err = fs.CreateObject("bucket", "one", "", int64(len("one")), bytes.NewBufferString("one"), nil) _, err = fs.CreateObject("bucket", "one", "", int64(len("one")), bytes.NewBufferString("one"), nil)
metadata, err := fs.GetObjectMetadata("bucket", "one") metadata, err := fs.GetObjectInfo("bucket", "one")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(metadata.ContentType, check.Equals, "application/octet-stream") c.Assert(metadata.ContentType, check.Equals, "application/octet-stream")
} }

@ -85,9 +85,9 @@ func testMultipartObjectCreation(c *check.C, create func() Filesystem) {
} }
completedPartsBytes, e := xml.Marshal(completedParts) completedPartsBytes, e := xml.Marshal(completedParts)
c.Assert(e, check.IsNil) c.Assert(e, check.IsNil)
objectMetadata, err := fs.CompleteMultipartUpload("bucket", "key", uploadID, bytes.NewReader(completedPartsBytes), nil) objectInfo, err := fs.CompleteMultipartUpload("bucket", "key", uploadID, bytes.NewReader(completedPartsBytes), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objectMetadata.MD5, check.Equals, "9b7d6f13ba00e24d0b02de92e814891b-10") c.Assert(objectInfo.MD5Sum, check.Equals, "9b7d6f13ba00e24d0b02de92e814891b-10")
} }
func testMultipartObjectAbort(c *check.C, create func() Filesystem) { func testMultipartObjectAbort(c *check.C, create func() Filesystem) {
@ -140,9 +140,9 @@ func testMultipleObjectCreation(c *check.C, create func() Filesystem) {
key := "obj" + strconv.Itoa(i) key := "obj" + strconv.Itoa(i)
objects[key] = []byte(randomString) objects[key] = []byte(randomString)
objectMetadata, err := fs.CreateObject("bucket", key, expectedmd5Sum, int64(len(randomString)), bytes.NewBufferString(randomString), nil) objectInfo, err := fs.CreateObject("bucket", key, expectedmd5Sum, int64(len(randomString)), bytes.NewBufferString(randomString), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objectMetadata.MD5, check.Equals, expectedmd5Sumhex) c.Assert(objectInfo.MD5Sum, check.Equals, expectedmd5Sumhex)
} }
for key, value := range objects { for key, value := range objects {
@ -151,7 +151,7 @@ func testMultipleObjectCreation(c *check.C, create func() Filesystem) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(byteBuffer.Bytes(), check.DeepEquals, value) c.Assert(byteBuffer.Bytes(), check.DeepEquals, value)
metadata, err := fs.GetObjectMetadata("bucket", key) metadata, err := fs.GetObjectInfo("bucket", key)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(metadata.Size, check.Equals, int64(len(value))) c.Assert(metadata.Size, check.Equals, int64(len(value)))
} }
@ -199,11 +199,11 @@ func testPaging(c *check.C, create func() Filesystem) {
{ {
result, err = fs.ListObjects("bucket", "", "", "", 1000) result, err = fs.ListObjects("bucket", "", "", "", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
c.Assert(result.Objects[2].Object, check.Equals, "obj0") c.Assert(result.Objects[2].Name, check.Equals, "obj0")
c.Assert(result.Objects[3].Object, check.Equals, "obj1") c.Assert(result.Objects[3].Name, check.Equals, "obj1")
c.Assert(result.Objects[4].Object, check.Equals, "obj10") c.Assert(result.Objects[4].Name, check.Equals, "obj10")
} }
// check delimited results with delimiter and prefix // check delimited results with delimiter and prefix
@ -222,11 +222,11 @@ func testPaging(c *check.C, create func() Filesystem) {
{ {
result, err = fs.ListObjects("bucket", "", "", "/", 1000) result, err = fs.ListObjects("bucket", "", "", "/", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
c.Assert(result.Objects[2].Object, check.Equals, "obj0") c.Assert(result.Objects[2].Name, check.Equals, "obj0")
c.Assert(result.Objects[3].Object, check.Equals, "obj1") c.Assert(result.Objects[3].Name, check.Equals, "obj1")
c.Assert(result.Objects[4].Object, check.Equals, "obj10") c.Assert(result.Objects[4].Name, check.Equals, "obj10")
c.Assert(result.Prefixes[0], check.Equals, "this/") c.Assert(result.Prefixes[0], check.Equals, "this/")
} }
@ -234,26 +234,26 @@ func testPaging(c *check.C, create func() Filesystem) {
{ {
result, err = fs.ListObjects("bucket", "", "newPrefix", "", 3) result, err = fs.ListObjects("bucket", "", "newPrefix", "", 3)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix2")
c.Assert(result.Objects[1].Object, check.Equals, "obj0") c.Assert(result.Objects[1].Name, check.Equals, "obj0")
c.Assert(result.Objects[2].Object, check.Equals, "obj1") c.Assert(result.Objects[2].Name, check.Equals, "obj1")
} }
// check ordering of results with prefix // check ordering of results with prefix
{ {
result, err = fs.ListObjects("bucket", "obj", "", "", 1000) result, err = fs.ListObjects("bucket", "obj", "", "", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "obj0") c.Assert(result.Objects[0].Name, check.Equals, "obj0")
c.Assert(result.Objects[1].Object, check.Equals, "obj1") c.Assert(result.Objects[1].Name, check.Equals, "obj1")
c.Assert(result.Objects[2].Object, check.Equals, "obj10") c.Assert(result.Objects[2].Name, check.Equals, "obj10")
c.Assert(result.Objects[3].Object, check.Equals, "obj2") c.Assert(result.Objects[3].Name, check.Equals, "obj2")
c.Assert(result.Objects[4].Object, check.Equals, "obj3") c.Assert(result.Objects[4].Name, check.Equals, "obj3")
} }
// check ordering of results with prefix and no paging // check ordering of results with prefix and no paging
{ {
result, err = fs.ListObjects("bucket", "new", "", "", 5) result, err = fs.ListObjects("bucket", "new", "", "", 5)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Object, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Object, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
} }
} }
@ -265,9 +265,9 @@ func testObjectOverwriteWorks(c *check.C, create func() Filesystem) {
hasher1.Write([]byte("one")) hasher1.Write([]byte("one"))
md5Sum1 := base64.StdEncoding.EncodeToString(hasher1.Sum(nil)) md5Sum1 := base64.StdEncoding.EncodeToString(hasher1.Sum(nil))
md5Sum1hex := hex.EncodeToString(hasher1.Sum(nil)) md5Sum1hex := hex.EncodeToString(hasher1.Sum(nil))
objectMetadata, err := fs.CreateObject("bucket", "object", md5Sum1, int64(len("one")), bytes.NewBufferString("one"), nil) objectInfo, err := fs.CreateObject("bucket", "object", md5Sum1, int64(len("one")), bytes.NewBufferString("one"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(md5Sum1hex, check.Equals, objectMetadata.MD5) c.Assert(md5Sum1hex, check.Equals, objectInfo.MD5Sum)
hasher2 := md5.New() hasher2 := md5.New()
hasher2.Write([]byte("three")) hasher2.Write([]byte("three"))
@ -305,9 +305,9 @@ func testPutObjectInSubdir(c *check.C, create func() Filesystem) {
hasher.Write([]byte("hello world")) hasher.Write([]byte("hello world"))
md5Sum1 := base64.StdEncoding.EncodeToString(hasher.Sum(nil)) md5Sum1 := base64.StdEncoding.EncodeToString(hasher.Sum(nil))
md5Sum1hex := hex.EncodeToString(hasher.Sum(nil)) md5Sum1hex := hex.EncodeToString(hasher.Sum(nil))
objectMetadata, err := fs.CreateObject("bucket", "dir1/dir2/object", md5Sum1, int64(len("hello world")), bytes.NewBufferString("hello world"), nil) objectInfo, err := fs.CreateObject("bucket", "dir1/dir2/object", md5Sum1, int64(len("hello world")), bytes.NewBufferString("hello world"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objectMetadata.MD5, check.Equals, md5Sum1hex) c.Assert(objectInfo.MD5Sum, check.Equals, md5Sum1hex)
var bytesBuffer bytes.Buffer var bytesBuffer bytes.Buffer
length, err := fs.GetObject(&bytesBuffer, "bucket", "dir1/dir2/object", 0, 0) length, err := fs.GetObject(&bytesBuffer, "bucket", "dir1/dir2/object", 0, 0)
@ -438,7 +438,7 @@ func testDefaultContentType(c *check.C, create func() Filesystem) {
// test empty // test empty
_, err = fs.CreateObject("bucket", "one", "", int64(len("one")), bytes.NewBufferString("one"), nil) _, err = fs.CreateObject("bucket", "one", "", int64(len("one")), bytes.NewBufferString("one"), nil)
metadata, err := fs.GetObjectMetadata("bucket", "one") metadata, err := fs.GetObjectInfo("bucket", "one")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(metadata.ContentType, check.Equals, "application/octet-stream") c.Assert(metadata.ContentType, check.Equals, "application/octet-stream")
} }

@ -75,9 +75,11 @@ func (f byName) Less(i, j int) bool {
// ObjectInfo - object info // ObjectInfo - object info
type ObjectInfo struct { type ObjectInfo struct {
Bucket string
Name string Name string
ModifiedTime time.Time ModifiedTime time.Time
Checksum string ContentType string
MD5Sum string
Size int64 Size int64
IsDir bool IsDir bool
Err error Err error
@ -99,26 +101,54 @@ func readDir(scanDir, namePrefix string) (objInfos []ObjectInfo) {
return return
} }
// Close the directory
f.Close() f.Close()
// Sort files by Name.
sort.Sort(byName(fis)) sort.Sort(byName(fis))
// make []ObjectInfo from []FileInfo // Populate []ObjectInfo from []FileInfo
for _, fi := range fis { for _, fi := range fis {
name := fi.Name() name := fi.Name()
size := fi.Size()
modTime := fi.ModTime()
isDir := fi.Mode().IsDir()
// Add prefix if name prefix exists.
if namePrefix != "" { if namePrefix != "" {
name = namePrefix + "/" + name name = namePrefix + "/" + name
} }
if fi.IsDir() { // For directories explicitly end with '/'.
if isDir {
name += "/" name += "/"
size = 0 // Size is set to '0' for directories explicitly.
}
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
// Handle symlink by doing an additional stat and follow the link.
st, e := os.Stat(filepath.Join(scanDir, name))
if e != nil {
objInfos = append(objInfos, ObjectInfo{Err: err})
return
}
size = st.Size()
modTime = st.ModTime()
isDir = st.Mode().IsDir()
// For directories explicitly end with '/'.
if isDir {
name += "/"
size = 0 // Size is set to '0' for directories explicitly.
}
} }
// Populate []ObjectInfo.
objInfos = append(objInfos, ObjectInfo{ objInfos = append(objInfos, ObjectInfo{
Name: name, Name: name,
ModifiedTime: fi.ModTime(), ModifiedTime: modTime,
Checksum: "", MD5Sum: "", // TODO
Size: fi.Size(), Size: size,
IsDir: fi.IsDir(), IsDir: isDir,
}) })
} }

@ -106,17 +106,15 @@ func (fs Filesystem) ListObjects(bucket, prefix, marker, delimiter string, maxKe
continue continue
} }
// Add the bucket.
objInfo.Bucket = bucket
if strings.HasPrefix(objInfo.Name, prefix) { if strings.HasPrefix(objInfo.Name, prefix) {
if objInfo.Name > marker { if objInfo.Name > marker {
if objInfo.IsDir { if objInfo.IsDir {
result.Prefixes = append(result.Prefixes, objInfo.Name) result.Prefixes = append(result.Prefixes, objInfo.Name)
} else { } else {
result.Objects = append(result.Objects, ObjectMetadata{ result.Objects = append(result.Objects, objInfo)
Bucket: bucket,
Object: objInfo.Name,
LastModified: objInfo.ModifiedTime,
Size: objInfo.Size,
})
} }
nextMarker = objInfo.Name nextMarker = objInfo.Name
i++ i++

@ -21,6 +21,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"github.com/minio/minio/pkg/disk" "github.com/minio/minio/pkg/disk"
"github.com/minio/minio/pkg/probe" "github.com/minio/minio/pkg/probe"
@ -55,13 +56,19 @@ func (fs Filesystem) DeleteBucket(bucket string) *probe.Error {
return nil return nil
} }
// BucketInfo - name and create date
type BucketInfo struct {
Name string
Created time.Time
}
// ListBuckets - Get service. // ListBuckets - Get service.
func (fs Filesystem) ListBuckets() ([]BucketMetadata, *probe.Error) { func (fs Filesystem) ListBuckets() ([]BucketInfo, *probe.Error) {
files, e := ioutil.ReadDir(fs.path) files, e := ioutil.ReadDir(fs.path)
if e != nil { if e != nil {
return []BucketMetadata{}, probe.NewError(e) return []BucketInfo{}, probe.NewError(e)
} }
var metadataList []BucketMetadata var metadataList []BucketInfo
for _, file := range files { for _, file := range files {
if !file.IsDir() { if !file.IsDir() {
// If not directory, ignore all file types. // If not directory, ignore all file types.
@ -72,7 +79,7 @@ func (fs Filesystem) ListBuckets() ([]BucketMetadata, *probe.Error) {
if !IsValidBucketName(dirName) { if !IsValidBucketName(dirName) {
continue continue
} }
metadata := BucketMetadata{ metadata := BucketInfo{
Name: dirName, Name: dirName,
Created: file.ModTime(), Created: file.ModTime(),
} }
@ -84,7 +91,7 @@ func (fs Filesystem) ListBuckets() ([]BucketMetadata, *probe.Error) {
} }
// removeDuplicateBuckets - remove duplicate buckets. // removeDuplicateBuckets - remove duplicate buckets.
func removeDuplicateBuckets(buckets []BucketMetadata) []BucketMetadata { func removeDuplicateBuckets(buckets []BucketInfo) []BucketInfo {
length := len(buckets) - 1 length := len(buckets) - 1
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
for j := i + 1; j <= length; j++ { for j := i + 1; j <= length; j++ {
@ -153,10 +160,10 @@ func (fs Filesystem) denormalizeBucket(bucket string) string {
return bucket return bucket
} }
// GetBucketMetadata - get bucket metadata. // GetBucketInfo - get bucket metadata.
func (fs Filesystem) GetBucketMetadata(bucket string) (BucketMetadata, *probe.Error) { func (fs Filesystem) GetBucketInfo(bucket string) (BucketInfo, *probe.Error) {
if !IsValidBucketName(bucket) { if !IsValidBucketName(bucket) {
return BucketMetadata{}, probe.NewError(BucketNameInvalid{Bucket: bucket}) return BucketInfo{}, probe.NewError(BucketNameInvalid{Bucket: bucket})
} }
bucket = fs.denormalizeBucket(bucket) bucket = fs.denormalizeBucket(bucket)
// Get bucket path. // Get bucket path.
@ -165,11 +172,11 @@ func (fs Filesystem) GetBucketMetadata(bucket string) (BucketMetadata, *probe.Er
if e != nil { if e != nil {
// Check if bucket exists. // Check if bucket exists.
if os.IsNotExist(e) { if os.IsNotExist(e) {
return BucketMetadata{}, probe.NewError(BucketNotFound{Bucket: bucket}) return BucketInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
} }
return BucketMetadata{}, probe.NewError(e) return BucketInfo{}, probe.NewError(e)
} }
bucketMetadata := BucketMetadata{} bucketMetadata := BucketInfo{}
bucketMetadata.Name = fi.Name() bucketMetadata.Name = fi.Name()
bucketMetadata.Created = fi.ModTime() bucketMetadata.Created = fi.ModTime()
return bucketMetadata, nil return bucketMetadata, nil

@ -153,7 +153,7 @@ func BenchmarkDeleteBucket(b *testing.B) {
} }
} }
func BenchmarkGetBucketMetadata(b *testing.B) { func BenchmarkGetBucketInfo(b *testing.B) {
// Make a temporary directory to use as the filesystem. // Make a temporary directory to use as the filesystem.
directory, fserr := ioutil.TempDir("", "minio-benchmark") directory, fserr := ioutil.TempDir("", "minio-benchmark")
if fserr != nil { if fserr != nil {
@ -177,7 +177,7 @@ func BenchmarkGetBucketMetadata(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
// Retrieve the metadata! // Retrieve the metadata!
_, err := filesystem.GetBucketMetadata("bucket") _, err := filesystem.GetBucketInfo("bucket")
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }

@ -16,28 +16,7 @@
package fs package fs
import ( import "time"
"os"
"time"
)
// BucketMetadata - name and create date
type BucketMetadata struct {
Name string
Created time.Time
}
// ObjectMetadata - object key and its relevant metadata
type ObjectMetadata struct {
Bucket string
Object string
ContentType string
LastModified time.Time
Mode os.FileMode
MD5 string
Size int64
}
// PartMetadata - various types of individual part resources // PartMetadata - various types of individual part resources
type PartMetadata struct { type PartMetadata struct {
@ -89,7 +68,7 @@ type BucketMultipartResourcesMetadata struct {
type ListObjectsResult struct { type ListObjectsResult struct {
IsTruncated bool IsTruncated bool
NextMarker string NextMarker string
Objects []ObjectMetadata Objects []ObjectInfo
Prefixes []string Prefixes []string
} }

@ -53,7 +53,7 @@ func (fs Filesystem) isValidUploadID(object, uploadID string) (ok bool) {
return return
} }
// byObjectMetadataKey is a sortable interface for UploadMetadata slice // byObjectInfoKey is a sortable interface for UploadMetadata slice
type byUploadMetadataKey []*UploadMetadata type byUploadMetadataKey []*UploadMetadata
func (b byUploadMetadataKey) Len() int { return len(b) } func (b byUploadMetadataKey) Len() int { return len(b) }
@ -456,20 +456,20 @@ func (fs Filesystem) CreateObjectPart(bucket, object, uploadID, expectedMD5Sum s
} }
// CompleteMultipartUpload - complete a multipart upload and persist the data // CompleteMultipartUpload - complete a multipart upload and persist the data
func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, data io.Reader, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, data io.Reader, signature *signature4.Sign) (ObjectInfo, *probe.Error) {
// Check bucket name is valid. // Check bucket name is valid.
if !IsValidBucketName(bucket) { if !IsValidBucketName(bucket) {
return ObjectMetadata{}, probe.NewError(BucketNameInvalid{Bucket: bucket}) return ObjectInfo{}, probe.NewError(BucketNameInvalid{Bucket: bucket})
} }
// Verify object path is legal. // Verify object path is legal.
if !IsValidObjectName(object) { if !IsValidObjectName(object) {
return ObjectMetadata{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object}) return ObjectInfo{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object})
} }
// Verify if valid upload for incoming object. // Verify if valid upload for incoming object.
if !fs.isValidUploadID(object, uploadID) { if !fs.isValidUploadID(object, uploadID) {
return ObjectMetadata{}, probe.NewError(InvalidUploadID{UploadID: uploadID}) return ObjectInfo{}, probe.NewError(InvalidUploadID{UploadID: uploadID})
} }
bucket = fs.denormalizeBucket(bucket) bucket = fs.denormalizeBucket(bucket)
@ -477,21 +477,21 @@ func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, da
if _, e := os.Stat(bucketPath); e != nil { if _, e := os.Stat(bucketPath); e != nil {
// Check bucket exists. // Check bucket exists.
if os.IsNotExist(e) { if os.IsNotExist(e) {
return ObjectMetadata{}, probe.NewError(BucketNotFound{Bucket: bucket}) return ObjectInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
} }
return ObjectMetadata{}, probe.NewError(InternalError{}) return ObjectInfo{}, probe.NewError(InternalError{})
} }
objectPath := filepath.Join(bucketPath, object) objectPath := filepath.Join(bucketPath, object)
objectWriter, e := atomic.FileCreateWithPrefix(objectPath, "$tmpobject") objectWriter, e := atomic.FileCreateWithPrefix(objectPath, "$tmpobject")
if e != nil { if e != nil {
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
partBytes, e := ioutil.ReadAll(data) partBytes, e := ioutil.ReadAll(data)
if e != nil { if e != nil {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
if signature != nil { if signature != nil {
sh := sha256.New() sh := sha256.New()
@ -499,21 +499,21 @@ func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, da
ok, err := signature.DoesSignatureMatch(hex.EncodeToString(sh.Sum(nil))) ok, err := signature.DoesSignatureMatch(hex.EncodeToString(sh.Sum(nil)))
if err != nil { if err != nil {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, err.Trace() return ObjectInfo{}, err.Trace()
} }
if !ok { if !ok {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(SignDoesNotMatch{}) return ObjectInfo{}, probe.NewError(SignDoesNotMatch{})
} }
} }
completeMultipartUpload := &CompleteMultipartUpload{} completeMultipartUpload := &CompleteMultipartUpload{}
if e = xml.Unmarshal(partBytes, completeMultipartUpload); e != nil { if e = xml.Unmarshal(partBytes, completeMultipartUpload); e != nil {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(MalformedXML{}) return ObjectInfo{}, probe.NewError(MalformedXML{})
} }
if !sort.IsSorted(completedParts(completeMultipartUpload.Part)) { if !sort.IsSorted(completedParts(completeMultipartUpload.Part)) {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(InvalidPartOrder{}) return ObjectInfo{}, probe.NewError(InvalidPartOrder{})
} }
// Save parts for verification. // Save parts for verification.
@ -526,14 +526,14 @@ func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, da
if !doPartsMatch(parts, savedParts) { if !doPartsMatch(parts, savedParts) {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(InvalidPart{}) return ObjectInfo{}, probe.NewError(InvalidPart{})
} }
// Parts successfully validated, save all the parts. // Parts successfully validated, save all the parts.
partPathPrefix := objectPath + uploadID partPathPrefix := objectPath + uploadID
if err := saveParts(partPathPrefix, objectWriter, parts); err != nil { if err := saveParts(partPathPrefix, objectWriter, parts); err != nil {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, err.Trace(partPathPrefix) return ObjectInfo{}, err.Trace(partPathPrefix)
} }
var md5Strs []string var md5Strs []string
for _, part := range savedParts { for _, part := range savedParts {
@ -543,7 +543,7 @@ func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, da
s3MD5, err := makeS3MD5(md5Strs...) s3MD5, err := makeS3MD5(md5Strs...)
if err != nil { if err != nil {
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, err.Trace(md5Strs...) return ObjectInfo{}, err.Trace(md5Strs...)
} }
// Successfully saved multipart, remove all parts in a routine. // Successfully saved multipart, remove all parts in a routine.
@ -555,18 +555,18 @@ func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, da
if err := saveMultipartsSession(*fs.multiparts); err != nil { if err := saveMultipartsSession(*fs.multiparts); err != nil {
fs.rwLock.Unlock() fs.rwLock.Unlock()
objectWriter.CloseAndPurge() objectWriter.CloseAndPurge()
return ObjectMetadata{}, err.Trace(partPathPrefix) return ObjectInfo{}, err.Trace(partPathPrefix)
} }
if e = objectWriter.Close(); e != nil { if e = objectWriter.Close(); e != nil {
fs.rwLock.Unlock() fs.rwLock.Unlock()
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
fs.rwLock.Unlock() fs.rwLock.Unlock()
// Send stat again to get object metadata. // Send stat again to get object metadata.
st, e := os.Stat(objectPath) st, e := os.Stat(objectPath)
if e != nil { if e != nil {
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
contentType := "application/octet-stream" contentType := "application/octet-stream"
@ -576,13 +576,13 @@ func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, da
contentType = content.ContentType contentType = content.ContentType
} }
} }
newObject := ObjectMetadata{ newObject := ObjectInfo{
Bucket: bucket, Bucket: bucket,
Object: object, Name: object,
LastModified: st.ModTime(), ModifiedTime: st.ModTime(),
Size: st.Size(), Size: st.Size(),
ContentType: contentType, ContentType: contentType,
MD5: s3MD5, MD5Sum: s3MD5,
} }
return newObject, nil return newObject, nil
} }

@ -102,15 +102,15 @@ func (fs Filesystem) GetObject(w io.Writer, bucket, object string, start, length
return count, nil return count, nil
} }
// GetObjectMetadata - get object metadata. // GetObjectInfo - get object info.
func (fs Filesystem) GetObjectMetadata(bucket, object string) (ObjectMetadata, *probe.Error) { func (fs Filesystem) GetObjectInfo(bucket, object string) (ObjectInfo, *probe.Error) {
// Input validation. // Input validation.
if !IsValidBucketName(bucket) { if !IsValidBucketName(bucket) {
return ObjectMetadata{}, probe.NewError(BucketNameInvalid{Bucket: bucket}) return ObjectInfo{}, probe.NewError(BucketNameInvalid{Bucket: bucket})
} }
if !IsValidObjectName(object) { if !IsValidObjectName(object) {
return ObjectMetadata{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: bucket}) return ObjectInfo{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: bucket})
} }
// Normalize buckets. // Normalize buckets.
@ -118,23 +118,20 @@ func (fs Filesystem) GetObjectMetadata(bucket, object string) (ObjectMetadata, *
bucketPath := filepath.Join(fs.path, bucket) bucketPath := filepath.Join(fs.path, bucket)
if _, e := os.Stat(bucketPath); e != nil { if _, e := os.Stat(bucketPath); e != nil {
if os.IsNotExist(e) { if os.IsNotExist(e) {
return ObjectMetadata{}, probe.NewError(BucketNotFound{Bucket: bucket}) return ObjectInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
} }
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
metadata, err := getMetadata(fs.path, bucket, object) info, err := getObjectInfo(fs.path, bucket, object)
if err != nil { if err != nil {
return ObjectMetadata{}, err.Trace(bucket, object) return ObjectInfo{}, err.Trace(bucket, object)
} }
if metadata.Mode.IsDir() { return info, nil
return ObjectMetadata{}, probe.NewError(ObjectNotFound{Bucket: bucket, Object: object})
}
return metadata, nil
} }
// getMetadata - get object metadata. // getObjectInfo - get object stat info.
func getMetadata(rootPath, bucket, object string) (ObjectMetadata, *probe.Error) { func getObjectInfo(rootPath, bucket, object string) (ObjectInfo, *probe.Error) {
// Do not use filepath.Join() since filepath.Join strips off any // Do not use filepath.Join() since filepath.Join strips off any
// object names with '/', use them as is in a static manner so // object names with '/', use them as is in a static manner so
// that we can send a proper 'ObjectNotFound' reply back upon // that we can send a proper 'ObjectNotFound' reply back upon
@ -149,9 +146,9 @@ func getMetadata(rootPath, bucket, object string) (ObjectMetadata, *probe.Error)
stat, err := os.Stat(objectPath) stat, err := os.Stat(objectPath)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return ObjectMetadata{}, probe.NewError(ObjectNotFound{Bucket: bucket, Object: object}) return ObjectInfo{}, probe.NewError(ObjectNotFound{Bucket: bucket, Object: object})
} }
return ObjectMetadata{}, probe.NewError(err) return ObjectInfo{}, probe.NewError(err)
} }
contentType := "application/octet-stream" contentType := "application/octet-stream"
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
@ -164,13 +161,13 @@ func getMetadata(rootPath, bucket, object string) (ObjectMetadata, *probe.Error)
contentType = content.ContentType contentType = content.ContentType
} }
} }
metadata := ObjectMetadata{ metadata := ObjectInfo{
Bucket: bucket, Bucket: bucket,
Object: object, Name: object,
LastModified: stat.ModTime(), ModifiedTime: stat.ModTime(),
Size: stat.Size(), Size: stat.Size(),
ContentType: contentType, ContentType: contentType,
Mode: stat.Mode(), IsDir: stat.Mode().IsDir(),
} }
return metadata, nil return metadata, nil
} }
@ -199,36 +196,36 @@ func isMD5SumEqual(expectedMD5Sum, actualMD5Sum string) bool {
} }
// CreateObject - create an object. // CreateObject - create an object.
func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size int64, data io.Reader, sig *signature4.Sign) (ObjectMetadata, *probe.Error) { func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size int64, data io.Reader, sig *signature4.Sign) (ObjectInfo, *probe.Error) {
di, e := disk.GetInfo(fs.path) di, e := disk.GetInfo(fs.path)
if e != nil { if e != nil {
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
// Remove 5% from total space for cumulative disk space used for // Remove 5% from total space for cumulative disk space used for
// journalling, inodes etc. // journalling, inodes etc.
availableDiskSpace := (float64(di.Free) / (float64(di.Total) - (0.05 * float64(di.Total)))) * 100 availableDiskSpace := (float64(di.Free) / (float64(di.Total) - (0.05 * float64(di.Total)))) * 100
if int64(availableDiskSpace) <= fs.minFreeDisk { if int64(availableDiskSpace) <= fs.minFreeDisk {
return ObjectMetadata{}, probe.NewError(RootPathFull{Path: fs.path}) return ObjectInfo{}, probe.NewError(RootPathFull{Path: fs.path})
} }
// Check bucket name valid. // Check bucket name valid.
if !IsValidBucketName(bucket) { if !IsValidBucketName(bucket) {
return ObjectMetadata{}, probe.NewError(BucketNameInvalid{Bucket: bucket}) return ObjectInfo{}, probe.NewError(BucketNameInvalid{Bucket: bucket})
} }
bucket = fs.denormalizeBucket(bucket) bucket = fs.denormalizeBucket(bucket)
bucketPath := filepath.Join(fs.path, bucket) bucketPath := filepath.Join(fs.path, bucket)
if _, e = os.Stat(bucketPath); e != nil { if _, e = os.Stat(bucketPath); e != nil {
if os.IsNotExist(e) { if os.IsNotExist(e) {
return ObjectMetadata{}, probe.NewError(BucketNotFound{Bucket: bucket}) return ObjectInfo{}, probe.NewError(BucketNotFound{Bucket: bucket})
} }
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
// Verify object path legal. // Verify object path legal.
if !IsValidObjectName(object) { if !IsValidObjectName(object) {
return ObjectMetadata{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object}) return ObjectInfo{}, probe.NewError(ObjectNameInvalid{Bucket: bucket, Object: object})
} }
// Get object path. // Get object path.
@ -238,7 +235,7 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in
expectedMD5SumBytes, e = base64.StdEncoding.DecodeString(expectedMD5Sum) expectedMD5SumBytes, e = base64.StdEncoding.DecodeString(expectedMD5Sum)
if e != nil { if e != nil {
// Pro-actively close the connection. // Pro-actively close the connection.
return ObjectMetadata{}, probe.NewError(InvalidDigest{MD5: expectedMD5Sum}) return ObjectInfo{}, probe.NewError(InvalidDigest{MD5: expectedMD5Sum})
} }
expectedMD5Sum = hex.EncodeToString(expectedMD5SumBytes) expectedMD5Sum = hex.EncodeToString(expectedMD5SumBytes)
} }
@ -250,12 +247,12 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in
case *os.PathError: case *os.PathError:
if e.Op == "mkdir" { if e.Op == "mkdir" {
if strings.Contains(e.Error(), "not a directory") { if strings.Contains(e.Error(), "not a directory") {
return ObjectMetadata{}, probe.NewError(ObjectExistsAsPrefix{Bucket: bucket, Prefix: object}) return ObjectInfo{}, probe.NewError(ObjectExistsAsPrefix{Bucket: bucket, Prefix: object})
} }
} }
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
default: default:
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
} }
@ -267,12 +264,12 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in
if size > 0 { if size > 0 {
if _, e = io.CopyN(objectWriter, data, size); e != nil { if _, e = io.CopyN(objectWriter, data, size); e != nil {
file.CloseAndPurge() file.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
} else { } else {
if _, e = io.Copy(objectWriter, data); e != nil { if _, e = io.Copy(objectWriter, data); e != nil {
file.CloseAndPurge() file.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
} }
@ -282,7 +279,7 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in
if expectedMD5Sum != "" { if expectedMD5Sum != "" {
if !isMD5SumEqual(expectedMD5Sum, md5Sum) { if !isMD5SumEqual(expectedMD5Sum, md5Sum) {
file.CloseAndPurge() file.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(BadDigest{MD5: expectedMD5Sum, Bucket: bucket, Object: object}) return ObjectInfo{}, probe.NewError(BadDigest{MD5: expectedMD5Sum, Bucket: bucket, Object: object})
} }
} }
sha256Sum := hex.EncodeToString(sha256Hasher.Sum(nil)) sha256Sum := hex.EncodeToString(sha256Hasher.Sum(nil))
@ -290,11 +287,11 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in
ok, err := sig.DoesSignatureMatch(sha256Sum) ok, err := sig.DoesSignatureMatch(sha256Sum)
if err != nil { if err != nil {
file.CloseAndPurge() file.CloseAndPurge()
return ObjectMetadata{}, err.Trace() return ObjectInfo{}, err.Trace()
} }
if !ok { if !ok {
file.CloseAndPurge() file.CloseAndPurge()
return ObjectMetadata{}, probe.NewError(SignDoesNotMatch{}) return ObjectInfo{}, probe.NewError(SignDoesNotMatch{})
} }
} }
file.Close() file.Close()
@ -302,7 +299,7 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in
// Set stat again to get the latest metadata. // Set stat again to get the latest metadata.
st, e := os.Stat(objectPath) st, e := os.Stat(objectPath)
if e != nil { if e != nil {
return ObjectMetadata{}, probe.NewError(e) return ObjectInfo{}, probe.NewError(e)
} }
contentType := "application/octet-stream" contentType := "application/octet-stream"
if objectExt := filepath.Ext(objectPath); objectExt != "" { if objectExt := filepath.Ext(objectPath); objectExt != "" {
@ -311,13 +308,13 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in
contentType = content.ContentType contentType = content.ContentType
} }
} }
newObject := ObjectMetadata{ newObject := ObjectInfo{
Bucket: bucket, Bucket: bucket,
Object: object, Name: object,
LastModified: st.ModTime(), ModifiedTime: st.ModTime(),
Size: st.Size(), Size: st.Size(),
ContentType: contentType, ContentType: contentType,
MD5: md5Sum, MD5Sum: md5Sum,
} }
return newObject, nil return newObject, nil
} }

Loading…
Cancel
Save