func readUserFile(c *container.Container, p string) (io.ReadCloser, error) { fp, err := symlink.FollowSymlinkInScope(filepath.Join(c.BaseFS, p), c.BaseFS) if err != nil { return nil, err } return os.Open(fp) }
// CopyImagePathContent copies files in destination to the volume. func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS) if err != nil { return err } if _, err = ioutil.ReadDir(rootfs); err != nil { if os.IsNotExist(err) { return nil } return err } id := stringid.GenerateNonCryptoID() path, err := v.Mount(id) if err != nil { return err } defer func() { if err := v.Unmount(id); err != nil { logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err) } }() return copyExistingContents(rootfs, path) }
func checkoutGit(fragment, root string) (string, error) { refAndDir := strings.SplitN(fragment, ":", 2) if len(refAndDir[0]) != 0 { if output, err := gitWithinDir(root, "checkout", refAndDir[0]); err != nil { return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output) } } if len(refAndDir) > 1 && len(refAndDir[1]) != 0 { newCtx, err := symlink.FollowSymlinkInScope(filepath.Join(root, refAndDir[1]), root) if err != nil { return "", fmt.Errorf("Error setting git context, %q not within git root: %s", refAndDir[1], err) } fi, err := os.Stat(newCtx) if err != nil { return "", err } if !fi.IsDir() { return "", fmt.Errorf("Error setting git context, not a directory: %s", newCtx) } root = newCtx } return root, nil }
// GetResourcePath evaluates `path` in the scope of the container's basefs, with proper path // sanitisation. Symlinks are all scoped to the basefs of the container, as // though the container's basefs was `/`. // // The basefs of a container is the host-facing path which is bind-mounted as // `/` inside the container. This method is essentially used to access a // particular path inside the container as though you were a process in that // container. // // NOTE: The returned path is *only* safely scoped inside the container's basefs // if no component of the returned path changes (such as a component // symlinking to a different path) between using this method and using the // path. See symlink.FollowSymlinkInScope for more details. func (container *Container) GetResourcePath(path string) (string, error) { // IMPORTANT - These are paths on the OS where the daemon is running, hence // any filepath operations must be done in an OS agnostic way. cleanPath := filepath.Join(string(os.PathSeparator), path) r, e := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, cleanPath), container.basefs) return r, e }
func (m *Mount) initialize() error { // No need to initialize anything since it's already been initialized if hostPath, exists := m.container.Volumes[m.MountToPath]; exists { // If this is a bind-mount/volumes-from, maybe it was passed in at start instead of create // We need to make sure bind-mounts/volumes-from passed on start can override existing ones. if (!m.volume.IsBindMount && !m.isBind) && m.from == nil { return nil } if m.volume.Path == hostPath { return nil } // Make sure we remove these old volumes we don't actually want now. // Ignore any errors here since this is just cleanup, maybe someone volumes-from'd this volume if v := m.container.daemon.volumes.Get(hostPath); v != nil { v.RemoveContainer(m.container.ID) m.container.daemon.volumes.Delete(v.Path) } } // This is the full path to container fs + mntToPath containerMntPath, err := symlink.FollowSymlinkInScope(filepath.Join(m.container.basefs, m.MountToPath), m.container.basefs) if err != nil { return err } m.container.VolumesRW[m.MountToPath] = m.Writable m.container.Volumes[m.MountToPath] = m.volume.Path m.volume.AddContainer(m.container.ID) if m.Writable && m.copyData { // Copy whatever is in the container at the mntToPath to the volume copyExistingContents(containerMntPath, m.volume.Path) } return nil }
func (c *tarSumContext) normalize(path string) (cleanpath, fullpath string, err error) { cleanpath = filepath.Clean(string(os.PathSeparator) + path)[1:] fullpath, err = symlink.FollowSymlinkInScope(filepath.Join(c.root, path), c.root) if err != nil { return "", "", fmt.Errorf("Forbidden path outside the build context: %s (%s)", path, fullpath) } _, err = os.Lstat(fullpath) if err != nil { return "", "", convertPathError(err, path) } return }
func (m *Mount) bindMount(rootfs, mountLabel string) error { var ( flags = syscall.MS_BIND | syscall.MS_REC dest = filepath.Join(rootfs, m.Destination) ) if !m.Writable { flags = flags | syscall.MS_RDONLY } if m.Slave { flags = flags | syscall.MS_SLAVE } stat, err := os.Stat(m.Source) if err != nil { return err } // FIXME: (crosbymichael) This does not belong here and should be done a layer above 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, mountLabel, m.Relabel); err != nil { return fmt.Errorf("relabeling %s to %s %s", m.Source, 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 }
// GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path // sanitisation. Symlinks are all scoped to the BaseFS of the container, as // though the container's BaseFS was `/`. // // The BaseFS of a container is the host-facing path which is bind-mounted as // `/` inside the container. This method is essentially used to access a // particular path inside the container as though you were a process in that // container. // // NOTE: The returned path is *only* safely scoped inside the container's BaseFS // if no component of the returned path changes (such as a component // symlinking to a different path) between using this method and using the // path. See symlink.FollowSymlinkInScope for more details. func (container *Container) GetResourcePath(path string) (string, error) { // IMPORTANT - These are paths on the OS where the daemon is running, hence // any filepath operations must be done in an OS agnostic way. cleanPath := cleanResourcePath(path) r, e := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, cleanPath), container.BaseFS) // Log this here on the daemon side as there's otherwise no indication apart // from the error being propagated all the way back to the client. This makes // debugging significantly easier and clearly indicates the error comes from the daemon. if e != nil { logrus.Errorf("Failed to FollowSymlinkInScope BaseFS %s cleanPath %s path %s %s\n", container.BaseFS, cleanPath, path, e) } return r, e }
func (v *Volume) initialize(container *Container) error { var err error v.VolPath = filepath.Clean(v.VolPath) // Do not initialize an existing volume if _, exists := container.Volumes[v.VolPath]; exists { return nil } // If it's not a bindmount we need to create the dir on the host if !v.isBindMount { v.HostPath, err = createVolumeHostPath(container) if err != nil { return err } } hostPath, err := filepath.EvalSymlinks(v.HostPath) if err != nil { return err } // Create the mountpoint // This is the path to the volume within the container FS // This differs from `hostPath` in that `hostPath` refers to the place where // the volume data is actually stored on the host fullVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, v.VolPath), container.basefs) if err != nil { return err } container.Volumes[v.VolPath] = hostPath container.VolumesRW[v.VolPath] = v.isRw() volIsDir, err := v.isDir() if err != nil { return err } if err := createIfNotExists(fullVolPath, volIsDir); err != nil { return err } // Do not copy or change permissions if we are mounting from the host if v.isRw() && !v.isBindMount { return copyExistingContents(fullVolPath, hostPath) } return nil }
// Reads a Dockerfile from the current context. It assumes that the // 'filename' is a relative path from the root of the context func (b *Builder) readDockerfile(origFile string) error { filename, err := symlink.FollowSymlinkInScope(filepath.Join(b.contextPath, origFile), b.contextPath) if err != nil { return fmt.Errorf("The Dockerfile (%s) must be within the build context", origFile) } fi, err := os.Lstat(filename) if os.IsNotExist(err) { return fmt.Errorf("Cannot locate specified Dockerfile: %s", origFile) } if fi.Size() == 0 { return ErrDockerfileEmpty } f, err := os.Open(filename) if err != nil { return err } b.dockerfile, err = parser.Parse(f) f.Close() if err != nil { return err } // After the Dockerfile has been parsed, we need to check the .dockerignore // file for either "Dockerfile" or ".dockerignore", and if either are // present then erase them from the build context. These files should never // have been sent from the client but we did send them to make sure that // we had the Dockerfile to actually parse, and then we also need the // .dockerignore file to know whether either file should be removed. // Note that this assumes the Dockerfile has been read into memory and // is now safe to be removed. excludes, _ := utils.ReadDockerIgnore(filepath.Join(b.contextPath, ".dockerignore")) if rm, _ := fileutils.Matches(".dockerignore", excludes); rm == true { os.Remove(filepath.Join(b.contextPath, ".dockerignore")) b.context.(tarsum.BuilderContext).Remove(".dockerignore") } if rm, _ := fileutils.Matches(b.dockerfileName, excludes); rm == true { os.Remove(filepath.Join(b.contextPath, b.dockerfileName)) b.context.(tarsum.BuilderContext).Remove(b.dockerfileName) } return nil }
func mountToRootfs(m *specs.Mount, rootfs, mountLabel string) error { // TODO: we don't use mountLabel here because it looks like mountLabel is // only significant when SELinux is enabled. var ( dest = m.Destination ) if !strings.HasPrefix(dest, rootfs) { dest = filepath.Join(rootfs, dest) } switch m.Type { case "proc", "sysfs", "mqueue", "tmpfs", "cgroup", "devpts": glog.V(3).Infof("Skip mount point %q of type %s", m.Destination, m.Type) return nil case "bind": stat, err := os.Stat(m.Source) if err != nil { // error out if the source of a bind mount does not exist as we will be // unable to bind anything to it. return err } // ensure that the destination of the bind mount is resolved of symlinks at mount time because // any previous mounts can invalidate the next mount's destination. // this can happen when a user specifies mounts within other mounts to cause breakouts or other // evil stuff to try to escape the container's rootfs. if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { return err } if err := checkMountDestination(rootfs, dest); err != nil { return err } // update the mount with the correct dest after symlinks are resolved. m.Destination = dest if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } if err := mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")); err != nil { return err } default: if err := os.MkdirAll(dest, 0755); err != nil { return err } return mount.Mount(m.Source, dest, m.Type, strings.Join(m.Options, ",")) } return nil }
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 }
// getDockerfileRelPath uses the given context directory for a `docker build` // and returns the absolute path to the context directory, the relative path of // the dockerfile in that context directory, and a non-nil error on success. func getDockerfileRelPath(givenContextDir, givenDockerfile string) (absContextDir, relDockerfile string, err error) { if absContextDir, err = filepath.Abs(givenContextDir); err != nil { return "", "", fmt.Errorf("unable to get absolute context directory: %v", err) } absDockerfile := givenDockerfile if absDockerfile == "" { // No -f/--file was specified so use the default relative to the // context directory. absDockerfile = filepath.Join(absContextDir, api.DefaultDockerfileName) // Just to be nice ;-) look for 'dockerfile' too but only // use it if we found it, otherwise ignore this check if _, err = os.Lstat(absDockerfile); os.IsNotExist(err) { altPath := filepath.Join(absContextDir, strings.ToLower(api.DefaultDockerfileName)) if _, err = os.Lstat(altPath); err == nil { absDockerfile = altPath } } } // If not already an absolute path, the Dockerfile path should be joined to // the base directory. if !filepath.IsAbs(absDockerfile) { absDockerfile = filepath.Join(absContextDir, absDockerfile) } // Verify that 'filename' is within the build context absDockerfile, err = symlink.FollowSymlinkInScope(absDockerfile, absContextDir) if err != nil { return "", "", fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", givenDockerfile, givenContextDir) } if _, err := os.Lstat(absDockerfile); err != nil { if os.IsNotExist(err) { return "", "", fmt.Errorf("Cannot locate Dockerfile: absDockerfile: %q", absDockerfile) } return "", "", fmt.Errorf("unable to stat Dockerfile: %v", err) } if relDockerfile, err = filepath.Rel(absContextDir, absDockerfile); err != nil { return "", "", fmt.Errorf("unable to get relative Dockerfile path: %v", err) } return absContextDir, relDockerfile, nil }
// CopyImagePathContent copies files in destination to the volume. func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error { rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS) if err != nil { return err } if _, err = ioutil.ReadDir(rootfs); err != nil { if os.IsNotExist(err) { return nil } return err } path, err := v.Mount() if err != nil { return err } defer v.Unmount() return copyExistingContents(rootfs, path) }
func (d *driver) setupMounts(container *configs.Config, c *execdriver.Command) error { userMounts := make(map[string]struct{}) for _, m := range c.Mounts { userMounts[m.Destination] = struct{}{} } // Filter out mounts that are overriden by user supplied mounts var defaultMounts []*configs.Mount _, mountDev := userMounts["/dev"] for _, m := range container.Mounts { if _, ok := userMounts[m.Destination]; !ok { if mountDev && strings.HasPrefix(m.Destination, "/dev/") { continue } defaultMounts = append(defaultMounts, m) } } container.Mounts = defaultMounts for _, m := range c.Mounts { dest, err := symlink.FollowSymlinkInScope(filepath.Join(c.Rootfs, m.Destination), c.Rootfs) if err != nil { return err } flags := syscall.MS_BIND | syscall.MS_REC if !m.Writable { flags |= syscall.MS_RDONLY } if m.Slave { flags |= syscall.MS_SLAVE } container.Mounts = append(container.Mounts, &configs.Mount{ Source: m.Source, Destination: dest, Device: "bind", Flags: flags, }) } return nil }
func (m *Mount) initialize() error { // No need to initialize anything since it's already been initialized if _, exists := m.container.Volumes[m.MountToPath]; exists { return nil } // This is the full path to container fs + mntToPath containerMntPath, err := symlink.FollowSymlinkInScope(filepath.Join(m.container.basefs, m.MountToPath), m.container.basefs) if err != nil { return err } m.container.VolumesRW[m.MountToPath] = m.Writable m.container.Volumes[m.MountToPath] = m.volume.Path m.volume.AddContainer(m.container.ID) if m.Writable && m.copyData { // Copy whatever is in the container at the mntToPath to the volume copyExistingContents(containerMntPath, m.volume.Path) } return nil }
func (m *Mount) tmpfsMount(rootfs, mountLabel string) error { var ( err error l = label.FormatMountLabel("", mountLabel) dest = filepath.Join(rootfs, m.Destination) ) // FIXME: (crosbymichael) This does not belong here and should be done a layer above if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil { return err } if err := createIfNotExists(dest, true); err != nil { return fmt.Errorf("creating new tmpfs mount target %s", err) } if err := syscall.Mount("tmpfs", dest, "tmpfs", uintptr(defaultMountFlags), l); err != nil { return fmt.Errorf("%s mounting %s in tmpfs", err, dest) } 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 mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { var ( dest = m.Destination data = label.FormatMountLabel(m.Data, mountLabel) ) if !strings.HasPrefix(dest, rootfs) { dest = filepath.Join(rootfs, dest) } switch m.Device { case "proc", "sysfs": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "") case "mqueue": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), ""); err != nil { return err } return label.SetFileLabel(dest, mountLabel) case "tmpfs": stat, err := os.Stat(dest) if err != nil { if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { return err } if stat != nil { if err = os.Chmod(dest, stat.Mode()); err != nil { return err } } return nil case "devpts": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data) case "bind": stat, err := os.Stat(m.Source) if err != nil { // error out if the source of a bind mount does not exist as we will be // unable to bind anything to it. return err } // ensure that the destination of the bind mount is resolved of symlinks at mount time because // any previous mounts can invalidate the next mount's destination. // this can happen when a user specifies mounts within other mounts to cause breakouts or other // evil stuff to try to escape the container's rootfs. if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { return err } if err := checkMountDestination(rootfs, dest); err != nil { return err } if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { return err } if m.Flags&syscall.MS_RDONLY != 0 { if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { return err } } if m.Relabel != "" { if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil { return err } } if m.Flags&syscall.MS_PRIVATE != 0 { if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { return err } } case "cgroup": mounts, err := cgroups.GetCgroupMounts() if err != nil { return err } var binds []*configs.Mount for _, mm := range mounts { dir, err := mm.GetThisCgroupDir() if err != nil { return err } binds = append(binds, &configs.Mount{ Device: "bind", Source: filepath.Join(mm.Mountpoint, dir), Destination: filepath.Join(m.Destination, strings.Join(mm.Subsystems, ",")), Flags: syscall.MS_BIND | syscall.MS_REC | syscall.MS_RDONLY, }) } tmpfs := &configs.Mount{ Device: "tmpfs", Destination: m.Destination, Flags: syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV, } if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil { return err } for _, b := range binds { if err := mountToRootfs(b, rootfs, mountLabel); err != nil { return err } } default: return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination) } return nil }
func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { var ( dest = m.Destination data = label.FormatMountLabel(m.Data, mountLabel) ) if !strings.HasPrefix(dest, rootfs) { dest = filepath.Join(rootfs, dest) } switch m.Device { case "proc", "sysfs": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "") case "mqueue": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), ""); err != nil { return err } return label.SetFileLabel(dest, mountLabel) case "tmpfs": stat, err := os.Stat(dest) if err != nil { if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { return err } if stat != nil { if err = os.Chmod(dest, stat.Mode()); err != nil { return err } } return nil case "devpts": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data) case "bind": stat, err := os.Stat(m.Source) if err != nil { // error out if the source of a bind mount does not exist as we will be // unable to bind anything to it. return err } // ensure that the destination of the bind mount is resolved of symlinks at mount time because // any previous mounts can invalidate the next mount's destination. // this can happen when a user specifies mounts within other mounts to cause breakouts or other // evil stuff to try to escape the container's rootfs. if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { return err } if err := checkMountDestination(rootfs, dest); err != nil { return err } if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { return err } if m.Flags&syscall.MS_RDONLY != 0 { if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { return err } } if m.Relabel != "" { if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil { return err } } if m.Flags&syscall.MS_PRIVATE != 0 { if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { return err } } case "cgroup": binds, err := getCgroupMounts(m) if err != nil { return err } var merged []string for _, b := range binds { ss := filepath.Base(b.Destination) if strings.Contains(ss, ",") { merged = append(merged, ss) } } tmpfs := &configs.Mount{ Source: "tmpfs", Device: "tmpfs", Destination: m.Destination, Flags: defaultMountFlags, } if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil { return err } for _, b := range binds { if err := mountToRootfs(b, rootfs, mountLabel); err != nil { return err } } // create symlinks for merged cgroups cwd, err := os.Getwd() if err != nil { return err } if err := os.Chdir(filepath.Join(rootfs, m.Destination)); err != nil { return err } for _, mc := range merged { for _, ss := range strings.Split(mc, ",") { if err := os.Symlink(mc, ss); err != nil { // if cgroup already exists, then okay(it could have been created before) if os.IsExist(err) { continue } os.Chdir(cwd) return err } } } if err := os.Chdir(cwd); err != nil { return err } default: return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination) } return nil }
// Reads a Dockerfile from the current context. It assumes that the // 'filename' is a relative path from the root of the context func (b *builder) readDockerfile() error { // If no -f was specified then look for 'Dockerfile'. If we can't find // that then look for 'dockerfile'. If neither are found then default // back to 'Dockerfile' and use that in the error message. if b.dockerfileName == "" { b.dockerfileName = api.DefaultDockerfileName tmpFN := filepath.Join(b.contextPath, api.DefaultDockerfileName) if _, err := os.Lstat(tmpFN); err != nil { tmpFN = filepath.Join(b.contextPath, strings.ToLower(api.DefaultDockerfileName)) if _, err := os.Lstat(tmpFN); err == nil { b.dockerfileName = strings.ToLower(api.DefaultDockerfileName) } } } origFile := b.dockerfileName filename, err := symlink.FollowSymlinkInScope(filepath.Join(b.contextPath, origFile), b.contextPath) if err != nil { return fmt.Errorf("The Dockerfile (%s) must be within the build context", origFile) } fi, err := os.Lstat(filename) if os.IsNotExist(err) { return fmt.Errorf("Cannot locate specified Dockerfile: %s", origFile) } if fi.Size() == 0 { return fmt.Errorf("The Dockerfile (%s) cannot be empty", origFile) } f, err := os.Open(filename) if err != nil { return err } b.dockerfile, err = parser.Parse(f) f.Close() if err != nil { return err } // After the Dockerfile has been parsed, we need to check the .dockerignore // file for either "Dockerfile" or ".dockerignore", and if either are // present then erase them from the build context. These files should never // have been sent from the client but we did send them to make sure that // we had the Dockerfile to actually parse, and then we also need the // .dockerignore file to know whether either file should be removed. // Note that this assumes the Dockerfile has been read into memory and // is now safe to be removed. excludes, _ := utils.ReadDockerIgnore(filepath.Join(b.contextPath, ".dockerignore")) if rm, _ := fileutils.Matches(".dockerignore", excludes); rm == true { os.Remove(filepath.Join(b.contextPath, ".dockerignore")) b.context.(tarsum.BuilderContext).Remove(".dockerignore") } if rm, _ := fileutils.Matches(b.dockerfileName, excludes); rm == true { os.Remove(filepath.Join(b.contextPath, b.dockerfileName)) b.context.(tarsum.BuilderContext).Remove(b.dockerfileName) } return nil }
func (container *Container) createVolumes() error { mounts := make(map[string]*volumeMount) // get the normal volumes for path := range container.Config.Volumes { path = filepath.Clean(path) // skip if there is already a volume for this container path if _, exists := container.Volumes[path]; exists { continue } realPath, err := container.GetResourcePath(path) if err != nil { return err } if stat, err := os.Stat(realPath); err == nil { if !stat.IsDir() { return fmt.Errorf("can't mount to container path, file exists - %s", path) } } mnt := &volumeMount{ containerPath: path, writable: true, copyData: true, } mounts[mnt.containerPath] = mnt } // Get all the bind mounts // track bind paths separately due to #10618 bindPaths := make(map[string]struct{}) for _, spec := range container.hostConfig.Binds { mnt, err := parseBindMountSpec(spec) if err != nil { return err } // #10618 if _, exists := bindPaths[mnt.containerPath]; exists { return fmt.Errorf("Duplicate volume mount %s", mnt.containerPath) } bindPaths[mnt.containerPath] = struct{}{} mounts[mnt.containerPath] = mnt } // Get volumes from for _, from := range container.hostConfig.VolumesFrom { cID, mode, err := parseVolumesFromSpec(from) if err != nil { return err } if _, exists := container.AppliedVolumesFrom[cID]; exists { // skip since it's already been applied continue } c, err := container.daemon.Get(cID) if err != nil { return fmt.Errorf("container %s not found, impossible to mount its volumes", cID) } for _, mnt := range c.volumeMounts() { mnt.writable = mnt.writable && (mode == "rw") mnt.from = cID mounts[mnt.containerPath] = mnt } } for _, mnt := range mounts { containerMntPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, mnt.containerPath), container.basefs) if err != nil { return err } // Create the actual volume v, err := container.daemon.volumes.FindOrCreateVolume(mnt.hostPath, mnt.writable) if err != nil { return err } container.VolumesRW[mnt.containerPath] = mnt.writable container.Volumes[mnt.containerPath] = v.Path v.AddContainer(container.ID) if mnt.from != "" { container.AppliedVolumesFrom[mnt.from] = struct{}{} } if mnt.writable && mnt.copyData { // Copy whatever is in the container at the containerPath to the volume copyExistingContents(containerMntPath, v.Path) } } return nil }
func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { var ( dest = m.Destination ) if !strings.HasPrefix(dest, rootfs) { dest = filepath.Join(rootfs, dest) } switch m.Device { case "proc", "sysfs": if err := os.MkdirAll(dest, 0755); err != nil { return err } // Selinux kernels do not support labeling of /proc or /sys return mountPropagate(m, rootfs, "") case "mqueue": if err := os.MkdirAll(dest, 0755); err != nil { return err } if err := mountPropagate(m, rootfs, mountLabel); err != nil { // older kernels do not support labeling of /dev/mqueue if err := mountPropagate(m, rootfs, ""); err != nil { return err } return label.SetFileLabel(dest, mountLabel) } return nil case "tmpfs": copyUp := m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP tmpDir := "" stat, err := os.Stat(dest) if err != nil { if err := os.MkdirAll(dest, 0755); err != nil { return err } } if copyUp { tmpDir, err = ioutil.TempDir("/tmp", "runctmpdir") if err != nil { return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir") } defer os.RemoveAll(tmpDir) m.Destination = tmpDir } if err := mountPropagate(m, rootfs, mountLabel); err != nil { return err } if copyUp { if err := fileutils.CopyDirectory(dest, tmpDir); err != nil { errMsg := fmt.Errorf("tmpcopyup: failed to copy %s to %s: %v", dest, tmpDir, err) if err1 := syscall.Unmount(tmpDir, syscall.MNT_DETACH); err1 != nil { return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) } return errMsg } if err := syscall.Mount(tmpDir, dest, "", syscall.MS_MOVE, ""); err != nil { errMsg := fmt.Errorf("tmpcopyup: failed to move mount %s to %s: %v", tmpDir, dest, err) if err1 := syscall.Unmount(tmpDir, syscall.MNT_DETACH); err1 != nil { return newSystemErrorWithCausef(err1, "tmpcopyup: %v: failed to unmount", errMsg) } return errMsg } } if stat != nil { if err = os.Chmod(dest, stat.Mode()); err != nil { return err } } return nil case "bind": stat, err := os.Stat(m.Source) if err != nil { // error out if the source of a bind mount does not exist as we will be // unable to bind anything to it. return err } // ensure that the destination of the bind mount is resolved of symlinks at mount time because // any previous mounts can invalidate the next mount's destination. // this can happen when a user specifies mounts within other mounts to cause breakouts or other // evil stuff to try to escape the container's rootfs. if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { return err } if err := checkMountDestination(rootfs, dest); err != nil { return err } // update the mount with the correct dest after symlinks are resolved. m.Destination = dest if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } if err := mountPropagate(m, rootfs, mountLabel); err != nil { return err } // bind mount won't change mount options, we need remount to make mount options effective. // first check that we have non-default options required before attempting a remount if m.Flags&^(syscall.MS_REC|syscall.MS_REMOUNT|syscall.MS_BIND) != 0 { // only remount if unique mount options are set if err := remount(m, rootfs); err != nil { return err } } if m.Relabel != "" { if err := label.Validate(m.Relabel); err != nil { return err } shared := label.IsShared(m.Relabel) if err := label.Relabel(m.Source, mountLabel, shared); err != nil { return err } } case "cgroup": binds, err := getCgroupMounts(m) if err != nil { return err } var merged []string for _, b := range binds { ss := filepath.Base(b.Destination) if strings.Contains(ss, ",") { merged = append(merged, ss) } } tmpfs := &configs.Mount{ Source: "tmpfs", Device: "tmpfs", Destination: m.Destination, Flags: defaultMountFlags, Data: "mode=755", PropagationFlags: m.PropagationFlags, } if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil { return err } for _, b := range binds { if err := mountToRootfs(b, rootfs, mountLabel); err != nil { return err } } for _, mc := range merged { for _, ss := range strings.Split(mc, ",") { // symlink(2) is very dumb, it will just shove the path into // the link and doesn't do any checks or relative path // conversion. Also, don't error out if the cgroup already exists. if err := os.Symlink(mc, filepath.Join(rootfs, m.Destination, ss)); err != nil && !os.IsExist(err) { return err } } } if m.Flags&syscall.MS_RDONLY != 0 { // remount cgroup root as readonly mcgrouproot := &configs.Mount{ Source: m.Destination, Device: "bind", Destination: m.Destination, Flags: defaultMountFlags | syscall.MS_RDONLY | syscall.MS_BIND, } if err := remount(mcgrouproot, rootfs); err != nil { return err } } default: if err := os.MkdirAll(dest, 0755); err != nil { return err } return mountPropagate(m, rootfs, mountLabel) } return nil }
func (b *Builder) 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 := chrootarchive.UntarPath(origPath, tarDest); err == nil { return nil } else if err != io.EOF { log.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 := chrootarchive.CopyWithTar(origPath, destPath); err != nil { return err } resPath := destPath if destExists && destStat.IsDir() { resPath = path.Join(destPath, path.Base(origPath)) } return fixPermissions(origPath, resPath, 0, 0, destExists) }
func (v *Volume) getResourcePath(path string) (string, error) { cleanPath := filepath.Join("/", path) return symlink.FollowSymlinkInScope(filepath.Join(v.Path, cleanPath), v.Path) }
func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { var ( dest = m.Destination ) if !strings.HasPrefix(dest, rootfs) { dest = filepath.Join(rootfs, dest) } switch m.Device { case "proc", "sysfs": if err := os.MkdirAll(dest, 0755); err != nil { return err } // Selinux kernels do not support labeling of /proc or /sys return mountPropagate(m, rootfs, "") case "mqueue": if err := os.MkdirAll(dest, 0755); err != nil { return err } if err := mountPropagate(m, rootfs, mountLabel); err != nil { // older kernels do not support labeling of /dev/mqueue if err := mountPropagate(m, rootfs, ""); err != nil { return err } } return label.SetFileLabel(dest, mountLabel) case "tmpfs": stat, err := os.Stat(dest) if err != nil { if err := os.MkdirAll(dest, 0755); err != nil { return err } } if err := mountPropagate(m, rootfs, mountLabel); err != nil { return err } if stat != nil { if err = os.Chmod(dest, stat.Mode()); err != nil { return err } } return nil case "devpts": if err := os.MkdirAll(dest, 0755); err != nil { return err } return mountPropagate(m, rootfs, mountLabel) case "securityfs": if err := os.MkdirAll(dest, 0755); err != nil { return err } return mountPropagate(m, rootfs, mountLabel) case "bind": stat, err := os.Stat(m.Source) if err != nil { // error out if the source of a bind mount does not exist as we will be // unable to bind anything to it. return err } // ensure that the destination of the bind mount is resolved of symlinks at mount time because // any previous mounts can invalidate the next mount's destination. // this can happen when a user specifies mounts within other mounts to cause breakouts or other // evil stuff to try to escape the container's rootfs. if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { return err } if err := checkMountDestination(rootfs, dest); err != nil { return err } // update the mount with the correct dest after symlinks are resolved. m.Destination = dest if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } if err := mountPropagate(m, rootfs, mountLabel); err != nil { return err } // bind mount won't change mount options, we need remount to make mount options effective. // first check that we have non-default options required before attempting a remount if m.Flags&^(syscall.MS_REC|syscall.MS_REMOUNT|syscall.MS_BIND) != 0 { // only remount if unique mount options are set if err := remount(m, rootfs); err != nil { return err } } if m.Relabel != "" { if err := label.Validate(m.Relabel); err != nil { return err } shared := label.IsShared(m.Relabel) if err := label.Relabel(m.Source, mountLabel, shared); err != nil { return err } } case "cgroup": binds, err := getCgroupMounts(m) if err != nil { return err } var merged []string for _, b := range binds { ss := filepath.Base(b.Destination) if strings.Contains(ss, ",") { merged = append(merged, ss) } } tmpfs := &configs.Mount{ Source: "tmpfs", Device: "tmpfs", Destination: m.Destination, Flags: defaultMountFlags, Data: "mode=755", PropagationFlags: m.PropagationFlags, } if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil { return err } for _, b := range binds { if err := mountToRootfs(b, rootfs, mountLabel); err != nil { return err } } // create symlinks for merged cgroups cwd, err := os.Getwd() if err != nil { return err } if err := os.Chdir(filepath.Join(rootfs, m.Destination)); err != nil { return err } for _, mc := range merged { for _, ss := range strings.Split(mc, ",") { if err := os.Symlink(mc, ss); err != nil { // if cgroup already exists, then okay(it could have been created before) if os.IsExist(err) { continue } os.Chdir(cwd) return err } } } if err := os.Chdir(cwd); err != nil { return err } if m.Flags&syscall.MS_RDONLY != 0 { // remount cgroup root as readonly mcgrouproot := &configs.Mount{ Destination: m.Destination, Flags: defaultMountFlags | syscall.MS_RDONLY, } if err := remount(mcgrouproot, rootfs); err != nil { return err } } default: return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination) } return nil }
// hyper build [OPTIONS] PATH func (cli *HyperClient) HyperCmdBuild(args ...string) error { var opts struct { ImageName string `long:"tag" short:"t" default:"" value-name:"\"\"" default-mask:"-" description:"Repository name (and optionally a tag) to be applied to the resulting image in case of success"` DockerfileName string `long:"file" short:"f" default:"" value-name:"\"\"" default-mask:"-" description:"Customized docker file"` } var parser = gflag.NewParser(&opts, gflag.Default) parser.Usage = "build [OPTIONS] PATH\n\nBuild a new image from the source code at PATH" args, err := parser.ParseArgs(args) if err != nil { if !strings.Contains(err.Error(), "Usage") { return err } else { return nil } } if len(args) == 0 { return fmt.Errorf("%s: \"build\" requires a minimum of 1 argument, See 'hyper build --help'.", os.Args[0]) } var ( filename = "" context archive.Archive name = "" ) root := args[0] if _, err := os.Stat(root); err != nil { return err } absRoot, err := filepath.Abs(root) if err != nil { return err } filename = opts.DockerfileName // path to Dockerfile if opts.DockerfileName == "" { // No -f/--file was specified so use the default opts.DockerfileName = api.DefaultDockerfileName filename = filepath.Join(absRoot, opts.DockerfileName) // Just to be nice ;-) look for 'dockerfile' too but only // use it if we found it, otherwise ignore this check if _, err = os.Lstat(filename); os.IsNotExist(err) { tmpFN := path.Join(absRoot, strings.ToLower(opts.DockerfileName)) if _, err = os.Lstat(tmpFN); err == nil { opts.DockerfileName = strings.ToLower(opts.DockerfileName) filename = tmpFN } } } origDockerfile := opts.DockerfileName // used for error msg if filename, err = filepath.Abs(filename); err != nil { return err } // Verify that 'filename' is within the build context filename, err = symlink.FollowSymlinkInScope(filename, absRoot) if err != nil { return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root) } // Now reset the dockerfileName to be relative to the build context opts.DockerfileName, err = filepath.Rel(absRoot, filename) if err != nil { return err } // And canonicalize dockerfile name to a platform-independent one opts.DockerfileName, err = archive.CanonicalTarNameForPath(opts.DockerfileName) if err != nil { return fmt.Errorf("Cannot canonicalize dockerfile path %s: %v", opts.DockerfileName, err) } if _, err = os.Lstat(filename); os.IsNotExist(err) { return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile) } var includes = []string{"."} f, err := os.Open(filepath.Join(root, ".dockerignore")) if err != nil && !os.IsNotExist(err) { return err } defer f.Close() var excludes []string if err == nil { excludes, err = utils.ReadDockerIgnore(f) if err != nil { return err } } if err := utils.ValidateContextDirectory(root, excludes); err != nil { return fmt.Errorf("Error checking context: '%s'.", err) } // If .dockerignore mentions .dockerignore or the Dockerfile // then make sure we send both files over to the daemon // because Dockerfile is, obviously, needed no matter what, and // .dockerignore is needed to know if either one needs to be // removed. The deamon will remove them for us, if needed, after it // parses the Dockerfile. keepThem1, _ := fileutils.Matches(".dockerignore", excludes) keepThem2, _ := fileutils.Matches(opts.DockerfileName, excludes) if keepThem1 || keepThem2 { includes = append(includes, ".dockerignore", opts.DockerfileName) } if err := utils.ValidateContextDirectory(root, excludes); err != nil { return fmt.Errorf("Error checking context: '%s'.", err) } options := &archive.TarOptions{ Compression: archive.Uncompressed, ExcludePatterns: excludes, IncludeFiles: includes, } context, err = archive.TarWithOptions(root, options) if err != nil { return err } var body io.Reader // Setup an upload progress bar // FIXME: ProgressReader shouldn't be this annoying to use if context != nil { sf := streamformatter.NewStreamFormatter() body = progressreader.New(progressreader.Config{ In: context, Out: os.Stdout, Formatter: sf, NewLines: true, ID: "", Action: "Sending build context to Docker daemon", }) } if opts.ImageName == "" { // set a image name name = rand.RandStr(10, "alphanum") } else { name = opts.ImageName repository, tag := parsers.ParseRepositoryTag(name) if err := registry.ValidateRepositoryName(repository); err != nil { return err } if len(tag) > 0 { if err := tags.ValidateTagName(tag); err != nil { return err } } } v := url.Values{} v.Set("name", name) headers := http.Header(make(map[string][]string)) if context != nil { headers.Set("Content-Type", "application/tar") } err = cli.stream("POST", "/image/build?"+v.Encode(), body, cli.out, headers) if err != nil { return err } return nil }
func safePath(base, path string) (string, error) { return symlink.FollowSymlinkInScope(filepath.Join(base, path), base) }
func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { var ( dest = m.Destination data = label.FormatMountLabel(m.Data, mountLabel) ) if !strings.HasPrefix(dest, rootfs) { dest = filepath.Join(rootfs, dest) } switch m.Device { case "proc", "mqueue", "sysfs": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "") case "tmpfs": stat, err := os.Stat(dest) if err != nil { if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { return err } if stat != nil { if err = os.Chmod(dest, stat.Mode()); err != nil { return err } } return nil case "devpts": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data) case "bind": stat, err := os.Stat(m.Source) if err != nil { // error out if the source of a bind mount does not exist as we will be // unable to bind anything to it. return err } // ensure that the destination of the bind mount is resolved of symlinks at mount time because // any previous mounts can invalidate the next mount's destination. // this can happen when a user specifies mounts within other mounts to cause breakouts or other // evil stuff to try to escape the container's rootfs. if dest, err = symlink.FollowSymlinkInScope(filepath.Join(rootfs, m.Destination), rootfs); err != nil { return err } if err := checkMountDestination(rootfs, dest); err != nil { return err } if err := createIfNotExists(dest, stat.IsDir()); err != nil { return err } if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), data); err != nil { return err } if m.Flags&syscall.MS_RDONLY != 0 { if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags|syscall.MS_REMOUNT), ""); err != nil { return err } } if m.Relabel != "" { if err := label.Relabel(m.Source, mountLabel, m.Relabel); err != nil { return err } } if m.Flags&syscall.MS_PRIVATE != 0 { if err := syscall.Mount("", dest, "none", uintptr(syscall.MS_PRIVATE), ""); err != nil { return err } } default: return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination) } return nil }
// CmdBuild builds a new image from the source code at a given path. // // If '-' is provided instead of a path or URL, Docker will build an image from either a Dockerfile or tar archive read from STDIN. // // Usage: docker build [OPTIONS] PATH | URL | - func (cli *DockerCli) CmdBuild(args ...string) error { cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH", true) tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image") suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers") noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image") rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build") forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers") pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image") dockerfileName := cmd.String([]string{"f", "-file"}, "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')") flMemoryString := cmd.String([]string{"m", "-memory"}, "", "Memory limit") flMemorySwap := cmd.String([]string{"-memory-swap"}, "", "Total memory (memory + swap), '-1' to disable swap") flCPUShares := cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)") flCpuQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota") flCPUSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)") flCPUSetMems := cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)") flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) var ( context archive.Archive isRemote bool err error ) _, err = exec.LookPath("git") hasGit := err == nil if cmd.Arg(0) == "-" { // As a special case, 'docker build -' will build from either an empty context with the // contents of stdin as a Dockerfile, or a tar-ed context from stdin. buf := bufio.NewReader(cli.in) magic, err := buf.Peek(tarHeaderSize) if err != nil && err != io.EOF { return fmt.Errorf("failed to peek context header from STDIN: %v", err) } if !archive.IsArchive(magic) { dockerfile, err := ioutil.ReadAll(buf) if err != nil { return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err) } // -f option has no meaning when we're reading it from stdin, // so just use our default Dockerfile name *dockerfileName = api.DefaultDockerfileName context, err = archive.Generate(*dockerfileName, string(dockerfile)) } else { context = ioutil.NopCloser(buf) } } else if urlutil.IsURL(cmd.Arg(0)) && (!urlutil.IsGitURL(cmd.Arg(0)) || !hasGit) { isRemote = true } else { root := cmd.Arg(0) if urlutil.IsGitURL(root) { root, err = utils.GitClone(root) if err != nil { return err } defer os.RemoveAll(root) } if _, err := os.Stat(root); err != nil { return err } absRoot, err := filepath.Abs(root) if err != nil { return err } filename := *dockerfileName // path to Dockerfile if *dockerfileName == "" { // No -f/--file was specified so use the default *dockerfileName = api.DefaultDockerfileName filename = filepath.Join(absRoot, *dockerfileName) // Just to be nice ;-) look for 'dockerfile' too but only // use it if we found it, otherwise ignore this check if _, err = os.Lstat(filename); os.IsNotExist(err) { tmpFN := path.Join(absRoot, strings.ToLower(*dockerfileName)) if _, err = os.Lstat(tmpFN); err == nil { *dockerfileName = strings.ToLower(*dockerfileName) filename = tmpFN } } } origDockerfile := *dockerfileName // used for error msg if filename, err = filepath.Abs(filename); err != nil { return err } // Verify that 'filename' is within the build context filename, err = symlink.FollowSymlinkInScope(filename, absRoot) if err != nil { return fmt.Errorf("The Dockerfile (%s) must be within the build context (%s)", origDockerfile, root) } // Now reset the dockerfileName to be relative to the build context *dockerfileName, err = filepath.Rel(absRoot, filename) if err != nil { return err } // And canonicalize dockerfile name to a platform-independent one *dockerfileName, err = archive.CanonicalTarNameForPath(*dockerfileName) if err != nil { return fmt.Errorf("Cannot canonicalize dockerfile path %s: %v", *dockerfileName, err) } if _, err = os.Lstat(filename); os.IsNotExist(err) { return fmt.Errorf("Cannot locate Dockerfile: %s", origDockerfile) } var includes = []string{"."} excludes, err := utils.ReadDockerIgnore(path.Join(root, ".dockerignore")) if err != nil { return err } // If .dockerignore mentions .dockerignore or the Dockerfile // then make sure we send both files over to the daemon // because Dockerfile is, obviously, needed no matter what, and // .dockerignore is needed to know if either one needs to be // removed. The deamon will remove them for us, if needed, after it // parses the Dockerfile. keepThem1, _ := fileutils.Matches(".dockerignore", excludes) keepThem2, _ := fileutils.Matches(*dockerfileName, excludes) if keepThem1 || keepThem2 { includes = append(includes, ".dockerignore", *dockerfileName) } if err := utils.ValidateContextDirectory(root, excludes); err != nil { return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err) } options := &archive.TarOptions{ Compression: archive.Uncompressed, ExcludePatterns: excludes, IncludeFiles: includes, } context, err = archive.TarWithOptions(root, options) if err != nil { return err } } // windows: show error message about modified file permissions // FIXME: this is not a valid warning when the daemon is running windows. should be removed once docker engine for windows can build. if runtime.GOOS == "windows" { logrus.Warn(`SECURITY WARNING: You are building a Docker image from Windows against a Linux Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`) } var body io.Reader // Setup an upload progress bar // FIXME: ProgressReader shouldn't be this annoying to use if context != nil { sf := streamformatter.NewStreamFormatter(false) body = progressreader.New(progressreader.Config{ In: context, Out: cli.out, Formatter: sf, NewLines: true, ID: "", Action: "Sending build context to Docker daemon", }) } var memory int64 if *flMemoryString != "" { parsedMemory, err := units.RAMInBytes(*flMemoryString) if err != nil { return err } memory = parsedMemory } var memorySwap int64 if *flMemorySwap != "" { if *flMemorySwap == "-1" { memorySwap = -1 } else { parsedMemorySwap, err := units.RAMInBytes(*flMemorySwap) if err != nil { return err } memorySwap = parsedMemorySwap } } // Send the build context v := &url.Values{} //Check if the given image name can be resolved if *tag != "" { repository, tag := parsers.ParseRepositoryTag(*tag) if err := registry.ValidateRepositoryName(repository); err != nil { return err } if len(tag) > 0 { if err := graph.ValidateTagName(tag); err != nil { return err } } } v.Set("t", *tag) if *suppressOutput { v.Set("q", "1") } if isRemote { v.Set("remote", cmd.Arg(0)) } if *noCache { v.Set("nocache", "1") } if *rm { v.Set("rm", "1") } else { v.Set("rm", "0") } if *forceRm { v.Set("forcerm", "1") } if *pull { v.Set("pull", "1") } v.Set("cpusetcpus", *flCPUSetCpus) v.Set("cpusetmems", *flCPUSetMems) v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10)) v.Set("cpuquota", strconv.FormatInt(*flCpuQuota, 10)) v.Set("memory", strconv.FormatInt(memory, 10)) v.Set("memswap", strconv.FormatInt(memorySwap, 10)) v.Set("cgroupparent", *flCgroupParent) v.Set("dockerfile", *dockerfileName) headers := http.Header(make(map[string][]string)) buf, err := json.Marshal(cli.configFile.AuthConfigs) if err != nil { return err } headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) if context != nil { headers.Set("Content-Type", "application/tar") } sopts := &streamOpts{ rawTerminal: true, in: body, out: cli.out, headers: headers, } err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), sopts) if jerr, ok := err.(*jsonmessage.JSONError); ok { // If no error code is set, default to 1 if jerr.Code == 0 { jerr.Code = 1 } return StatusError{Status: jerr.Message, StatusCode: jerr.Code} } return err }