backup.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package winio
  2. import (
  3. "encoding/binary"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "runtime"
  10. "syscall"
  11. "unicode/utf16"
  12. )
  13. //sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
  14. //sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
  15. const (
  16. BackupData = uint32(iota + 1)
  17. BackupEaData
  18. BackupSecurity
  19. BackupAlternateData
  20. BackupLink
  21. BackupPropertyData
  22. BackupObjectId
  23. BackupReparseData
  24. BackupSparseBlock
  25. BackupTxfsData
  26. StreamSparseAttributes = uint32(8)
  27. )
  28. // BackupHeader represents a backup stream of a file.
  29. type BackupHeader struct {
  30. Id uint32 // The backup stream ID
  31. Attributes uint32 // Stream attributes
  32. Size int64 // The size of the stream in bytes
  33. Name string // The name of the stream (for BackupAlternateData only).
  34. Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
  35. }
  36. type win32StreamId struct {
  37. StreamId uint32
  38. Attributes uint32
  39. Size uint64
  40. NameSize uint32
  41. }
  42. // BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
  43. // of BackupHeader values.
  44. type BackupStreamReader struct {
  45. r io.Reader
  46. bytesLeft int64
  47. }
  48. // NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
  49. func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
  50. return &BackupStreamReader{r, 0}
  51. }
  52. // Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if
  53. // it was not completely read.
  54. func (r *BackupStreamReader) Next() (*BackupHeader, error) {
  55. if r.bytesLeft > 0 {
  56. if _, err := io.Copy(ioutil.Discard, r); err != nil {
  57. return nil, err
  58. }
  59. }
  60. var wsi win32StreamId
  61. if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
  62. return nil, err
  63. }
  64. hdr := &BackupHeader{
  65. Id: wsi.StreamId,
  66. Attributes: wsi.Attributes,
  67. Size: int64(wsi.Size),
  68. }
  69. if wsi.NameSize != 0 {
  70. name := make([]uint16, int(wsi.NameSize/2))
  71. if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
  72. return nil, err
  73. }
  74. hdr.Name = syscall.UTF16ToString(name)
  75. }
  76. if wsi.StreamId == BackupSparseBlock {
  77. if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
  78. return nil, err
  79. }
  80. hdr.Size -= 8
  81. }
  82. r.bytesLeft = hdr.Size
  83. return hdr, nil
  84. }
  85. // Read reads from the current backup stream.
  86. func (r *BackupStreamReader) Read(b []byte) (int, error) {
  87. if r.bytesLeft == 0 {
  88. return 0, io.EOF
  89. }
  90. if int64(len(b)) > r.bytesLeft {
  91. b = b[:r.bytesLeft]
  92. }
  93. n, err := r.r.Read(b)
  94. r.bytesLeft -= int64(n)
  95. if err == io.EOF {
  96. err = io.ErrUnexpectedEOF
  97. } else if r.bytesLeft == 0 && err == nil {
  98. err = io.EOF
  99. }
  100. return n, err
  101. }
  102. // BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
  103. type BackupStreamWriter struct {
  104. w io.Writer
  105. bytesLeft int64
  106. }
  107. // NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
  108. func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
  109. return &BackupStreamWriter{w, 0}
  110. }
  111. // WriteHeader writes the next backup stream header and prepares for calls to Write().
  112. func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
  113. if w.bytesLeft != 0 {
  114. return fmt.Errorf("missing %d bytes", w.bytesLeft)
  115. }
  116. name := utf16.Encode([]rune(hdr.Name))
  117. wsi := win32StreamId{
  118. StreamId: hdr.Id,
  119. Attributes: hdr.Attributes,
  120. Size: uint64(hdr.Size),
  121. NameSize: uint32(len(name) * 2),
  122. }
  123. if hdr.Id == BackupSparseBlock {
  124. // Include space for the int64 block offset
  125. wsi.Size += 8
  126. }
  127. if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
  128. return err
  129. }
  130. if len(name) != 0 {
  131. if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
  132. return err
  133. }
  134. }
  135. if hdr.Id == BackupSparseBlock {
  136. if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
  137. return err
  138. }
  139. }
  140. w.bytesLeft = hdr.Size
  141. return nil
  142. }
  143. // Write writes to the current backup stream.
  144. func (w *BackupStreamWriter) Write(b []byte) (int, error) {
  145. if w.bytesLeft < int64(len(b)) {
  146. return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
  147. }
  148. n, err := w.w.Write(b)
  149. w.bytesLeft -= int64(n)
  150. return n, err
  151. }
  152. // BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
  153. type BackupFileReader struct {
  154. f *os.File
  155. includeSecurity bool
  156. ctx uintptr
  157. }
  158. // NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
  159. // Read will attempt to read the security descriptor of the file.
  160. func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
  161. r := &BackupFileReader{f, includeSecurity, 0}
  162. runtime.SetFinalizer(r, func(r *BackupFileReader) { r.Close() })
  163. return r
  164. }
  165. // Read reads a backup stream from the file by calling the Win32 API BackupRead().
  166. func (r *BackupFileReader) Read(b []byte) (int, error) {
  167. var bytesRead uint32
  168. err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
  169. if err != nil {
  170. return 0, &os.PathError{"BackupRead", r.f.Name(), err}
  171. }
  172. if bytesRead == 0 {
  173. return 0, io.EOF
  174. }
  175. return int(bytesRead), nil
  176. }
  177. // Close frees Win32 resources associated with the BackupFileReader. It does not close
  178. // the underlying file.
  179. func (r *BackupFileReader) Close() error {
  180. if r.ctx != 0 {
  181. backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
  182. r.ctx = 0
  183. }
  184. return nil
  185. }
  186. // BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
  187. type BackupFileWriter struct {
  188. f *os.File
  189. includeSecurity bool
  190. ctx uintptr
  191. }
  192. // NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true,
  193. // Write() will attempt to restore the security descriptor from the stream.
  194. func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
  195. w := &BackupFileWriter{f, includeSecurity, 0}
  196. runtime.SetFinalizer(w, func(w *BackupFileWriter) { w.Close() })
  197. return w
  198. }
  199. // Write restores a portion of the file using the provided backup stream.
  200. func (w *BackupFileWriter) Write(b []byte) (int, error) {
  201. var bytesWritten uint32
  202. err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
  203. if err != nil {
  204. return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
  205. }
  206. if int(bytesWritten) != len(b) {
  207. return int(bytesWritten), errors.New("not all bytes could be written")
  208. }
  209. return len(b), nil
  210. }
  211. // Close frees Win32 resources associated with the BackupFileWriter. It does not
  212. // close the underlying file.
  213. func (w *BackupFileWriter) Close() error {
  214. if w.ctx != 0 {
  215. backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
  216. w.ctx = 0
  217. }
  218. return nil
  219. }