<commit_before>// Copyright (c) 2015, huangjunwei <huangjunwei@youmi.net>. All rights reserved.

package blog4go

import (
	"fmt"
	"os"
	"path"
	"strings"
	"sync"
	"time"
)

// ByteSize is type of sizes
type ByteSize int64

const (
	// unit of sizes

	_ = iota // ignore first value by assigning to blank identifier
	// KB unit of kilobyte
	KB ByteSize = 1 << (10 * iota)
	// MB unit of megabyte
	MB
	// GB unit of gigabyte
	GB

	// default logrotate condition

	// DefaultRotateSize is default size when size base logrotate needed
	DefaultRotateSize = 500 * MB
	// DefaultRotateLines is default lines when lines base logrotate needed
	DefaultRotateLines = 2000000 // 2 million
)

// baseFileWriter defines a writer for single file.
// It suppurts partially write while formatting message, logging level filtering,
// logrotate, user defined hook for every logging action, change configuration
// on the fly and logging with colors.
type baseFileWriter struct {
	// configuration about file
	// full path of the file
	fileName string
	// the file object
	file *os.File

	// the BLog
	blog *BLog

	// close sign, default false
	// set this tag true if writer is closed
	closed bool

	// configuration about logrotate
	// exclusive lock use in logrotate
	rotateLock *sync.Mutex

	// configuration about time base logrotate
	// sign of time base logrotate, default false
	// set this tag true if logrotate in time base mode
	timeRotated bool
	// signal send when time base rotate needed
	timeRotateSig chan bool

	// configuration about size && line base logrotate
	// sign of line base logrotate, default false
	// set this tag true if logrotate in line base mode
	lineRotated bool
	// line base logrotate threshold
	rotateLines int
	// total lines written from last size && line base logrotate
	currentLines int
	// sign of size base logrotate, default false
	// set this tag true if logrotate in size base mode
	sizeRotated bool
	// size rotate按行数、大小rotate, 后缀 xxx.1, xxx.2
	// signal send when size && line base logrotate
	sizeRotateSig chan bool
	// size base logrotate threshold
	rotateSize ByteSize
	// total size written after last size && line logrotate
	currentSize ByteSize
	// times of size && line base logrotate executions
	sizeRotateTimes int
	// channel used to sum up sizes written from last logrotate
	logSizeChan chan int

	// sign decided logging with colors or not, default false
	colored bool
}

// NewbaseFileWriter create a single file writer instance and return the poionter
// of it. When any errors happened during creation, a null writer and appropriate
// will be returned.
// fileName must be an absolute path to the destination log file
func newBaseFileWriter(fileName string) (fileWriter *baseFileWriter, err error) {
	fileWriter = new(baseFileWriter)
	fileWriter.fileName = fileName
	// open file target file
	file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
	fileWriter.file = file
	if nil != err {
		return fileWriter, err
	}
	fileWriter.blog = NewBLog(file)

	fileWriter.closed = false

	// about logrotate
	fileWriter.rotateLock = new(sync.Mutex)
	fileWriter.timeRotated = false
	fileWriter.timeRotateSig = make(chan bool)
	fileWriter.sizeRotateSig = make(chan bool)
	fileWriter.logSizeChan = make(chan int, 4096)

	fileWriter.lineRotated = false
	fileWriter.rotateSize = DefaultRotateSize
	fileWriter.currentSize = 0

	fileWriter.sizeRotated = false
	fileWriter.rotateLines = DefaultRotateLines
	fileWriter.currentLines = 0

	fileWriter.colored = false

	go fileWriter.daemon()

	return fileWriter, nil
}

// NewFileWriter initialize a file writer
// baseDir must be base directory of log files
func NewFileWriter(baseDir string) (err error) {
	singltonLock.Lock()
	defer singltonLock.Unlock()
	if nil != blog {
		return
	}

	fileWriter := new(MultiWriter)
	fileWriter.level = DEBUG
	fileWriter.closed = false

	fileWriter.writers = make(map[Level]Writer)
	for _, level := range Levels {
		fileName := fmt.Sprintf("%s.log", strings.ToLower(level.String()))
		writer, err := newBaseFileWriter(path.Join(baseDir, fileName))
		if nil != err {
			return err
		}
		fileWriter.writers[level] = writer
	}

	// log hook
	fileWriter.hook = nil
	fileWriter.hookLevel = DEBUG

	blog = fileWriter
	return
}

// daemon run in background as NewbaseFileWriter called.
// It flushes writer buffer every 1 second.
// It decides whether a time base when logrotate is needed.
// It sums up lines && sizes already written. Alse it does the lines &&
// size base logrotate
func (writer *baseFileWriter) daemon() {
	// tick every seconds
	// time base logrotate
	t := time.Tick(1 * time.Second)
	// tick every 1 second
	// auto flush writer buffer
	f := time.Tick(1 * time.Second)

DaemonLoop:
	for {
		select {
		case <-f:
			if writer.closed {
				break DaemonLoop
			}

			writer.blog.flush()
		case <-t:
			if writer.closed {
				break DaemonLoop
			}

			writer.rotateLock.Lock()

			date := time.Now().Format(DateFormat)

			if writer.timeRotated && date != timeCache.date {
				// need time base logrotate
				writer.sizeRotateTimes = 0

				fileName := fmt.Sprintf("%s.%s", writer.fileName, timeCache.dateYesterday)
				file, _ := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))

				writer.file.Close()
				writer.blog.resetFile(file)
				writer.file = file
			}

			writer.rotateLock.Unlock()
		// analyse lines && size written
		// do lines && size base logrotate
		case size := <-writer.logSizeChan:
			if writer.closed {
				break DaemonLoop
			}

			if !writer.sizeRotated && !writer.lineRotated {
				continue
			}

			writer.rotateLock.Lock()

			writer.currentSize += ByteSize(size)
			writer.currentLines++

			if (writer.sizeRotated && writer.currentSize > writer.rotateSize) || (writer.lineRotated && writer.currentLines > writer.rotateLines) {
				// need lines && size base logrotate
				writer.sizeRotateTimes++
				writer.currentSize = 0
				writer.currentLines = 0

				fileName := fmt.Sprintf("%s.%d", writer.fileName, writer.sizeRotateTimes+1)
				if writer.timeRotated {
					fileName = fmt.Sprintf("%s.%s.%d", writer.fileName, timeCache.date, writer.sizeRotateTimes+1)
				}
				file, _ := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))

				writer.file.Close()
				writer.blog.resetFile(file)
				writer.file = file
			}
			writer.rotateLock.Unlock()
		}
	}
}

// write writes pure message with specific level
func (writer *baseFileWriter) write(level Level, format string) {
	var size = 0
	defer func() {
		// logrotate
		if writer.sizeRotated || writer.lineRotated {
			writer.logSizeChan <- size
		}
	}()

	if writer.closed {
		return
	}

	size = writer.blog.write(level, format)
}

// write formats message with specific level and write it
func (writer *baseFileWriter) writef(level Level, format string, args ...interface{}) {
	// 格式化构造message
	// 边解析边输出
	// 使用 % 作占位符

	// 统计日志size
	var size = 0

	defer func() {
		// logrotate
		if writer.sizeRotated || writer.lineRotated {
			writer.logSizeChan <- size
		}
	}()

	if writer.closed {
		return
	}

	size = writer.blog.writef(level, format, args...)
}

// Close close file writer
func (writer *baseFileWriter) Close() {
	if writer.closed {
		return
	}

	writer.blog.Close()
	writer.blog = nil
	writer.closed = true
}

// SetTimeRotated toggle time base logrotate on the fly
func (writer *baseFileWriter) SetTimeRotated(timeRotated bool) {
	writer.timeRotated = timeRotated
}

// RotateSize return size threshold when logrotate
func (writer *baseFileWriter) RotateSize() ByteSize {
	return writer.rotateSize
}

// SetRotateSize set size when logroatate
func (writer *baseFileWriter) SetRotateSize(rotateSize ByteSize) {
	if rotateSize > ByteSize(0) {
		writer.sizeRotated = true
		writer.rotateSize = rotateSize
	} else {
		writer.sizeRotated = false
	}
}

// RotateLine return line threshold when logrotate
func (writer *baseFileWriter) RotateLine() int {
	return writer.rotateLines
}

// SetRotateLines set line number when logrotate
func (writer *baseFileWriter) SetRotateLines(rotateLines int) {
	if rotateLines > 0 {
		writer.lineRotated = true
		writer.rotateLines = rotateLines
	} else {
		writer.lineRotated = false
	}
}

// Colored return whether writer log with color
func (writer *baseFileWriter) Colored() bool {
	return writer.colored
}

// SetColored set logging color
func (writer *baseFileWriter) SetColored(colored bool) {
	if colored == writer.colored {
		return
	}

	writer.colored = colored
	initPrefix(colored)
}

// Level return logging level threshold
func (writer *baseFileWriter) Level() Level {
	return writer.blog.Level()
}

// SetLevel set logging level threshold
func (writer *baseFileWriter) SetLevel(level Level) {
	writer.blog.SetLevel(level)
}

// SetHook do nothing
func (writer *baseFileWriter) SetHook(hook Hook) {
	return
}

// SetHookLevel do nothing
func (writer *baseFileWriter) SetHookLevel(level Level) {
	return
}

// flush flush logs to disk
func (writer *baseFileWriter) flush() {
	writer.blog.flush()
}

// Debug do nothing
func (writer *baseFileWriter) Debug(format string) {
	return
}

// Debugf do nothing
func (writer *baseFileWriter) Debugf(format string, args ...interface{}) {
	return
}

// Trace do nothing
func (writer *baseFileWriter) Trace(format string) {
	return
}

// Tracef do nothing
func (writer *baseFileWriter) Tracef(format string, args ...interface{}) {
	return
}

// Info do nothing
func (writer *baseFileWriter) Info(format string) {
	return
}

// Infof do nothing
func (writer *baseFileWriter) Infof(format string, args ...interface{}) {
	return
}

// Warn do nothing
func (writer *baseFileWriter) Warn(format string) {
	return
}

// Warnf do nothing
func (writer *baseFileWriter) Warnf(format string, args ...interface{}) {
	return
}

// Error do nothing
func (writer *baseFileWriter) Error(format string) {
	return
}

// Errorf do nothing
func (writer *baseFileWriter) Errorf(format string, args ...interface{}) {
	return
}

// Critical do nothing
func (writer *baseFileWriter) Critical(format string) {
	return
}

// Criticalf do nothing
func (writer *baseFileWriter) Criticalf(format string, args ...interface{}) {
	return
}
<commit_msg>fix file date<commit_after>// Copyright (c) 2015, huangjunwei <huangjunwei@youmi.net>. All rights reserved.

package blog4go

import (
	"fmt"
	"os"
	"path"
	"strings"
	"sync"
	"time"
)

// ByteSize is type of sizes
type ByteSize int64

const (
	// unit of sizes

	_ = iota // ignore first value by assigning to blank identifier
	// KB unit of kilobyte
	KB ByteSize = 1 << (10 * iota)
	// MB unit of megabyte
	MB
	// GB unit of gigabyte
	GB

	// default logrotate condition

	// DefaultRotateSize is default size when size base logrotate needed
	DefaultRotateSize = 500 * MB
	// DefaultRotateLines is default lines when lines base logrotate needed
	DefaultRotateLines = 2000000 // 2 million
)

