func setupBindmounts(rootfs string, mountConfig *MountConfig) error { bindMounts := mountConfig.Mounts for _, m := range bindMounts.OfType("bind") { var ( flags = syscall.MS_BIND | syscall.MS_REC dest = filepath.Join(rootfs, m.Destination) ) if !m.Writable { flags = flags | syscall.MS_RDONLY } stat, err := os.Stat(m.Source) if err != nil { return err } dest, err = symlink.FollowSymlinkInScope(dest, rootfs) if err != nil { return err } if err := createIfNotExists(dest, stat.IsDir()); err != nil { return fmt.Errorf("Creating new bind-mount target, %s", err) } if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags), ""); err != nil { return fmt.Errorf("mounting %s into %s %s", m.Source, dest, err) } if !m.Writable { if err := syscall.Mount(m.Source, dest, "bind", uintptr(flags|syscall.MS_REMOUNT), ""); err != nil { return fmt.Errorf("remounting %s into %s %s", m.Source, dest, err) } } if m.Relabel != "" { if err := label.Relabel(m.Source, mountConfig.MountLabel, m.Relabel); err != nil { return fmt.Errorf("relabeling %s to %s %s", m.Source, mountConfig.MountLabel, err) } } if m.Private { if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { return fmt.Errorf("mounting %s private %s", dest, err) } } } return nil }
func (container *Container) Copy(resource string) (io.ReadCloser, error) { if err := container.Mount(); err != nil { return nil, err } var filter []string resPath := container.getResourcePath(resource) basePath, err := symlink.FollowSymlinkInScope(resPath, container.basefs) if err != nil { container.Unmount() return nil, err } stat, err := os.Stat(basePath) if err != nil { container.Unmount() return nil, err } if !stat.IsDir() { d, f := path.Split(basePath) basePath = d filter = []string{f} } else { filter = []string{path.Base(basePath)} basePath = path.Dir(basePath) } archive, err := archive.TarFilter(basePath, &archive.TarOptions{ Compression: archive.Uncompressed, Includes: filter, }) if err != nil { container.Unmount() return nil, err } return utils.NewReadCloserWrapper(archive, func() error { err := archive.Close() container.Unmount() return err }), nil }
func (b *buildFile) addContext(container *daemon.Container, orig, dest string, remote bool) error { var ( err error destExists = true origPath = path.Join(b.contextPath, orig) destPath = path.Join(container.RootfsPath(), dest) ) if destPath != container.RootfsPath() { destPath, err = symlink.FollowSymlinkInScope(destPath, container.RootfsPath()) if err != nil { return err } } // Preserve the trailing '/' if strings.HasSuffix(dest, "/") { destPath = destPath + "/" } destStat, err := os.Stat(destPath) if err != nil { if os.IsNotExist(err) { destExists = false } else { return err } } fi, err := os.Stat(origPath) if err != nil { if os.IsNotExist(err) { return fmt.Errorf("%s: no such file or directory", orig) } return err } fixPermsR := func(destPath string, uid, gid int) error { return filepath.Walk(destPath, func(path string, info os.FileInfo, err error) error { if err := os.Lchown(path, uid, gid); err != nil && !os.IsNotExist(err) { return err } return nil }) } if fi.IsDir() { if err := archive.CopyWithTar(origPath, destPath); err != nil { return err } if destExists { files, err := ioutil.ReadDir(origPath) if err != nil { return err } for _, file := range files { if err := fixPermsR(filepath.Join(destPath, file.Name()), 0, 0); err != nil { return err } } } else { if err := fixPermsR(destPath, 0, 0); err != nil { return err } } return nil } // First try to unpack the source as an archive // to support the untar feature we need to clean up the path a little bit // because tar is very forgiving. First we need to strip off the archive's // filename from the path but this is only added if it does not end in / . tarDest := destPath if strings.HasSuffix(tarDest, "/") { tarDest = filepath.Dir(destPath) } // If we are adding a remote file, do not try to untar it if !remote { // try to successfully untar the orig if err := archive.UntarPath(origPath, tarDest); err == nil { return nil } utils.Debugf("Couldn't untar %s to %s: %s", origPath, destPath, err) } // If that fails, just copy it as a regular file // but do not use all the magic path handling for the tar path if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil { return err } if err := archive.CopyWithTar(origPath, destPath); err != nil { return err } resPath := destPath if destExists && destStat.IsDir() { resPath = path.Join(destPath, path.Base(origPath)) } if err := fixPermsR(resPath, 0, 0); err != nil { return err } return nil }
func (container *Container) getRootResourcePath(path string) (string, error) { cleanPath := filepath.Join("/", path) return symlink.FollowSymlinkInScope(filepath.Join(container.root, cleanPath), container.root) }
func (b *buildFile) addContext(container *daemon.Container, orig, dest string, decompress bool) error { var ( err error destExists = true origPath = path.Join(b.contextPath, orig) destPath = path.Join(container.RootfsPath(), dest) ) if destPath != container.RootfsPath() { destPath, err = symlink.FollowSymlinkInScope(destPath, container.RootfsPath()) if err != nil { return err } } // Preserve the trailing '/' if strings.HasSuffix(dest, "/") || dest == "." { destPath = destPath + "/" } destStat, err := os.Stat(destPath) if err != nil { if !os.IsNotExist(err) { return err } destExists = false } fi, err := os.Stat(origPath) if err != nil { if os.IsNotExist(err) { return fmt.Errorf("%s: no such file or directory", orig) } return err } if fi.IsDir() { return copyAsDirectory(origPath, destPath, destExists) } // If we are adding a remote file (or we've been told not to decompress), do not try to untar it if decompress { // First try to unpack the source as an archive // to support the untar feature we need to clean up the path a little bit // because tar is very forgiving. First we need to strip off the archive's // filename from the path but this is only added if it does not end in / . tarDest := destPath if strings.HasSuffix(tarDest, "/") { tarDest = filepath.Dir(destPath) } // try to successfully untar the orig if err := archive.UntarPath(origPath, tarDest); err == nil { return nil } else if err != io.EOF { utils.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err) } } if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil { return err } if err := archive.CopyWithTar(origPath, destPath); err != nil { return err } resPath := destPath if destExists && destStat.IsDir() { resPath = path.Join(destPath, path.Base(origPath)) } return fixPermissions(resPath, 0, 0) }
func createVolumes(container *Container) error { binds, err := getBindMap(container) if err != nil { return err } volumesDriver := container.daemon.volumes.Driver() // Create the requested volumes if they don't exist for volPath := range container.Config.Volumes { volPath = filepath.Clean(volPath) volIsDir := true // Skip existing volumes if _, exists := container.Volumes[volPath]; exists { continue } var srcPath string var isBindMount bool srcRW := false // If an external bind is defined for this volume, use that as a source if bindMap, exists := binds[volPath]; exists { isBindMount = true srcPath = bindMap.SrcPath if !filepath.IsAbs(srcPath) { return fmt.Errorf("%s must be an absolute path", srcPath) } if strings.ToLower(bindMap.Mode) == "rw" { srcRW = true } if stat, err := os.Stat(bindMap.SrcPath); err != nil { return err } else { volIsDir = stat.IsDir() } // Otherwise create an directory in $ROOT/volumes/ and use that } else { // Do not pass a container as the parameter for the volume creation. // The graph driver using the container's information ( Image ) to // create the parent. c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil) if err != nil { return err } srcPath, err = volumesDriver.Get(c.ID, "") if err != nil { return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err) } srcRW = true // RW by default } if p, err := filepath.EvalSymlinks(srcPath); err != nil { return err } else { srcPath = p } // Create the mountpoint rootVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs) if err != nil { return err } newVolPath, err := filepath.Rel(container.basefs, rootVolPath) if err != nil { return err } newVolPath = "/" + newVolPath if volPath != newVolPath { delete(container.Volumes, volPath) delete(container.VolumesRW, volPath) } container.Volumes[newVolPath] = srcPath container.VolumesRW[newVolPath] = srcRW if err := createIfNotExists(rootVolPath, volIsDir); err != nil { return err } // Do not copy or change permissions if we are mounting from the host if srcRW && !isBindMount { volList, err := ioutil.ReadDir(rootVolPath) if err != nil { return err } if len(volList) > 0 { srcList, err := ioutil.ReadDir(srcPath) if err != nil { return err } if len(srcList) == 0 { // If the source volume is empty copy files from the root into the volume if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil { return err } } } var stat syscall.Stat_t if err := syscall.Stat(rootVolPath, &stat); err != nil { return err } var srcStat syscall.Stat_t if err := syscall.Stat(srcPath, &srcStat); err != nil { return err } // Change the source volume's ownership if it differs from the root // files that were just copied if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid { if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil { return err } } } } return nil }
func initializeVolume(container *Container, volPath string, binds map[string]BindMap) error { volumesDriver := container.daemon.volumes.Driver() volPath = filepath.Clean(volPath) // Skip existing volumes if _, exists := container.Volumes[volPath]; exists { return nil } var ( srcPath string isBindMount bool volIsDir = true srcRW = false ) // If an external bind is defined for this volume, use that as a source if bindMap, exists := binds[volPath]; exists { isBindMount = true srcPath = bindMap.SrcPath if !filepath.IsAbs(srcPath) { return fmt.Errorf("%s must be an absolute path", srcPath) } if strings.ToLower(bindMap.Mode) == "rw" { srcRW = true } if stat, err := os.Stat(bindMap.SrcPath); err != nil { return err } else { volIsDir = stat.IsDir() } // Otherwise create an directory in $ROOT/volumes/ and use that } else { // Do not pass a container as the parameter for the volume creation. // The graph driver using the container's information ( Image ) to // create the parent. c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil) if err != nil { return err } srcPath, err = volumesDriver.Get(c.ID, "") if err != nil { return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err) } srcRW = true // RW by default } if p, err := filepath.EvalSymlinks(srcPath); err != nil { return err } else { srcPath = p } // Create the mountpoint rootVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs) if err != nil { return err } newVolPath, err := filepath.Rel(container.basefs, rootVolPath) if err != nil { return err } newVolPath = "/" + newVolPath if volPath != newVolPath { delete(container.Volumes, volPath) delete(container.VolumesRW, volPath) } container.Volumes[newVolPath] = srcPath container.VolumesRW[newVolPath] = srcRW if err := createIfNotExists(rootVolPath, volIsDir); err != nil { return err } // Do not copy or change permissions if we are mounting from the host if srcRW && !isBindMount { if err := copyExistingContents(rootVolPath, srcPath); err != nil { return err } } return nil }