"Fossies" - the Fresh Open Source Software Archive

Member "geoipupdate-4.3.0/pkg/geoipupdate/database/local_file_writer.go" (16 Apr 2020, 4861 Bytes) of package /linux/misc/geoipupdate-4.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "local_file_writer.go": 4.2.0_vs_4.2.1.

    1 package database
    2 
    3 import (
    4     "crypto/md5"
    5     "fmt"
    6     "hash"
    7     "io"
    8     "log"
    9     "os"
   10     "path/filepath"
   11     "strings"
   12     "time"
   13 
   14     "github.com/gofrs/flock"
   15     "github.com/pkg/errors"
   16 )
   17 
   18 // LocalFileDatabaseWriter is a database.Writer that stores the database to the
   19 // local file system.
   20 type LocalFileDatabaseWriter struct {
   21     filePath      string
   22     lockFilePath  string
   23     verbose       bool
   24     lock          *flock.Flock
   25     oldHash       string
   26     fileWriter    io.Writer
   27     temporaryFile *os.File
   28     md5Writer     hash.Hash
   29 }
   30 
   31 // NewLocalFileDatabaseWriter create a LocalFileDatabaseWriter. It creates the
   32 // necessary lock and temporary files to protect the database from concurrent
   33 // writes.
   34 func NewLocalFileDatabaseWriter(filePath string, lockFilePath string, verbose bool) (*LocalFileDatabaseWriter, error) {
   35     dbWriter := &LocalFileDatabaseWriter{
   36         filePath:     filePath,
   37         lockFilePath: lockFilePath,
   38         verbose:      verbose,
   39     }
   40 
   41     var err error
   42     if dbWriter.lock, err = CreateLockFile(lockFilePath, verbose); err != nil {
   43         return nil, err
   44     }
   45     if err = dbWriter.createOldMD5Hash(); err != nil {
   46         return nil, err
   47     }
   48 
   49     temporaryFilename := fmt.Sprintf("%s.temporary", dbWriter.filePath)
   50     dbWriter.temporaryFile, err = os.OpenFile( //nolint:gosec
   51         temporaryFilename,
   52         os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
   53         0644,
   54     )
   55     if err != nil {
   56         return nil, errors.Wrap(err, "error creating temporary file")
   57     }
   58     dbWriter.md5Writer = md5.New()
   59     dbWriter.fileWriter = io.MultiWriter(dbWriter.md5Writer, dbWriter.temporaryFile)
   60 
   61     return dbWriter, nil
   62 }
   63 
   64 func (writer *LocalFileDatabaseWriter) createOldMD5Hash() error {
   65     currentDatabaseFile, err := os.Open(writer.filePath)
   66     if err != nil {
   67         if os.IsNotExist(err) {
   68             writer.oldHash = ZeroMD5
   69             return nil
   70         }
   71         return errors.Wrap(err, "error opening database")
   72     }
   73 
   74     defer func() {
   75         err := currentDatabaseFile.Close()
   76         if err != nil {
   77             log.Println(errors.Wrap(err, "error closing database"))
   78         }
   79     }()
   80     oldHash := md5.New()
   81     if _, err := io.Copy(oldHash, currentDatabaseFile); err != nil {
   82         return errors.Wrap(err, "error calculating database hash")
   83     }
   84     writer.oldHash = fmt.Sprintf("%x", oldHash.Sum(nil))
   85     if writer.verbose {
   86         log.Printf("Calculated MD5 sum for %s: %s", writer.filePath, writer.oldHash)
   87     }
   88     return nil
   89 }
   90 
   91 // Write writes to the temporary file.
   92 func (writer *LocalFileDatabaseWriter) Write(p []byte) (int, error) {
   93     return writer.fileWriter.Write(p)
   94 }
   95 
   96 // Close closes the temporary file and releases the file lock.
   97 func (writer *LocalFileDatabaseWriter) Close() error {
   98     err := writer.temporaryFile.Close()
   99     if err != nil {
  100         if perr, ok := err.(*os.PathError); !ok || perr.Err != os.ErrClosed {
  101             return errors.Wrap(err, "error closing temporary file")
  102         }
  103     }
  104 
  105     if err := os.Remove(writer.temporaryFile.Name()); err != nil && !os.IsNotExist(err) {
  106         return errors.Wrap(err, "error removing temporary file")
  107     }
  108     if err := writer.lock.Unlock(); err != nil {
  109         return errors.Wrap(err, "error releasing lock file")
  110     }
  111     return nil
  112 }
  113 
  114 // ValidHash checks that the temporary file's MD5 matches the given hash.
  115 func (writer *LocalFileDatabaseWriter) ValidHash(expectedHash string) error {
  116     actualHash := fmt.Sprintf("%x", writer.md5Writer.Sum(nil))
  117     if !strings.EqualFold(actualHash, expectedHash) {
  118         return errors.Errorf("md5 of new database (%s) does not match expected md5 (%s)", actualHash, expectedHash)
  119     }
  120     return nil
  121 }
  122 
  123 // SetFileModificationTime sets the database's file access and modified times
  124 // to the given time.
  125 func (writer *LocalFileDatabaseWriter) SetFileModificationTime(lastModified time.Time) error {
  126     if err := os.Chtimes(writer.filePath, lastModified, lastModified); err != nil {
  127         return errors.Wrap(err, "error setting times on file")
  128     }
  129     return nil
  130 }
  131 
  132 // Commit renames the temporary file to the name of the database file and syncs
  133 // the directory.
  134 func (writer *LocalFileDatabaseWriter) Commit() error {
  135     if err := writer.temporaryFile.Sync(); err != nil {
  136         return errors.Wrap(err, "error syncing temporary file")
  137     }
  138     if err := writer.temporaryFile.Close(); err != nil {
  139         return errors.Wrap(err, "error closing temporary file")
  140     }
  141     if err := os.Rename(writer.temporaryFile.Name(), writer.filePath); err != nil {
  142         return errors.Wrap(err, "error moving database into place")
  143     }
  144 
  145     // fsync the directory. http://austingroupbugs.net/view.php?id=672
  146     dh, err := os.Open(filepath.Dir(writer.filePath))
  147     if err != nil {
  148         return errors.Wrap(err, "error opening database directory")
  149     }
  150     defer func() {
  151         if err := dh.Close(); err != nil {
  152             log.Fatalf("Error closing directory: %+v", errors.Wrap(err, "closing directory"))
  153         }
  154     }()
  155 
  156     // We ignore Sync errors as they primarily happen on file systems that do
  157     // not support sync.
  158     _ = dh.Sync()
  159     return nil
  160 }
  161 
  162 // GetHash returns the hash of the current database file.
  163 func (writer *LocalFileDatabaseWriter) GetHash() string {
  164     return writer.oldHash
  165 }