...

Source file src/github.com/Microsoft/go-winio/backup.go

Documentation: github.com/Microsoft/go-winio

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

View as plain text