storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/fs-v1-metadata.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016, 2017, 2017 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"context"
    21  	"encoding/hex"
    22  	"encoding/json"
    23  	"io"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"os"
    27  	pathutil "path"
    28  	"time"
    29  
    30  	jsoniter "github.com/json-iterator/go"
    31  
    32  	xhttp "storj.io/minio/cmd/http"
    33  	"storj.io/minio/cmd/logger"
    34  	"storj.io/minio/pkg/lock"
    35  	"storj.io/minio/pkg/mimedb"
    36  )
    37  
    38  // FS format, and object metadata.
    39  const (
    40  	// fs.json object metadata.
    41  	fsMetaJSONFile = "fs.json"
    42  )
    43  
    44  // FS metadata constants.
    45  const (
    46  	// FS backend meta 1.0.0 version.
    47  	fsMetaVersion100 = "1.0.0"
    48  
    49  	// FS backend meta 1.0.1 version.
    50  	fsMetaVersion101 = "1.0.1"
    51  
    52  	// FS backend meta 1.0.2
    53  	// Removed the fields "Format" and "MinIO" from fsMetaV1 as they were unused. Added "Checksum" field - to be used in future for bit-rot protection.
    54  	fsMetaVersion = "1.0.2"
    55  
    56  	// Add more constants here.
    57  )
    58  
    59  // FSChecksumInfoV1 - carries checksums of individual blocks on disk.
    60  type FSChecksumInfoV1 struct {
    61  	Algorithm string
    62  	Blocksize int64
    63  	Hashes    [][]byte
    64  }
    65  
    66  // MarshalJSON marshals the FSChecksumInfoV1 struct
    67  func (c FSChecksumInfoV1) MarshalJSON() ([]byte, error) {
    68  	type checksuminfo struct {
    69  		Algorithm string   `json:"algorithm"`
    70  		Blocksize int64    `json:"blocksize"`
    71  		Hashes    []string `json:"hashes"`
    72  	}
    73  	var hashes []string
    74  	for _, h := range c.Hashes {
    75  		hashes = append(hashes, hex.EncodeToString(h))
    76  	}
    77  	info := checksuminfo{
    78  		Algorithm: c.Algorithm,
    79  		Hashes:    hashes,
    80  		Blocksize: c.Blocksize,
    81  	}
    82  	return json.Marshal(info)
    83  }
    84  
    85  // UnmarshalJSON unmarshals the the given data into the FSChecksumInfoV1 struct
    86  func (c *FSChecksumInfoV1) UnmarshalJSON(data []byte) error {
    87  	type checksuminfo struct {
    88  		Algorithm string   `json:"algorithm"`
    89  		Blocksize int64    `json:"blocksize"`
    90  		Hashes    []string `json:"hashes"`
    91  	}
    92  
    93  	var info checksuminfo
    94  	var json = jsoniter.ConfigCompatibleWithStandardLibrary
    95  	err := json.Unmarshal(data, &info)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	c.Algorithm = info.Algorithm
   100  	c.Blocksize = info.Blocksize
   101  	var hashes [][]byte
   102  	for _, hashStr := range info.Hashes {
   103  		h, err := hex.DecodeString(hashStr)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		hashes = append(hashes, h)
   108  	}
   109  	c.Hashes = hashes
   110  	return nil
   111  }
   112  
   113  // A fsMetaV1 represents a metadata header mapping keys to sets of values.
   114  type fsMetaV1 struct {
   115  	Version string `json:"version"`
   116  	// checksums of blocks on disk.
   117  	Checksum FSChecksumInfoV1 `json:"checksum,omitempty"`
   118  	// Metadata map for current object.
   119  	Meta map[string]string `json:"meta,omitempty"`
   120  	// parts info for current object - used in encryption.
   121  	Parts []ObjectPartInfo `json:"parts,omitempty"`
   122  }
   123  
   124  // IsValid - tells if the format is sane by validating the version
   125  // string and format style.
   126  func (m fsMetaV1) IsValid() bool {
   127  	return isFSMetaValid(m.Version)
   128  }
   129  
   130  // Verifies if the backend format metadata is same by validating
   131  // the version string.
   132  func isFSMetaValid(version string) bool {
   133  	return (version == fsMetaVersion || version == fsMetaVersion100 || version == fsMetaVersion101)
   134  }
   135  
   136  // Converts metadata to object info.
   137  func (m fsMetaV1) ToObjectInfo(bucket, object string, fi os.FileInfo) ObjectInfo {
   138  	if len(m.Meta) == 0 {
   139  		m.Meta = make(map[string]string)
   140  	}
   141  
   142  	// Guess content-type from the extension if possible.
   143  	if m.Meta["content-type"] == "" {
   144  		m.Meta["content-type"] = mimedb.TypeByExtension(pathutil.Ext(object))
   145  	}
   146  
   147  	if HasSuffix(object, SlashSeparator) {
   148  		m.Meta["etag"] = emptyETag // For directories etag is d41d8cd98f00b204e9800998ecf8427e
   149  		m.Meta["content-type"] = "application/octet-stream"
   150  	}
   151  
   152  	objInfo := ObjectInfo{
   153  		Bucket: bucket,
   154  		Name:   object,
   155  	}
   156  
   157  	// We set file info only if its valid.
   158  	objInfo.ModTime = timeSentinel
   159  	if fi != nil {
   160  		objInfo.ModTime = fi.ModTime()
   161  		objInfo.Size = fi.Size()
   162  		if fi.IsDir() {
   163  			// Directory is always 0 bytes in S3 API, treat it as such.
   164  			objInfo.Size = 0
   165  			objInfo.IsDir = fi.IsDir()
   166  		}
   167  	}
   168  
   169  	objInfo.ETag = extractETag(m.Meta)
   170  	objInfo.ContentType = m.Meta["content-type"]
   171  	objInfo.ContentEncoding = m.Meta["content-encoding"]
   172  	if storageClass, ok := m.Meta[xhttp.AmzStorageClass]; ok {
   173  		objInfo.StorageClass = storageClass
   174  	} else {
   175  		objInfo.StorageClass = globalMinioDefaultStorageClass
   176  	}
   177  	var (
   178  		t time.Time
   179  		e error
   180  	)
   181  	if exp, ok := m.Meta["expires"]; ok {
   182  		if t, e = time.Parse(http.TimeFormat, exp); e == nil {
   183  			objInfo.Expires = t.UTC()
   184  		}
   185  	}
   186  
   187  	// Add user tags to the object info
   188  	objInfo.UserTags = m.Meta[xhttp.AmzObjectTagging]
   189  
   190  	// etag/md5Sum has already been extracted. We need to
   191  	// remove to avoid it from appearing as part of
   192  	// response headers. e.g, X-Minio-* or X-Amz-*.
   193  	// Tags have also been extracted, we remove that as well.
   194  	objInfo.UserDefined = cleanMetadata(m.Meta)
   195  
   196  	// All the parts per object.
   197  	objInfo.Parts = m.Parts
   198  
   199  	// Success..
   200  	return objInfo
   201  }
   202  
   203  func (m *fsMetaV1) WriteTo(lk *lock.LockedFile) (n int64, err error) {
   204  	if err = jsonSave(lk, m); err != nil {
   205  		return 0, err
   206  	}
   207  	fi, err := lk.Stat()
   208  	if err != nil {
   209  		return 0, err
   210  	}
   211  	return fi.Size(), nil
   212  }
   213  
   214  func (m *fsMetaV1) ReadFrom(ctx context.Context, lk *lock.LockedFile) (n int64, err error) {
   215  	var fsMetaBuf []byte
   216  	fi, err := lk.Stat()
   217  	if err != nil {
   218  		logger.LogIf(ctx, err)
   219  		return 0, err
   220  	}
   221  
   222  	fsMetaBuf, err = ioutil.ReadAll(io.NewSectionReader(lk, 0, fi.Size()))
   223  	if err != nil {
   224  		logger.LogIf(ctx, err)
   225  		return 0, err
   226  	}
   227  
   228  	if len(fsMetaBuf) == 0 {
   229  		return 0, io.EOF
   230  	}
   231  
   232  	var json = jsoniter.ConfigCompatibleWithStandardLibrary
   233  	if err = json.Unmarshal(fsMetaBuf, m); err != nil {
   234  		return 0, err
   235  	}
   236  
   237  	// Verify if the format is valid, return corrupted format
   238  	// for unrecognized formats.
   239  	if !isFSMetaValid(m.Version) {
   240  		logger.GetReqInfo(ctx).AppendTags("file", lk.Name())
   241  		logger.LogIf(ctx, errCorruptedFormat)
   242  		return 0, errCorruptedFormat
   243  	}
   244  
   245  	// Success.
   246  	return int64(len(fsMetaBuf)), nil
   247  }
   248  
   249  // newFSMetaV1 - initializes new fsMetaV1.
   250  func newFSMetaV1() (fsMeta fsMetaV1) {
   251  	fsMeta = fsMetaV1{}
   252  	fsMeta.Version = fsMetaVersion
   253  	return fsMeta
   254  }