// baseFileWriter defines a writer for single file.
// It suppurts partially write while formatting message, logging level filtering,
// logrotate, user defined hook for every logging action, change configuration
// on the fly and logging with colors.
type baseFileWriter struct {
	// configuration about file
	// full path of the file
	fileName string
	// the file object
	file *os.File

	// the BLog
	blog *BLog

	// close sign, default false
	// set this tag true if writer is closed
	closed bool

	// configuration about logrotate
	// exclusive lock use in logrotate
	rotateLock *sync.Mutex

	// configuration about time base logrotate
	// sign of time base logrotate, default false
	// set this tag true if logrotate in time base mode
	timeRotated bool
	// signal send when time base rotate needed
	timeRotateSig chan bool

	// configuration about size && line base logrotate
	// sign of line base logrotate, default false
	// set this tag true if logrotate in line base mode
	lineRotated bool
	// line base logrotate threshold
	rotateLines int
	// total lines written from last size && line base logrotate
	currentLines int
	// sign of size base logrotate, default false
	// set this tag true if logrotate in size base mode
	sizeRotated bool
	// size rotate按行数、大小rotate, 后缀 xxx.1, xxx.2
	// signal send when size && line base logrotate
	sizeRotateSig chan bool
	// size base logrotate threshold
	rotateSize ByteSize
	// total size written after last size && line logrotate
	currentSize ByteSize
	// times of size && line base logrotate executions
	sizeRotateTimes int
	// channel used to sum up sizes written from last logrotate
	logSizeChan chan int

	// sign decided logging with colors or not, default false
	colored bool
}

// NewbaseFileWriter create a single file writer instance and return the poionter
// of it. When any errors happened during creation, a null writer and appropriate
// will be returned.
// fileName must be an absolute path to the destination log file
func newBaseFileWriter(fileName string) (fileWriter *baseFileWriter, err error) {
	fileWriter = new(baseFileWriter)
	fileWriter.fileName = fileName
	// open file target file
	file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
	fileWriter.file = file
	if nil != err {
		return fileWriter, err
	}
	fileWriter.blog = NewBLog(file)

	fileWriter.closed = false

	// about logrotate
	fileWriter.rotateLock = new(sync.Mutex)
	fileWriter.timeRotated = false
	fileWriter.timeRotateSig = make(chan bool)
	fileWriter.sizeRotateSig = make(chan bool)
	fileWriter.logSizeChan = make(chan int, 4096)

	fileWriter.lineRotated = false
	fileWriter.rotateSize = DefaultRotateSize
	fileWriter.currentSize = 0

	fileWriter.sizeRotated = false
	fileWriter.rotateLines = DefaultRotateLines
	fileWriter.currentLines = 0

	fileWriter.colored = false

	go fileWriter.daemon()

	return fileWriter, nil
}

// NewFileWriter initialize a file writer
// baseDir must be base directory of log files
func NewFileWriter(baseDir string) (err error) {
	singltonLock.Lock()
	defer singltonLock.Unlock()
	if nil != blog {
		return
	}

	fileWriter := new(MultiWriter)
	fileWriter.level = DEBUG
	fileWriter.closed = false

	fileWriter.writers = make(map[Level]Writer)
	for _, level := range Levels {
		fileName := fmt.Sprintf("%s.log", strings.ToLower(level.String()))
		writer, err := newBaseFileWriter(path.Join(baseDir, fileName))
		if nil != err {
			return err
		}
		fileWriter.writers[level] = writer
	}

	// log hook
	fileWriter.hook = nil
	fileWriter.hookLevel = DEBUG

	blog = fileWriter
	return
}

// daemon run in background as NewbaseFileWriter called.
// It flushes writer buffer every 1 second.
// It decides whether a time base when logrotate is needed.
// It sums up lines && sizes already written. Alse it does the lines &&
// size base logrotate
func (writer *baseFileWriter) daemon() {
	// tick every seconds
	// time base logrotate
	t := time.Tick(1 * time.Second)
	// tick every 1 second
	// auto flush writer buffer
	f := time.Tick(1 * time.Second)

DaemonLoop:
	for {
		select {
		case <-f:
			if writer.closed {
				break DaemonLoop
			}

			writer.blog.flush()
		case <-t:
			if writer.closed {
				break DaemonLoop
			}

			writer.rotateLock.Lock()

			date := time.Now().Format(DateFormat)

			if writer.timeRotated && date != timeCache.date {
				// need time base logrotate
				writer.sizeRotateTimes = 0

				fileName := fmt.Sprintf("%s.%s", writer.fileName, timeCache.date)
				file, _ := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))

				writer.file.Close()
				writer.blog.resetFile(file)
				writer.file = file
			}

			writer.rotateLock.Unlock()
		// analyse lines && size written
		// do lines && size base logrotate
		case size := <-writer.logSizeChan:
			if writer.closed {
				break DaemonLoop
			}

			if !writer.sizeRotated && !writer.lineRotated {
				continue
			}

			writer.rotateLock.Lock()

			writer.currentSize += ByteSize(size)
			writer.currentLines++

			if (writer.sizeRotated && writer.currentSize > writer.rotateSize) || (writer.lineRotated && writer.currentLines > writer.rotateLines) {
				// need lines && size base logrotate
				writer.sizeRotateTimes++
				writer.currentSize = 0
				writer.currentLines = 0

				fileName := fmt.Sprintf("%s.%d", writer.fileName, writer.sizeRotateTimes+1)
				if writer.timeRotated {
					fileName = fmt.Sprintf("%s.%s.%d", writer.fileName, timeCache.date, writer.sizeRotateTimes+1)
				}
				file, _ := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))

				writer.file.Close()
				writer.blog.resetFile(file)
				writer.file = file
			}
			writer.rotateLock.Unlock()
		}
	}
}

// write writes pure message with specific level
func (writer *baseFileWriter) write(level Level, format string) {
	var size = 0
	defer func() {
		// logrotate
		if writer.sizeRotated || writer.lineRotated {
			writer.logSizeChan <- size
		}
	}()

	if writer.closed {
		return
	}

	size = writer.blog.write(level, format)
}

// write formats message with specific level and write it
func (writer *baseFileWriter) writef(level Level, format string, args ...interface{}) {
	// 格式化构造message
	// 边解析边输出
	// 使用 % 作占位符

	// 统计日志size
	var size = 0

	defer func() {
		// logrotate
		if writer.sizeRotated || writer.lineRotated {
			writer.logSizeChan <- size
		}
	}()

	if writer.closed {
		return
	}

	size = writer.blog.writef(level, format, args...)
}

// Close close file writer
func (writer *baseFileWriter) Close() {
	if writer.closed {
		return
	}

	writer.blog.Close()
	writer.blog = nil
	writer.closed = true
}

// SetTimeRotated toggle time base logrotate on the fly
func (writer *baseFileWriter) SetTimeRotated(timeRotated bool) {
	writer.timeRotated = timeRotated
}

// RotateSize return size threshold when logrotate
func (writer *baseFileWriter) RotateSize() ByteSize {
	return writer.rotateSize
}

// SetRotateSize set size when logroatate
func (writer *baseFileWriter) SetRotateSize(rotateSize ByteSize) {
	if rotateSize > ByteSize(0) {
		writer.sizeRotated = true
		writer.rotateSize = rotateSize
	} else {
		writer.sizeRotated = false
	}
}

// RotateLine return line threshold when logrotate
func (writer *baseFileWriter) RotateLine() int {
	return writer.rotateLines
}

// SetRotateLines set line number when logrotate
func (writer *baseFileWriter) SetRotateLines(rotateLines int) {
	if rotateLines > 0 {
		writer.lineRotated = true
		writer.rotateLines = rotateLines
	} else {
		writer.lineRotated = false
	}
}

// Colored return whether writer log with color
func (writer *baseFileWriter) Colored() bool {
	return writer.colored
}

// SetColored set logging color
func (writer *baseFileWriter) SetColored(colored bool) {
	if colored == writer.colored {
		return
	}

	writer.colored = colored
	initPrefix(colored)
}

// Level return logging level threshold
func (writer *baseFileWriter) Level() Level {
	return writer.blog.Level()
}

// SetLevel set logging level threshold
func (writer *baseFileWriter) SetLevel(level Level) {
	writer.blog.SetLevel(level)
}

// SetHook do nothing
func (writer *baseFileWriter) SetHook(hook Hook) {
	return
}

// SetHookLevel do nothing
func (writer *baseFileWriter) SetHookLevel(level Level) {
	return
}

// flush flush logs to disk
func (writer *baseFileWriter) flush() {
	writer.blog.flush()
}

// Debug do nothing
func (writer *baseFileWriter) Debug(format string) {
	return
}

// Debugf do nothing
func (writer *baseFileWriter) Debugf(format string, args ...interface{}) {
	return
}

// Trace do nothing
func (writer *baseFileWriter) Trace(format string) {
	return
}

// Tracef do nothing
func (writer *baseFileWriter) Tracef(format string, args ...interface{}) {
	return
}

// Info do nothing
func (writer *baseFileWriter) Info(format string) {
	return
}

// Infof do nothing
func (writer *baseFileWriter) Infof(format string, args ...interface{}) {
	return
}

// Warn do nothing
func (writer *baseFileWriter) Warn(format string) {
	return
}

// Warnf do nothing
func (writer *baseFileWriter) Warnf(format string, args ...interface{}) {
	return
}

// Error do nothing
func (writer *baseFileWriter) Error(format string) {
	return
}

// Errorf do nothing
func (writer *baseFileWriter) Errorf(format string, args ...interface{}) {
	return
}

// Critical do nothing
func (writer *baseFileWriter) Critical(format string) {
	return
}

// Criticalf do nothing
func (writer *baseFileWriter) Criticalf(format string, args ...interface{}) {
	return
}
<|endoftext|>


<commit_before>package docker

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"syscall"
)

type Filesystem struct {
	RootFS string
	RWPath string
	Layers []string
}

func (fs *Filesystem) createMountPoints() error {
	if err := os.Mkdir(fs.RootFS, 0700); err != nil && !os.IsExist(err) {
		return err
	}
	if err := os.Mkdir(fs.RWPath, 0700); err != nil && !os.IsExist(err) {
		return err
	}
	return nil
}

func (fs *Filesystem) Mount() error {
	if fs.IsMounted() {
		return errors.New("Mount: Filesystem already mounted")
	}
	if err := fs.createMountPoints(); err != nil {
		return err
	}
	rwBranch := fmt.Sprintf("%v=rw", fs.RWPath)
	roBranches := ""
	for _, layer := range fs.Layers {
		roBranches += fmt.Sprintf("%v=ro:", layer)
	}
	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
	return syscall.Mount("none", fs.RootFS, "aufs", 0, branches)
}

func (fs *Filesystem) Umount() error {
	if !fs.IsMounted() {
		return errors.New("Umount: Filesystem not mounted")
	}
	return syscall.Unmount(fs.RootFS, 0)
}

