Ensure that ownership and perms are copied to volume

This only works if the file or dir is already created in
the image before setting it to be a volume.  There is no way around this
because we don't have the data avaliable to set the volume at the
beginning of the dockerfile
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
Michael Crosby 2014-06-02 14:16:30 -07:00
parent f65fadbda0
commit f41ced96af
1 changed files with 59 additions and 53 deletions

View File

@ -97,13 +97,16 @@ func applyVolumesFrom(container *Container) error {
if _, exists := container.Volumes[volPath]; exists {
continue
}
stat, err := os.Stat(c.getResourcePath(volPath))
if err != nil {
return err
}
if err := createIfNotExists(container.getResourcePath(volPath), stat.IsDir()); err != nil {
return err
}
container.Volumes[volPath] = id
if isRW, exists := c.VolumesRW[volPath]; exists {
container.VolumesRW[volPath] = isRW && mountRW
@ -180,38 +183,39 @@ func createVolumes(container *Container) error {
return nil
}
func createIfNotExists(path string, isDir bool) error {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
if isDir {
if err := os.MkdirAll(path, 0755); err != nil {
return err
}
} else {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
f, err := os.OpenFile(path, os.O_CREATE, 0755)
if err != nil {
return err
}
defer f.Close()
func createIfNotExists(destination string, isDir bool) error {
if _, err := os.Stat(destination); err != nil && os.IsNotExist(err) {
if isDir {
if err := os.MkdirAll(destination, 0755); err != nil {
return err
}
} else {
if err := os.MkdirAll(filepath.Dir(destination), 0755); err != nil {
return err
}
f, err := os.OpenFile(destination, os.O_CREATE, 0755)
if err != nil {
return err
}
f.Close()
}
}
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
destination string
isBindMount bool
volIsDir = true
@ -221,19 +225,21 @@ func initializeVolume(container *Container, volPath string, binds map[string]Bin
// 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)
destination = bindMap.SrcPath
if !filepath.IsAbs(destination) {
return fmt.Errorf("%s must be an absolute path", destination)
}
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
@ -242,26 +248,28 @@ func initializeVolume(container *Container, volPath string, binds map[string]Bin
if err != nil {
return err
}
srcPath, err = volumesDriver.Get(c.ID, "")
destination, 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
srcRW = true
}
if p, err := filepath.EvalSymlinks(srcPath); err != nil {
if p, err := filepath.EvalSymlinks(destination); err != nil {
return err
} else {
srcPath = p
destination = p
}
// Create the mountpoint
rootVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs)
source, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs)
if err != nil {
return err
}
newVolPath, err := filepath.Rel(container.basefs, rootVolPath)
newVolPath, err := filepath.Rel(container.basefs, source)
if err != nil {
return err
}
@ -272,59 +280,57 @@ func initializeVolume(container *Container, volPath string, binds map[string]Bin
delete(container.VolumesRW, volPath)
}
container.Volumes[newVolPath] = srcPath
container.Volumes[newVolPath] = destination
container.VolumesRW[newVolPath] = srcRW
if err := createIfNotExists(rootVolPath, volIsDir); err != nil {
if err := createIfNotExists(source, 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 {
if err := copyExistingContents(source, destination); err != nil {
return err
}
}
return nil
}
func copyExistingContents(rootVolPath, srcPath string) error {
volList, err := ioutil.ReadDir(rootVolPath)
func copyExistingContents(source, destination string) error {
volList, err := ioutil.ReadDir(source)
if err != nil {
return err
}
if len(volList) > 0 {
srcList, err := ioutil.ReadDir(srcPath)
srcList, err := ioutil.ReadDir(destination)
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 {
if err := archive.CopyWithTar(source, destination); err != nil {
return err
}
}
}
var (
stat syscall.Stat_t
srcStat syscall.Stat_t
)
if err := syscall.Stat(rootVolPath, &stat); err != nil {
return err
}
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
return copyOwnership(source, destination)
}
// copyOwnership copies the permissions and uid:gid of the source file
// into the destination file
func copyOwnership(source, destination string) error {
var stat syscall.Stat_t
if err := syscall.Stat(source, &stat); err != nil {
return err
}
if err := os.Chown(destination, int(stat.Uid), int(stat.Gid)); err != nil {
return err
}
return os.Chmod(destination, os.FileMode(stat.Mode))
}