github.com/demonoid81/containerd@v1.3.4/snapshots/devmapper/metadata.go (about) 1 // +build linux 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package devmapper 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "strconv" 26 27 "github.com/pkg/errors" 28 bolt "go.etcd.io/bbolt" 29 ) 30 31 type ( 32 // DeviceInfoCallback is a callback used for device updates 33 DeviceInfoCallback func(deviceInfo *DeviceInfo) error 34 ) 35 36 type deviceIDState byte 37 38 const ( 39 deviceFree deviceIDState = iota 40 deviceTaken 41 deviceFaulty 42 ) 43 44 // Bucket names 45 var ( 46 devicesBucketName = []byte("devices") // Contains thin devices metadata <device_name>=<DeviceInfo> 47 deviceIDBucketName = []byte("device_ids") // Tracks used device ids <device_id_[0..maxDeviceID)>=<byte_[0/1]> 48 ) 49 50 var ( 51 // ErrNotFound represents an error returned when object not found in meta store 52 ErrNotFound = errors.New("not found") 53 // ErrAlreadyExists represents an error returned when object can't be duplicated in meta store 54 ErrAlreadyExists = errors.New("object already exists") 55 ) 56 57 // PoolMetadata keeps device info for the given thin-pool device, it also responsible for 58 // generating next available device ids and tracking devmapper transaction numbers 59 type PoolMetadata struct { 60 db *bolt.DB 61 } 62 63 // NewPoolMetadata creates new or open existing pool metadata database 64 func NewPoolMetadata(dbfile string) (*PoolMetadata, error) { 65 db, err := bolt.Open(dbfile, 0600, nil) 66 if err != nil { 67 return nil, err 68 } 69 70 metadata := &PoolMetadata{db: db} 71 if err := metadata.ensureDatabaseInitialized(); err != nil { 72 return nil, errors.Wrap(err, "failed to initialize database") 73 } 74 75 return metadata, nil 76 } 77 78 // ensureDatabaseInitialized creates buckets required for metadata store in order 79 // to avoid bucket existence checks across the code 80 func (m *PoolMetadata) ensureDatabaseInitialized() error { 81 return m.db.Update(func(tx *bolt.Tx) error { 82 if _, err := tx.CreateBucketIfNotExists(devicesBucketName); err != nil { 83 return err 84 } 85 86 if _, err := tx.CreateBucketIfNotExists(deviceIDBucketName); err != nil { 87 return err 88 } 89 90 return nil 91 }) 92 } 93 94 // AddDevice saves device info to database. 95 func (m *PoolMetadata) AddDevice(ctx context.Context, info *DeviceInfo) error { 96 err := m.db.Update(func(tx *bolt.Tx) error { 97 devicesBucket := tx.Bucket(devicesBucketName) 98 99 // Make sure device name is unique. If there is already a device with the same name, 100 // but in Faulty state, give it a try with another devmapper device ID. 101 // See https://github.com/containerd/containerd/pull/3436 for more context. 102 var existing DeviceInfo 103 if err := getObject(devicesBucket, info.Name, &existing); err == nil && existing.State != Faulty { 104 return ErrAlreadyExists 105 } 106 107 // Find next available device ID 108 deviceID, err := getNextDeviceID(tx) 109 if err != nil { 110 return err 111 } 112 113 info.DeviceID = deviceID 114 115 return putObject(devicesBucket, info.Name, info, true) 116 }) 117 118 if err != nil { 119 return errors.Wrapf(err, "failed to save metadata for device %q (parent: %q)", info.Name, info.ParentName) 120 } 121 122 return nil 123 } 124 125 // MarkFaulty marks the given device and corresponding devmapper device ID as faulty. 126 // The snapshotter might attempt to recreate a device in 'Faulty' state with another devmapper ID in 127 // subsequent calls, and in case of success it's status will be changed to 'Created' or 'Activated'. 128 // The devmapper dev ID will remain in 'deviceFaulty' state until manually handled by a user. 129 func (m *PoolMetadata) MarkFaulty(ctx context.Context, name string) error { 130 return m.db.Update(func(tx *bolt.Tx) error { 131 var ( 132 device = DeviceInfo{} 133 devBucket = tx.Bucket(devicesBucketName) 134 ) 135 136 if err := getObject(devBucket, name, &device); err != nil { 137 return err 138 } 139 140 device.State = Faulty 141 142 if err := putObject(devBucket, name, &device, true); err != nil { 143 return err 144 } 145 146 return markDeviceID(tx, device.DeviceID, deviceFaulty) 147 }) 148 } 149 150 // getNextDeviceID finds the next free device ID by taking a cursor 151 // through the deviceIDBucketName bucket and finding the next sequentially 152 // unassigned ID. Device ID state is marked by a byte deviceFree or 153 // deviceTaken. Low device IDs will be reused sooner. 154 func getNextDeviceID(tx *bolt.Tx) (uint32, error) { 155 bucket := tx.Bucket(deviceIDBucketName) 156 cursor := bucket.Cursor() 157 158 // Check if any device id can be reused. 159 // Bolt stores its keys in byte-sorted order within a bucket. 160 // This makes sequential iteration extremely fast. 161 for key, taken := cursor.First(); key != nil; key, taken = cursor.Next() { 162 isFree := taken[0] == byte(deviceFree) 163 if !isFree { 164 continue 165 } 166 167 parsedID, err := strconv.ParseUint(string(key), 10, 32) 168 if err != nil { 169 return 0, err 170 } 171 172 id := uint32(parsedID) 173 if err := markDeviceID(tx, id, deviceTaken); err != nil { 174 return 0, err 175 } 176 177 return id, nil 178 } 179 180 // Try allocate new device ID 181 seq, err := bucket.NextSequence() 182 if err != nil { 183 return 0, err 184 } 185 186 if seq >= maxDeviceID { 187 return 0, errors.Errorf("dm-meta: couldn't find free device key") 188 } 189 190 id := uint32(seq) 191 if err := markDeviceID(tx, id, deviceTaken); err != nil { 192 return 0, err 193 } 194 195 return id, nil 196 } 197 198 // markDeviceID marks a device as deviceFree or deviceTaken 199 func markDeviceID(tx *bolt.Tx, deviceID uint32, state deviceIDState) error { 200 var ( 201 bucket = tx.Bucket(deviceIDBucketName) 202 key = strconv.FormatUint(uint64(deviceID), 10) 203 value = []byte{byte(state)} 204 ) 205 206 if err := bucket.Put([]byte(key), value); err != nil { 207 return errors.Wrapf(err, "failed to free device id %q", key) 208 } 209 210 return nil 211 } 212 213 // UpdateDevice updates device info in metadata store. 214 // The callback should be used to indicate whether device info update was successful or not. 215 // An error returned from the callback will rollback the update transaction in the database. 216 // Name and Device ID are not allowed to change. 217 func (m *PoolMetadata) UpdateDevice(ctx context.Context, name string, fn DeviceInfoCallback) error { 218 return m.db.Update(func(tx *bolt.Tx) error { 219 var ( 220 device = &DeviceInfo{} 221 bucket = tx.Bucket(devicesBucketName) 222 ) 223 224 if err := getObject(bucket, name, device); err != nil { 225 return err 226 } 227 228 // Don't allow changing these values, keep things in sync with devmapper 229 name := device.Name 230 devID := device.DeviceID 231 232 if err := fn(device); err != nil { 233 return err 234 } 235 236 if name != device.Name { 237 return fmt.Errorf("failed to update device info, name didn't match: %q %q", name, device.Name) 238 } 239 240 if devID != device.DeviceID { 241 return fmt.Errorf("failed to update device info, device id didn't match: %d %d", devID, device.DeviceID) 242 } 243 244 return putObject(bucket, name, device, true) 245 }) 246 } 247 248 // GetDevice retrieves device info by name from database 249 func (m *PoolMetadata) GetDevice(ctx context.Context, name string) (*DeviceInfo, error) { 250 var ( 251 dev DeviceInfo 252 err error 253 ) 254 255 err = m.db.View(func(tx *bolt.Tx) error { 256 bucket := tx.Bucket(devicesBucketName) 257 return getObject(bucket, name, &dev) 258 }) 259 260 return &dev, err 261 } 262 263 // RemoveDevice removes device info from store. 264 func (m *PoolMetadata) RemoveDevice(ctx context.Context, name string) error { 265 return m.db.Update(func(tx *bolt.Tx) error { 266 var ( 267 device = &DeviceInfo{} 268 bucket = tx.Bucket(devicesBucketName) 269 ) 270 271 if err := getObject(bucket, name, device); err != nil { 272 return err 273 } 274 275 if err := bucket.Delete([]byte(name)); err != nil { 276 return errors.Wrapf(err, "failed to delete device info for %q", name) 277 } 278 279 return markDeviceID(tx, device.DeviceID, deviceFree) 280 }) 281 } 282 283 // WalkDevices walks all devmapper devices in metadata store and invokes the callback with device info. 284 // The provided callback function must not modify the bucket. 285 func (m *PoolMetadata) WalkDevices(ctx context.Context, cb func(info *DeviceInfo) error) error { 286 return m.db.View(func(tx *bolt.Tx) error { 287 bucket := tx.Bucket(devicesBucketName) 288 return bucket.ForEach(func(key, value []byte) error { 289 device := &DeviceInfo{} 290 if err := json.Unmarshal(value, device); err != nil { 291 return errors.Wrapf(err, "failed to unmarshal %s", key) 292 } 293 294 return cb(device) 295 }) 296 }) 297 } 298 299 // GetDeviceNames retrieves the list of device names currently stored in database 300 func (m *PoolMetadata) GetDeviceNames(ctx context.Context) ([]string, error) { 301 var ( 302 names []string 303 err error 304 ) 305 306 err = m.db.View(func(tx *bolt.Tx) error { 307 bucket := tx.Bucket(devicesBucketName) 308 return bucket.ForEach(func(k, _ []byte) error { 309 names = append(names, string(k)) 310 return nil 311 }) 312 }) 313 314 if err != nil { 315 return nil, err 316 } 317 318 return names, nil 319 } 320 321 // Close closes metadata store 322 func (m *PoolMetadata) Close() error { 323 if err := m.db.Close(); err != nil && err != bolt.ErrDatabaseNotOpen { 324 return err 325 } 326 327 return nil 328 } 329 330 func putObject(bucket *bolt.Bucket, key string, obj interface{}, overwrite bool) error { 331 keyBytes := []byte(key) 332 333 if !overwrite && bucket.Get(keyBytes) != nil { 334 return errors.Errorf("object with key %q already exists", key) 335 } 336 337 data, err := json.Marshal(obj) 338 if err != nil { 339 return errors.Wrapf(err, "failed to marshal object with key %q", key) 340 } 341 342 if err := bucket.Put(keyBytes, data); err != nil { 343 return errors.Wrapf(err, "failed to insert object with key %q", key) 344 } 345 346 return nil 347 } 348 349 func getObject(bucket *bolt.Bucket, key string, obj interface{}) error { 350 data := bucket.Get([]byte(key)) 351 if data == nil { 352 return ErrNotFound 353 } 354 355 if obj != nil { 356 if err := json.Unmarshal(data, obj); err != nil { 357 return errors.Wrapf(err, "failed to unmarshal object with key %q", key) 358 } 359 } 360 361 return nil 362 }