2021-04-18 19:41:13 +00:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2016-04-22 03:07:47 +00:00
|
|
|
|
2016-08-18 23:23:42 +00:00
|
|
|
package cmd
|
2016-04-08 02:01:15 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
2024-03-01 06:49:01 +00:00
|
|
|
|
|
|
|
"github.com/minio/minio/internal/crypto"
|
2016-04-08 02:01:15 +00:00
|
|
|
)
|
|
|
|
|
2024-02-19 22:54:46 +00:00
|
|
|
//go:generate msgp -file=$GOFILE
|
|
|
|
|
2022-07-11 16:15:54 +00:00
|
|
|
// DeleteOptions represents the disk level delete options available for the APIs
|
|
|
|
type DeleteOptions struct {
|
2023-12-29 23:52:41 +00:00
|
|
|
BaseOptions
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-21 01:09:35 +00:00
|
|
|
Recursive bool `msg:"r"`
|
2023-11-29 06:35:16 +00:00
|
|
|
Immediate bool `msg:"i"`
|
2023-12-29 23:52:41 +00:00
|
|
|
UndoWrite bool `msg:"u"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BaseOptions represents common options for all Storage API calls
|
|
|
|
type BaseOptions struct{}
|
|
|
|
|
|
|
|
// RenameOptions represents rename API options, currently its same as BaseOptions
|
|
|
|
type RenameOptions struct {
|
|
|
|
BaseOptions
|
2022-07-11 16:15:54 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 20:45:46 +00:00
|
|
|
// DiskInfoOptions options for requesting custom results.
|
|
|
|
type DiskInfoOptions struct {
|
|
|
|
DiskID string `msg:"id"`
|
|
|
|
Metrics bool `msg:"m"`
|
|
|
|
NoOp bool `msg:"np"`
|
|
|
|
}
|
|
|
|
|
2020-11-04 18:10:54 +00:00
|
|
|
// DiskInfo is an extended type which returns current
|
|
|
|
// disk usage per path.
|
2021-01-03 19:27:57 +00:00
|
|
|
// The above means that any added/deleted fields are incompatible.
|
2022-08-26 19:52:29 +00:00
|
|
|
//
|
|
|
|
// The above means that any added/deleted fields are incompatible.
|
|
|
|
//
|
|
|
|
//msgp:tuple DiskInfo
|
2020-11-04 18:10:54 +00:00
|
|
|
type DiskInfo struct {
|
2020-12-27 06:58:06 +00:00
|
|
|
Total uint64
|
|
|
|
Free uint64
|
|
|
|
Used uint64
|
|
|
|
UsedInodes uint64
|
2021-05-06 06:03:40 +00:00
|
|
|
FreeInodes uint64
|
2022-08-16 14:13:49 +00:00
|
|
|
Major uint32
|
|
|
|
Minor uint32
|
2023-09-11 21:48:54 +00:00
|
|
|
NRRequests uint64
|
2020-12-27 06:58:06 +00:00
|
|
|
FSType string
|
|
|
|
RootDisk bool
|
|
|
|
Healing bool
|
2022-08-16 14:13:49 +00:00
|
|
|
Scanning bool
|
2020-12-27 06:58:06 +00:00
|
|
|
Endpoint string
|
|
|
|
MountPath string
|
|
|
|
ID string
|
2023-07-31 22:20:48 +00:00
|
|
|
Rotational bool
|
2021-03-17 03:06:57 +00:00
|
|
|
Metrics DiskMetrics
|
2020-12-27 06:58:06 +00:00
|
|
|
Error string // carries the error over the network
|
2020-11-04 18:10:54 +00:00
|
|
|
}
|
|
|
|
|
2021-03-17 03:06:57 +00:00
|
|
|
// DiskMetrics has the information about XL Storage APIs
|
|
|
|
// the number of calls of each API and the moving average of
|
|
|
|
// the duration of each API.
|
|
|
|
type DiskMetrics struct {
|
2023-07-30 06:26:26 +00:00
|
|
|
LastMinute map[string]AccElem `json:"apiLatencies,omitempty"`
|
|
|
|
APICalls map[string]uint64 `json:"apiCalls,omitempty"`
|
2024-01-18 19:17:43 +00:00
|
|
|
TotalWaiting uint32 `json:"totalWaiting,omitempty"`
|
2023-07-30 06:26:26 +00:00
|
|
|
TotalErrorsAvailability uint64 `json:"totalErrsAvailability"`
|
|
|
|
TotalErrorsTimeout uint64 `json:"totalErrsTimeout"`
|
2023-12-29 23:52:41 +00:00
|
|
|
TotalWrites uint64 `json:"totalWrites"`
|
|
|
|
TotalDeletes uint64 `json:"totalDeletes"`
|
2021-03-17 03:06:57 +00:00
|
|
|
}
|
|
|
|
|
2020-11-04 18:10:54 +00:00
|
|
|
// VolsInfo is a collection of volume(bucket) information
|
|
|
|
type VolsInfo []VolInfo
|
|
|
|
|
2016-06-09 13:24:11 +00:00
|
|
|
// VolInfo - represents volume stat information.
|
2021-01-03 19:27:57 +00:00
|
|
|
// The above means that any added/deleted fields are incompatible.
|
2022-08-26 19:52:29 +00:00
|
|
|
//
|
|
|
|
// The above means that any added/deleted fields are incompatible.
|
|
|
|
//
|
|
|
|
//msgp:tuple VolInfo
|
2016-04-08 17:37:38 +00:00
|
|
|
type VolInfo struct {
|
2016-06-09 13:24:11 +00:00
|
|
|
// Name of the volume.
|
|
|
|
Name string
|
|
|
|
|
|
|
|
// Date and time when the volume was created.
|
2016-04-08 17:37:38 +00:00
|
|
|
Created time.Time
|
|
|
|
}
|
|
|
|
|
2019-05-14 20:49:10 +00:00
|
|
|
// FilesInfo represent a list of files, additionally
|
|
|
|
// indicates if the list is last.
|
2022-08-26 19:52:29 +00:00
|
|
|
//
|
|
|
|
//msgp:tuple FileInfo
|
2019-05-14 20:49:10 +00:00
|
|
|
type FilesInfo struct {
|
|
|
|
Files []FileInfo
|
|
|
|
IsTruncated bool
|
|
|
|
}
|
|
|
|
|
2022-01-10 17:07:49 +00:00
|
|
|
// Size returns size of all versions for the object 'Name'
|
|
|
|
func (f FileInfoVersions) Size() (size int64) {
|
|
|
|
for _, v := range f.Versions {
|
|
|
|
size += v.Size
|
|
|
|
}
|
|
|
|
return size
|
2020-06-13 03:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FileInfoVersions represent a list of versions for a given file.
|
2021-11-01 17:50:07 +00:00
|
|
|
// The above means that any added/deleted fields are incompatible.
|
2022-08-26 19:52:29 +00:00
|
|
|
//
|
|
|
|
// The above means that any added/deleted fields are incompatible.
|
|
|
|
//
|
|
|
|
//msgp:tuple FileInfoVersions
|
2020-06-13 03:04:01 +00:00
|
|
|
type FileInfoVersions struct {
|
|
|
|
// Name of the volume.
|
2021-11-01 17:50:07 +00:00
|
|
|
Volume string `msg:"v,omitempty"`
|
2020-06-13 03:04:01 +00:00
|
|
|
|
|
|
|
// Name of the file.
|
2021-11-01 17:50:07 +00:00
|
|
|
Name string `msg:"n,omitempty"`
|
2021-01-27 10:19:28 +00:00
|
|
|
|
2020-06-13 03:04:01 +00:00
|
|
|
// Represents the latest mod time of the
|
|
|
|
// latest version.
|
2021-11-01 17:50:07 +00:00
|
|
|
LatestModTime time.Time `msg:"lm"`
|
2020-06-13 03:04:01 +00:00
|
|
|
|
2021-11-20 01:54:10 +00:00
|
|
|
Versions []FileInfo `msg:"vs"`
|
|
|
|
FreeVersions []FileInfo `msg:"fvs"`
|
2020-06-13 03:04:01 +00:00
|
|
|
}
|
|
|
|
|
2021-02-11 18:22:03 +00:00
|
|
|
// findVersionIndex will return the version index where the version
|
|
|
|
// was found. Returns -1 if not found.
|
|
|
|
func (f *FileInfoVersions) findVersionIndex(v string) int {
|
|
|
|
if f == nil || v == "" {
|
|
|
|
return -1
|
2020-10-28 16:18:35 +00:00
|
|
|
}
|
|
|
|
for i, ver := range f.Versions {
|
|
|
|
if ver.VersionID == v {
|
2021-02-11 18:22:03 +00:00
|
|
|
return i
|
2020-10-28 16:18:35 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-11 18:22:03 +00:00
|
|
|
return -1
|
2020-10-28 16:18:35 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 19:49:05 +00:00
|
|
|
// RawFileInfo - represents raw file stat information as byte array.
|
|
|
|
// The above means that any added/deleted fields are incompatible.
|
|
|
|
// Make sure to bump the internode version at storage-rest-common.go
|
|
|
|
type RawFileInfo struct {
|
|
|
|
// Content of entire xl.meta (may contain data depending on what was requested by the caller.
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-21 01:09:35 +00:00
|
|
|
Buf []byte `msg:"b,allownil"`
|
2022-04-20 19:49:05 +00:00
|
|
|
}
|
|
|
|
|
2016-06-09 13:24:11 +00:00
|
|
|
// FileInfo - represents file stat information.
|
2020-11-03 01:07:52 +00:00
|
|
|
// The above means that any added/deleted fields are incompatible.
|
2022-04-20 19:49:05 +00:00
|
|
|
// Make sure to bump the internode version at storage-rest-common.go
|
2016-04-08 02:01:15 +00:00
|
|
|
type FileInfo struct {
|
2016-06-09 13:24:11 +00:00
|
|
|
// Name of the volume.
|
2021-11-01 17:50:07 +00:00
|
|
|
Volume string `msg:"v,omitempty"`
|
2016-06-09 13:24:11 +00:00
|
|
|
|
|
|
|
// Name of the file.
|
2021-11-01 17:50:07 +00:00
|
|
|
Name string `msg:"n,omitempty"`
|
2016-06-09 13:24:11 +00:00
|
|
|
|
2020-06-13 03:04:01 +00:00
|
|
|
// Version of the file.
|
2021-11-01 17:50:07 +00:00
|
|
|
VersionID string `msg:"vid,omitempty"`
|
2020-06-13 03:04:01 +00:00
|
|
|
|
|
|
|
// Indicates if the version is the latest
|
2021-11-01 17:50:07 +00:00
|
|
|
IsLatest bool `msg:"is"`
|
2020-06-13 03:04:01 +00:00
|
|
|
|
|
|
|
// Deleted is set when this FileInfo represents
|
|
|
|
// a deleted marker for a versioned bucket.
|
2021-11-01 17:50:07 +00:00
|
|
|
Deleted bool `msg:"del"`
|
2020-06-13 03:04:01 +00:00
|
|
|
|
2020-11-12 20:12:09 +00:00
|
|
|
// TransitionStatus is set to Pending/Complete for transitioned
|
|
|
|
// entries based on state of transition
|
2021-11-01 17:50:07 +00:00
|
|
|
TransitionStatus string `msg:"ts"`
|
2021-04-19 17:30:42 +00:00
|
|
|
// TransitionedObjName is the object name on the remote tier corresponding
|
|
|
|
// to object (version) on the source tier.
|
2021-11-01 17:50:07 +00:00
|
|
|
TransitionedObjName string `msg:"to"`
|
2021-04-19 17:30:42 +00:00
|
|
|
// TransitionTier is the storage class label assigned to remote tier.
|
2021-11-01 17:50:07 +00:00
|
|
|
TransitionTier string `msg:"tt"`
|
2021-06-03 21:26:51 +00:00
|
|
|
// TransitionVersionID stores a version ID of the object associate
|
|
|
|
// with the remote tier.
|
2021-11-01 17:50:07 +00:00
|
|
|
TransitionVersionID string `msg:"tv"`
|
2021-04-19 17:30:42 +00:00
|
|
|
// ExpireRestored indicates that the restored object is to be expired.
|
2021-11-01 17:50:07 +00:00
|
|
|
ExpireRestored bool `msg:"exp"`
|
2020-11-12 20:12:09 +00:00
|
|
|
|
2020-06-13 03:04:01 +00:00
|
|
|
// DataDir of the file
|
2021-11-01 17:50:07 +00:00
|
|
|
DataDir string `msg:"dd"`
|
2020-06-13 03:04:01 +00:00
|
|
|
|
2020-07-18 00:41:29 +00:00
|
|
|
// Indicates if this object is still in V1 format.
|
2021-11-01 17:50:07 +00:00
|
|
|
XLV1 bool `msg:"v1"`
|
2020-07-18 00:41:29 +00:00
|
|
|
|
2020-06-13 03:04:01 +00:00
|
|
|
// Date and time when the file was last modified, if Deleted
|
|
|
|
// is 'true' this value represents when while was deleted.
|
2021-11-01 17:50:07 +00:00
|
|
|
ModTime time.Time `msg:"mt"`
|
2016-06-09 13:24:11 +00:00
|
|
|
|
|
|
|
// Total file size.
|
2021-11-01 17:50:07 +00:00
|
|
|
Size int64 `msg:"sz"`
|
2016-06-09 13:24:11 +00:00
|
|
|
|
|
|
|
// File mode bits.
|
2021-11-01 17:50:07 +00:00
|
|
|
Mode uint32 `msg:"m"`
|
2019-05-14 20:49:10 +00:00
|
|
|
|
2022-06-27 10:59:41 +00:00
|
|
|
// WrittenByVersion is the unix time stamp of the MinIO
|
|
|
|
// version that created this version of the object.
|
|
|
|
WrittenByVersion uint64 `msg:"wv"`
|
|
|
|
|
2019-05-14 20:49:10 +00:00
|
|
|
// File metadata
|
2021-11-01 17:50:07 +00:00
|
|
|
Metadata map[string]string `msg:"meta"`
|
2019-05-14 20:49:10 +00:00
|
|
|
|
|
|
|
// All the parts per object.
|
2021-11-01 17:50:07 +00:00
|
|
|
Parts []ObjectPartInfo `msg:"parts"`
|
2019-05-14 20:49:10 +00:00
|
|
|
|
2020-06-13 03:04:01 +00:00
|
|
|
// Erasure info for all objects.
|
2021-11-01 17:50:07 +00:00
|
|
|
Erasure ErasureInfo `msg:"ei"`
|
|
|
|
|
|
|
|
MarkDeleted bool `msg:"md"` // mark this version as deleted
|
|
|
|
ReplicationState ReplicationState `msg:"rs"` // Internal replication state to be passed back in ObjectInfo
|
2020-11-20 02:43:58 +00:00
|
|
|
|
2021-11-01 17:50:07 +00:00
|
|
|
Data []byte `msg:"d,allownil"` // optionally carries object data
|
2021-01-08 03:27:31 +00:00
|
|
|
|
2021-11-01 17:50:07 +00:00
|
|
|
NumVersions int `msg:"nv"`
|
|
|
|
SuccessorModTime time.Time `msg:"smt"`
|
2021-02-01 17:52:11 +00:00
|
|
|
|
2021-11-01 17:50:07 +00:00
|
|
|
Fresh bool `msg:"fr"` // indicates this is a first time call to write FileInfo.
|
2021-08-10 18:12:22 +00:00
|
|
|
|
2021-11-01 17:50:07 +00:00
|
|
|
// Position of this version or object in a multi-object delete call,
|
|
|
|
// no other caller must set this value other than multi-object delete call.
|
|
|
|
// usage in other calls in undefined please avoid.
|
|
|
|
Idx int `msg:"i"`
|
2021-12-21 18:08:26 +00:00
|
|
|
|
2022-08-29 23:57:16 +00:00
|
|
|
// Combined checksum when object was uploaded.
|
2022-08-31 15:13:23 +00:00
|
|
|
Checksum []byte `msg:"cs,allownil"`
|
2023-08-27 16:57:11 +00:00
|
|
|
|
|
|
|
// Versioned - indicates if this file is versioned or not.
|
|
|
|
Versioned bool `msg:"vs"`
|
2021-12-21 18:08:26 +00:00
|
|
|
}
|
|
|
|
|
2023-11-22 21:46:17 +00:00
|
|
|
// ShallowCopy - copies minimal information for READ MRF checks.
|
|
|
|
func (fi FileInfo) ShallowCopy() (n FileInfo) {
|
|
|
|
n.Volume = fi.Volume
|
|
|
|
n.Name = fi.Name
|
|
|
|
n.VersionID = fi.VersionID
|
|
|
|
n.Deleted = fi.Deleted
|
|
|
|
n.Erasure = fi.Erasure
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-09-15 19:43:49 +00:00
|
|
|
// WriteQuorum returns expected write quorum for this FileInfo
|
|
|
|
func (fi FileInfo) WriteQuorum(dquorum int) int {
|
|
|
|
if fi.Deleted {
|
|
|
|
return dquorum
|
|
|
|
}
|
|
|
|
quorum := fi.Erasure.DataBlocks
|
|
|
|
if fi.Erasure.DataBlocks == fi.Erasure.ParityBlocks {
|
|
|
|
quorum++
|
|
|
|
}
|
|
|
|
return quorum
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadQuorum returns expected read quorum for this FileInfo
|
|
|
|
func (fi FileInfo) ReadQuorum(dquorum int) int {
|
|
|
|
if fi.Deleted {
|
|
|
|
return dquorum
|
|
|
|
}
|
|
|
|
return fi.Erasure.DataBlocks
|
|
|
|
}
|
|
|
|
|
2021-12-21 18:08:26 +00:00
|
|
|
// Equals checks if fi(FileInfo) matches ofi(FileInfo)
|
|
|
|
func (fi FileInfo) Equals(ofi FileInfo) (ok bool) {
|
2024-03-01 06:49:01 +00:00
|
|
|
typ1, ok1 := crypto.IsEncrypted(fi.Metadata)
|
|
|
|
typ2, ok2 := crypto.IsEncrypted(ofi.Metadata)
|
|
|
|
if ok1 != ok2 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if typ1 != typ2 {
|
2021-12-21 18:08:26 +00:00
|
|
|
return false
|
|
|
|
}
|
2024-03-01 06:49:01 +00:00
|
|
|
if fi.IsCompressed() != ofi.IsCompressed() {
|
2021-12-21 18:08:26 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !fi.TransitionInfoEquals(ofi) {
|
|
|
|
return false
|
|
|
|
}
|
2023-06-19 01:20:15 +00:00
|
|
|
if !fi.ModTime.Equal(ofi.ModTime) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return fi.Erasure.Equal(ofi.Erasure)
|
2020-11-20 02:43:58 +00:00
|
|
|
}
|
|
|
|
|
2021-11-21 18:41:30 +00:00
|
|
|
// GetDataDir returns an expected dataDir given FileInfo
|
2022-08-26 19:52:29 +00:00
|
|
|
// - deleteMarker returns "delete-marker"
|
|
|
|
// - returns "legacy" if FileInfo is XLV1 and DataDir is
|
|
|
|
// empty, returns DataDir otherwise
|
|
|
|
// - returns "dataDir"
|
2021-11-21 18:41:30 +00:00
|
|
|
func (fi FileInfo) GetDataDir() string {
|
|
|
|
if fi.Deleted {
|
|
|
|
return "delete-marker"
|
|
|
|
}
|
|
|
|
if fi.XLV1 && fi.DataDir == "" {
|
|
|
|
return "legacy"
|
|
|
|
}
|
|
|
|
return fi.DataDir
|
|
|
|
}
|
|
|
|
|
2024-03-01 06:49:01 +00:00
|
|
|
// IsCompressed returns true if the object is marked as compressed.
|
|
|
|
func (fi FileInfo) IsCompressed() bool {
|
|
|
|
_, ok := fi.Metadata[ReservedMetadataPrefix+"compression"]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2021-07-16 16:38:27 +00:00
|
|
|
// InlineData returns true if object contents are inlined alongside its metadata.
|
|
|
|
func (fi FileInfo) InlineData() bool {
|
|
|
|
_, ok := fi.Metadata[ReservedMetadataPrefixLower+"inline-data"]
|
2023-11-09 04:00:02 +00:00
|
|
|
// Earlier MinIO versions didn't reset "x-minio-internal-inline-data"
|
|
|
|
// from fi.Metadata when the object was tiered. So, tiered objects
|
|
|
|
// would return true for InlineData() in these versions even though the
|
|
|
|
// object isn't inlined in xl.meta
|
|
|
|
return ok && !fi.IsRemote()
|
2021-07-16 16:38:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetInlineData marks object (version) as inline.
|
2021-08-13 15:25:54 +00:00
|
|
|
func (fi *FileInfo) SetInlineData() {
|
|
|
|
if fi.Metadata == nil {
|
|
|
|
fi.Metadata = make(map[string]string, 1)
|
|
|
|
}
|
2021-07-16 16:38:27 +00:00
|
|
|
fi.Metadata[ReservedMetadataPrefixLower+"inline-data"] = "true"
|
|
|
|
}
|
|
|
|
|
2020-11-20 02:43:58 +00:00
|
|
|
// VersionPurgeStatusKey denotes purge status in metadata
|
2022-01-10 17:07:49 +00:00
|
|
|
const (
|
|
|
|
VersionPurgeStatusKey = ReservedMetadataPrefixLower + "purgestatus"
|
|
|
|
)
|
2020-11-20 02:43:58 +00:00
|
|
|
|
2020-06-13 03:04:01 +00:00
|
|
|
// newFileInfo - initializes new FileInfo, allocates a fresh erasure info.
|
|
|
|
func newFileInfo(object string, dataBlocks, parityBlocks int) (fi FileInfo) {
|
|
|
|
fi.Erasure = ErasureInfo{
|
|
|
|
Algorithm: erasureAlgorithm,
|
|
|
|
DataBlocks: dataBlocks,
|
|
|
|
ParityBlocks: parityBlocks,
|
[feat]: change erasure coding default block size from 10MiB to 1MiB (#11721)
major performance improvements in range GETs to avoid large
read amplification when ranges are tiny and random
```
-------------------
Operation: GET
Operations: 142014 -> 339421
Duration: 4m50s -> 4m56s
* Average: +139.41% (+1177.3 MiB/s) throughput, +139.11% (+658.4) obj/s
* Fastest: +125.24% (+1207.4 MiB/s) throughput, +132.32% (+612.9) obj/s
* 50% Median: +139.06% (+1175.7 MiB/s) throughput, +133.46% (+660.9) obj/s
* Slowest: +203.40% (+1267.9 MiB/s) throughput, +198.59% (+753.5) obj/s
```
TTFB from 10MiB BlockSize
```
* First Access TTFB: Avg: 81ms, Median: 61ms, Best: 20ms, Worst: 2.056s
```
TTFB from 1MiB BlockSize
```
* First Access TTFB: Avg: 22ms, Median: 21ms, Best: 8ms, Worst: 91ms
```
Full object reads however do see a slight change which won't be
noticeable in real world, so not doing any comparisons
TTFB still had improvements with full object reads with 1MiB
```
* First Access TTFB: Avg: 68ms, Median: 35ms, Best: 11ms, Worst: 1.16s
```
v/s
TTFB with 10MiB
```
* First Access TTFB: Avg: 388ms, Median: 98ms, Best: 20ms, Worst: 4.156s
```
This change should affect all new uploads, previous uploads should
continue to work with business as usual. But dramatic improvements can
be seen with these changes.
2021-03-06 22:09:34 +00:00
|
|
|
BlockSize: blockSizeV2,
|
2020-06-13 03:04:01 +00:00
|
|
|
Distribution: hashOrder(object, dataBlocks+parityBlocks),
|
2020-02-25 15:52:28 +00:00
|
|
|
}
|
2020-06-13 03:04:01 +00:00
|
|
|
return fi
|
2020-02-25 15:52:28 +00:00
|
|
|
}
|
2022-07-19 15:35:29 +00:00
|
|
|
|
|
|
|
// ReadMultipleReq contains information of multiple files to read from disk.
|
|
|
|
type ReadMultipleReq struct {
|
|
|
|
Bucket string // Bucket. Can be empty if multiple buckets.
|
|
|
|
Prefix string // Shared prefix of all files. Can be empty. Will be joined to filename without modification.
|
|
|
|
Files []string // Individual files to read.
|
|
|
|
MaxSize int64 // Return error if size is exceed.
|
|
|
|
MetadataOnly bool // Read as XL meta and truncate data.
|
|
|
|
AbortOn404 bool // Stop reading after first file not found.
|
2022-07-21 23:47:58 +00:00
|
|
|
MaxResults int // Stop after this many successful results. <= 0 means all.
|
2022-07-19 15:35:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ReadMultipleResp contains a single response from a ReadMultipleReq.
|
|
|
|
type ReadMultipleResp struct {
|
|
|
|
Bucket string // Bucket as given by request.
|
|
|
|
Prefix string // Prefix as given by request.
|
|
|
|
File string // File name as given in request.
|
|
|
|
Exists bool // Returns whether the file existed on disk.
|
|
|
|
Error string // Returns any error when reading.
|
|
|
|
Data []byte // Contains all data of file.
|
|
|
|
Modtime time.Time // Modtime of file on disk.
|
|
|
|
}
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-21 01:09:35 +00:00
|
|
|
|
|
|
|
// DeleteVersionHandlerParams are parameters for DeleteVersionHandler
|
|
|
|
type DeleteVersionHandlerParams struct {
|
2023-12-29 23:52:41 +00:00
|
|
|
DiskID string `msg:"id"`
|
|
|
|
Volume string `msg:"v"`
|
|
|
|
FilePath string `msg:"fp"`
|
|
|
|
ForceDelMarker bool `msg:"fdm"`
|
|
|
|
Opts DeleteOptions `msg:"do"`
|
|
|
|
FI FileInfo `msg:"fi"`
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-21 01:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MetadataHandlerParams is request info for UpdateMetadataHandle and WriteMetadataHandler.
|
|
|
|
type MetadataHandlerParams struct {
|
|
|
|
DiskID string `msg:"id"`
|
|
|
|
Volume string `msg:"v"`
|
2024-01-30 20:43:25 +00:00
|
|
|
OrigVolume string `msg:"ov"`
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-21 01:09:35 +00:00
|
|
|
FilePath string `msg:"fp"`
|
|
|
|
UpdateOpts UpdateMetadataOpts `msg:"uo"`
|
|
|
|
FI FileInfo `msg:"fi"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateMetadataOpts provides an optional input to indicate if xl.meta updates need to be fully synced to disk.
|
|
|
|
type UpdateMetadataOpts struct {
|
|
|
|
NoPersistence bool `msg:"np"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// CheckPartsHandlerParams are parameters for CheckPartsHandler
|
|
|
|
type CheckPartsHandlerParams struct {
|
|
|
|
DiskID string `msg:"id"`
|
|
|
|
Volume string `msg:"v"`
|
|
|
|
FilePath string `msg:"fp"`
|
|
|
|
FI FileInfo `msg:"fi"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteFileHandlerParams are parameters for DeleteFileHandler
|
|
|
|
type DeleteFileHandlerParams struct {
|
|
|
|
DiskID string `msg:"id"`
|
|
|
|
Volume string `msg:"v"`
|
|
|
|
FilePath string `msg:"fp"`
|
|
|
|
Opts DeleteOptions `msg:"do"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenameDataHandlerParams are parameters for RenameDataHandler.
|
|
|
|
type RenameDataHandlerParams struct {
|
2023-12-29 23:52:41 +00:00
|
|
|
DiskID string `msg:"id"`
|
|
|
|
SrcVolume string `msg:"sv"`
|
|
|
|
SrcPath string `msg:"sp"`
|
|
|
|
DstVolume string `msg:"dv"`
|
|
|
|
DstPath string `msg:"dp"`
|
|
|
|
FI FileInfo `msg:"fi"`
|
|
|
|
Opts RenameOptions `msg:"ro"`
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-21 01:09:35 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 20:45:46 +00:00
|
|
|
// RenameFileHandlerParams are parameters for RenameFileHandler.
|
|
|
|
type RenameFileHandlerParams struct {
|
|
|
|
DiskID string `msg:"id"`
|
|
|
|
SrcVolume string `msg:"sv"`
|
|
|
|
SrcFilePath string `msg:"sp"`
|
|
|
|
DstVolume string `msg:"dv"`
|
|
|
|
DstFilePath string `msg:"dp"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadAllHandlerParams are parameters for ReadAllHandler.
|
|
|
|
type ReadAllHandlerParams struct {
|
|
|
|
DiskID string `msg:"id"`
|
|
|
|
Volume string `msg:"v"`
|
|
|
|
FilePath string `msg:"fp"`
|
|
|
|
}
|
|
|
|
|
2024-02-28 17:54:52 +00:00
|
|
|
// WriteAllHandlerParams are parameters for WriteAllHandler.
|
|
|
|
type WriteAllHandlerParams struct {
|
|
|
|
DiskID string `msg:"id"`
|
|
|
|
Volume string `msg:"v"`
|
|
|
|
FilePath string `msg:"fp"`
|
|
|
|
Buf []byte `msg:"b"`
|
|
|
|
}
|
|
|
|
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-21 01:09:35 +00:00
|
|
|
// RenameDataResp - RenameData()'s response.
|
|
|
|
type RenameDataResp struct {
|
|
|
|
Signature uint64 `msg:"sig"`
|
|
|
|
}
|
2024-02-01 18:47:20 +00:00
|
|
|
|
|
|
|
// LocalDiskIDs - GetLocalIDs response.
|
|
|
|
type LocalDiskIDs struct {
|
|
|
|
IDs []string
|
|
|
|
}
|
2024-02-19 22:54:46 +00:00
|
|
|
|
|
|
|
// ListDirResult - ListDir()'s response.
|
|
|
|
type ListDirResult struct {
|
|
|
|
Entries []string `msg:"e"`
|
|
|
|
}
|