func (fs *Filesystem) IsMounted() bool {
	f, err := os.Open(fs.RootFS)
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
		panic(err)
	}
	list, err := f.Readdirnames(1)
	f.Close()
	if err != nil {
		return false
	}
	if len(list) > 0 {
		return true
	}
	return false
}

type ChangeType int

const (
	ChangeModify = iota
	ChangeAdd
	ChangeDelete
)

type Change struct {
	Path string
	Kind ChangeType
}

func (fs *Filesystem) Changes() ([]Change, error) {
	var changes []Change
	err := filepath.Walk(fs.RWPath, func(path string, f os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// Rebase path
		path, err = filepath.Rel(fs.RWPath, path)
		if err != nil {
			return err
		}
		path = filepath.Join("/", path)

		// Skip root
		if path == "/" {
			return nil
		}

		// Skip AUFS metadata
		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
			return err
		}

		change := Change{
			Path: path,
		}

		// Find out what kind of modification happened
		file := filepath.Base(path)
		// If there is a whiteout, then the file was removed
		if strings.HasPrefix(file, ".wh.") {
			originalFile := strings.TrimLeft(file, ".wh.")
			change.Path = filepath.Join(filepath.Dir(path), originalFile)
			change.Kind = ChangeDelete
		} else {
			// Otherwise, the file was added
			change.Kind = ChangeAdd

			// ...Unless it already existed in a top layer, in which case, it's a modification
			for _, layer := range fs.Layers {
				stat, err := os.Stat(filepath.Join(layer, path))
				if err != nil && !os.IsNotExist(err) {
					return err
				}
				if err == nil {
					// The file existed in the top layer, so that's a modification

					// However, if it's a directory, maybe it wasn't actually modified.
					// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
					if stat.IsDir() && f.IsDir() {
						if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
							// Both directories are the same, don't record the change
							return nil
						}
					}
					change.Kind = ChangeModify
					break
				}
			}
		}

		// Record change
		changes = append(changes, change)
		return nil
	})
	if err != nil {
		return nil, err
	}
	return changes, nil
}

func newFilesystem(rootfs string, rwpath string, layers []string) *Filesystem {
	return &Filesystem{
		RootFS: rootfs,
		RWPath: rwpath,
		Layers: layers,
	}
}
<commit_msg>Filesystem: Re-implemented Umount() and IsMounted() to work around AUFS issues.<commit_after>package docker

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

type Filesystem struct {
	RootFS string
	RWPath string
	Layers []string
}

func (fs *Filesystem) createMountPoints() error {
	if err := os.Mkdir(fs.RootFS, 0700); err != nil && !os.IsExist(err) {
		return err
	}
	if err := os.Mkdir(fs.RWPath, 0700); err != nil && !os.IsExist(err) {
		return err
	}
	return nil
}

func (fs *Filesystem) Mount() error {
	if fs.IsMounted() {
		return errors.New("Mount: Filesystem already mounted")
	}
	if err := fs.createMountPoints(); err != nil {
		return err
	}
	rwBranch := fmt.Sprintf("%v=rw", fs.RWPath)
	roBranches := ""
	for _, layer := range fs.Layers {
		roBranches += fmt.Sprintf("%v=ro:", layer)
	}
	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
	if err := syscall.Mount("none", fs.RootFS, "aufs", 0, branches); err != nil {
		return err
	}
	if !fs.IsMounted() {
		return errors.New("Mount failed")
	}
	return nil
}

func (fs *Filesystem) Umount() error {
	if !fs.IsMounted() {
		return errors.New("Umount: Filesystem not mounted")
	}
	if err := syscall.Unmount(fs.RootFS, 0); err != nil {
		return err
	}
	if fs.IsMounted() {
		return fmt.Errorf("Umount: Filesystem still mounted after calling umount(%v)", fs.RootFS)
	}
	for retries := 0; retries < 1000; retries++ {
		err := os.Remove(fs.RootFS)
		if err == nil {
			// rm mntpoint succeeded
			return nil
		}
		if os.IsNotExist(err) {
			// mntpoint doesn't exist anymore. Success.
			return nil
		}
		// fmt.Printf("(%v) Remove %v returned: %v\n", retries, fs.RootFS, err)
		time.Sleep(10 * time.Millisecond)
	}
	return fmt.Errorf("Umount: Failed to umount %v", fs.RootFS)
}

func (fs *Filesystem) IsMounted() bool {
	mntpoint, err := os.Stat(fs.RootFS)
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
		panic(err)
	}
	parent, err := os.Stat(filepath.Join(fs.RootFS, ".."))
	if err != nil {
		panic(err)
	}

	mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
	parentSt := parent.Sys().(*syscall.Stat_t)
	return mntpointSt.Dev != parentSt.Dev
}

type ChangeType int

const (
	ChangeModify = iota
	ChangeAdd
	ChangeDelete
)

type Change struct {
	Path string
	Kind ChangeType
}

func (fs *Filesystem) Changes() ([]Change, error) {
	var changes []Change
	err := filepath.Walk(fs.RWPath, func(path string, f os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// Rebase path
		path, err = filepath.Rel(fs.RWPath, path)
		if err != nil {
			return err
		}
		path = filepath.Join("/", path)

		// Skip root
		if path == "/" {
			return nil
		}

		// Skip AUFS metadata
		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
			return err
		}

		change := Change{
			Path: path,
		}

		// Find out what kind of modification happened
		file := filepath.Base(path)
		// If there is a whiteout, then the file was removed
		if strings.HasPrefix(file, ".wh.") {
			originalFile := strings.TrimLeft(file, ".wh.")
			change.Path = filepath.Join(filepath.Dir(path), originalFile)
			change.Kind = ChangeDelete
		} else {
			// Otherwise, the file was added
			change.Kind = ChangeAdd

			// ...Unless it already existed in a top layer, in which case, it's a modification
			for _, layer := range fs.Layers {
				stat, err := os.Stat(filepath.Join(layer, path))
				if err != nil && !os.IsNotExist(err) {
					return err
				}
				if err == nil {
					// The file existed in the top layer, so that's a modification

					// However, if it's a directory, maybe it wasn't actually modified.
					// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
					if stat.IsDir() && f.IsDir() {
						if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
							// Both directories are the same, don't record the change
							return nil
						}
					}
					change.Kind = ChangeModify
					break
				}
			}
		}

		// Record change
		changes = append(changes, change)
		return nil
	})
	if err != nil {
		return nil, err
	}
	return changes, nil
}

