2015-06-26 05:10:47 -04:00
|
|
|
/*
|
2019-04-09 14:39:42 -04:00
|
|
|
* MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc.
|
2015-06-26 05:10:47 -04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2016-08-18 19:23:42 -04:00
|
|
|
package cmd
|
2015-06-26 05:10:47 -04:00
|
|
|
|
2018-02-15 20:45:57 -05:00
|
|
|
import (
|
2018-02-23 18:07:21 -05:00
|
|
|
"io"
|
2018-02-15 20:45:57 -05:00
|
|
|
"time"
|
|
|
|
|
2018-02-23 18:07:21 -05:00
|
|
|
"github.com/minio/minio/pkg/hash"
|
2018-02-15 20:45:57 -05:00
|
|
|
"github.com/minio/minio/pkg/madmin"
|
|
|
|
)
|
2015-07-04 00:02:31 -04:00
|
|
|
|
2016-10-05 15:48:07 -04:00
|
|
|
// BackendType - represents different backend types.
|
|
|
|
type BackendType int
|
|
|
|
|
|
|
|
// Enum for different backend types.
|
|
|
|
const (
|
|
|
|
Unknown BackendType = iota
|
|
|
|
// Filesystem backend.
|
2018-08-24 02:35:37 -04:00
|
|
|
BackendFS
|
|
|
|
// Multi disk BackendErasure (single, distributed) backend.
|
|
|
|
BackendErasure
|
2019-11-01 19:58:11 -04:00
|
|
|
// Gateway backend.
|
|
|
|
BackendGateway
|
2016-10-05 15:48:07 -04:00
|
|
|
// Add your own backend.
|
|
|
|
)
|
|
|
|
|
2016-05-26 17:13:10 -04:00
|
|
|
// StorageInfo - represents total capacity of underlying storage.
|
|
|
|
type StorageInfo struct {
|
2019-10-23 00:01:14 -04:00
|
|
|
Used []uint64 // Used total used per disk.
|
2018-05-23 06:11:29 -04:00
|
|
|
|
2019-10-23 00:01:14 -04:00
|
|
|
Total []uint64 // Total disk space per disk.
|
2019-04-05 00:21:50 -04:00
|
|
|
|
2019-10-23 00:01:14 -04:00
|
|
|
Available []uint64 // Total disk space available per disk.
|
|
|
|
|
|
|
|
MountPaths []string // Disk mountpoints
|
2019-04-05 00:21:50 -04:00
|
|
|
|
2016-10-05 15:48:07 -04:00
|
|
|
// Backend type.
|
|
|
|
Backend struct {
|
2019-11-01 19:58:11 -04:00
|
|
|
// Represents various backend types, currently on FS, Erasure and Gateway
|
2016-10-05 15:48:07 -04:00
|
|
|
Type BackendType
|
|
|
|
|
2019-11-01 19:58:11 -04:00
|
|
|
// Following fields are only meaningful if BackendType is Gateway.
|
|
|
|
GatewayOnline bool
|
|
|
|
|
2017-02-10 02:26:44 -05:00
|
|
|
// Following fields are only meaningful if BackendType is Erasure.
|
2019-10-23 00:01:14 -04:00
|
|
|
OnlineDisks madmin.BackendDisks // Online disks during server startup.
|
|
|
|
OfflineDisks madmin.BackendDisks // Offline disks during server startup.
|
|
|
|
StandardSCData int // Data disks for currently configured Standard storage class.
|
|
|
|
StandardSCParity int // Parity disks for currently configured Standard storage class.
|
|
|
|
RRSCData int // Data disks for currently configured Reduced Redundancy storage class.
|
|
|
|
RRSCParity int // Parity disks for currently configured Reduced Redundancy storage class.
|
2018-02-15 20:45:57 -05:00
|
|
|
|
|
|
|
// List of all disk status, this is only meaningful if BackendType is Erasure.
|
|
|
|
Sets [][]madmin.DriveInfo
|
2016-10-05 15:48:07 -04:00
|
|
|
}
|
2016-05-26 17:13:10 -04:00
|
|
|
}
|
|
|
|
|
2016-06-09 09:24:11 -04:00
|
|
|
// BucketInfo - represents bucket metadata.
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
type BucketInfo struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Name of the bucket.
|
|
|
|
Name string
|
|
|
|
|
|
|
|
// Date and time when the bucket was created.
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
Created time.Time
|
2017-01-17 13:02:58 -05:00
|
|
|
}
|
|
|
|
|
2016-06-09 09:24:11 -04:00
|
|
|
// ObjectInfo - represents object metadata.
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
type ObjectInfo struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Name of the bucket.
|
|
|
|
Bucket string
|
|
|
|
|
|
|
|
// Name of the object.
|
|
|
|
Name string
|
|
|
|
|
|
|
|
// Date and time when the object was last modified.
|
|
|
|
ModTime time.Time
|
|
|
|
|
|
|
|
// Total object size.
|
|
|
|
Size int64
|
|
|
|
|
|
|
|
// IsDir indicates if the object is prefix.
|
|
|
|
IsDir bool
|
|
|
|
|
2017-05-14 15:05:51 -04:00
|
|
|
// Hex encoded unique entity tag of the object.
|
|
|
|
ETag string
|
2016-06-09 09:24:11 -04:00
|
|
|
|
|
|
|
// A standard MIME type describing the format of the object.
|
|
|
|
ContentType string
|
|
|
|
|
|
|
|
// Specifies what content encodings have been applied to the object and thus
|
|
|
|
// what decoding mechanisms must be applied to obtain the object referenced
|
|
|
|
// by the Content-Type header field.
|
2016-05-19 20:10:08 -04:00
|
|
|
ContentEncoding string
|
2016-07-12 15:45:17 -04:00
|
|
|
|
2019-02-28 14:01:25 -05:00
|
|
|
// Date and time at which the object is no longer able to be cached
|
|
|
|
Expires time.Time
|
|
|
|
|
2018-04-05 13:56:28 -04:00
|
|
|
// Specify object storage class
|
|
|
|
StorageClass string
|
|
|
|
|
2016-07-12 15:45:17 -04:00
|
|
|
// User-Defined metadata
|
2018-01-22 17:54:55 -05:00
|
|
|
UserDefined map[string]string
|
2018-02-23 18:07:21 -05:00
|
|
|
|
2018-03-01 14:37:57 -05:00
|
|
|
// List of individual parts, maximum size of upto 10,000
|
2019-01-05 17:16:43 -05:00
|
|
|
Parts []ObjectPartInfo `json:"-"`
|
2018-03-01 14:37:57 -05:00
|
|
|
|
2018-02-23 18:07:21 -05:00
|
|
|
// Implements writer and reader used by CopyObject API
|
|
|
|
Writer io.WriteCloser `json:"-"`
|
|
|
|
Reader *hash.Reader `json:"-"`
|
2018-11-14 20:36:41 -05:00
|
|
|
PutObjReader *PutObjReader `json:"-"`
|
|
|
|
|
2018-02-23 18:07:21 -05:00
|
|
|
metadataOnly bool
|
2018-10-15 14:07:36 -04:00
|
|
|
|
2018-03-28 17:14:06 -04:00
|
|
|
// Date and time when the object was last accessed.
|
|
|
|
AccTime time.Time
|
2018-10-15 14:07:36 -04:00
|
|
|
|
|
|
|
// backendType indicates which backend filled this structure
|
|
|
|
backendType BackendType
|
2015-07-04 00:02:31 -04:00
|
|
|
}
|
|
|
|
|
2016-06-09 09:24:11 -04:00
|
|
|
// ListPartsInfo - represents list of all parts.
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
type ListPartsInfo struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Name of the bucket.
|
|
|
|
Bucket string
|
|
|
|
|
|
|
|
// Name of the object.
|
|
|
|
Object string
|
|
|
|
|
|
|
|
// Upload ID identifying the multipart upload whose parts are being listed.
|
|
|
|
UploadID string
|
|
|
|
|
|
|
|
// The class of storage used to store the object.
|
|
|
|
StorageClass string
|
|
|
|
|
|
|
|
// Part number after which listing begins.
|
|
|
|
PartNumberMarker int
|
|
|
|
|
|
|
|
// When a list is truncated, this element specifies the last part in the list,
|
|
|
|
// as well as the value to use for the part-number-marker request parameter
|
|
|
|
// in a subsequent request.
|
2015-07-04 00:02:31 -04:00
|
|
|
NextPartNumberMarker int
|
|
|
|
|
2016-06-09 09:24:11 -04:00
|
|
|
// Maximum number of parts that were allowed in the response.
|
|
|
|
MaxParts int
|
|
|
|
|
|
|
|
// Indicates whether the returned list of parts is truncated.
|
|
|
|
IsTruncated bool
|
|
|
|
|
|
|
|
// List of all parts.
|
2017-01-31 12:38:34 -05:00
|
|
|
Parts []PartInfo
|
2016-06-09 09:24:11 -04:00
|
|
|
|
2018-03-01 14:37:57 -05:00
|
|
|
// Any metadata set during InitMultipartUpload, including encryption headers.
|
|
|
|
UserDefined map[string]string
|
|
|
|
|
2016-06-09 09:24:11 -04:00
|
|
|
EncodingType string // Not supported yet.
|
2015-07-04 00:02:31 -04:00
|
|
|
}
|
|
|
|
|
2016-06-09 09:24:11 -04:00
|
|
|
// ListMultipartsInfo - represnets bucket resources for incomplete multipart uploads.
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
type ListMultipartsInfo struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Together with upload-id-marker, this parameter specifies the multipart upload
|
|
|
|
// after which listing should begin.
|
|
|
|
KeyMarker string
|
|
|
|
|
|
|
|
// Together with key-marker, specifies the multipart upload after which listing
|
|
|
|
// should begin. If key-marker is not specified, the upload-id-marker parameter
|
|
|
|
// is ignored.
|
|
|
|
UploadIDMarker string
|
|
|
|
|
|
|
|
// When a list is truncated, this element specifies the value that should be
|
|
|
|
// used for the key-marker request parameter in a subsequent request.
|
|
|
|
NextKeyMarker string
|
|
|
|
|
|
|
|
// When a list is truncated, this element specifies the value that should be
|
|
|
|
// used for the upload-id-marker request parameter in a subsequent request.
|
2015-07-04 00:02:31 -04:00
|
|
|
NextUploadIDMarker string
|
2016-06-09 09:24:11 -04:00
|
|
|
|
|
|
|
// Maximum number of multipart uploads that could have been included in the
|
|
|
|
// response.
|
|
|
|
MaxUploads int
|
|
|
|
|
|
|
|
// Indicates whether the returned list of multipart uploads is truncated. A
|
|
|
|
// value of true indicates that the list was truncated. The list can be truncated
|
|
|
|
// if the number of multipart uploads exceeds the limit allowed or specified
|
|
|
|
// by max uploads.
|
|
|
|
IsTruncated bool
|
|
|
|
|
|
|
|
// List of all pending uploads.
|
2017-11-14 03:25:10 -05:00
|
|
|
Uploads []MultipartInfo
|
2016-06-09 09:24:11 -04:00
|
|
|
|
|
|
|
// When a prefix is provided in the request, The result contains only keys
|
|
|
|
// starting with the specified prefix.
|
|
|
|
Prefix string
|
|
|
|
|
|
|
|
// A character used to truncate the object prefixes.
|
|
|
|
// NOTE: only supported delimiter is '/'.
|
|
|
|
Delimiter string
|
|
|
|
|
|
|
|
// CommonPrefixes contains all (if there are any) keys between Prefix and the
|
|
|
|
// next occurrence of the string specified by delimiter.
|
|
|
|
CommonPrefixes []string
|
|
|
|
|
|
|
|
EncodingType string // Not supported yet.
|
2015-07-04 00:02:31 -04:00
|
|
|
}
|
|
|
|
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
// ListObjectsInfo - container for list objects.
|
|
|
|
type ListObjectsInfo struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Indicates whether the returned list objects response is truncated. A
|
|
|
|
// value of true indicates that the list was truncated. The list can be truncated
|
|
|
|
// if the number of objects exceeds the limit allowed or specified
|
|
|
|
// by max keys.
|
2016-01-19 20:49:48 -05:00
|
|
|
IsTruncated bool
|
2016-06-09 09:24:11 -04:00
|
|
|
|
2019-10-28 16:18:12 -04:00
|
|
|
// When response is truncated (the IsTruncated element value in the response is true),
|
|
|
|
// you can use the key name in this field as marker in the subsequent
|
2016-06-09 09:24:11 -04:00
|
|
|
// request to get next set of objects.
|
|
|
|
//
|
2019-10-28 16:18:12 -04:00
|
|
|
// NOTE: AWS S3 returns NextMarker only if you have delimiter request parameter specified,
|
|
|
|
// MinIO always returns NextMarker.
|
2016-06-09 09:24:11 -04:00
|
|
|
NextMarker string
|
|
|
|
|
|
|
|
// List of objects info for this request.
|
|
|
|
Objects []ObjectInfo
|
|
|
|
|
|
|
|
// List of prefixes for this request.
|
|
|
|
Prefixes []string
|
2016-01-19 20:49:48 -05:00
|
|
|
}
|
|
|
|
|
2017-04-27 14:26:00 -04:00
|
|
|
// ListObjectsV2Info - container for list objects version 2.
|
|
|
|
type ListObjectsV2Info struct {
|
|
|
|
// Indicates whether the returned list objects response is truncated. A
|
|
|
|
// value of true indicates that the list was truncated. The list can be truncated
|
|
|
|
// if the number of objects exceeds the limit allowed or specified
|
|
|
|
// by max keys.
|
|
|
|
IsTruncated bool
|
|
|
|
|
|
|
|
// When response is truncated (the IsTruncated element value in the response
|
|
|
|
// is true), you can use the key name in this field as marker in the subsequent
|
|
|
|
// request to get next set of objects.
|
|
|
|
//
|
|
|
|
// NOTE: This element is returned only if you have delimiter request parameter
|
|
|
|
// specified.
|
|
|
|
ContinuationToken string
|
|
|
|
NextContinuationToken string
|
|
|
|
|
|
|
|
// List of objects info for this request.
|
|
|
|
Objects []ObjectInfo
|
|
|
|
|
|
|
|
// List of prefixes for this request.
|
|
|
|
Prefixes []string
|
|
|
|
}
|
|
|
|
|
2017-01-31 12:38:34 -05:00
|
|
|
// PartInfo - represents individual part metadata.
|
|
|
|
type PartInfo struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Part number that identifies the part. This is a positive integer between
|
|
|
|
// 1 and 10,000.
|
|
|
|
PartNumber int
|
|
|
|
|
|
|
|
// Date and time at which the part was uploaded.
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
LastModified time.Time
|
2016-06-09 09:24:11 -04:00
|
|
|
|
|
|
|
// Entity tag returned when the part was initially uploaded.
|
|
|
|
ETag string
|
|
|
|
|
|
|
|
// Size in bytes of the part.
|
|
|
|
Size int64
|
2018-09-27 23:36:17 -04:00
|
|
|
|
|
|
|
// Decompressed Size.
|
|
|
|
ActualSize int64
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
}
|
|
|
|
|
2017-11-14 03:25:10 -05:00
|
|
|
// MultipartInfo - represents metadata in progress multipart upload.
|
|
|
|
type MultipartInfo struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Object name for which the multipart upload was initiated.
|
|
|
|
Object string
|
|
|
|
|
|
|
|
// Unique identifier for this multipart upload.
|
|
|
|
UploadID string
|
|
|
|
|
|
|
|
// Date and time at which the multipart upload was initiated.
|
|
|
|
Initiated time.Time
|
|
|
|
|
|
|
|
StorageClass string // Not supported yet.
|
objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
2016-04-03 04:34:20 -04:00
|
|
|
}
|
|
|
|
|
2017-11-14 03:25:10 -05:00
|
|
|
// CompletePart - represents the part that was completed, this is sent by the client
|
|
|
|
// during CompleteMultipartUpload request.
|
|
|
|
type CompletePart struct {
|
2016-06-09 09:24:11 -04:00
|
|
|
// Part number identifying the part. This is a positive integer between 1 and
|
|
|
|
// 10,000
|
2015-10-16 14:26:01 -04:00
|
|
|
PartNumber int
|
2016-06-09 09:24:11 -04:00
|
|
|
|
|
|
|
// Entity tag returned when the part was uploaded.
|
|
|
|
ETag string
|
2015-10-16 14:26:01 -04:00
|
|
|
}
|
|
|
|
|
2017-11-14 03:25:10 -05:00
|
|
|
// CompletedParts - is a collection satisfying sort.Interface.
|
|
|
|
type CompletedParts []CompletePart
|
2015-10-16 14:26:01 -04:00
|
|
|
|
2017-11-14 03:25:10 -05:00
|
|
|
func (a CompletedParts) Len() int { return len(a) }
|
|
|
|
func (a CompletedParts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func (a CompletedParts) Less(i, j int) bool { return a[i].PartNumber < a[j].PartNumber }
|
2015-10-16 14:26:01 -04:00
|
|
|
|
2017-11-14 03:25:10 -05:00
|
|
|
// CompleteMultipartUpload - represents list of parts which are completed, this is sent by the
|
|
|
|
// client during CompleteMultipartUpload request.
|
|
|
|
type CompleteMultipartUpload struct {
|
|
|
|
Parts []CompletePart `xml:"Part"`
|
2015-10-16 14:26:01 -04:00
|
|
|
}
|