package fs import ( "errors" "fmt" "github.com/dotcloud/docker/future" "io" "io/ioutil" "os" "path" "path/filepath" ) type LayerStore struct { Root string } func NewLayerStore(root string) (*LayerStore, error) { abspath, err := filepath.Abs(root) if err != nil { return nil, err } // Create the root directory if it doesn't exists if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) { return nil, err } return &LayerStore{ Root: abspath, }, nil } func (store *LayerStore) List() []string { files, err := ioutil.ReadDir(store.Root) if err != nil { return []string{} } var layers []string for _, st := range files { if st.IsDir() { layers = append(layers, path.Join(store.Root, st.Name())) } } return layers } func (store *LayerStore) Get(id string) string { if !store.Exists(id) { return "" } return store.layerPath(id) } func (store *LayerStore) rootExists() (bool, error) { if stat, err := os.Stat(store.Root); err != nil { if os.IsNotExist(err) { return false, nil } return false, err } else if !stat.IsDir() { return false, errors.New("Not a directory: " + store.Root) } return true, nil } func (store *LayerStore) Init() error { if exists, err := store.rootExists(); err != nil { return err } else if exists { return nil } return os.Mkdir(store.Root, 0700) } func (store *LayerStore) Mktemp() (string, error) { tmpName := future.RandomId() tmpPath := path.Join(store.Root, "tmp-"+tmpName) if err := os.Mkdir(tmpPath, 0700); err != nil { return "", err } return tmpPath, nil } func (store *LayerStore) layerPath(id string) string { return path.Join(store.Root, id) } func (store *LayerStore) AddLayer(id string, archive Archive) (string, error) { if _, err := os.Stat(store.layerPath(id)); err == nil { return "", fmt.Errorf("Layer already exists: %v", id) } errors := make(chan error) // Untar tmp, err := store.Mktemp() defer os.RemoveAll(tmp) if err != nil { return "", fmt.Errorf("Mktemp failed: %s", err) } untarR, untarW := io.Pipe() go func() { errors <- Untar(untarR, tmp) }() _, err = io.Copy(untarW, archive) untarW.Close() if err != nil { return "", err } // Wait for goroutines for i := 0; i < 1; i += 1 { select { case err := <-errors: { if err != nil { return "", err } } } } layer := store.layerPath(id) if !store.Exists(id) { if err := os.Rename(tmp, layer); err != nil { return "", fmt.Errorf("Could not rename temp dir to layer %s: %s", layer, err) } } return layer, nil } func (store *LayerStore) Exists(id string) bool { st, err := os.Stat(store.layerPath(id)) if err != nil { return false } return st.IsDir() }