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 { if _, exists := container.Volumes[volPath]; exists {
continue continue
} }
stat, err := os.Stat(c.getResourcePath(volPath)) stat, err := os.Stat(c.getResourcePath(volPath))
if err != nil { if err != nil {
return err return err
} }
if err := createIfNotExists(container.getResourcePath(volPath), stat.IsDir()); err != nil { if err := createIfNotExists(container.getResourcePath(volPath), stat.IsDir()); err != nil {
return err return err
} }
container.Volumes[volPath] = id container.Volumes[volPath] = id
if isRW, exists := c.VolumesRW[volPath]; exists { if isRW, exists := c.VolumesRW[volPath]; exists {
container.VolumesRW[volPath] = isRW && mountRW container.VolumesRW[volPath] = isRW && mountRW
@ -180,38 +183,39 @@ func createVolumes(container *Container) error {
return nil return nil
} }
func createIfNotExists(path string, isDir bool) error { func createIfNotExists(destination string, isDir bool) error {
if _, err := os.Stat(path); err != nil { if _, err := os.Stat(destination); err != nil && os.IsNotExist(err) {
if os.IsNotExist(err) { if isDir {
if isDir { if err := os.MkdirAll(destination, 0755); err != nil {
if err := os.MkdirAll(path, 0755); err != nil { return err
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()
} }
} 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 return nil
} }
func initializeVolume(container *Container, volPath string, binds map[string]BindMap) error { func initializeVolume(container *Container, volPath string, binds map[string]BindMap) error {
volumesDriver := container.daemon.volumes.Driver() volumesDriver := container.daemon.volumes.Driver()
volPath = filepath.Clean(volPath) volPath = filepath.Clean(volPath)
// Skip existing volumes // Skip existing volumes
if _, exists := container.Volumes[volPath]; exists { if _, exists := container.Volumes[volPath]; exists {
return nil return nil
} }
var ( var (
srcPath string destination string
isBindMount bool isBindMount bool
volIsDir = true 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 an external bind is defined for this volume, use that as a source
if bindMap, exists := binds[volPath]; exists { if bindMap, exists := binds[volPath]; exists {
isBindMount = true isBindMount = true
srcPath = bindMap.SrcPath destination = bindMap.SrcPath
if !filepath.IsAbs(srcPath) {
return fmt.Errorf("%s must be an absolute path", srcPath) if !filepath.IsAbs(destination) {
return fmt.Errorf("%s must be an absolute path", destination)
} }
if strings.ToLower(bindMap.Mode) == "rw" { if strings.ToLower(bindMap.Mode) == "rw" {
srcRW = true srcRW = true
} }
if stat, err := os.Stat(bindMap.SrcPath); err != nil { if stat, err := os.Stat(bindMap.SrcPath); err != nil {
return err return err
} else { } else {
volIsDir = stat.IsDir() volIsDir = stat.IsDir()
} }
// Otherwise create an directory in $ROOT/volumes/ and use that
} else { } else {
// Do not pass a container as the parameter for the volume creation. // Do not pass a container as the parameter for the volume creation.
// The graph driver using the container's information ( Image ) to // 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 { if err != nil {
return err return err
} }
srcPath, err = volumesDriver.Get(c.ID, "")
destination, err = volumesDriver.Get(c.ID, "")
if err != nil { if err != nil {
return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err) 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 return err
} else { } else {
srcPath = p destination = p
} }
// Create the mountpoint // 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 { if err != nil {
return err return err
} }
newVolPath, err := filepath.Rel(container.basefs, rootVolPath) newVolPath, err := filepath.Rel(container.basefs, source)
if err != nil { if err != nil {
return err return err
} }
@ -272,59 +280,57 @@ func initializeVolume(container *Container, volPath string, binds map[string]Bin
delete(container.VolumesRW, volPath) delete(container.VolumesRW, volPath)
} }
container.Volumes[newVolPath] = srcPath container.Volumes[newVolPath] = destination
container.VolumesRW[newVolPath] = srcRW container.VolumesRW[newVolPath] = srcRW
if err := createIfNotExists(rootVolPath, volIsDir); err != nil { if err := createIfNotExists(source, volIsDir); err != nil {
return err return err
} }
// Do not copy or change permissions if we are mounting from the host // Do not copy or change permissions if we are mounting from the host
if srcRW && !isBindMount { if srcRW && !isBindMount {
if err := copyExistingContents(rootVolPath, srcPath); err != nil { if err := copyExistingContents(source, destination); err != nil {
return err return err
} }
} }
return nil return nil
} }
func copyExistingContents(rootVolPath, srcPath string) error { func copyExistingContents(source, destination string) error {
volList, err := ioutil.ReadDir(rootVolPath) volList, err := ioutil.ReadDir(source)
if err != nil { if err != nil {
return err return err
} }
if len(volList) > 0 { if len(volList) > 0 {
srcList, err := ioutil.ReadDir(srcPath) srcList, err := ioutil.ReadDir(destination)
if err != nil { if err != nil {
return err return err
} }
if len(srcList) == 0 { if len(srcList) == 0 {
// If the source volume is empty copy files from the root into the volume // 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 return err
} }
} }
} }
var ( return copyOwnership(source, destination)
stat syscall.Stat_t }
srcStat syscall.Stat_t
) // copyOwnership copies the permissions and uid:gid of the source file
// into the destination file
if err := syscall.Stat(rootVolPath, &stat); err != nil { func copyOwnership(source, destination string) error {
return err var stat syscall.Stat_t
}
if err := syscall.Stat(srcPath, &srcStat); err != nil { if err := syscall.Stat(source, &stat); err != nil {
return err return err
} }
// Change the source volume's ownership if it differs from the root
// files that were just copied if err := os.Chown(destination, int(stat.Uid), int(stat.Gid)); err != nil {
if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid { return err
if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil { }
return err
} return os.Chmod(destination, os.FileMode(stat.Mode))
}
return nil
} }