storage.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. // Copyright (c) 2011 CZ.NIC z.s.p.o. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // blame: jnml, labs.nic.cz
  5. // WIP: Package storage defines and implements storage providers and store accessors.
  6. package storage
  7. import (
  8. "os"
  9. "sync"
  10. "time"
  11. )
  12. // FileInfo is a type implementing os.FileInfo which has setable fields, like
  13. // the older os.FileInfo used to have. It is used wehere e.g. the Size is
  14. // needed to be faked (encapsulated/memory only file, file cache, etc.).
  15. type FileInfo struct {
  16. FName string // base name of the file
  17. FSize int64 // length in bytes
  18. FMode os.FileMode // file mode bits
  19. FModTime time.Time // modification time
  20. FIsDir bool // abbreviation for Mode().IsDir()
  21. sys interface{} // underlying data source (can be nil)
  22. }
  23. // NewFileInfo creates FileInfo from os.FileInfo fi.
  24. func NewFileInfo(fi os.FileInfo, sys interface{}) *FileInfo {
  25. return &FileInfo{fi.Name(), fi.Size(), fi.Mode(), fi.ModTime(), fi.IsDir(), sys}
  26. }
  27. // Implementation of os.FileInfo
  28. func (fi *FileInfo) Name() string {
  29. return fi.FName
  30. }
  31. // Implementation of os.FileInfo
  32. func (fi *FileInfo) Size() int64 {
  33. return fi.FSize
  34. }
  35. // Implementation of os.FileInfo
  36. func (fi *FileInfo) Mode() os.FileMode {
  37. return fi.FMode
  38. }
  39. // Implementation of os.FileInfo
  40. func (fi *FileInfo) ModTime() time.Time {
  41. return fi.FModTime
  42. }
  43. // Implementation of os.FileInfo
  44. func (fi *FileInfo) IsDir() bool {
  45. return fi.FIsDir
  46. }
  47. func (fi *FileInfo) Sys() interface{} {
  48. return fi.sys
  49. }
  50. // Accessor provides I/O methods to access a store.
  51. type Accessor interface {
  52. // Close closes the store, rendering it unusable for I/O. It returns an
  53. // error, if any.
  54. Close() error
  55. // Name returns the name of the file as presented to Open.
  56. Name() string
  57. // ReadAt reads len(b) bytes from the store starting at byte offset off.
  58. // It returns the number of bytes read and the error, if any.
  59. // EOF is signaled by a zero count with err set to os.EOF.
  60. // ReadAt always returns a non-nil Error when n != len(b).
  61. ReadAt(b []byte, off int64) (n int, err error)
  62. // Stat returns the FileInfo structure describing the store. It returns
  63. // the os.FileInfo and an error, if any.
  64. Stat() (fi os.FileInfo, err error)
  65. // Sync commits the current contents of the store to stable storage.
  66. // Typically, this means flushing the file system's in-memory copy of
  67. // recently written data to disk.
  68. Sync() (err error)
  69. // Truncate changes the size of the store. It does not change the I/O
  70. // offset.
  71. Truncate(size int64) error
  72. // WriteAt writes len(b) bytes to the store starting at byte offset off.
  73. // It returns the number of bytes written and an error, if any.
  74. // WriteAt returns a non-nil Error when n != len(b).
  75. WriteAt(b []byte, off int64) (n int, err error)
  76. // Before every [structural] change of a store the BeginUpdate is to be
  77. // called and paired with EndUpdate after the change makes the store's
  78. // state consistent again. Invocations of BeginUpdate may nest. On
  79. // invoking the last non nested EndUpdate an implicit "commit" should
  80. // be performed by the store/provider. The concrete mechanism is
  81. // unspecified. It could be for example a write-ahead log. Stores may
  82. // implement BeginUpdate and EndUpdate as a (documented) no op.
  83. BeginUpdate() error
  84. EndUpdate() error
  85. }
  86. // Mutate is a helper/wrapper for executing f in between a.BeginUpdate and
  87. // a.EndUpdate. Any parameters and/or return values except an error should be
  88. // captured by a function literal passed as f. The returned err is either nil
  89. // or the first non nil error returned from the sequence of execution:
  90. // BeginUpdate, [f,] EndUpdate. The pair BeginUpdate/EndUpdate *is* invoked
  91. // always regardles of any possible errors produced. Mutate doesn't handle
  92. // panic, it should be used only with a function [literal] which doesn't panic.
  93. // Otherwise the pairing of BeginUpdate/EndUpdate is not guaranteed.
  94. //
  95. // NOTE: If BeginUpdate, which is invoked before f, returns a non-nil error,
  96. // then f is not invoked at all (but EndUpdate still is).
  97. func Mutate(a Accessor, f func() error) (err error) {
  98. defer func() {
  99. if e := a.EndUpdate(); e != nil && err == nil {
  100. err = e
  101. }
  102. }()
  103. if err = a.BeginUpdate(); err != nil {
  104. return
  105. }
  106. return f()
  107. }
  108. // LockedMutate wraps Mutate in yet another layer consisting of a
  109. // l.Lock/l.Unlock pair. All other limitations apply as in Mutate, e.g. no
  110. // panics are allowed to happen - otherwise no guarantees can be made about
  111. // Unlock matching the Lock.
  112. func LockedMutate(a Accessor, l sync.Locker, f func() error) (err error) {
  113. l.Lock()
  114. defer l.Unlock()
  115. return Mutate(a, f)
  116. }