github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/bucket/versioning/versioning.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package versioning 19 20 import ( 21 "encoding/xml" 22 "io" 23 "strings" 24 25 "github.com/minio/pkg/v2/wildcard" 26 ) 27 28 // State - enabled/disabled/suspended states 29 // for multifactor and status of versioning. 30 type State string 31 32 // Various supported states 33 const ( 34 Enabled State = "Enabled" 35 // Disabled State = "Disabled" only used by MFA Delete not supported yet. 36 Suspended State = "Suspended" 37 ) 38 39 var ( 40 errExcludedPrefixNotSupported = Errorf("excluded prefixes extension supported only when versioning is enabled") 41 errTooManyExcludedPrefixes = Errorf("too many excluded prefixes") 42 ) 43 44 // ExcludedPrefix - holds individual prefixes excluded from being versioned. 45 type ExcludedPrefix struct { 46 Prefix string 47 } 48 49 // Versioning - Configuration for bucket versioning. 50 type Versioning struct { 51 XMLNS string `xml:"xmlns,attr,omitempty"` 52 XMLName xml.Name `xml:"VersioningConfiguration"` 53 // MFADelete State `xml:"MFADelete,omitempty"` // not supported yet. 54 Status State `xml:"Status,omitempty"` 55 // MinIO extension - allows selective, prefix-level versioning exclusion. 56 // Requires versioning to be enabled 57 ExcludedPrefixes []ExcludedPrefix `xml:",omitempty"` 58 ExcludeFolders bool `xml:",omitempty"` 59 } 60 61 // Validate - validates the versioning configuration 62 func (v Versioning) Validate() error { 63 // Not supported yet 64 // switch v.MFADelete { 65 // case Enabled, Disabled: 66 // default: 67 // return Errorf("unsupported MFADelete state %s", v.MFADelete) 68 // } 69 switch v.Status { 70 case Enabled: 71 const maxExcludedPrefixes = 10 72 if len(v.ExcludedPrefixes) > maxExcludedPrefixes { 73 return errTooManyExcludedPrefixes 74 } 75 76 case Suspended: 77 if len(v.ExcludedPrefixes) > 0 { 78 return errExcludedPrefixNotSupported 79 } 80 default: 81 return Errorf("unsupported Versioning status %s", v.Status) 82 } 83 return nil 84 } 85 86 // Enabled - returns true if versioning is enabled 87 func (v Versioning) Enabled() bool { 88 return v.Status == Enabled 89 } 90 91 // Versioned returns if 'prefix' has versioning enabled or suspended. 92 func (v Versioning) Versioned(prefix string) bool { 93 return v.PrefixEnabled(prefix) || v.PrefixSuspended(prefix) 94 } 95 96 // PrefixEnabled - returns true if versioning is enabled at the bucket and given 97 // prefix, false otherwise. 98 func (v Versioning) PrefixEnabled(prefix string) bool { 99 if v.Status != Enabled { 100 return false 101 } 102 103 if prefix == "" { 104 return true 105 } 106 if v.ExcludeFolders && strings.HasSuffix(prefix, "/") { 107 return false 108 } 109 110 for _, sprefix := range v.ExcludedPrefixes { 111 // Note: all excluded prefix patterns end with `/` (See Validate) 112 sprefix.Prefix += "*" 113 114 if matched := wildcard.MatchSimple(sprefix.Prefix, prefix); matched { 115 return false 116 } 117 } 118 return true 119 } 120 121 // Suspended - returns true if versioning is suspended 122 func (v Versioning) Suspended() bool { 123 return v.Status == Suspended 124 } 125 126 // PrefixSuspended - returns true if versioning is suspended at the bucket level 127 // or suspended on the given prefix. 128 func (v Versioning) PrefixSuspended(prefix string) bool { 129 if v.Status == Suspended { 130 return true 131 } 132 if v.Status == Enabled { 133 if prefix == "" { 134 return false 135 } 136 if v.ExcludeFolders && strings.HasSuffix(prefix, "/") { 137 return true 138 } 139 140 for _, sprefix := range v.ExcludedPrefixes { 141 // Note: all excluded prefix patterns end with `/` (See Validate) 142 sprefix.Prefix += "*" 143 if matched := wildcard.MatchSimple(sprefix.Prefix, prefix); matched { 144 return true 145 } 146 } 147 } 148 return false 149 } 150 151 // PrefixesExcluded returns true if v contains one or more excluded object 152 // prefixes or if ExcludeFolders is true. 153 func (v Versioning) PrefixesExcluded() bool { 154 return len(v.ExcludedPrefixes) > 0 || v.ExcludeFolders 155 } 156 157 // ParseConfig - parses data in given reader to VersioningConfiguration. 158 func ParseConfig(reader io.Reader) (*Versioning, error) { 159 var v Versioning 160 if err := xml.NewDecoder(reader).Decode(&v); err != nil { 161 return nil, err 162 } 163 if err := v.Validate(); err != nil { 164 return nil, err 165 } 166 return &v, nil 167 }