backup.go 7.6 KB

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