github.com/containerd/nerdctl@v1.7.7/pkg/namestore/namestore.go (about)

     1  /*
     2     Copyright The containerd Authors.
     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 namestore
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"strings"
    24  
    25  	"github.com/containerd/containerd/identifiers"
    26  	"github.com/containerd/nerdctl/pkg/lockutil"
    27  )
    28  
    29  func New(dataStore, ns string) (NameStore, error) {
    30  	dir := filepath.Join(dataStore, "names", ns)
    31  	if err := os.MkdirAll(dir, 0700); err != nil {
    32  		return nil, err
    33  	}
    34  	store := &nameStore{
    35  		dir: dir,
    36  	}
    37  	return store, nil
    38  }
    39  
    40  type NameStore interface {
    41  	Acquire(name, id string) error
    42  	Release(name, id string) error
    43  	Rename(oldName, id, newName string) error
    44  }
    45  
    46  type nameStore struct {
    47  	dir string
    48  }
    49  
    50  func (x *nameStore) Acquire(name, id string) error {
    51  	if err := identifiers.Validate(name); err != nil {
    52  		return fmt.Errorf("invalid name %q: %w", name, err)
    53  	}
    54  	if strings.TrimSpace(id) != id {
    55  		return fmt.Errorf("untrimmed ID %q", id)
    56  	}
    57  	fn := func() error {
    58  		fileName := filepath.Join(x.dir, name)
    59  		if b, err := os.ReadFile(fileName); err == nil {
    60  			return fmt.Errorf("name %q is already used by ID %q", name, string(b))
    61  		}
    62  		return os.WriteFile(fileName, []byte(id), 0600)
    63  	}
    64  	return lockutil.WithDirLock(x.dir, fn)
    65  }
    66  
    67  func (x *nameStore) Release(name, id string) error {
    68  	if name == "" {
    69  		return nil
    70  	}
    71  	if err := identifiers.Validate(name); err != nil {
    72  		return fmt.Errorf("invalid name %q: %w", name, err)
    73  	}
    74  	if strings.TrimSpace(id) != id {
    75  		return fmt.Errorf("untrimmed ID %q", id)
    76  	}
    77  	fn := func() error {
    78  		fileName := filepath.Join(x.dir, name)
    79  		b, err := os.ReadFile(fileName)
    80  		if err != nil {
    81  			if os.IsNotExist(err) {
    82  				err = nil
    83  			}
    84  			return err
    85  		}
    86  		if s := strings.TrimSpace(string(b)); s != id {
    87  			return fmt.Errorf("name %q is used by ID %q, not by %q", name, s, id)
    88  		}
    89  		return os.RemoveAll(fileName)
    90  	}
    91  	return lockutil.WithDirLock(x.dir, fn)
    92  }
    93  
    94  func (x *nameStore) Rename(oldName, id, newName string) error {
    95  	if oldName == "" || newName == "" {
    96  		return nil
    97  	}
    98  	if err := identifiers.Validate(newName); err != nil {
    99  		return fmt.Errorf("invalid name %q: %w", oldName, err)
   100  	}
   101  	fn := func() error {
   102  		oldFileName := filepath.Join(x.dir, oldName)
   103  		b, err := os.ReadFile(oldFileName)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		if s := strings.TrimSpace(string(b)); s != id {
   108  			return fmt.Errorf("name %q is used by ID %q, not by %q", oldName, s, id)
   109  		}
   110  		newFileName := filepath.Join(x.dir, newName)
   111  		if b, err := os.ReadFile(newFileName); err == nil {
   112  			return fmt.Errorf("name %q is already used by ID %q", newName, string(b))
   113  		}
   114  		return os.Rename(oldFileName, newFileName)
   115  	}
   116  	return lockutil.WithDirLock(x.dir, fn)
   117  }