123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- package fs
- import (
- "database/sql"
- "fmt"
- "github.com/dotcloud/docker/future"
- _ "github.com/mattn/go-sqlite3"
- "github.com/shykes/gorp" //Forked to implement CreateTablesOpts
- "io"
- "io/ioutil"
- "os"
- "path"
- "path/filepath"
- "strings"
- "syscall"
- "time"
- )
- type Store struct {
- Root string
- db *sql.DB
- orm *gorp.DbMap
- layers *LayerStore
- }
- type Archive io.Reader
- func New(root string) (*Store, error) {
- isNewStore := true
- if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) {
- return nil, err
- }
- db, err := sql.Open("sqlite3", path.Join(root, "db"))
- if err != nil {
- return nil, err
- }
- orm := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
- orm.AddTableWithName(Image{}, "images").SetKeys(false, "Id")
- orm.AddTableWithName(Path{}, "paths").SetKeys(false, "Path", "Image")
- orm.AddTableWithName(Mountpoint{}, "mountpoints").SetKeys(false, "Root")
- orm.AddTableWithName(Tag{}, "tags").SetKeys(false, "TagName")
- if isNewStore {
- if err := orm.CreateTablesOpts(true); err != nil {
- return nil, err
- }
- }
- layers, err := NewLayerStore(path.Join(root, "layers"))
- if err != nil {
- return nil, err
- }
- return &Store{
- Root: root,
- db: db,
- orm: orm,
- layers: layers,
- }, nil
- }
- func (store *Store) imageList(src []interface{}) []*Image {
- var images []*Image
- for _, i := range src {
- img := i.(*Image)
- img.store = store
- images = append(images, img)
- }
- return images
- }
- func (store *Store) Images() ([]*Image, error) {
- images, err := store.orm.Select(Image{}, "select * from images")
- if err != nil {
- return nil, err
- }
- return store.imageList(images), nil
- }
- func (store *Store) Paths() ([]string, error) {
- var paths []string
- rows, err := store.db.Query("select distinct Path from paths order by Path")
- if err != nil {
- return nil, err
- }
- for rows.Next() {
- var path string
- if err := rows.Scan(&path); err != nil {
- return nil, err
- }
- paths = append(paths, path)
- }
- return paths, nil
- }
- func (store *Store) RemoveInPath(pth string) error {
- images, err := store.List(pth)
- if err != nil {
- return err
- }
- for _, img := range images {
- if err = store.Remove(img); err != nil {
- return err
- }
- }
- return nil
- }
- func (store *Store) Remove(img *Image) error {
- _, err := store.orm.Delete(img)
- return err
- }
- func (store *Store) List(pth string) ([]*Image, error) {
- pth = path.Clean(pth)
- images, err := store.orm.Select(Image{}, "select images.* from images, paths where Path=? and paths.Image=images.Id order by images.Created desc", pth)
- if err != nil {
- return nil, err
- }
- return store.imageList(images), nil
- }
- func (store *Store) Find(pth string) (*Image, error) {
- pth = path.Clean(pth)
- img, err := store.Get(pth)
- if err != nil {
- return nil, err
- } else if img != nil {
- return img, nil
- }
- var q string
- var args []interface{}
- // FIXME: this breaks if the path contains a ':'
- // If format is path:rev
- if parts := strings.SplitN(pth, ":", 2); len(parts) == 2 {
- q = "select Images.* from images, paths where Path=? and images.Id=? and paths.Image=images.Id"
- args = []interface{}{parts[0], parts[1]}
- // If format is path:rev
- } else {
- q = "select images.* from images, paths where Path=? and paths.Image=images.Id order by images.Created desc limit 1"
- args = []interface{}{parts[0]}
- }
- images, err := store.orm.Select(Image{}, q, args...)
- if err != nil {
- return nil, err
- } else if len(images) < 1 {
- return nil, nil
- }
- img = images[0].(*Image)
- img.store = store
- return img, nil
- }
- func (store *Store) Get(id string) (*Image, error) {
- img, err := store.orm.Get(Image{}, id)
- if img == nil {
- return nil, err
- }
- res := img.(*Image)
- res.store = store
- return res, err
- }
- func (store *Store) Create(layerData Archive, parent *Image, pth, comment string) (*Image, error) {
- // FIXME: actually do something with the layer...
- img := &Image{
- Id: future.RandomId(),
- Comment: comment,
- Created: time.Now().Unix(),
- store: store,
- }
- if parent != nil {
- img.Parent = parent.Id
- }
- // FIXME: Archive should contain compression info. For now we only support uncompressed.
- err := store.Register(layerData, img, pth)
- return img, err
- }
- func (store *Store) Register(layerData Archive, img *Image, pth string) error {
- img.store = store
- _, err := store.layers.AddLayer(img.Id, layerData)
- if err != nil {
- return fmt.Errorf("Could not add layer: %s", err)
- }
- pathObj := &Path{
- Path: path.Clean(pth),
- Image: img.Id,
- }
- trans, err := store.orm.Begin()
- if err != nil {
- return fmt.Errorf("Could not begin transaction: %s", err)
- }
- if err := trans.Insert(img); err != nil {
- return fmt.Errorf("Could not insert image info: %s", err)
- }
- if err := trans.Insert(pathObj); err != nil {
- return fmt.Errorf("Could not insert path info: %s", err)
- }
- if err := trans.Commit(); err != nil {
- return fmt.Errorf("Could not commit transaction: %s", err)
- }
- return nil
- }
- func (store *Store) Layers() []string {
- return store.layers.List()
- }
- type Image struct {
- Id string
- Parent string
- Comment string
- Created int64
- store *Store `db:"-"`
- }
- func (image *Image) Copy(pth string) (*Image, error) {
- if err := image.store.orm.Insert(&Path{Path: pth, Image: image.Id}); err != nil {
- return nil, err
- }
- return image, nil
- }
- type Mountpoint struct {
- Image string
- Root string
- Rw string
- Store *Store `db:"-"`
- }
- func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) {
- mountpoint := &Mountpoint{
- Root: path.Clean(root),
- Rw: path.Clean(rw),
- Image: image.Id,
- Store: image.store,
- }
- if err := image.store.orm.Insert(mountpoint); err != nil {
- return nil, err
- }
- return mountpoint, nil
- }
- func (image *Image) layers() ([]string, error) {
- var list []string
- var err error
- currentImg := image
- for currentImg != nil {
- if layer := image.store.layers.Get(currentImg.Id); layer != "" {
- list = append(list, layer)
- } else {
- return list, fmt.Errorf("Layer not found for image %s", image.Id)
- }
- currentImg, err = currentImg.store.Get(currentImg.Parent)
- if err != nil {
- return list, fmt.Errorf("Error while getting parent image: %v", err)
- }
- }
- if len(list) == 0 {
- return nil, fmt.Errorf("No layer found for image %s\n", image.Id)
- }
- return list, nil
- }
- func (image *Image) Mountpoints() ([]*Mountpoint, error) {
- var mountpoints []*Mountpoint
- res, err := image.store.orm.Select(Mountpoint{}, "select * from mountpoints where Image=?", image.Id)
- if err != nil {
- return nil, err
- }
- for _, mp := range res {
- mountpoints = append(mountpoints, mp.(*Mountpoint))
- }
- return mountpoints, nil
- }
- func (image *Image) Mount(root, rw string) (*Mountpoint, error) {
- var mountpoint *Mountpoint
- if mp, err := image.store.FetchMountpoint(root, rw); err != nil {
- return nil, err
- } else if mp == nil {
- mountpoint, err = image.Mountpoint(root, rw)
- if err != nil {
- return nil, fmt.Errorf("Could not create mountpoint: %s", err)
- } else if mountpoint == nil {
- return nil, fmt.Errorf("No mountpoint created")
- }
- } else {
- mountpoint = mp
- }
- if err := mountpoint.createFolders(); err != nil {
- return nil, err
- }
- // FIXME: Now mount the layers
- rwBranch := fmt.Sprintf("%v=rw", mountpoint.Rw)
- roBranches := ""
- layers, err := image.layers()
- if err != nil {
- return nil, err
- }
- for _, layer := range layers {
- roBranches += fmt.Sprintf("%v=ro:", layer)
- }
- branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
- if err := mount("none", mountpoint.Root, "aufs", 0, branches); err != nil {
- return mountpoint, err
- }
- if !mountpoint.Mounted() {
- return mountpoint, fmt.Errorf("Mount failed")
- }
- // FIXME: Create tests for deletion
- // FIXME: move this part to change.go, maybe refactor
- // fs.Change() to avoid the fake mountpoint
- // Retrieve the changeset from the parent and apply it to the container
- // - Retrieve the changes
- changes, err := image.store.Changes(&Mountpoint{
- Image: image.Id,
- Root: layers[0],
- Rw: layers[0],
- Store: image.store})
- if err != nil {
- return nil, err
- }
- // Iterate on changes
- for _, c := range changes {
- // If there is a delete
- if c.Kind == ChangeDelete {
- // Make sure the directory exists
- file_path, file_name := path.Dir(c.Path), path.Base(c.Path)
- if err := os.MkdirAll(path.Join(mountpoint.Rw, file_path), 0755); err != nil {
- return nil, err
- }
- // And create the whiteout (we just need to create empty file, discard the return)
- if _, err := os.Create(path.Join(path.Join(mountpoint.Rw, file_path),
- ".wh."+path.Base(file_name))); err != nil {
- return nil, err
- }
- }
- }
- return mountpoint, nil
- }
- func (mp *Mountpoint) EnsureMounted() error {
- if mp.Mounted() {
- return nil
- }
- img, err := mp.Store.Get(mp.Image)
- if err != nil {
- return err
- }
- _, err = img.Mount(mp.Root, mp.Rw)
- return err
- }
- func (mp *Mountpoint) createFolders() error {
- if err := os.Mkdir(mp.Root, 0755); err != nil && !os.IsExist(err) {
- return err
- }
- if err := os.Mkdir(mp.Rw, 0755); err != nil && !os.IsExist(err) {
- return err
- }
- return nil
- }
- func (mp *Mountpoint) Mounted() bool {
- root, err := os.Stat(mp.Root)
- if err != nil {
- if os.IsNotExist(err) {
- return false
- }
- panic(err)
- }
- parent, err := os.Stat(filepath.Join(mp.Root, ".."))
- if err != nil {
- panic(err)
- }
- rootSt := root.Sys().(*syscall.Stat_t)
- parentSt := parent.Sys().(*syscall.Stat_t)
- return rootSt.Dev != parentSt.Dev
- }
- func (mp *Mountpoint) Umount() error {
- if !mp.Mounted() {
- return fmt.Errorf("Mountpoint doesn't seem to be mounted")
- }
- if err := syscall.Unmount(mp.Root, 0); err != nil {
- return fmt.Errorf("Unmount syscall failed: %v", err)
- }
- if mp.Mounted() {
- return fmt.Errorf("Umount: Filesystem still mounted after calling umount(%v)", mp.Root)
- }
- // Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
- // for some time. We'll just keep retrying until it succeeds.
- for retries := 0; retries < 1000; retries++ {
- err := os.Remove(mp.Root)
- if err == nil {
- // rm mntpoint succeeded
- return nil
- }
- if os.IsNotExist(err) {
- // mntpoint doesn't exist anymore. Success.
- return nil
- }
- // fmt.Printf("(%v) Remove %v returned: %v\n", retries, mp.Root, err)
- time.Sleep(10 * time.Millisecond)
- }
- return fmt.Errorf("Umount: Failed to umount %v", mp.Root)
- }
- func (mp *Mountpoint) Deregister() error {
- if mp.Mounted() {
- return fmt.Errorf("Mountpoint is currently mounted, can't deregister")
- }
- _, err := mp.Store.orm.Delete(mp)
- return err
- }
- func (store *Store) FetchMountpoint(root, rw string) (*Mountpoint, error) {
- res, err := store.orm.Select(Mountpoint{}, "select * from mountpoints where Root=? and Rw=?", root, rw)
- if err != nil {
- return nil, err
- } else if len(res) < 1 || res[0] == nil {
- return nil, nil
- }
- mp := res[0].(*Mountpoint)
- mp.Store = store
- return mp, nil
- }
- // OpenFile opens the named file for reading.
- func (mp *Mountpoint) OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
- if err := mp.EnsureMounted(); err != nil {
- return nil, err
- }
- return os.OpenFile(filepath.Join(mp.Root, path), flag, perm)
- }
- // ReadDir reads the directory named by dirname, relative to the Mountpoint's root,
- // and returns a list of sorted directory entries
- func (mp *Mountpoint) ReadDir(dirname string) ([]os.FileInfo, error) {
- if err := mp.EnsureMounted(); err != nil {
- return nil, err
- }
- return ioutil.ReadDir(filepath.Join(mp.Root, dirname))
- }
- func (store *Store) AddTag(imageId, tagName string) error {
- if image, err := store.Get(imageId); err != nil {
- return err
- } else if image == nil {
- return fmt.Errorf("No image with ID %s", imageId)
- }
- err2 := store.orm.Insert(&Tag{
- TagName: tagName,
- Image: imageId,
- })
- return err2
- }
- func (store *Store) GetByTag(tagName string) (*Image, error) {
- res, err := store.orm.Get(Tag{}, tagName)
- if err != nil {
- return nil, err
- } else if res == nil {
- return nil, fmt.Errorf("No image associated to tag \"%s\"", tagName)
- }
- tag := res.(*Tag)
- img, err2 := store.Get(tag.Image)
- if err2 != nil {
- return nil, err2
- } else if img == nil {
- return nil, fmt.Errorf("Tag was found but image seems to be inexistent.")
- }
- return img, nil
- }
- type Path struct {
- Path string
- Image string
- }
- type Tag struct {
- TagName string
- Image string
- }
|