cloudinit/system/file.go

117 lines
2.8 KiB
Go
Raw Normal View History

// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-03-18 20:00:41 +04:00
package system
import (
"fmt"
"io/ioutil"
2014-11-25 03:42:31 +03:00
"log"
2014-03-18 20:00:41 +04:00
"os"
"os/exec"
"path"
"strconv"
"github.com/vtolstov/cloudinit/config"
2014-03-18 20:00:41 +04:00
)
// File is a top-level structure which embeds its underlying configuration,
// config.File, and provides the system-specific Permissions().
2014-03-18 20:00:41 +04:00
type File struct {
config.File
2014-03-18 20:00:41 +04:00
}
func (f *File) Permissions() (os.FileMode, error) {
if f.RawFilePermissions == "" {
return os.FileMode(0644), nil
}
// Parse string representation of file mode as integer
perm, err := strconv.ParseInt(f.RawFilePermissions, 8, 32)
2014-03-18 20:00:41 +04:00
if err != nil {
return 0, fmt.Errorf("Unable to parse file permissions %q as integer", f.RawFilePermissions)
2014-03-18 20:00:41 +04:00
}
return os.FileMode(perm), nil
}
func WriteFile(f *File, root string) (string, error) {
2014-11-25 03:42:31 +03:00
fullpath := path.Join(root, f.Path)
dir := path.Dir(fullpath)
log.Printf("Writing file to %q", fullpath)
content, err := config.DecodeContent(f.Content, f.Encoding)
if err != nil {
return "", fmt.Errorf("Unable to decode %s (%v)", f.Path, err)
2014-03-18 20:00:41 +04:00
}
if err := EnsureDirectoryExists(dir); err != nil {
return "", err
2014-03-18 20:00:41 +04:00
}
perm, err := f.Permissions()
if err != nil {
return "", err
}
var tmp *os.File
// Create a temporary file in the same directory to ensure it's on the same filesystem
if tmp, err = ioutil.TempFile(dir, "cloudinit-temp"); err != nil {
return "", err
2014-03-18 20:00:41 +04:00
}
if err := ioutil.WriteFile(tmp.Name(), content, perm); err != nil {
return "", err
}
if err := tmp.Close(); err != nil {
return "", err
}
// Ensure the permissions are as requested (since WriteFile can be affected by sticky bit)
if err := os.Chmod(tmp.Name(), perm); err != nil {
return "", err
2014-03-18 20:00:41 +04:00
}
if f.Owner != "" {
// We shell out since we don't have a way to look up unix groups natively
cmd := exec.Command("chown", f.Owner, tmp.Name())
2014-03-18 20:00:41 +04:00
if err := cmd.Run(); err != nil {
return "", err
2014-03-18 20:00:41 +04:00
}
}
if err := os.Rename(tmp.Name(), fullpath); err != nil {
return "", err
}
2014-11-25 03:42:31 +03:00
log.Printf("Wrote file to %q", fullpath)
return fullpath, nil
2014-03-18 20:00:41 +04:00
}
func EnsureDirectoryExists(dir string) error {
info, err := os.Stat(dir)
if err == nil {
if !info.IsDir() {
return fmt.Errorf("%s is not a directory", dir)
}
} else {
err = os.MkdirAll(dir, 0755)
if err != nil {
return err
}
}
return nil
}