|
|
@ -460,6 +460,8 @@ func (z *erasureZones) MakeBucketWithLocation(ctx context.Context, bucket string |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (z *erasureZones) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) { |
|
|
|
func (z *erasureZones) GetObjectNInfo(ctx context.Context, bucket, object string, rs *HTTPRangeSpec, h http.Header, lockType LockType, opts ObjectOptions) (gr *GetObjectReader, err error) { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
|
|
|
|
|
|
|
|
for _, zone := range z.zones { |
|
|
|
for _, zone := range z.zones { |
|
|
|
gr, err = zone.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts) |
|
|
|
gr, err = zone.GetObjectNInfo(ctx, bucket, object, rs, h, lockType, opts) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -477,6 +479,8 @@ func (z *erasureZones) GetObjectNInfo(ctx context.Context, bucket, object string |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (z *erasureZones) GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) error { |
|
|
|
func (z *erasureZones) GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) error { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
|
|
|
|
|
|
|
|
for _, zone := range z.zones { |
|
|
|
for _, zone := range z.zones { |
|
|
|
if err := zone.GetObject(ctx, bucket, object, startOffset, length, writer, etag, opts); err != nil { |
|
|
|
if err := zone.GetObject(ctx, bucket, object, startOffset, length, writer, etag, opts); err != nil { |
|
|
|
if isErrObjectNotFound(err) || isErrVersionNotFound(err) { |
|
|
|
if isErrObjectNotFound(err) || isErrVersionNotFound(err) { |
|
|
@ -493,6 +497,7 @@ func (z *erasureZones) GetObject(ctx context.Context, bucket, object string, sta |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (z *erasureZones) GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
func (z *erasureZones) GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
for _, zone := range z.zones { |
|
|
|
for _, zone := range z.zones { |
|
|
|
objInfo, err = zone.GetObjectInfo(ctx, bucket, object, opts) |
|
|
|
objInfo, err = zone.GetObjectInfo(ctx, bucket, object, opts) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -503,6 +508,7 @@ func (z *erasureZones) GetObjectInfo(ctx context.Context, bucket, object string, |
|
|
|
} |
|
|
|
} |
|
|
|
return objInfo, nil |
|
|
|
return objInfo, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
object = decodeDirObject(object) |
|
|
|
if opts.VersionID != "" { |
|
|
|
if opts.VersionID != "" { |
|
|
|
return objInfo, VersionNotFound{Bucket: bucket, Object: object, VersionID: opts.VersionID} |
|
|
|
return objInfo, VersionNotFound{Bucket: bucket, Object: object, VersionID: opts.VersionID} |
|
|
|
} |
|
|
|
} |
|
|
@ -511,6 +517,8 @@ func (z *erasureZones) GetObjectInfo(ctx context.Context, bucket, object string, |
|
|
|
|
|
|
|
|
|
|
|
// PutObject - writes an object to least used erasure zone.
|
|
|
|
// PutObject - writes an object to least used erasure zone.
|
|
|
|
func (z *erasureZones) PutObject(ctx context.Context, bucket string, object string, data *PutObjReader, opts ObjectOptions) (ObjectInfo, error) { |
|
|
|
func (z *erasureZones) PutObject(ctx context.Context, bucket string, object string, data *PutObjReader, opts ObjectOptions) (ObjectInfo, error) { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
|
|
|
|
|
|
|
|
if z.SingleZone() { |
|
|
|
if z.SingleZone() { |
|
|
|
return z.zones[0].PutObject(ctx, bucket, object, data, opts) |
|
|
|
return z.zones[0].PutObject(ctx, bucket, object, data, opts) |
|
|
|
} |
|
|
|
} |
|
|
@ -525,6 +533,8 @@ func (z *erasureZones) PutObject(ctx context.Context, bucket string, object stri |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (z *erasureZones) DeleteObject(ctx context.Context, bucket string, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
func (z *erasureZones) DeleteObject(ctx context.Context, bucket string, object string, opts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
|
|
|
|
|
|
|
|
if z.SingleZone() { |
|
|
|
if z.SingleZone() { |
|
|
|
return z.zones[0].DeleteObject(ctx, bucket, object, opts) |
|
|
|
return z.zones[0].DeleteObject(ctx, bucket, object, opts) |
|
|
|
} |
|
|
|
} |
|
|
@ -545,6 +555,8 @@ func (z *erasureZones) DeleteObjects(ctx context.Context, bucket string, objects |
|
|
|
dobjects := make([]DeletedObject, len(objects)) |
|
|
|
dobjects := make([]DeletedObject, len(objects)) |
|
|
|
objSets := set.NewStringSet() |
|
|
|
objSets := set.NewStringSet() |
|
|
|
for i := range derrs { |
|
|
|
for i := range derrs { |
|
|
|
|
|
|
|
objects[i].ObjectName = encodeDirObject(objects[i].ObjectName) |
|
|
|
|
|
|
|
|
|
|
|
derrs[i] = checkDelObjArgs(ctx, bucket, objects[i].ObjectName) |
|
|
|
derrs[i] = checkDelObjArgs(ctx, bucket, objects[i].ObjectName) |
|
|
|
objSets.Add(objects[i].ObjectName) |
|
|
|
objSets.Add(objects[i].ObjectName) |
|
|
|
} |
|
|
|
} |
|
|
@ -576,6 +588,9 @@ func (z *erasureZones) DeleteObjects(ctx context.Context, bucket string, objects |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (z *erasureZones) CopyObject(ctx context.Context, srcBucket, srcObject, dstBucket, dstObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
func (z *erasureZones) CopyObject(ctx context.Context, srcBucket, srcObject, dstBucket, dstObject string, srcInfo ObjectInfo, srcOpts, dstOpts ObjectOptions) (objInfo ObjectInfo, err error) { |
|
|
|
|
|
|
|
srcObject = encodeDirObject(srcObject) |
|
|
|
|
|
|
|
dstObject = encodeDirObject(dstObject) |
|
|
|
|
|
|
|
|
|
|
|
cpSrcDstSame := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(dstBucket, dstObject)) |
|
|
|
cpSrcDstSame := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(dstBucket, dstObject)) |
|
|
|
|
|
|
|
|
|
|
|
zoneIdx, err := z.getZoneIdx(ctx, dstBucket, dstObject, dstOpts, srcInfo.Size) |
|
|
|
zoneIdx, err := z.getZoneIdx(ctx, dstBucket, dstObject, dstOpts, srcInfo.Size) |
|
|
@ -935,7 +950,16 @@ func lexicallySortedEntryZone(zoneEntryChs [][]FileInfoCh, zoneEntries [][]FileI |
|
|
|
zoneIndex = i |
|
|
|
zoneIndex = i |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
if zoneEntries[i][j].Name < lentry.Name { |
|
|
|
str1 := zoneEntries[i][j].Name |
|
|
|
|
|
|
|
str2 := lentry.Name |
|
|
|
|
|
|
|
if HasSuffix(str1, globalDirSuffix) { |
|
|
|
|
|
|
|
str1 = strings.TrimSuffix(str1, globalDirSuffix) + slashSeparator |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if HasSuffix(str2, globalDirSuffix) { |
|
|
|
|
|
|
|
str2 = strings.TrimSuffix(str2, globalDirSuffix) + slashSeparator |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if str1 < str2 { |
|
|
|
lentry = zoneEntries[i][j] |
|
|
|
lentry = zoneEntries[i][j] |
|
|
|
zoneIndex = i |
|
|
|
zoneIndex = i |
|
|
|
} |
|
|
|
} |
|
|
@ -968,6 +992,10 @@ func lexicallySortedEntryZone(zoneEntryChs [][]FileInfoCh, zoneEntries [][]FileI |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if HasSuffix(lentry.Name, globalDirSuffix) { |
|
|
|
|
|
|
|
lentry.Name = strings.TrimSuffix(lentry.Name, globalDirSuffix) + slashSeparator |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return lentry, lexicallySortedEntryCount, zoneIndex, isTruncated |
|
|
|
return lentry, lexicallySortedEntryCount, zoneIndex, isTruncated |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1013,7 +1041,16 @@ func lexicallySortedEntryZoneVersions(zoneEntryChs [][]FileInfoVersionsCh, zoneE |
|
|
|
zoneIndex = i |
|
|
|
zoneIndex = i |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
if zoneEntries[i][j].Name < lentry.Name { |
|
|
|
str1 := zoneEntries[i][j].Name |
|
|
|
|
|
|
|
str2 := lentry.Name |
|
|
|
|
|
|
|
if HasSuffix(str1, globalDirSuffix) { |
|
|
|
|
|
|
|
str1 = strings.TrimSuffix(str1, globalDirSuffix) + slashSeparator |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if HasSuffix(str2, globalDirSuffix) { |
|
|
|
|
|
|
|
str2 = strings.TrimSuffix(str2, globalDirSuffix) + slashSeparator |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if str1 < str2 { |
|
|
|
lentry = zoneEntries[i][j] |
|
|
|
lentry = zoneEntries[i][j] |
|
|
|
zoneIndex = i |
|
|
|
zoneIndex = i |
|
|
|
} |
|
|
|
} |
|
|
@ -1046,6 +1083,10 @@ func lexicallySortedEntryZoneVersions(zoneEntryChs [][]FileInfoVersionsCh, zoneE |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if HasSuffix(lentry.Name, globalDirSuffix) { |
|
|
|
|
|
|
|
lentry.Name = strings.TrimSuffix(lentry.Name, globalDirSuffix) + slashSeparator |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return lentry, lexicallySortedEntryCount, zoneIndex, isTruncated |
|
|
|
return lentry, lexicallySortedEntryCount, zoneIndex, isTruncated |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1058,6 +1099,7 @@ func mergeZonesEntriesVersionsCh(zonesEntryChs [][]FileInfoVersionsCh, maxKeys i |
|
|
|
zonesEntriesInfos = append(zonesEntriesInfos, make([]FileInfoVersions, len(entryChs))) |
|
|
|
zonesEntriesInfos = append(zonesEntriesInfos, make([]FileInfoVersions, len(entryChs))) |
|
|
|
zonesEntriesValid = append(zonesEntriesValid, make([]bool, len(entryChs))) |
|
|
|
zonesEntriesValid = append(zonesEntriesValid, make([]bool, len(entryChs))) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for { |
|
|
|
for { |
|
|
|
fi, quorumCount, zoneIndex, ok := lexicallySortedEntryZoneVersions(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) |
|
|
|
fi, quorumCount, zoneIndex, ok := lexicallySortedEntryZoneVersions(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) |
|
|
|
if !ok { |
|
|
|
if !ok { |
|
|
@ -1089,6 +1131,7 @@ func mergeZonesEntriesCh(zonesEntryChs [][]FileInfoCh, maxKeys int, zonesListTol |
|
|
|
zonesEntriesInfos = append(zonesEntriesInfos, make([]FileInfo, len(entryChs))) |
|
|
|
zonesEntriesInfos = append(zonesEntriesInfos, make([]FileInfo, len(entryChs))) |
|
|
|
zonesEntriesValid = append(zonesEntriesValid, make([]bool, len(entryChs))) |
|
|
|
zonesEntriesValid = append(zonesEntriesValid, make([]bool, len(entryChs))) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var prevEntry string |
|
|
|
for { |
|
|
|
for { |
|
|
|
fi, quorumCount, zoneIndex, ok := lexicallySortedEntryZone(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) |
|
|
|
fi, quorumCount, zoneIndex, ok := lexicallySortedEntryZone(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) |
|
|
|
if !ok { |
|
|
|
if !ok { |
|
|
@ -1101,12 +1144,17 @@ func mergeZonesEntriesCh(zonesEntryChs [][]FileInfoCh, maxKeys int, zonesListTol |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if HasSuffix(fi.Name, slashSeparator) && fi.Name == prevEntry { |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
entries.Files = append(entries.Files, fi) |
|
|
|
entries.Files = append(entries.Files, fi) |
|
|
|
i++ |
|
|
|
i++ |
|
|
|
if i == maxKeys { |
|
|
|
if i == maxKeys { |
|
|
|
entries.IsTruncated = isTruncatedZones(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) |
|
|
|
entries.IsTruncated = isTruncatedZones(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
prevEntry = fi.Name |
|
|
|
} |
|
|
|
} |
|
|
|
return entries |
|
|
|
return entries |
|
|
|
} |
|
|
|
} |
|
|
@ -1836,6 +1884,8 @@ func (z *erasureZones) HealObjects(ctx context.Context, bucket, prefix string, o |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (z *erasureZones) HealObject(ctx context.Context, bucket, object, versionID string, opts madmin.HealOpts) (madmin.HealResultItem, error) { |
|
|
|
func (z *erasureZones) HealObject(ctx context.Context, bucket, object, versionID string, opts madmin.HealOpts) (madmin.HealResultItem, error) { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
|
|
|
|
|
|
|
|
lk := z.NewNSLock(ctx, bucket, object) |
|
|
|
lk := z.NewNSLock(ctx, bucket, object) |
|
|
|
if bucket == minioMetaBucket { |
|
|
|
if bucket == minioMetaBucket { |
|
|
|
// For .minio.sys bucket heals we should hold write locks.
|
|
|
|
// For .minio.sys bucket heals we should hold write locks.
|
|
|
@ -1956,6 +2006,7 @@ func (z *erasureZones) Health(ctx context.Context, opts HealthOptions) HealthRes |
|
|
|
|
|
|
|
|
|
|
|
parityDrives := globalStorageClass.GetParityForSC(storageclass.STANDARD) |
|
|
|
parityDrives := globalStorageClass.GetParityForSC(storageclass.STANDARD) |
|
|
|
diskCount := z.SetDriveCount() |
|
|
|
diskCount := z.SetDriveCount() |
|
|
|
|
|
|
|
|
|
|
|
if parityDrives == 0 { |
|
|
|
if parityDrives == 0 { |
|
|
|
parityDrives = getDefaultParityBlocks(diskCount) |
|
|
|
parityDrives = getDefaultParityBlocks(diskCount) |
|
|
|
} |
|
|
|
} |
|
|
@ -2019,6 +2070,7 @@ func (z *erasureZones) Health(ctx context.Context, opts HealthOptions) HealthRes |
|
|
|
|
|
|
|
|
|
|
|
// PutObjectTags - replace or add tags to an existing object
|
|
|
|
// PutObjectTags - replace or add tags to an existing object
|
|
|
|
func (z *erasureZones) PutObjectTags(ctx context.Context, bucket, object string, tags string, opts ObjectOptions) error { |
|
|
|
func (z *erasureZones) PutObjectTags(ctx context.Context, bucket, object string, tags string, opts ObjectOptions) error { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
if z.SingleZone() { |
|
|
|
if z.SingleZone() { |
|
|
|
return z.zones[0].PutObjectTags(ctx, bucket, object, tags, opts) |
|
|
|
return z.zones[0].PutObjectTags(ctx, bucket, object, tags, opts) |
|
|
|
} |
|
|
|
} |
|
|
@ -2048,6 +2100,7 @@ func (z *erasureZones) PutObjectTags(ctx context.Context, bucket, object string, |
|
|
|
|
|
|
|
|
|
|
|
// DeleteObjectTags - delete object tags from an existing object
|
|
|
|
// DeleteObjectTags - delete object tags from an existing object
|
|
|
|
func (z *erasureZones) DeleteObjectTags(ctx context.Context, bucket, object string, opts ObjectOptions) error { |
|
|
|
func (z *erasureZones) DeleteObjectTags(ctx context.Context, bucket, object string, opts ObjectOptions) error { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
if z.SingleZone() { |
|
|
|
if z.SingleZone() { |
|
|
|
return z.zones[0].DeleteObjectTags(ctx, bucket, object, opts) |
|
|
|
return z.zones[0].DeleteObjectTags(ctx, bucket, object, opts) |
|
|
|
} |
|
|
|
} |
|
|
@ -2076,6 +2129,7 @@ func (z *erasureZones) DeleteObjectTags(ctx context.Context, bucket, object stri |
|
|
|
|
|
|
|
|
|
|
|
// GetObjectTags - get object tags from an existing object
|
|
|
|
// GetObjectTags - get object tags from an existing object
|
|
|
|
func (z *erasureZones) GetObjectTags(ctx context.Context, bucket, object string, opts ObjectOptions) (*tags.Tags, error) { |
|
|
|
func (z *erasureZones) GetObjectTags(ctx context.Context, bucket, object string, opts ObjectOptions) (*tags.Tags, error) { |
|
|
|
|
|
|
|
object = encodeDirObject(object) |
|
|
|
if z.SingleZone() { |
|
|
|
if z.SingleZone() { |
|
|
|
return z.zones[0].GetObjectTags(ctx, bucket, object, opts) |
|
|
|
return z.zones[0].GetObjectTags(ctx, bucket, object, opts) |
|
|
|
} |
|
|
|
} |
|
|
|