// Write implements EnvironInfo.Write. func (info *environInfo) Write() error { info.mu.Lock() defer info.mu.Unlock() data, err := goyaml.Marshal(info.EnvInfo) if err != nil { return errors.Annotate(err, "cannot marshal environment info") } // Create a temporary file and rename it, so that the data // changes atomically. parent, _ := filepath.Split(info.path) tmpFile, err := ioutil.TempFile(parent, "") if err != nil { return errors.Annotate(err, "cannot create temporary file") } _, err = tmpFile.Write(data) // N.B. We need to close the file before renaming it // otherwise it will fail under Windows with a file-in-use // error. tmpFile.Close() if err != nil { return errors.Annotate(err, "cannot write temporary file") } if err := utils.ReplaceFile(tmpFile.Name(), info.path); err != nil { os.Remove(tmpFile.Name()) return errors.Annotate(err, "cannot rename new environment info file") } info.initialized = true return nil }
// Unlock releases a held lock. If the lock is not held ErrLockNotHeld is // returned. func (lock *Lock) Unlock() error { if !lock.IsLockHeld() { return ErrLockNotHeld } // To ensure reasonable unlocking, we should rename to a temp name, and delete that. lock.declareDead() tempLockName := fmt.Sprintf(".%s.%s", lock.name, lock.nonce) tempDirName := path.Join(lock.parent, tempLockName) // Now move the lock directory to the temp directory to release the lock. for i := 0; ; i++ { err := utils.ReplaceFile(lock.lockDir(), tempDirName) if err == nil { break } if i == 100 { logger.Debugf("Failed to replace lock, giving up: (%s)", err) return err } logger.Debugf("Failed to replace lock, retrying: (%s)", err) runtime.Gosched() } // And now cleanup. if err := os.RemoveAll(tempDirName); err != nil { logger.Debugf("Failed to remove lock: %s", err) return err } return nil }
func (f *fileStorageWriter) Put(name string, r io.Reader, length int64) error { if isInternalPath(name) { return &os.PathError{ Op: "Put", Path: name, Err: os.ErrPermission, } } fullpath := f.fullPath(name) dir := filepath.Dir(fullpath) if err := os.MkdirAll(dir, 0755); err != nil { return err } tmpdir := filepath.Join(f.path, ".tmp") if err := os.MkdirAll(tmpdir, 0755); err != nil { return err } defer os.Remove(tmpdir) // Write to a temporary file first, and then move (atomically). file, err := ioutil.TempFile(tmpdir, "juju-filestorage-") if err != nil { return err } _, err = io.CopyN(file, r, length) file.Close() if err != nil { os.Remove(file.Name()) return err } return utils.ReplaceFile(file.Name(), fullpath) }
// Get returns the charm referenced by curl. // CacheDir must have been set, otherwise Get will panic. func (s *LegacyCharmStore) Get(curl *charm.URL) (charm.Charm, error) { // The cache location must have been previously set. if CacheDir == "" { panic("charm cache directory path is empty") } if err := os.MkdirAll(CacheDir, os.FileMode(0755)); err != nil { return nil, err } revInfo, err := s.revisions(curl) if err != nil { return nil, err } if len(revInfo) != 1 { return nil, fmt.Errorf("expected 1 result, got %d", len(revInfo)) } if revInfo[0].Err != nil { return nil, revInfo[0].Err } rev, digest := revInfo[0].Revision, revInfo[0].Sha256 if curl.Revision == -1 { curl = curl.WithRevision(rev) } else if curl.Revision != rev { return nil, fmt.Errorf("store returned charm with wrong revision %d for %q", rev, curl.String()) } path := filepath.Join(CacheDir, charm.Quote(curl.String())+".charm") if verify(path, digest) != nil { store_url := s.BaseURL + "/charm/" + url.QueryEscape(curl.Path()) if s.testMode { store_url = store_url + "?stats=0" } resp, err := s.get(store_url) if err != nil { return nil, err } defer resp.Body.Close() f, err := ioutil.TempFile(CacheDir, "charm-download") if err != nil { return nil, err } dlPath := f.Name() _, err = io.Copy(f, resp.Body) if cerr := f.Close(); err == nil { err = cerr } if err != nil { os.Remove(dlPath) return nil, err } if err := utils.ReplaceFile(dlPath, path); err != nil { return nil, err } } if err := verify(path, digest); err != nil { return nil, err } return charm.ReadCharmArchive(path) }
// Get implements Interface.Get. func (s *CharmStore) Get(curl *charm.URL) (charm.Charm, error) { // The cache location must have been previously set. if CacheDir == "" { panic("charm cache directory path is empty") } if curl.Series == "bundle" { return nil, errgo.Newf("expected a charm URL, got bundle URL %q", curl) } // Prepare the cache directory and retrieve the charm. if err := os.MkdirAll(CacheDir, 0755); err != nil { return nil, errgo.Notef(err, "cannot create the cache directory") } r, id, expectHash, expectSize, err := s.client.GetArchive(curl.Reference()) if err != nil { if errgo.Cause(err) == params.ErrNotFound { // Make a prettier error message for the user. return nil, errgo.WithCausef(nil, params.ErrNotFound, "cannot retrieve charm %q: charm not found", curl) } return nil, errgo.NoteMask(err, fmt.Sprintf("cannot retrieve charm %q", curl), errgo.Any) } defer r.Close() // Check if the archive already exists in the cache. path := filepath.Join(CacheDir, charm.Quote(id.String())+".charm") if verifyHash384AndSize(path, expectHash, expectSize) == nil { return charm.ReadCharmArchive(path) } // Verify and save the new archive. f, err := ioutil.TempFile(CacheDir, "charm-download") if err != nil { return nil, errgo.Notef(err, "cannot make temporary file") } defer f.Close() hash := sha512.New384() size, err := io.Copy(io.MultiWriter(hash, f), r) if err != nil { return nil, errgo.Notef(err, "cannot read charm archive") } if size != expectSize { return nil, errgo.Newf("size mismatch; network corruption?") } if fmt.Sprintf("%x", hash.Sum(nil)) != expectHash { return nil, errgo.Newf("hash mismatch; network corruption?") } // Move the archive to the expected place, and return the charm. if err := utils.ReplaceFile(f.Name(), path); err != nil { return nil, errgo.Notef(err, "cannot move the charm archive") } return charm.ReadCharmArchive(path) }
// Unlock releases a held lock. If the lock is not held ErrLockNotHeld is // returned. func (lock *Lock) Unlock() error { if !lock.IsLockHeld() { return ErrLockNotHeld } // To ensure reasonable unlocking, we should rename to a temp name, and delete that. tempLockName := fmt.Sprintf(".%s.%x", lock.name, lock.nonce) tempDirName := path.Join(lock.parent, tempLockName) // Now move the lock directory to the temp directory to release the lock. if err := utils.ReplaceFile(lock.lockDir(), tempDirName); err != nil { return err } // And now cleanup. return os.RemoveAll(tempDirName) }
// If message is set, it will write the message to the lock directory as the // lock is taken. func (lock *Lock) acquire(message string) (bool, error) { // If the lockDir exists, then the lock is held by someone else. _, err := os.Stat(lock.lockDir()) if err == nil { return false, nil } if !os.IsNotExist(err) { return false, err } // Create a temporary directory (in the parent dir), and then move it to // the right name. Using the same directory to make sure the directories // are on the same filesystem. Use a directory name starting with "." as // it isn't a valid lock name. tempLockName := fmt.Sprintf(".%s", lock.nonce) tempDirName, err := ioutil.TempDir(lock.parent, tempLockName) if err != nil { return false, err // this shouldn't really fail... } // write lock into the temp dir l := onDisk{ PID: lock.PID, Nonce: lock.nonce, Message: message, } lockInfo, err := goyaml.Marshal(&l) if err != nil { return false, err // this shouldn't fail either... } err = ioutil.WriteFile(path.Join(tempDirName, heldFilename), lockInfo, 0664) if err != nil { return false, err } // Now move the temp directory to the lock directory. err = utils.ReplaceFile(tempDirName, lock.lockDir()) if err != nil { // Any error on rename means we failed. // Beaten to it, clean up temporary directory. os.RemoveAll(tempDirName) return false, nil } // We now have the lock. lock.createAliveFile() return true, nil }
// finishDeploy persists the fact that we've finished deploying the staged bundle. func (d *manifestDeployer) finishDeploy() error { logger.Debugf("finishing deploy of charm %q", d.staged.url) oldPath := d.CharmPath(deployingURLPath) newPath := d.CharmPath(charmURLPath) return utils.ReplaceFile(oldPath, newPath) }