You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

backup.go 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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 Read(). 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 s, ok := r.r.(io.Seeker); ok {
  65. // Make sure Seek on io.SeekCurrent sometimes succeeds
  66. // before trying the actual seek.
  67. if _, err := s.Seek(0, io.SeekCurrent); err == nil {
  68. if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
  69. return nil, err
  70. }
  71. r.bytesLeft = 0
  72. }
  73. }
  74. if _, err := io.Copy(ioutil.Discard, r); err != nil {
  75. return nil, err
  76. }
  77. }
  78. var wsi win32StreamId
  79. if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
  80. return nil, err
  81. }
  82. hdr := &BackupHeader{
  83. Id: wsi.StreamId,
  84. Attributes: wsi.Attributes,
  85. Size: int64(wsi.Size),
  86. }
  87. if wsi.NameSize != 0 {
  88. name := make([]uint16, int(wsi.NameSize/2))
  89. if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
  90. return nil, err
  91. }
  92. hdr.Name = syscall.UTF16ToString(name)
  93. }
  94. if wsi.StreamId == BackupSparseBlock {
  95. if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
  96. return nil, err
  97. }
  98. hdr.Size -= 8
  99. }
  100. r.bytesLeft = hdr.Size
  101. return hdr, nil
  102. }
  103. // Read reads from the current backup stream.
  104. func (r *BackupStreamReader) Read(b []byte) (int, error) {
  105. if r.bytesLeft == 0 {
  106. return 0, io.EOF
  107. }
  108. if int64(len(b)) > r.bytesLeft {
  109. b = b[:r.bytesLeft]
  110. }
  111. n, err := r.r.Read(b)
  112. r.bytesLeft -= int64(n)
  113. if err == io.EOF {
  114. err = io.ErrUnexpectedEOF
  115. } else if r.bytesLeft == 0 && err == nil {
  116. err = io.EOF
  117. }
  118. return n, err
  119. }
  120. // BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
  121. type BackupStreamWriter struct {
  122. w io.Writer
  123. bytesLeft int64
  124. }
  125. // NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
  126. func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
  127. return &BackupStreamWriter{w, 0}
  128. }
  129. // WriteHeader writes the next backup stream header and prepares for calls to Write().
  130. func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
  131. if w.bytesLeft != 0 {
  132. return fmt.Errorf("missing %d bytes", w.bytesLeft)
  133. }
  134. name := utf16.Encode([]rune(hdr.Name))
  135. wsi := win32StreamId{
  136. StreamId: hdr.Id,
  137. Attributes: hdr.Attributes,
  138. Size: uint64(hdr.Size),
  139. NameSize: uint32(len(name) * 2),
  140. }
  141. if hdr.Id == BackupSparseBlock {
  142. // Include space for the int64 block offset
  143. wsi.Size += 8
  144. }
  145. if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
  146. return err
  147. }
  148. if len(name) != 0 {
  149. if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
  150. return err
  151. }
  152. }
  153. if hdr.Id == BackupSparseBlock {
  154. if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
  155. return err
  156. }
  157. }
  158. w.bytesLeft = hdr.Size
  159. return nil
  160. }
  161. // Write writes to the current backup stream.
  162. func (w *BackupStreamWriter) Write(b []byte) (int, error) {
  163. if w.bytesLeft < int64(len(b)) {
  164. return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
  165. }
  166. n, err := w.w.Write(b)
  167. w.bytesLeft -= int64(n)
  168. return n, err
  169. }
  170. // BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
  171. type BackupFileReader struct {
  172. f *os.File
  173. includeSecurity bool
  174. ctx uintptr
  175. }
  176. // NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
  177. // Read will attempt to read the security descriptor of the file.
  178. func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
  179. r := &BackupFileReader{f, includeSecurity, 0}
  180. return r
  181. }
  182. // Read reads a backup stream from the file by calling the Win32 API BackupRead().
  183. func (r *BackupFileReader) Read(b []byte) (int, error) {
  184. var bytesRead uint32
  185. err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
  186. if err != nil {
  187. return 0, &os.PathError{"BackupRead", r.f.Name(), err}
  188. }
  189. runtime.KeepAlive(r.f)
  190. if bytesRead == 0 {
  191. return 0, io.EOF
  192. }
  193. return int(bytesRead), nil
  194. }
  195. // Close frees Win32 resources associated with the BackupFileReader. It does not close
  196. // the underlying file.
  197. func (r *BackupFileReader) Close() error {
  198. if r.ctx != 0 {
  199. backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
  200. runtime.KeepAlive(r.f)
  201. r.ctx = 0
  202. }
  203. return nil
  204. }
  205. // BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
  206. type BackupFileWriter struct {
  207. f *os.File
  208. includeSecurity bool
  209. ctx uintptr
  210. }
  211. // NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
  212. // Write() will attempt to restore the security descriptor from the stream.
  213. func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
  214. w := &BackupFileWriter{f, includeSecurity, 0}
  215. return w
  216. }
  217. // Write restores a portion of the file using the provided backup stream.
  218. func (w *BackupFileWriter) Write(b []byte) (int, error) {
  219. var bytesWritten uint32
  220. err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
  221. if err != nil {
  222. return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
  223. }
  224. runtime.KeepAlive(w.f)
  225. if int(bytesWritten) != len(b) {
  226. return int(bytesWritten), errors.New("not all bytes could be written")
  227. }
  228. return len(b), nil
  229. }
  230. // Close frees Win32 resources associated with the BackupFileWriter. It does not
  231. // close the underlying file.
  232. func (w *BackupFileWriter) Close() error {
  233. if w.ctx != 0 {
  234. backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
  235. runtime.KeepAlive(w.f)
  236. w.ctx = 0
  237. }
  238. return nil
  239. }
  240. // OpenForBackup opens a file or directory, potentially skipping access checks if the backup
  241. // or restore privileges have been acquired.
  242. //
  243. // If the file opened was a directory, it cannot be used with Readdir().
  244. func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
  245. winPath, err := syscall.UTF16FromString(path)
  246. if err != nil {
  247. return nil, err
  248. }
  249. h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
  250. if err != nil {
  251. err = &os.PathError{Op: "open", Path: path, Err: err}
  252. return nil, err
  253. }
  254. return os.NewFile(uintptr(h), path), nil
  255. }