func newFilesystem(rootfs string, rwpath string, layers []string) *Filesystem {
	return &Filesystem{
		RootFS: rootfs,
		RWPath: rwpath,
		Layers: layers,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/user"
	"path/filepath"
	"runtime"
)

// HomeDir is the user's home directory
var HomeDir = homeDir()

// AppDir is the .heroku path
func AppDir() string {
	if runtime.GOOS == "windows" {
		dir := os.Getenv("LOCALAPPDATA")
		if dir != "" {
			return filepath.Join(dir, "heroku")
		}
	}
	return filepath.Join(HomeDir, ".heroku")
}

func homeDir() string {
	home := os.Getenv("HOME")
	if home != "" {
		return home
	}
	user, err := user.Current()
	if err != nil {
		panic(err)
	}
	return user.HomeDir
}
<commit_msg>use $XDG_DATA_HOME if available<commit_after>package main

import (
	"os"
	"os/user"
	"path/filepath"
	"runtime"
)

// HomeDir is the user's home directory
var HomeDir = homeDir()

// AppDir is the .heroku path
func AppDir() string {
	if runtime.GOOS == "windows" {
		dir := os.Getenv("LOCALAPPDATA")
		if dir != "" {
			return filepath.Join(dir, "heroku")
		}
	}
	dir := os.Getenv("XDG_DATA_HOME")
	if dir != "" {
		return filepath.Join(dir, "heroku")
	}
	return filepath.Join(HomeDir, ".heroku")
}

func homeDir() string {
	home := os.Getenv("HOME")
	if home != "" {
		return home
	}
	user, err := user.Current()
	if err != nil {
		panic(err)
	}
	return user.HomeDir
}
<|endoftext|>


<commit_before>package test

import (
	"cryptsetup"
	"cryptsetup/devicetypes"
	"testing"
)

func Test_LUKS1_DefaultLUKS1(test *testing.T) {
	luks1 := devicetypes.DefaultLUKS1()

	if luks1.Hash != "sha256" {
		test.Error("Default Hash should be 'sha256'.")
	}
}

func Test_LUKS1_Format(test *testing.T) {
	device, err := cryptsetup.Init(DevicePath)
	if err != nil {
		test.Error(err)
	}

	hashBeforeFormat := getFileMD5(DevicePath, test)

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	if err != nil {
		test.Error(err)
	}

	hashAfterFormat := getFileMD5(DevicePath, test)

	if hashBeforeFormat == hashAfterFormat {
		test.Error("Unsuccessful call to Format() when using LUKS1 parameters.")
	}

	if device.Type() != "LUKS1" {
		test.Error("Expected type: LUKS1.")
	}
}

func Test_LUKS1_ActivateByPassphrase_Deactivate(test *testing.T) {
	device, err := cryptsetup.Init(DevicePath)
	if err != nil {
		test.Error(err)
	}

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	if err != nil {
		test.Error(err)
	}

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	if err != nil {
		test.Error(err)
	}

	err = device.ActivateByPassphrase("testDeviceName", 0, "testPassphrase", cryptsetup.CRYPT_ACTIVATE_READONLY)
	if err != nil {
		test.Error(err)
	}

	err = device.Deactivate("testDeviceName")
	if err != nil {
		test.Error(err)
	}
}

func Test_LUKS1_Load(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	if err != nil {
		test.Error(err)
	}

	luks1 := devicetypes.DefaultLUKS1()
	err = device.Format(luks1, cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	err = device.Load(luks1)
	testWrapper.AssertNoError(err)

	if device.Type() != "LUKS1" {
		test.Error("Expected type: LUKS1.")
	}
}

func Test_LUKS1_AddPassphraseByVolumeKey(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	testWrapper.AssertNoError(err)

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	testWrapper.AssertError(err)
	testWrapper.AssertErrorCodeEquals(err, -22)
}

func Test_LUKS1_AddPassphraseByPassphrase(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	testWrapper.AssertNoError(err)

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByPassphrase(1, "testPassphrase", "secondTestPassphrase")
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByPassphrase(1, "testPassphrase", "secondTestPassphrase")
	testWrapper.AssertError(err)
	testWrapper.AssertErrorCodeEquals(err, -22)
}
<commit_msg>Refactor some more tests.<commit_after>package test

import (
	"cryptsetup"
	"cryptsetup/devicetypes"
	"testing"
)

func Test_LUKS1_DefaultLUKS1(test *testing.T) {
	luks1 := devicetypes.DefaultLUKS1()

	if luks1.Hash != "sha256" {
		test.Error("Default Hash should be 'sha256'.")
	}
}

func Test_LUKS1_Format(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	testWrapper.AssertNoError(err)

	hashBeforeFormat := getFileMD5(DevicePath, test)

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	hashAfterFormat := getFileMD5(DevicePath, test)

	if hashBeforeFormat == hashAfterFormat {
		test.Error("Unsuccessful call to Format() when using LUKS1 parameters.")
	}

	if device.Type() != "LUKS1" {
		test.Error("Expected type: LUKS1.")
	}
}

func Test_LUKS1_ActivateByPassphrase_Deactivate(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	testWrapper.AssertNoError(err)

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	testWrapper.AssertNoError(err)

	err = device.ActivateByPassphrase("testDeviceName", 0, "testPassphrase", cryptsetup.CRYPT_ACTIVATE_READONLY)
	testWrapper.AssertNoError(err)

	err = device.Deactivate("testDeviceName")
	testWrapper.AssertNoError(err)
}

func Test_LUKS1_Load(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	testWrapper.AssertNoError(err)

	luks1 := devicetypes.DefaultLUKS1()
	err = device.Format(luks1, cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	err = device.Load(luks1)
	testWrapper.AssertNoError(err)

	if device.Type() != "LUKS1" {
		test.Error("Expected type: LUKS1.")
	}
}

func Test_LUKS1_AddPassphraseByVolumeKey(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	testWrapper.AssertNoError(err)

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	testWrapper.AssertError(err)
	testWrapper.AssertErrorCodeEquals(err, -22)
}

func Test_LUKS1_AddPassphraseByPassphrase(test *testing.T) {
	testWrapper := TestWrapper{test}

	device, err := cryptsetup.Init(DevicePath)
	testWrapper.AssertNoError(err)

	err = device.Format(devicetypes.DefaultLUKS1(), cryptsetup.DefaultGenericParams())
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByVolumeKey(0, "", "testPassphrase")
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByPassphrase(1, "testPassphrase", "secondTestPassphrase")
	testWrapper.AssertNoError(err)

	err = device.AddPassphraseByPassphrase(1, "testPassphrase", "secondTestPassphrase")
	testWrapper.AssertError(err)
	testWrapper.AssertErrorCodeEquals(err, -22)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Numgrad Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

package parser

import (
	"io"
	"math/big"
	"strings"
	"testing"

	"numgrad.io/lang/token"
)

type scannerTest struct {
	input   string
	token   token.Token
	literal interface{}
}

var scannerBothTests = []scannerTest{
	{"foo", token.Ident, "foo"},
	{"+", token.Add, nil},
	{"*", token.Mul, nil},
	{"bar", token.Ident, "bar"},
	{"9", token.Int, big.NewInt(9)},
	{"+=", token.AddAssign, nil},
	{"++", token.Inc, nil},
	{"break", token.Break, nil},
	{"/* a block comment */", token.Comment, "/* a block comment */"},
	{"(", token.LeftParen, nil},
	{"4.62e12", token.Float, big.NewFloat(4.62e12)},
	{")", token.RightParen, nil},
	{"//final", token.Comment, "//final"},
}

var scannerSepTests = append([]scannerTest{
	{"// a line comment", token.Comment, "// a line comment"},
	{"      // spaced ", token.Comment, "// spaced "},
}, scannerBothTests...)

var scannerJoinTests = append([]scannerTest{}, scannerBothTests...)

func TestScannerSep(t *testing.T) {
	for _, test := range scannerSepTests {
		s := NewScanner([]byte(test.input))
		if err := s.Next(); err != nil {
			t.Errorf("scanning %q: %v", test.input, err)
			continue
		}
		if s.Token != test.token {
			t.Errorf("%q: got %s, want %s", test.input, s.Token, test.token)
		}
		if !equalLiteral(s.Literal, test.literal) {
			t.Errorf("%q literal: got %s, want %s", test.input, s.Literal, test.literal)
		}
		if err := s.Next(); err != io.EOF {
			t.Errorf("%q: expected EOF, got: %v", test.input, err)
		}
	}
}

func TestScannerJoin(t *testing.T) {
	var input []string
	for _, test := range scannerJoinTests {
		input = append(input, test.input)
	}
	s := NewScanner([]byte(strings.Join(input, " ")))
	for _, test := range scannerJoinTests {
		if err := s.Next(); err != nil {
			t.Fatalf("scanning for %q: %v", test.input, err)
		}
		if s.Token != test.token {
			t.Errorf("%q: got %s, want %s", test.input, s.Token, test.token)
		}
		if !equalLiteral(s.Literal, test.literal) {
			t.Errorf("%q literal: got %s, want %s", test.input, s.Literal, test.literal)
		}
	}
}
<commit_msg>parser: scanner tests need to expect injected semicolon<commit_after>// Copyright 2015 The Numgrad Authors. All rights reserved.
// See the LICENSE file for rights to use this source code.

package parser

import (
	"io"
	"math/big"
	"strings"
	"testing"

	"numgrad.io/lang/token"
)

type scannerTest struct {
	input       string
	token       token.Token
	literal     interface{}
	semiFollows bool
}

var scannerBothTests = []scannerTest{
	{"foo", token.Ident, "foo", true},
	{"+", token.Add, nil, false},
	{"*", token.Mul, nil, false},
	{"bar", token.Ident, "bar", true},
	{"9", token.Int, big.NewInt(9), true},
	{"+=", token.AddAssign, nil, false},
	{"++", token.Inc, nil, true},
	{"break", token.Break, nil, true},
	{"/* a block comment */", token.Comment, "/* a block comment */", false},
	{"(", token.LeftParen, nil, false},
	{"4.62e12", token.Float, big.NewFloat(4.62e12), true},
	{")", token.RightParen, nil, true},
	{"//final", token.Comment, "//final", false},
}

var scannerSepTests = append([]scannerTest{
	{"// a line comment", token.Comment, "// a line comment", false},
	{"      // spaced ", token.Comment, "// spaced ", false},
}, scannerBothTests...)

var scannerJoinTests = append([]scannerTest{}, scannerBothTests...)

func TestScannerSep(t *testing.T) {
	for _, test := range scannerSepTests {
		s := NewScanner([]byte(test.input))
		if err := s.Next(); err != nil {
			t.Errorf("scanning %q: %v", test.input, err)
			continue
		}
		if s.Token != test.token {
			t.Errorf("%q: got %s, want %s", test.input, s.Token, test.token)
			continue
		}
		if !equalLiteral(s.Literal, test.literal) {
			t.Errorf("%q literal: got %s, want %s", test.input, s.Literal, test.literal)
			continue
		}
		if test.semiFollows {
			if err := s.Next(); err != nil {
				t.Errorf("%q: error expecting ';': %v", test.input, err)
				continue
			}
			if s.Token != token.Semicolon {
				t.Errorf("%q: expected ';', got: %v", test.input, s.Token)
				continue
			}
		}
		if err := s.Next(); err != io.EOF {
			t.Errorf("%q: expected EOF, got: %v", test.input, err)
		}
	}
}

func TestScannerJoin(t *testing.T) {
	var input []string
	for _, test := range scannerJoinTests {
		input = append(input, test.input)
	}
	s := NewScanner([]byte(strings.Join(input, " ")))
	for _, test := range scannerJoinTests {
		if err := s.Next(); err != nil {
			t.Fatalf("scanning for %q: %v", test.input, err)
		}
		if s.Token != test.token {
			t.Errorf("%q: got %s, want %s", test.input, s.Token, test.token)
		}
		if !equalLiteral(s.Literal, test.literal) {
			t.Errorf("%q literal: got %s, want %s", test.input, s.Literal, test.literal)
		}
	}
}
<|endoftext|>


<commit_before>package kasper

import (
	"log"

	"github.com/Shopify/sarama"
)

type partitionProcessor struct {
	topicProcessor                 *TopicProcessor
	coordinator                    Coordinator
	consumer                       sarama.Consumer
	partitionConsumers             []sarama.PartitionConsumer
	offsetManagers                 map[string]sarama.PartitionOffsetManager
	messageProcessor               MessageProcessor
	inputTopics                    []string
	partition                      int
	inFlightMessageGroups          map[string][]*inFlightMessageGroup
	commitNextInFlightMessageGroup bool
}

func (pp *partitionProcessor) consumerMessageChannels() []<-chan *sarama.ConsumerMessage {
	chans := make([]<-chan *sarama.ConsumerMessage, len(pp.partitionConsumers))
	for i, consumer := range pp.partitionConsumers {
		chans[i] = consumer.Messages()
	}
	return chans
}

func newPartitionProcessor(tp *TopicProcessor, mp MessageProcessor, partition int) *partitionProcessor {
	consumer, err := sarama.NewConsumerFromClient(tp.client)
	if err != nil {
		log.Fatal(err)
	}
	partitionConsumers := make([]sarama.PartitionConsumer, len(tp.inputTopics))
	partitionOffsetManagers := make(map[string]sarama.PartitionOffsetManager)
	for i, topic := range tp.inputTopics {
		pom, err := tp.offsetManager.ManagePartition(string(topic), int32(partition))
		if err != nil {
			log.Fatal(err)
		}
		newestOffset, err := tp.client.GetOffset(string(topic), int32(partition), sarama.OffsetNewest)
		if err != nil {
			log.Fatal(err)
		}
		nextOffset, _ := pom.NextOffset()
		if nextOffset > newestOffset {
			nextOffset = sarama.OffsetNewest
		}
		c, err := consumer.ConsumePartition(string(topic), int32(partition), nextOffset)
		if err != nil {
			log.Fatal(err)
		}
		partitionConsumers[i] = c
		partitionOffsetManagers[topic] = pom
	}
	pp := &partitionProcessor{
		tp,
		nil,
		consumer,
		partitionConsumers,
		partitionOffsetManagers,
		mp,
		tp.inputTopics,
		partition,
		make(map[string][]*inFlightMessageGroup),
		false,
	}
	pp.coordinator = &partitionProcessorCoordinator{pp}
	return pp
}

func (pp *partitionProcessor) process(consumerMessage *sarama.ConsumerMessage) []*sarama.ProducerMessage {
	topicSerde, ok := pp.topicProcessor.config.TopicSerdes[string(consumerMessage.Topic)]
	if !ok {
		log.Fatalf("Could not find Serde for topic '%s'", consumerMessage.Topic)
	}
	incomingMessage := IncomingMessage{
		Topic:     consumerMessage.Topic,
		Partition: int(consumerMessage.Partition),
		Offset:    consumerMessage.Offset,
		Key:       topicSerde.KeySerde.Deserialize(consumerMessage.Key),
		Value:     topicSerde.ValueSerde.Deserialize(consumerMessage.Value),
		Timestamp: consumerMessage.Timestamp,
	}
	sender := newSender(pp, &incomingMessage)
	pp.commitNextInFlightMessageGroup = false
	pp.messageProcessor.Process(incomingMessage, sender, pp.coordinator)
	inFlightMessageGroup := sender.createInFlightMessageGroup(pp.commitNextInFlightMessageGroup)
	pp.inFlightMessageGroups[consumerMessage.Topic] = append(
		pp.inFlightMessageGroups[consumerMessage.Topic],
		inFlightMessageGroup,
	)
	return sender.producerMessages
}

func (pp *partitionProcessor) onProcessCompleted() {
	pp.pruneInFlightMessageGroups()
}

func (pp *partitionProcessor) pruneInFlightMessageGroups() {
	for _, topic := range pp.topicProcessor.inputTopics {
		pp.pruneInFlightMessageGroupsForTopic(topic)
	}
}

func (pp *partitionProcessor) pruneInFlightMessageGroupsForTopic(topic string) {
	for len(pp.inFlightMessageGroups[topic]) > 1 {
		headGroup := pp.inFlightMessageGroups[topic][0]
		nextGroup := pp.inFlightMessageGroups[topic][1]
		if !headGroup.allAcksAreTrue() || !nextGroup.allAcksAreTrue() {
			break
		}
		pp.inFlightMessageGroups[topic] = pp.inFlightMessageGroups[topic][1:]
	}
}

func (pp *partitionProcessor) isReadyForMessage(msg *sarama.ConsumerMessage) bool {
	maxGroups := pp.topicProcessor.config.Config.MaxInFlightMessageGroups
	return len(pp.inFlightMessageGroups[msg.Topic]) <= maxGroups
}

func (pp *partitionProcessor) markOffsetsIfPossible() {
	for _, topic := range pp.topicProcessor.inputTopics {
		pp.markOffsetsForTopicIfPossible(topic)
	}
}

func (pp *partitionProcessor) markOffsetsForTopicIfPossible(topic string) {
	var offset int64 = -1
	for len(pp.inFlightMessageGroups[topic]) > 0 {
		group := pp.inFlightMessageGroups[topic][0]
		if !group.allAcksAreTrue() {
			break
		}
		offset = group.incomingMessage.Offset
		if group.committed && pp.topicProcessor.config.markOffsetsManually() {
			offsetManager := pp.offsetManagers[topic]
			offsetManager.MarkOffset(offset+1, "")
		}
		pp.inFlightMessageGroups[topic] = pp.inFlightMessageGroups[topic][1:]
	}
	if offset != -1 && pp.topicProcessor.config.markOffsetsAutomatically() {
		offsetManager := pp.offsetManagers[topic]
		offsetManager.MarkOffset(offset+1, "")
	}
}

func (pp *partitionProcessor) onProducerAck(sentMessage *sarama.ProducerMessage) {
	incomingMessage := sentMessage.Metadata.(*IncomingMessage)
	foundGroup := false
	for _, group := range pp.inFlightMessageGroups[incomingMessage.Topic] {
		if group.incomingMessage == incomingMessage {
			foundGroup = true
			foundMsg := false
			for _, inFlightMessage := range group.inFlightMessages {
				if inFlightMessage.msg == sentMessage {
					foundMsg = true
					inFlightMessage.ack = true
					break
				}
			}
			if !foundMsg {
				log.Fatal("Could not find producer message in inFlightMessageGroups")
			}
			break
		}
	}
	if !foundGroup {
		log.Fatal("Could not find group in inFlightMessageGroups")
	}
}

func (pp *partitionProcessor) onShutdown() {
	for _, pom := range pp.offsetManagers {
		pom.Close()
	}
	for _, pc := range pp.partitionConsumers {
		pc.Close()
	}
	pp.consumer.Close()
}
<commit_msg>remove useless convertions, error handling<commit_after>package kasper

import (
	"log"

	"github.com/Shopify/sarama"
)

type partitionProcessor struct {
	topicProcessor                 *TopicProcessor
	coordinator                    Coordinator
	consumer                       sarama.Consumer
	partitionConsumers             []sarama.PartitionConsumer
	offsetManagers                 map[string]sarama.PartitionOffsetManager
	messageProcessor               MessageProcessor
	inputTopics                    []string
	partition                      int
	inFlightMessageGroups          map[string][]*inFlightMessageGroup
	commitNextInFlightMessageGroup bool
}

func (pp *partitionProcessor) consumerMessageChannels() []<-chan *sarama.ConsumerMessage {
	chans := make([]<-chan *sarama.ConsumerMessage, len(pp.partitionConsumers))
	for i, consumer := range pp.partitionConsumers {
		chans[i] = consumer.Messages()
	}
	return chans
}

func newPartitionProcessor(tp *TopicProcessor, mp MessageProcessor, partition int) *partitionProcessor {
	consumer, err := sarama.NewConsumerFromClient(tp.client)
	if err != nil {
		log.Fatal(err)
	}
	partitionConsumers := make([]sarama.PartitionConsumer, len(tp.inputTopics))
	partitionOffsetManagers := make(map[string]sarama.PartitionOffsetManager)
	for i, topic := range tp.inputTopics {
		pom, err := tp.offsetManager.ManagePartition(topic, int32(partition))
		if err != nil {
			log.Fatal(err)
		}
		newestOffset, err := tp.client.GetOffset(topic, int32(partition), sarama.OffsetNewest)
		if err != nil {
			log.Fatal(err)
		}
		nextOffset, _ := pom.NextOffset()
		if nextOffset > newestOffset {
			nextOffset = sarama.OffsetNewest
		}
		c, err := consumer.ConsumePartition(topic, int32(partition), nextOffset)
		if err != nil {
			log.Fatal(err)
		}
		partitionConsumers[i] = c
		partitionOffsetManagers[topic] = pom
	}
	pp := &partitionProcessor{
		tp,
		nil,
		consumer,
		partitionConsumers,
		partitionOffsetManagers,
		mp,
		tp.inputTopics,
		partition,
		make(map[string][]*inFlightMessageGroup),
		false,
	}
	pp.coordinator = &partitionProcessorCoordinator{pp}
	return pp
}

func (pp *partitionProcessor) process(consumerMessage *sarama.ConsumerMessage) []*sarama.ProducerMessage {
	topicSerde, ok := pp.topicProcessor.config.TopicSerdes[consumerMessage.Topic]
	if !ok {
		log.Fatalf("Could not find Serde for topic '%s'", consumerMessage.Topic)
	}
	incomingMessage := IncomingMessage{
		Topic:     consumerMessage.Topic,
		Partition: int(consumerMessage.Partition),
		Offset:    consumerMessage.Offset,
		Key:       topicSerde.KeySerde.Deserialize(consumerMessage.Key),
		Value:     topicSerde.ValueSerde.Deserialize(consumerMessage.Value),
		Timestamp: consumerMessage.Timestamp,
	}
	sender := newSender(pp, &incomingMessage)
	pp.commitNextInFlightMessageGroup = false
	pp.messageProcessor.Process(incomingMessage, sender, pp.coordinator)
	inFlightMessageGroup := sender.createInFlightMessageGroup(pp.commitNextInFlightMessageGroup)
	pp.inFlightMessageGroups[consumerMessage.Topic] = append(
		pp.inFlightMessageGroups[consumerMessage.Topic],
		inFlightMessageGroup,
	)
	return sender.producerMessages
}

func (pp *partitionProcessor) onProcessCompleted() {
	pp.pruneInFlightMessageGroups()
}

func (pp *partitionProcessor) pruneInFlightMessageGroups() {
	for _, topic := range pp.topicProcessor.inputTopics {
		pp.pruneInFlightMessageGroupsForTopic(topic)
	}
}

func (pp *partitionProcessor) pruneInFlightMessageGroupsForTopic(topic string) {
	for len(pp.inFlightMessageGroups[topic]) > 1 {
		headGroup := pp.inFlightMessageGroups[topic][0]
		nextGroup := pp.inFlightMessageGroups[topic][1]
		if !headGroup.allAcksAreTrue() || !nextGroup.allAcksAreTrue() {
			break
		}
		pp.inFlightMessageGroups[topic] = pp.inFlightMessageGroups[topic][1:]
	}
}

func (pp *partitionProcessor) isReadyForMessage(msg *sarama.ConsumerMessage) bool {
	maxGroups := pp.topicProcessor.config.Config.MaxInFlightMessageGroups
	return len(pp.inFlightMessageGroups[msg.Topic]) <= maxGroups
}

func (pp *partitionProcessor) markOffsetsIfPossible() {
	for _, topic := range pp.topicProcessor.inputTopics {
		pp.markOffsetsForTopicIfPossible(topic)
	}
}

func (pp *partitionProcessor) markOffsetsForTopicIfPossible(topic string) {
	var offset int64 = -1
	for len(pp.inFlightMessageGroups[topic]) > 0 {
		group := pp.inFlightMessageGroups[topic][0]
		if !group.allAcksAreTrue() {
			break
		}
		offset = group.incomingMessage.Offset
		if group.committed && pp.topicProcessor.config.markOffsetsManually() {
			offsetManager := pp.offsetManagers[topic]
			offsetManager.MarkOffset(offset+1, "")
		}
		pp.inFlightMessageGroups[topic] = pp.inFlightMessageGroups[topic][1:]
	}
	if offset != -1 && pp.topicProcessor.config.markOffsetsAutomatically() {
		offsetManager := pp.offsetManagers[topic]
		offsetManager.MarkOffset(offset+1, "")
	}
}

func (pp *partitionProcessor) onProducerAck(sentMessage *sarama.ProducerMessage) {
	incomingMessage := sentMessage.Metadata.(*IncomingMessage)
	foundGroup := false
	for _, group := range pp.inFlightMessageGroups[incomingMessage.Topic] {
		if group.incomingMessage == incomingMessage {
			foundGroup = true
			foundMsg := false
			for _, inFlightMessage := range group.inFlightMessages {
				if inFlightMessage.msg == sentMessage {
					foundMsg = true
					inFlightMessage.ack = true
					break
				}
			}
			if !foundMsg {
				log.Fatal("Could not find producer message in inFlightMessageGroups")
			}
			break
		}
	}
	if !foundGroup {
		log.Fatal("Could not find group in inFlightMessageGroups")
	}
}

func (pp *partitionProcessor) onShutdown() {
	var err error
	for _, pom := range pp.offsetManagers {
		err = pom.Close()
		if err != nil {
			log.Printf("Cannot close offset manager: %s", err)
		}

	}
	for _, pc := range pp.partitionConsumers {
		err = pc.Close()
		if err != nil {
			log.Printf("Cannot close partition consumer: %s", err)
		}
	}
	err = pp.consumer.Close()
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package cbor

import (
	"io"
	"math"
	"reflect"
	"time"
	"unsafe"

	"github.com/segmentio/objconv/objutil"
)

// Emitter implements a MessagePack emitter that satisfies the objconv.Emitter
// interface.
type Emitter struct {
	w io.Writer
	b [10]byte

	// This stack is used to keep track of the array map lengths being parsed.
	// The sback array is the initial backend array for the stack.
	stack []int
	sback [16]int
}

func NewEmitter(w io.Writer) *Emitter {
	e := &Emitter{w: w}
	e.stack = e.sback[:0]
	return e
}

func (e *Emitter) Reset(w io.Writer) {
	e.w = w
	e.stack = e.stack[:0]
}

func (e *Emitter) EmitNil() (err error) {
	e.b[0] = majorByte(majorType7, svNull)
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitBool(v bool) (err error) {
	if v {
		e.b[0] = majorByte(majorType7, svTrue)
	} else {
		e.b[0] = majorByte(majorType7, svFalse)
	}
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitInt(v int64, _ int) (err error) {
	if v >= 0 {
		return e.emitUint(majorType0, uint64(v))
	}
	return e.emitUint(majorType1, uint64(-(v + 1)))
}

func (e *Emitter) EmitUint(v uint64, _ int) (err error) {
	return e.emitUint(majorType0, v)
}

func (e *Emitter) EmitFloat(v float64, bitSize int) (err error) {
	n := 0

	if bitSize == 32 {
		n = 5
		e.b[0] = majorByte(majorType7, svFloat32)
		putUint32(e.b[1:], math.Float32bits(float32(v)))
	} else {
		n = 9
		e.b[0] = majorByte(majorType7, svFloat64)
		putUint64(e.b[1:], math.Float64bits(v))
	}

	_, err = e.w.Write(e.b[:n])
	return
}

func (e *Emitter) EmitString(v string) (err error) {
	if err = e.emitUint(majorType3, uint64(len(v))); err != nil {
		return
	}
	s := *(*reflect.StringHeader)(unsafe.Pointer(&v))
	b := reflect.SliceHeader{
		Data: s.Data,
		Len:  s.Len,
		Cap:  s.Len,
	}
	_, err = e.w.Write(*((*[]byte)(unsafe.Pointer(&b))))
	return
}

func (e *Emitter) EmitBytes(v []byte) (err error) {
	if err = e.emitUint(majorType2, uint64(len(v))); err != nil {
		return
	}
	if len(v) != 0 {
		_, err = e.w.Write(v)
	}
	return
}

func (e *Emitter) EmitTime(v time.Time) (err error) {
	e.b[0] = majorByte(majorType6, tagDateTime)

	if _, err = e.w.Write(e.b[:1]); err != nil {
		return
	}

	var a [64]byte
	var b = v.AppendFormat(a[:0], time.RFC3339Nano)

	if err = e.emitUint(majorType3, uint64(len(b))); err != nil {
		return
	}

	_, err = e.w.Write(append(e.b[:0], b...))
	return
}

func (e *Emitter) EmitDuration(v time.Duration) (err error) {
	return e.EmitString(string(objutil.AppendDuration(e.b[:0], v)))
}

func (e *Emitter) EmitError(v error) (err error) {
	return e.EmitString(v.Error())
}

func (e *Emitter) EmitArrayBegin(n int) (err error) {
	e.stack = append(e.stack, n)

	if n >= 0 {
		return e.emitUint(majorType4, uint64(n))
	}

	e.b[0] = majorByte(majorType4, 31)
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitArrayEnd() (err error) {
	i := len(e.stack) - 1
	n := e.stack[i]
	e.stack = e.stack[:i]

	if n < 0 {
		e.b[0] = 0xFF
		_, err = e.w.Write(e.b[:1])
	}
	return
}

func (e *Emitter) EmitArrayNext() (err error) {
	return
}

func (e *Emitter) EmitMapBegin(n int) (err error) {
	e.stack = append(e.stack, n)

	if n >= 0 {
		return e.emitUint(majorType5, uint64(n))
	}

	e.b[0] = majorByte(majorType5, 31)
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitMapEnd() (err error) {
	i := len(e.stack) - 1
	n := e.stack[i]
	e.stack = e.stack[:i]

	if n < 0 {
		e.b[0] = 0xFF
		_, err = e.w.Write(e.b[:1])
	}
	return
}

func (e *Emitter) EmitMapValue() (err error) {
	return
}

func (e *Emitter) EmitMapNext() (err error) {
	return
}

func (e *Emitter) emitUint(m byte, v uint64) (err error) {
	var n int

	switch {
	case v <= 23:
		n = 1
		e.b[0] = majorByte(m, byte(v))

	case v <= objutil.Uint8Max:
		n = 2
		e.b[0] = majorByte(m, iUint8)
		e.b[1] = uint8(v)

	case v <= objutil.Uint16Max:
		n = 3
		e.b[0] = majorByte(m, iUint16)
		putUint16(e.b[1:], uint16(v))

	case v <= objutil.Uint32Max:
		n = 5
		e.b[0] = majorByte(m, iUint32)
		putUint32(e.b[1:], uint32(v))

	default:
		n = 9
		e.b[0] = majorByte(m, iUint64)
		putUint64(e.b[1:], v)
	}

	_, err = e.w.Write(e.b[:n])
	return
}
<commit_msg>use a 16 byte internal buffer so we use all the padding<commit_after>package cbor

import (
	"io"
	"math"
	"reflect"
	"time"
	"unsafe"

	"github.com/segmentio/objconv/objutil"
)

// Emitter implements a MessagePack emitter that satisfies the objconv.Emitter
// interface.
type Emitter struct {
	w io.Writer
	b [16]byte

	// This stack is used to keep track of the array map lengths being parsed.
	// The sback array is the initial backend array for the stack.
	stack []int
	sback [16]int
}

func NewEmitter(w io.Writer) *Emitter {
	e := &Emitter{w: w}
	e.stack = e.sback[:0]
	return e
}

func (e *Emitter) Reset(w io.Writer) {
	e.w = w
	e.stack = e.stack[:0]
}

func (e *Emitter) EmitNil() (err error) {
	e.b[0] = majorByte(majorType7, svNull)
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitBool(v bool) (err error) {
	if v {
		e.b[0] = majorByte(majorType7, svTrue)
	} else {
		e.b[0] = majorByte(majorType7, svFalse)
	}
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitInt(v int64, _ int) (err error) {
	if v >= 0 {
		return e.emitUint(majorType0, uint64(v))
	}
	return e.emitUint(majorType1, uint64(-(v + 1)))
}

func (e *Emitter) EmitUint(v uint64, _ int) (err error) {
	return e.emitUint(majorType0, v)
}

func (e *Emitter) EmitFloat(v float64, bitSize int) (err error) {
	n := 0

	if bitSize == 32 {
		n = 5
		e.b[0] = majorByte(majorType7, svFloat32)
		putUint32(e.b[1:], math.Float32bits(float32(v)))
	} else {
		n = 9
		e.b[0] = majorByte(majorType7, svFloat64)
		putUint64(e.b[1:], math.Float64bits(v))
	}

	_, err = e.w.Write(e.b[:n])
	return
}

func (e *Emitter) EmitString(v string) (err error) {
	if err = e.emitUint(majorType3, uint64(len(v))); err != nil {
		return
	}
	s := *(*reflect.StringHeader)(unsafe.Pointer(&v))
	b := reflect.SliceHeader{
		Data: s.Data,
		Len:  s.Len,
		Cap:  s.Len,
	}
	_, err = e.w.Write(*((*[]byte)(unsafe.Pointer(&b))))
	return
}

func (e *Emitter) EmitBytes(v []byte) (err error) {
	if err = e.emitUint(majorType2, uint64(len(v))); err != nil {
		return
	}
	if len(v) != 0 {
		_, err = e.w.Write(v)
	}
	return
}

func (e *Emitter) EmitTime(v time.Time) (err error) {
	e.b[0] = majorByte(majorType6, tagDateTime)

	if _, err = e.w.Write(e.b[:1]); err != nil {
		return
	}

	var a [64]byte
	var b = v.AppendFormat(a[:0], time.RFC3339Nano)

	if err = e.emitUint(majorType3, uint64(len(b))); err != nil {
		return
	}

	_, err = e.w.Write(append(e.b[:0], b...))
	return
}

func (e *Emitter) EmitDuration(v time.Duration) (err error) {
	return e.EmitString(string(objutil.AppendDuration(e.b[:0], v)))
}

func (e *Emitter) EmitError(v error) (err error) {
	return e.EmitString(v.Error())
}

func (e *Emitter) EmitArrayBegin(n int) (err error) {
	e.stack = append(e.stack, n)

	if n >= 0 {
		return e.emitUint(majorType4, uint64(n))
	}

	e.b[0] = majorByte(majorType4, 31)
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitArrayEnd() (err error) {
	i := len(e.stack) - 1
	n := e.stack[i]
	e.stack = e.stack[:i]

	if n < 0 {
		e.b[0] = 0xFF
		_, err = e.w.Write(e.b[:1])
	}
	return
}

func (e *Emitter) EmitArrayNext() (err error) {
	return
}

func (e *Emitter) EmitMapBegin(n int) (err error) {
	e.stack = append(e.stack, n)

	if n >= 0 {
		return e.emitUint(majorType5, uint64(n))
	}

	e.b[0] = majorByte(majorType5, 31)
	_, err = e.w.Write(e.b[:1])
	return
}

func (e *Emitter) EmitMapEnd() (err error) {
	i := len(e.stack) - 1
	n := e.stack[i]
	e.stack = e.stack[:i]

	if n < 0 {
		e.b[0] = 0xFF
		_, err = e.w.Write(e.b[:1])
	}
	return
}

func (e *Emitter) EmitMapValue() (err error) {
	return
}

func (e *Emitter) EmitMapNext() (err error) {
	return
}

func (e *Emitter) emitUint(m byte, v uint64) (err error) {
	var n int

	switch {
	case v <= 23:
		n = 1
		e.b[0] = majorByte(m, byte(v))

	case v <= objutil.Uint8Max:
		n = 2
		e.b[0] = majorByte(m, iUint8)
		e.b[1] = uint8(v)

	case v <= objutil.Uint16Max:
		n = 3
		e.b[0] = majorByte(m, iUint16)
		putUint16(e.b[1:], uint16(v))

	case v <= objutil.Uint32Max:
		n = 5
		e.b[0] = majorByte(m, iUint32)
		putUint32(e.b[1:], uint32(v))

	default:
		n = 9
		e.b[0] = majorByte(m, iUint64)
		putUint64(e.b[1:], v)
	}

	_, err = e.w.Write(e.b[:n])
	return
}
<|endoftext|>


<commit_before>// +build !windows

package util

import (
	"os"
)

// Ensure that the specified path is only accessible to the current user. On
// Unix platforms, this requires nothing since the correct permissions are set
// when the path is created.
func SecurePath(path string) error {
	return nil
}
<commit_msg>Fixed unused import bug.<commit_after>// +build !windows

package util

// Ensure that the specified path is only accessible to the current user. On
// Unix platforms, this requires nothing since the correct permissions are set
// when the path is created.
func SecurePath(path string) error {
	return nil
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Moriyoshi Koizumi
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *    * Neither the name of Elazar Leibovich. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package main

import (
	"crypto"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type CertCache struct {
	CacheDir   string
	Logger     *logrus.Logger
	certs      map[string]*tls.Certificate
	issuerCert *x509.Certificate
	privateKey crypto.PrivateKey
}

const certificateFileName = "cert.pem"
const certificateBlockName = "CERTIFICATE"
const privateKeyFileName = "key.pem"
const privateKeyBlockName = "PRIVATE KEY"

func buildKeyString(hosts []string) string {
	key := strings.Join(hosts, ";")
	return key
}

func (c *CertCache) writeCertificate(key string, cert *tls.Certificate) (err error) {
	leadingDir, ok := c.buildPathToCachedCert(key)
	if !ok {
		return
	}

	leadingDirTmp := leadingDir + "$tmp$"
	err = os.MkdirAll(leadingDirTmp, os.FileMode(0700))
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			os.RemoveAll(leadingDirTmp)
		}
	}()

	certFilePath := filepath.Join(leadingDirTmp, certificateFileName)
	privKeyFilePath := filepath.Join(leadingDirTmp, privateKeyFileName)

	err = func(certFilePath string) (err error) {
		w, err := os.OpenFile(certFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(0600))
		if err != nil {
			return
		}
		defer w.Close()

		for _, x509Cert := range cert.Certificate {
			err = pem.Encode(w, &pem.Block{Type: certificateBlockName, Bytes: x509Cert})
			if err != nil {
				return
			}
			_, err = w.Write([]byte{'\n'})
			if err != nil {
				return
			}
		}
		return
	}(certFilePath)
	if err != nil {
		return
	}

	err = func(privKeyFilePath string) (err error) {
		privKeyBytes, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey)
		if err != nil {
			return
		}

		w, err := os.OpenFile(privKeyFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(0666))
		if err != nil {
			return
		}
		defer w.Close()
		err = pem.Encode(w, &pem.Block{Type: privateKeyBlockName, Bytes: privKeyBytes})
		if err != nil {
			return
		}
		return
	}(privKeyFilePath)
	if err != nil {
		return
	}

	err = os.Rename(leadingDirTmp, leadingDir)
	return
}

func (c *CertCache) buildPathToCachedCert(key string) (string, bool) {
	if c.CacheDir == "" {
		return "", false
	}
	return filepath.Join(c.CacheDir, key), true
}

func (c *CertCache) readAndValidateCertificate(key string, hosts []string, now time.Time) (*tls.Certificate, error) {
	leadingDir, ok := c.buildPathToCachedCert(key)
	if !ok {
		return nil, nil
	}

	if _, err := os.Stat(leadingDir); os.IsNotExist(err) {
		return nil, nil
	}

	var x509Cert *x509.Certificate
	var certDerBytes [][]byte
	{
		certFilePath := filepath.Join(leadingDir, certificateFileName)
		pemBytes, err := ioutil.ReadFile(certFilePath)
		if err != nil {
			return nil, err
		}

		for {
			var pemBlock *pem.Block
			pemBlock, pemBytes = pem.Decode(pemBytes)
			if pemBlock == nil {
				break
			}
			if pemBlock.Type == certificateBlockName {
				certDerBytes = append(certDerBytes, pemBlock.Bytes)
			}
		}
		if len(certDerBytes) == 0 {
			return nil, errors.Errorf("no valid certificate contained in %s", certFilePath)
		}

		x509Cert, err = x509.ParseCertificate(certDerBytes[0])
		if err != nil {
			return nil, errors.Wrapf(err, "invalid certificate found in %s", certFilePath)
		}
		if len(certDerBytes) == 1 && c.issuerCert != nil {
			err = x509Cert.CheckSignatureFrom(c.issuerCert)
			if err != nil {
				return nil, errors.Wrapf(err, "invalid certificate found in %s", certFilePath)
			}
		}

		if !now.Before(x509Cert.NotAfter) {
			return nil, errors.Errorf("ceritificate no longer valid (not after: %s, now: %s)", x509Cert.NotAfter.Local().Format(time.RFC1123), now.Local().Format(time.RFC1123))
		}
	}

	var privKey crypto.PrivateKey
	{
		privKeyFilePath := filepath.Join(leadingDir, privateKeyFileName)
		pemBytes, err := ioutil.ReadFile(privKeyFilePath)
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
		}
		if err == nil {
			b, _ := pem.Decode(pemBytes)
			privKey, err = x509.ParsePKCS8PrivateKey(b.Bytes)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to parse private key %s", privKeyFilePath)
			}
		} else {
			if c != nil {
				privKey = c.privateKey
			}
			err = nil
		}
	}

	if privKey == nil {
		return nil, errors.Errorf("no private key is available (cache is broken)")
	}

outer:
	for _, a := range hosts {
		dnsNameMatched := false
		for _, b := range x509Cert.DNSNames {
			if wildMatch(b, a) {
				dnsNameMatched = true
				break outer
			}
		}
		if !dnsNameMatched {
			dnsNameMatched = wildMatch(x509Cert.Subject.CommonName, a)
		}
		if !dnsNameMatched {
			return nil, errors.Errorf("certificate does not cover the host name %s", a)
		}
	}

	return &tls.Certificate{
		Certificate: certDerBytes,
		PrivateKey:  privKey,
	}, nil
}

func (c *CertCache) evict(key string) error {
	c.Logger.Debugf("evicting cache foe %s", key)
	leadingDir, ok := c.buildPathToCachedCert(key)
	if !ok {
		return nil
	}
	return os.RemoveAll(leadingDir)
}

func (c *CertCache) readCertificate(key string, hosts []string, now time.Time) (cert *tls.Certificate, err error) {
	cert, err = c.readAndValidateCertificate(
		key,
		hosts,
		now,
	)
	if err != nil {
		c.evict(key)
		c.Logger.Warn(err.Error())
		err = nil
	}
	return
}

func (c *CertCache) Put(hosts []string, cert *tls.Certificate) error {
	key := buildKeyString(hosts)
	c.certs[key] = cert
	err := c.writeCertificate(key, cert)
	if err != nil {
		c.Logger.Warn(err.Error())
		err = nil
	}
	return err
}

func (c *CertCache) Get(hosts []string, now time.Time) (cert *tls.Certificate, err error) {
	key := buildKeyString(hosts)
	cert, ok := c.certs[key]
	if !ok {
		c.Logger.Debug("certificate not found in in-process cache")
		cert, err = c.readCertificate(key, hosts, now)
		if err != nil {
			return
		}
		if cert != nil {
			c.certs[key] = cert
		} else {
			c.Logger.Debug("certificate not found in cache directory")
		}
	}
	return
}

func NewCertCache(cacheDir string, logger *logrus.Logger, issuerCert *x509.Certificate, privateKey crypto.PrivateKey) *CertCache {
	return &CertCache{
		CacheDir:   cacheDir,
		Logger:     logger,
		certs:      make(map[string]*tls.Certificate),
		issuerCert: issuerCert,
		privateKey: privateKey,
	}
}
<commit_msg>Fix race condition in certcache<commit_after>/*
 * Copyright (c) 2016 Moriyoshi Koizumi
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *    * Neither the name of Elazar Leibovich. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package main

import (
	"crypto"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

type CertCache struct {
	mu         sync.Mutex
	CacheDir   string
	Logger     *logrus.Logger
	certs      map[string]*tls.Certificate
	issuerCert *x509.Certificate
	privateKey crypto.PrivateKey
}

const certificateFileName = "cert.pem"
const certificateBlockName = "CERTIFICATE"
const privateKeyFileName = "key.pem"
const privateKeyBlockName = "PRIVATE KEY"

func buildKeyString(hosts []string) string {
	key := strings.Join(hosts, ";")
	return key
}

func (c *CertCache) writeCertificate(key string, cert *tls.Certificate) (err error) {
	leadingDir, ok := c.buildPathToCachedCert(key)
	if !ok {
		return
	}

	leadingDirTmp := leadingDir + "$tmp$"
	err = os.MkdirAll(leadingDirTmp, os.FileMode(0700))
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			os.RemoveAll(leadingDirTmp)
		}
	}()

	certFilePath := filepath.Join(leadingDirTmp, certificateFileName)
	privKeyFilePath := filepath.Join(leadingDirTmp, privateKeyFileName)

	err = func(certFilePath string) (err error) {
		w, err := os.OpenFile(certFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(0600))
		if err != nil {
			return
		}
		defer w.Close()

		for _, x509Cert := range cert.Certificate {
			err = pem.Encode(w, &pem.Block{Type: certificateBlockName, Bytes: x509Cert})
			if err != nil {
				return
			}
			_, err = w.Write([]byte{'\n'})
			if err != nil {
				return
			}
		}
		return
	}(certFilePath)
	if err != nil {
		return
	}

	err = func(privKeyFilePath string) (err error) {
		privKeyBytes, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey)
		if err != nil {
			return
		}

		w, err := os.OpenFile(privKeyFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(0666))
		if err != nil {
			return
		}
		defer w.Close()
		err = pem.Encode(w, &pem.Block{Type: privateKeyBlockName, Bytes: privKeyBytes})
		if err != nil {
			return
		}
		return
	}(privKeyFilePath)
	if err != nil {
		return
	}

	err = os.Rename(leadingDirTmp, leadingDir)
	return
}

func (c *CertCache) buildPathToCachedCert(key string) (string, bool) {
	if c.CacheDir == "" {
		return "", false
	}
	return filepath.Join(c.CacheDir, key), true
}

func (c *CertCache) readAndValidateCertificate(key string, hosts []string, now time.Time) (*tls.Certificate, error) {
	leadingDir, ok := c.buildPathToCachedCert(key)
	if !ok {
		return nil, nil
	}

	if _, err := os.Stat(leadingDir); os.IsNotExist(err) {
		return nil, nil
	}

	var x509Cert *x509.Certificate
	var certDerBytes [][]byte
	{
		certFilePath := filepath.Join(leadingDir, certificateFileName)
		pemBytes, err := ioutil.ReadFile(certFilePath)
		if err != nil {
			return nil, err
		}

		for {
			var pemBlock *pem.Block
			pemBlock, pemBytes = pem.Decode(pemBytes)
			if pemBlock == nil {
				break
			}
			if pemBlock.Type == certificateBlockName {
				certDerBytes = append(certDerBytes, pemBlock.Bytes)
			}
		}
		if len(certDerBytes) == 0 {
			return nil, errors.Errorf("no valid certificate contained in %s", certFilePath)
		}

		x509Cert, err = x509.ParseCertificate(certDerBytes[0])
		if err != nil {
			return nil, errors.Wrapf(err, "invalid certificate found in %s", certFilePath)
		}
		if len(certDerBytes) == 1 && c.issuerCert != nil {
			err = x509Cert.CheckSignatureFrom(c.issuerCert)
			if err != nil {
				return nil, errors.Wrapf(err, "invalid certificate found in %s", certFilePath)
			}
		}

		if !now.Before(x509Cert.NotAfter) {
			return nil, errors.Errorf("ceritificate no longer valid (not after: %s, now: %s)", x509Cert.NotAfter.Local().Format(time.RFC1123), now.Local().Format(time.RFC1123))
		}
	}

	var privKey crypto.PrivateKey
	{
		privKeyFilePath := filepath.Join(leadingDir, privateKeyFileName)
		pemBytes, err := ioutil.ReadFile(privKeyFilePath)
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
		}
		if err == nil {
			b, _ := pem.Decode(pemBytes)
			privKey, err = x509.ParsePKCS8PrivateKey(b.Bytes)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to parse private key %s", privKeyFilePath)
			}
		} else {
			if c != nil {
				privKey = c.privateKey
			}
			err = nil
		}
	}

	if privKey == nil {
		return nil, errors.Errorf("no private key is available (cache is broken)")
	}

outer:
	for _, a := range hosts {
		dnsNameMatched := false
		for _, b := range x509Cert.DNSNames {
			if wildMatch(b, a) {
				dnsNameMatched = true
				break outer
			}
		}
		if !dnsNameMatched {
			dnsNameMatched = wildMatch(x509Cert.Subject.CommonName, a)
		}
		if !dnsNameMatched {
			return nil, errors.Errorf("certificate does not cover the host name %s", a)
		}
	}

	return &tls.Certificate{
		Certificate: certDerBytes,
		PrivateKey:  privKey,
	}, nil
}

func (c *CertCache) evict(key string) error {
	c.Logger.Debugf("evicting cache foe %s", key)
	leadingDir, ok := c.buildPathToCachedCert(key)
	if !ok {
		return nil
	}
	return os.RemoveAll(leadingDir)
}

func (c *CertCache) readCertificate(key string, hosts []string, now time.Time) (cert *tls.Certificate, err error) {
	cert, err = c.readAndValidateCertificate(
		key,
		hosts,
		now,
	)
	if err != nil {
		c.evict(key)
		c.Logger.Warn(err.Error())
		err = nil
	}
	return
}

func (c *CertCache) Put(hosts []string, cert *tls.Certificate) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	key := buildKeyString(hosts)
	c.certs[key] = cert
	err := c.writeCertificate(key, cert)
	if err != nil {
		c.Logger.Warn(err.Error())
		err = nil
	}
	return err
}

func (c *CertCache) Get(hosts []string, now time.Time) (cert *tls.Certificate, err error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	key := buildKeyString(hosts)
	cert, ok := c.certs[key]
	if !ok {
		c.Logger.Debug("certificate not found in in-process cache")
		cert, err = c.readCertificate(key, hosts, now)
		if err != nil {
			return
		}
		if cert != nil {
			c.certs[key] = cert
		} else {
			c.Logger.Debug("certificate not found in cache directory")
		}
	}
	return
}

func NewCertCache(cacheDir string, logger *logrus.Logger, issuerCert *x509.Certificate, privateKey crypto.PrivateKey) *CertCache {
	return &CertCache{
		CacheDir:   cacheDir,
		Logger:     logger,
		certs:      make(map[string]*tls.Certificate),
		issuerCert: issuerCert,
		privateKey: privateKey,
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.

// +build !windows

package flock

import (
	"syscall"
)

// Lock is a blocking call to try and take an exclusive file lock. It will wait
// until it is able to obtain the exclusive file lock. It's recommended that
// TryLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already exclusive-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
//
// If the *Flock has a shared lock (RLock), this may transparently replace the
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
// careful when using exclusive locks in conjunction with shared locks
// (RLock()), because calling Unlock() may accidentally release the exclusive
// lock that was once a shared lock.
func (f *Flock) Lock() error {
	return f.lock(&f.l, syscall.LOCK_EX)
}

// RLock is a blocking call to try and take a ahred file lock. It will wait
// until it is able to obtain the shared file lock. It's recommended that
// TryRLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already shared-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) RLock() error {
	return f.lock(&f.r, syscall.LOCK_SH)
}

func (f *Flock) lock(locked *bool, flag int) error {
	f.m.Lock()
	defer f.m.Unlock()

	if *locked {
		return nil
	}

	if f.fh == nil {
		if err := f.setFh(); err != nil {
			return err
		}
	}

	if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
		return err
	}

	*locked = true
	return nil
}

// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
// while it is running the Locked() and RLocked() functions will be blocked.
//
// This function short-circuits if we are unlocked already. If not, it calls
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
// remove the file from disk. It's up to your application to do.
//
// Please note, if your shared lock became an exclusive lock this may
// unintentionally drop the exclusive lock if called by the consumer that
// believes they have a shared lock. Please see Lock() for more details.
func (f *Flock) Unlock() error {
	f.m.Lock()
	defer f.m.Unlock()

	// if we aren't locked or if the lockfile instance is nil
	// just return a nil error because we are unlocked
	if (!f.l && !f.r) || f.fh == nil {
		return nil
	}

	// mark the file as unlocked
	if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil {
		return err
	}

	f.fh.Close()

	f.l = false
	f.r = false
	f.fh = nil

	return nil
}

// TryLock is the preferred function for taking an exclusive file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the exclusive
// file lock, the function will return false instead of waiting for the lock. If
// we get the lock, we also set the *Flock instance as being exclusive-locked.
func (f *Flock) TryLock() (bool, error) {
	return f.try(&f.l, syscall.LOCK_EX)
}

// TryRLock is the preferred function for taking a shared file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the shared file
// lock, the function will return false instead of waiting for the lock. If we
// get the lock, we also set the *Flock instance as being share-locked.
func (f *Flock) TryRLock() (bool, error) {
	return f.try(&f.r, syscall.LOCK_SH)
}

func (f *Flock) try(locked *bool, flag int) (bool, error) {
	f.m.Lock()
	defer f.m.Unlock()

	if *locked {
		return true, nil
	}

	if f.fh == nil {
		if err := f.setFh(); err != nil {
			return false, err
		}
	}

	err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB)

	switch err {
	case syscall.EWOULDBLOCK:
		return false, nil
	case nil:
		*locked = true
		return true, nil
	}

	return false, err
}
<commit_msg>Fix exclusive lock to work on Linux+NFS<commit_after>// Copyright 2015 Tim Heckman. All rights reserved.
// Use of this source code is governed by the BSD 3-Clause
// license that can be found in the LICENSE file.

// +build !windows

package flock

import (
	"os"
	"syscall"
)

// Lock is a blocking call to try and take an exclusive file lock. It will wait
// until it is able to obtain the exclusive file lock. It's recommended that
// TryLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already exclusive-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
//
// If the *Flock has a shared lock (RLock), this may transparently replace the
// shared lock with an exclusive lock on some UNIX-like operating systems. Be
// careful when using exclusive locks in conjunction with shared locks
// (RLock()), because calling Unlock() may accidentally release the exclusive
// lock that was once a shared lock.
func (f *Flock) Lock() error {
	return f.lock(&f.l, syscall.LOCK_EX)
}

// RLock is a blocking call to try and take a ahred file lock. It will wait
// until it is able to obtain the shared file lock. It's recommended that
// TryRLock() be used over this function. This function may block the ability to
// query the current Locked() or RLocked() status due to a RW-mutex lock.
//
// If we are already shared-locked, this function short-circuits and returns
// immediately assuming it can take the mutex lock.
func (f *Flock) RLock() error {
	return f.lock(&f.r, syscall.LOCK_SH)
}

func (f *Flock) lock(locked *bool, flag int) error {
	f.m.Lock()
	defer f.m.Unlock()

	if *locked {
		return nil
	}

	if f.fh == nil {
		if err := f.setFh(); err != nil {
			return err
		}
	}

	if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
		shouldRetry, reopenErr := f.reopenFDOnError(err)
		if reopenErr != nil {
			return reopenErr
		}

		if !shouldRetry {
			return err
		}

		if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil {
			return err
		}
	}

	*locked = true
	return nil
}

// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so
// while it is running the Locked() and RLocked() functions will be blocked.
//
// This function short-circuits if we are unlocked already. If not, it calls
// syscall.LOCK_UN on the file and closes the file descriptor. It does not
// remove the file from disk. It's up to your application to do.
//
// Please note, if your shared lock became an exclusive lock this may
// unintentionally drop the exclusive lock if called by the consumer that
// believes they have a shared lock. Please see Lock() for more details.
func (f *Flock) Unlock() error {
	f.m.Lock()
	defer f.m.Unlock()

	// if we aren't locked or if the lockfile instance is nil
	// just return a nil error because we are unlocked
	if (!f.l && !f.r) || f.fh == nil {
		return nil
	}

	// mark the file as unlocked
	if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil {
		return err
	}

	f.fh.Close()

	f.l = false
	f.r = false
	f.fh = nil

	return nil
}

// TryLock is the preferred function for taking an exclusive file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the exclusive
// file lock, the function will return false instead of waiting for the lock. If
// we get the lock, we also set the *Flock instance as being exclusive-locked.
func (f *Flock) TryLock() (bool, error) {
	return f.try(&f.l, syscall.LOCK_EX)
}

// TryRLock is the preferred function for taking a shared file lock. This
// function takes an RW-mutex lock before it tries to lock the file, so there is
// the possibility that this function may block for a short time if another
// goroutine is trying to take any action.
//
// The actual file lock is non-blocking. If we are unable to get the shared file
// lock, the function will return false instead of waiting for the lock. If we
// get the lock, we also set the *Flock instance as being share-locked.
func (f *Flock) TryRLock() (bool, error) {
	return f.try(&f.r, syscall.LOCK_SH)
}

func (f *Flock) try(locked *bool, flag int) (bool, error) {
	f.m.Lock()
	defer f.m.Unlock()

	if *locked {
		return true, nil
	}

	if f.fh == nil {
		if err := f.setFh(); err != nil {
			return false, err
		}
	}

	var retried bool
retry:
	err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB)

	switch err {
	case syscall.EWOULDBLOCK:
		return false, nil
	case nil:
		*locked = true
		return true, nil
	}
	if !retried {
		if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil {
			return false, reopenErr
		} else if shouldRetry {
			retried = true
			goto retry
		}
	}

	return false, err
}

// reopenFDOnError determines whether we should reopen the file handle
// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c:
//  Since Linux 3.4 (commit 55725513)
//  Probably NFSv4 where flock() is emulated by fcntl().
func (f *Flock) reopenFDOnError(err error) (bool, error) {
	if err != syscall.EIO && err != syscall.EBADF {
		return false, nil
	}
	if st, err := f.fh.Stat(); err == nil {
		// if the file is able to be read and written
		if st.Mode()&0600 == 0600 {
			f.fh.Close()
			f.fh = nil

			// reopen in read-write mode and set the filehandle
			fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600))
			if err != nil {
				return false, err
			}
			f.fh = fh
			return true, nil
		}
	}

	return false, nil
}
<|endoftext|>


<commit_before>package utils

import (
	"container/list"
	"fmt"
	"sync"
)
/**
  队列实现
 */
type Queue struct {
	data *list.List
	lock sync.Mutex
}

func NewQueue() *Queue {
	q := new(Queue)
	q.data = list.New()
	return q
}

func (q *Queue) push(v interface{}) {
	defer 	q.lock.Unlock()
	q.lock.Lock()
	q.data.PushFront(v)
}

func (q *Queue) pop() interface{} {
	defer q.lock.Unlock()
	q.lock.Lock()
	iter := q.data.Back()
	if iter!=nil {

	v := iter.Value
	q.data.Remove(iter)
	return v
	}
	return nil
}

func (q *Queue) dump() {
	for iter := q.data.Back(); iter != nil; iter = iter.Prev() {
		fmt.Println("item:", iter.Value)
	}
}
<commit_msg>fix sync bug for queque<commit_after>package utils

import (
	"container/list"
	"fmt"
	"sync"
)

/**
  队列实现
 */
type Queue struct {
	data *list.List
	lock sync.Mutex
}

func NewQueue() *Queue {
	q := new(Queue)
	q.data = list.New()
	return q
}

func (q *Queue) push(v interface{}) {
	q.lock.Lock()
	q.data.PushFront(v)
	q.lock.Unlock()
}

func (q *Queue) pop() interface{} {
	q.lock.Lock()
	iter := q.data.Back()
	if iter != nil {
		v := iter.Value
		q.data.Remove(iter)
		q.lock.Unlock()
		return v
	}

	q.lock.Unlock()
	return nil
}

func (q *Queue) dump() {
	for iter := q.data.Back(); iter != nil; iter = iter.Prev() {
		fmt.Println("item:", iter.Value)
	}
}
<|endoftext|>