// Create attempts to create a FileJournal at the given path, creating // any subdirectories needed by the path. An implementation of ValueType // must be given that defines the type of data to be stored. The // time units between each data point must also be given. For a time // series file that records data points every 60 seconds must have interval // set to 60. The meta parameter is a value defined by the application. func Create(path string, interval int64, factory ValueType, meta []int64) (*FileJournal, error) { // Create the base directory, if needed dir := filepath.Dir(path) dirInfo, err := os.Stat(dir) if os.IsNotExist(err) { err2 := os.MkdirAll(dir, 0777) if err2 != nil { return nil, err } } else if err != nil { return nil, err } else if !dirInfo.IsDir() { return nil, fmt.Errorf("File in the way of directory creation: %s", dirInfo.Name()) } if len(meta) > MaxMeta { return nil, fmt.Errorf("Length of metadata slice too long") } // Open a file handle -- truncates existing file, lock new file fd, err := os.Create(path) if err != nil { return nil, err } err = lock.Exclusive(fd) if err != nil { fd.Close() return nil, err } // Allocate and fill in our structs j := FileJournal{ header: FileHeader{ Magic: Magic, Version: Version, Type: factory.Type(), Width: factory.Width(), Interval: interval, Epoch: 0, }, fd: fd, readonly: false, points: 0, factory: factory, } copy(j.header.Meta[:], meta) // Write out the header err = binary.Write(j.fd, binary.LittleEndian, j.header) if err != nil { return nil, err } j.fd.Sync() return &j, nil }
// Open finds the time series journal referenced by the given path, opens // the file and returns a FileJournal struct and any possible error. Try to // open the underlying file read/write. If that fails, open the file // read-only which means Write() calls will return an error. func Open(path string) (*FileJournal, error) { readonly := false fd, err := os.OpenFile(path, os.O_RDWR, 0666) if os.IsPermission(err) { fd, err = os.Open(path) readonly = true } if err != nil { return nil, err } if readonly { err = lock.Share(fd) } else { err = lock.Exclusive(fd) } if err != nil { fd.Close() return nil, err } j := FileJournal{} j.fd = fd j.readonly = readonly err = binary.Read(j.fd, binary.LittleEndian, &(j.header)) if err != nil { // We couldn't fill the header struct -- corrupt file? return nil, err } if j.header.Magic != Magic { return nil, fmt.Errorf("Not a journal timeseries: %s", path) } // Type factory j.factory = GetValueType(j.header.Type, j.header.Width) // How large are we? stat, err := j.fd.Stat() if err != nil { return nil, err } if (stat.Size()-HeaderSize)%int64(j.header.Width) != 0 { // XXX: How can we recover from a partial Write()? return nil, fmt.Errorf("Corrupt or partial data!") } j.points = (stat.Size() - HeaderSize) / int64(j.header.Width) return &j, nil }