github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/ledgerutil/compare.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ledgerutil
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"encoding/json"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"os"
    16  	"path/filepath"
    17  	"sort"
    18  	"strings"
    19  
    20  	"github.com/golang/protobuf/proto"
    21  	"github.com/hechain20/hechain/common/ledger/util"
    22  	"github.com/hechain20/hechain/core/ledger/kvledger"
    23  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/privacyenabledstate"
    24  	"github.com/hechain20/hechain/internal/fileutil"
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  const (
    29  	// AllPubDiffsByKey - Filename for the json output that contains all public differences ordered by key
    30  	AllPubDiffsByKey = "all_pub_diffs_by_key.json"
    31  	// AllPvtDiffsByKey - Filename for the json output that contains all private differences ordered by key
    32  	AllPvtDiffsByKey = "all_pvt_diffs_by_key.json"
    33  	// FirstDiffsByHeight - Filename for the json output that contains the first n differences ordered by height
    34  	FirstDiffsByHeight = "first_diffs_by_height.json"
    35  )
    36  
    37  // Compare - Compares two ledger snapshots and outputs the differences in snapshot records
    38  // This function will throw an error if the output directory already exist in the outputDirLoc
    39  // Function will return count of -1 if the public state and private state hashes are the same
    40  func Compare(snapshotDir1 string, snapshotDir2 string, outputDirLoc string, firstDiffs int) (count int, outputDirPath string, err error) {
    41  	// firstRecords - Slice of diffRecords that stores found differences based on block height, used to generate first n differences output file
    42  	firstRecords := &firstRecords{records: &diffRecordSlice{}, highestRecord: &diffRecord{}, highestIndex: 0, limit: firstDiffs}
    43  
    44  	// Check the hashes between two files
    45  	hashPath1 := filepath.Join(snapshotDir1, kvledger.SnapshotSignableMetadataFileName)
    46  	hashPath2 := filepath.Join(snapshotDir2, kvledger.SnapshotSignableMetadataFileName)
    47  
    48  	equalPub, equalPvt, channelName, blockHeight, err := hashesEqual(hashPath1, hashPath2)
    49  	if err != nil {
    50  		return 0, "", err
    51  	}
    52  	// Snapshot public and private hashes are the same
    53  	if equalPub && equalPvt {
    54  		return -1, "", nil
    55  	}
    56  
    57  	// Output directory creation
    58  	outputDirName := fmt.Sprintf("%s_%d_comparison", channelName, blockHeight)
    59  	outputDirPath = filepath.Join(outputDirLoc, outputDirName)
    60  
    61  	empty, err := fileutil.CreateDirIfMissing(outputDirPath)
    62  	if err != nil {
    63  		return 0, "", err
    64  	}
    65  	if !empty {
    66  		switch outputDirLoc {
    67  		case ".":
    68  			outputDirLoc = "the current directory"
    69  		case "..":
    70  			outputDirLoc = "the parent directory"
    71  		}
    72  		return 0, "", errors.Errorf("%s already exists in %s. Choose a different location or remove the existing results. Aborting compare", outputDirName, outputDirLoc)
    73  	}
    74  
    75  	// Generate all public data differences between snapshots
    76  	if !equalPub {
    77  		snapshotPubReader1, err := privacyenabledstate.NewSnapshotReader(snapshotDir1,
    78  			privacyenabledstate.PubStateDataFileName, privacyenabledstate.PubStateMetadataFileName)
    79  		if err != nil {
    80  			return 0, "", err
    81  		}
    82  		snapshotPubReader2, err := privacyenabledstate.NewSnapshotReader(snapshotDir2,
    83  			privacyenabledstate.PubStateDataFileName, privacyenabledstate.PubStateMetadataFileName)
    84  		if err != nil {
    85  			return 0, "", err
    86  		}
    87  		outputPubFileWriter, err := findAndWriteDifferences(outputDirPath, AllPubDiffsByKey, snapshotPubReader1, snapshotPubReader2, firstDiffs, firstRecords)
    88  		if err != nil {
    89  			return 0, "", err
    90  		}
    91  		count += outputPubFileWriter.count
    92  	}
    93  
    94  	// Generate all private data differences between snapshots
    95  	if !equalPvt {
    96  		snapshotPvtReader1, err := privacyenabledstate.NewSnapshotReader(snapshotDir1,
    97  			privacyenabledstate.PvtStateHashesFileName, privacyenabledstate.PvtStateHashesMetadataFileName)
    98  		if err != nil {
    99  			return 0, "", err
   100  		}
   101  		snapshotPvtReader2, err := privacyenabledstate.NewSnapshotReader(snapshotDir2,
   102  			privacyenabledstate.PvtStateHashesFileName, privacyenabledstate.PvtStateHashesMetadataFileName)
   103  		if err != nil {
   104  			return 0, "", err
   105  		}
   106  		outputPvtFileWriter, err := findAndWriteDifferences(outputDirPath, AllPvtDiffsByKey, snapshotPvtReader1, snapshotPvtReader2, firstDiffs, firstRecords)
   107  		if err != nil {
   108  			return 0, "", err
   109  		}
   110  		count += outputPvtFileWriter.count
   111  	}
   112  
   113  	// Generate early differences output file
   114  	if firstDiffs != 0 {
   115  		firstDiffsOutputFileWriter, err := newJSONFileWriter(filepath.Join(outputDirPath, FirstDiffsByHeight))
   116  		if err != nil {
   117  			return 0, "", err
   118  		}
   119  		sort.Sort(*firstRecords.records)
   120  		for _, r := range *firstRecords.records {
   121  			firstDiffsOutputFileWriter.addRecord(*r)
   122  		}
   123  		err = firstDiffsOutputFileWriter.close()
   124  		if err != nil {
   125  			return 0, "", err
   126  		}
   127  	}
   128  
   129  	return count, outputDirPath, nil
   130  }
   131  
   132  // Finds the differing records between two snapshot data files using SnapshotReaders and saves differences
   133  // to an output file. Simultaneously, keep track of the first n differences.
   134  func findAndWriteDifferences(outputDirPath string, outputFilename string, snapshotReader1 *privacyenabledstate.SnapshotReader, snapshotReader2 *privacyenabledstate.SnapshotReader,
   135  	firstDiffs int, firstRecords *firstRecords) (outputFileWriter *jsonArrayFileWriter, err error) {
   136  	// Create the output file
   137  	outputFileWriter, err = newJSONFileWriter(filepath.Join(outputDirPath, outputFilename))
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	// Read each snapshot record  to begin looking for differences
   143  	namespace1, snapshotRecord1, err := snapshotReader1.Next()
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	namespace2, snapshotRecord2, err := snapshotReader2.Next()
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	// Main snapshot record comparison loop
   153  	for snapshotRecord1 != nil && snapshotRecord2 != nil {
   154  
   155  		// nsKeys used for comparing snapshot records
   156  		key1 := &nsKey{namespace: namespace1, key: snapshotRecord1.Key}
   157  		key2 := &nsKey{namespace: namespace2, key: snapshotRecord2.Key}
   158  
   159  		// Determine the difference in records by comparing nsKeys
   160  		switch nsKeyCompare(key1, key2) {
   161  
   162  		case 0: // Keys are the same, look for a difference in records
   163  			if !(proto.Equal(snapshotRecord1, snapshotRecord2)) {
   164  				// Keys are the same but records are different
   165  				diffRecord, err := newDiffRecord(namespace1, snapshotRecord1, snapshotRecord2)
   166  				if err != nil {
   167  					return nil, err
   168  				}
   169  				// Add difference to output JSON file
   170  				err = outputFileWriter.addRecord(*diffRecord)
   171  				if err != nil {
   172  					return nil, err
   173  				}
   174  				if firstDiffs != 0 {
   175  					firstRecords.addRecord(diffRecord)
   176  				}
   177  			}
   178  			// Advance both snapshot readers
   179  			namespace1, snapshotRecord1, err = snapshotReader1.Next()
   180  			if err != nil {
   181  				return nil, err
   182  			}
   183  			namespace2, snapshotRecord2, err = snapshotReader2.Next()
   184  			if err != nil {
   185  				return nil, err
   186  			}
   187  
   188  		case 1: // Key 1 is bigger, snapshot 1 is missing a record
   189  			// Snapshot 2 has the missing record, add missing to output JSON file
   190  			diffRecord, err := newDiffRecord(namespace2, nil, snapshotRecord2)
   191  			if err != nil {
   192  				return nil, err
   193  			}
   194  			// Add missing record to output JSON file
   195  			err = outputFileWriter.addRecord(*diffRecord)
   196  			if err != nil {
   197  				return nil, err
   198  			}
   199  			if firstDiffs != 0 {
   200  				firstRecords.addRecord(diffRecord)
   201  			}
   202  			// Advance the second snapshot reader
   203  			namespace2, snapshotRecord2, err = snapshotReader2.Next()
   204  			if err != nil {
   205  				return nil, err
   206  			}
   207  
   208  		case -1: // Key 2 is bigger, snapshot 2 is missing a record
   209  			// Snapshot 1 has the missing record, add missing to output JSON file
   210  			diffRecord, err := newDiffRecord(namespace1, snapshotRecord1, nil)
   211  			if err != nil {
   212  				return nil, err
   213  			}
   214  			// Add missing record to output JSON file
   215  			err = outputFileWriter.addRecord(*diffRecord)
   216  			if err != nil {
   217  				return nil, err
   218  			}
   219  			if firstDiffs != 0 {
   220  				firstRecords.addRecord(diffRecord)
   221  			}
   222  			// Advance the first snapshot reader
   223  			namespace1, snapshotRecord1, err = snapshotReader1.Next()
   224  			if err != nil {
   225  				return nil, err
   226  			}
   227  
   228  		default:
   229  			panic("unexpected code path: bug")
   230  		}
   231  	}
   232  
   233  	// Check for tailing records
   234  	switch {
   235  
   236  	case snapshotRecord1 != nil: // Snapshot 2 is missing a record
   237  		for snapshotRecord1 != nil {
   238  			// Add missing to output JSON file
   239  			diffRecord, err := newDiffRecord(namespace1, snapshotRecord1, nil)
   240  			if err != nil {
   241  				return nil, err
   242  			}
   243  			err = outputFileWriter.addRecord(*diffRecord)
   244  			if err != nil {
   245  				return nil, err
   246  			}
   247  			if firstDiffs != 0 {
   248  				firstRecords.addRecord(diffRecord)
   249  			}
   250  			namespace1, snapshotRecord1, err = snapshotReader1.Next()
   251  			if err != nil {
   252  				return nil, err
   253  			}
   254  		}
   255  
   256  	case snapshotRecord2 != nil: // Snapshot 1 is missing a record
   257  		for snapshotRecord2 != nil {
   258  			// Add missing to output JSON file
   259  			diffRecord, err := newDiffRecord(namespace2, nil, snapshotRecord2)
   260  			if err != nil {
   261  				return nil, err
   262  			}
   263  			err = outputFileWriter.addRecord(*diffRecord)
   264  			if err != nil {
   265  				return nil, err
   266  			}
   267  			if firstDiffs != 0 {
   268  				firstRecords.addRecord(diffRecord)
   269  			}
   270  			namespace2, snapshotRecord2, err = snapshotReader2.Next()
   271  			if err != nil {
   272  				return nil, err
   273  			}
   274  		}
   275  	}
   276  
   277  	err = outputFileWriter.close()
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	return outputFileWriter, nil
   283  }
   284  
   285  // firstRecords is a struct used to hold only the earliest records up to a given limit
   286  type firstRecords struct {
   287  	records       *diffRecordSlice
   288  	highestRecord *diffRecord
   289  	highestIndex  int
   290  	limit         int
   291  }
   292  
   293  func (s *firstRecords) addRecord(r *diffRecord) {
   294  	if len(*s.records) < s.limit {
   295  		*s.records = append(*s.records, r)
   296  		s.setHighestRecord()
   297  	} else {
   298  		if r.lessThan(s.highestRecord) {
   299  			(*s.records)[s.highestIndex] = r
   300  			s.setHighestRecord()
   301  		}
   302  	}
   303  }
   304  
   305  func (s *firstRecords) setHighestRecord() {
   306  	if len(*s.records) == 1 {
   307  		s.highestRecord = (*s.records)[0]
   308  		s.highestIndex = 0
   309  		return
   310  	}
   311  	for i, r := range *s.records {
   312  		if s.highestRecord.lessThan(r) {
   313  			s.highestRecord = r
   314  			s.highestIndex = i
   315  		}
   316  	}
   317  }
   318  
   319  type diffRecordSlice []*diffRecord
   320  
   321  func (s diffRecordSlice) Len() int {
   322  	return len(s)
   323  }
   324  
   325  func (s diffRecordSlice) Swap(i, j int) {
   326  	s[i], s[j] = s[j], s[i]
   327  }
   328  
   329  func (s diffRecordSlice) Less(i, j int) bool {
   330  	return (s[i]).lessThan(s[j])
   331  }
   332  
   333  // diffRecord represents a diverging record in json
   334  type diffRecord struct {
   335  	Namespace string          `json:"namespace,omitempty"`
   336  	Key       string          `json:"key,omitempty"`
   337  	Record1   *snapshotRecord `json:"snapshotrecord1"`
   338  	Record2   *snapshotRecord `json:"snapshotrecord2"`
   339  }
   340  
   341  // Creates a new diffRecord
   342  func newDiffRecord(namespace string, record1 *privacyenabledstate.SnapshotRecord,
   343  	record2 *privacyenabledstate.SnapshotRecord) (*diffRecord, error) {
   344  	var s1, s2 *snapshotRecord = nil, nil // snapshot records
   345  	var k string                          // key
   346  	var err error
   347  
   348  	// Snapshot2 has a missing record
   349  	if record1 != nil {
   350  		k = string(record1.Key)
   351  		s1, err = newSnapshotRecord(record1)
   352  		if err != nil {
   353  			return nil, err
   354  		}
   355  	}
   356  	// Snapshot1 has a missing record
   357  	if record2 != nil {
   358  		k = string(record2.Key)
   359  		s2, err = newSnapshotRecord(record2)
   360  		if err != nil {
   361  			return nil, err
   362  		}
   363  	}
   364  
   365  	return &diffRecord{
   366  		Namespace: namespace,
   367  		Key:       k,
   368  		Record1:   s1,
   369  		Record2:   s2,
   370  	}, nil
   371  }
   372  
   373  // Get height from a diffRecord
   374  func (d *diffRecord) getHeight() (blockNum uint64, txNum uint64) {
   375  	r := earlierRecord(d.Record1, d.Record2)
   376  	return r.BlockNum, r.TxNum
   377  }
   378  
   379  // Returns true if d is an earlier diffRecord than e
   380  func (d *diffRecord) lessThan(e *diffRecord) bool {
   381  	dBlockNum, dTxNum := d.getHeight()
   382  	eBlockNum, eTxNum := e.getHeight()
   383  
   384  	if dBlockNum == eBlockNum {
   385  		return dTxNum <= eTxNum
   386  	}
   387  	return dBlockNum < eBlockNum
   388  }
   389  
   390  // snapshotRecord represents the data of a snapshot record in json
   391  type snapshotRecord struct {
   392  	Value    string `json:"value"`
   393  	BlockNum uint64 `json:"blockNum"`
   394  	TxNum    uint64 `json:"txNum"`
   395  }
   396  
   397  // Returns the snapshotRecord with the earlier height
   398  func earlierRecord(r1 *snapshotRecord, r2 *snapshotRecord) *snapshotRecord {
   399  	if r1 == nil {
   400  		return r2
   401  	}
   402  	if r2 == nil {
   403  		return r1
   404  	}
   405  	// Determine earlier record by block height
   406  	if r1.BlockNum < r2.BlockNum {
   407  		return r1
   408  	}
   409  	if r2.BlockNum < r1.BlockNum {
   410  		return r2
   411  	}
   412  	// Record block heights are the same, determine earlier transaction
   413  	if r1.TxNum < r2.TxNum {
   414  		return r1
   415  	}
   416  	return r2
   417  }
   418  
   419  // Creates a new SnapshotRecord
   420  func newSnapshotRecord(record *privacyenabledstate.SnapshotRecord) (*snapshotRecord, error) {
   421  	blockNum, txNum, err := heightFromBytes(record.Version)
   422  	if err != nil {
   423  		return nil, err
   424  	}
   425  
   426  	return &snapshotRecord{
   427  		Value:    string(record.Value),
   428  		BlockNum: blockNum,
   429  		TxNum:    txNum,
   430  	}, nil
   431  }
   432  
   433  // Obtain the block height and transaction height of a snapshot from its version bytes
   434  func heightFromBytes(b []byte) (uint64, uint64, error) {
   435  	blockNum, n1, err := util.DecodeOrderPreservingVarUint64(b)
   436  	if err != nil {
   437  		return 0, 0, err
   438  	}
   439  	txNum, _, err := util.DecodeOrderPreservingVarUint64(b[n1:])
   440  	if err != nil {
   441  		return 0, 0, err
   442  	}
   443  
   444  	return blockNum, txNum, nil
   445  }
   446  
   447  // nsKey is used to compare between snapshot records using both the namespace and key
   448  type nsKey struct {
   449  	namespace string
   450  	key       []byte
   451  }
   452  
   453  // Compares two nsKeys
   454  // Returns:
   455  // -1 if k1 > k2
   456  // 1 if k1 < k2
   457  // 0 if k1 == k2
   458  func nsKeyCompare(k1, k2 *nsKey) int {
   459  	res := strings.Compare(k1.namespace, k2.namespace)
   460  	if res != 0 {
   461  		return res
   462  	}
   463  	return bytes.Compare(k1.key, k2.key)
   464  }
   465  
   466  // Extracts metadata from provided filepath
   467  func readMetadata(fpath string) (*kvledger.SnapshotSignableMetadata, error) {
   468  	var mdata kvledger.SnapshotSignableMetadata
   469  
   470  	// Open file
   471  	f, err := ioutil.ReadFile(fpath)
   472  	if err != nil {
   473  		return nil, err
   474  	}
   475  	// Unmarshal bytes
   476  	err = json.Unmarshal([]byte(f), &mdata)
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  
   481  	return &mdata, nil
   482  }
   483  
   484  // Compares hashes of snapshots to determine if they can be compared, then returns channel name and block height for the output directory name
   485  // Return values:
   486  // equalPub - True if snapshot public data hashes are the same, false otherwise. If true, public differences will not be generated.
   487  // equalPvt - True if snapshot private data hashes are the same, false otherwise. If true, private differences will not be generated.
   488  // chName - Channel name shared between snapshots, used to name output directory. If channel names are not the same, no comparison is made.
   489  // lastBN - Block height shared between snapshots, used to name output directory. If block heights are not the same, no comparison is made.
   490  func hashesEqual(fpath1 string, fpath2 string) (equalPub bool, equalPvt bool, chName string, lastBN uint64, err error) {
   491  	var mdata1, mdata2 *kvledger.SnapshotSignableMetadata
   492  
   493  	// Read metadata from snapshot metadata filepaths
   494  	mdata1, err = readMetadata(fpath1)
   495  	if err != nil {
   496  		return false, false, "", 0, err
   497  	}
   498  	mdata2, err = readMetadata(fpath2)
   499  	if err != nil {
   500  		return false, false, "", 0, err
   501  	}
   502  
   503  	if mdata1.ChannelName != mdata2.ChannelName {
   504  		return false, false, "", 0, errors.Errorf("the supplied snapshots appear to be non-comparable. Channel names do not match."+
   505  			"\nSnapshot1 channel name: %s\nSnapshot2 channel name: %s", mdata1.ChannelName, mdata2.ChannelName)
   506  	}
   507  
   508  	if mdata1.LastBlockNumber != mdata2.LastBlockNumber {
   509  		return false, false, "", 0, errors.Errorf("the supplied snapshots appear to be non-comparable. Last block numbers do not match."+
   510  			"\nSnapshot1 last block number: %v\nSnapshot2 last block number: %v", mdata1.LastBlockNumber, mdata2.LastBlockNumber)
   511  	}
   512  
   513  	if mdata1.LastBlockHashInHex != mdata2.LastBlockHashInHex {
   514  		return false, false, "", 0, errors.Errorf("the supplied snapshots appear to be non-comparable. Last block hashes do not match."+
   515  			"\nSnapshot1 last block hash: %s\nSnapshot2 last block hash: %s", mdata1.LastBlockHashInHex, mdata2.LastBlockHashInHex)
   516  	}
   517  
   518  	if mdata1.StateDBType != mdata2.StateDBType {
   519  		return false, false, "", 0, errors.Errorf("the supplied snapshots appear to be non-comparable. State db types do not match."+
   520  			"\nSnapshot1 state db type: %s\nSnapshot2 state db type: %s", mdata1.StateDBType, mdata2.StateDBType)
   521  	}
   522  
   523  	pubDataHash1 := mdata1.FilesAndHashes[privacyenabledstate.PubStateDataFileName]
   524  	pubMdataHash1 := mdata1.FilesAndHashes[privacyenabledstate.PubStateMetadataFileName]
   525  	pvtDataHash1 := mdata1.FilesAndHashes[privacyenabledstate.PvtStateHashesFileName]
   526  	pvtMdataHash1 := mdata1.FilesAndHashes[privacyenabledstate.PvtStateHashesMetadataFileName]
   527  
   528  	pubDataHash2 := mdata2.FilesAndHashes[privacyenabledstate.PubStateDataFileName]
   529  	pubMdataHash2 := mdata2.FilesAndHashes[privacyenabledstate.PubStateMetadataFileName]
   530  	pvtDataHash2 := mdata2.FilesAndHashes[privacyenabledstate.PvtStateHashesFileName]
   531  	pvtMdataHash2 := mdata2.FilesAndHashes[privacyenabledstate.PvtStateHashesMetadataFileName]
   532  
   533  	equalPub = pubDataHash1 == pubDataHash2 && pubMdataHash1 == pubMdataHash2
   534  	equalPvt = pvtDataHash1 == pvtDataHash2 && pvtMdataHash1 == pvtMdataHash2
   535  	return equalPub, equalPvt, mdata1.ChannelName, mdata1.LastBlockNumber, nil
   536  }
   537  
   538  // jsonArrayFileWriter writes a list of diffRecords to a json file
   539  type jsonArrayFileWriter struct {
   540  	file               *os.File
   541  	buffer             *bufio.Writer
   542  	encoder            *json.Encoder
   543  	firstRecordWritten bool
   544  	count              int
   545  }
   546  
   547  func newJSONFileWriter(filePath string) (*jsonArrayFileWriter, error) {
   548  	f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0o644)
   549  	if err != nil {
   550  		return nil, err
   551  	}
   552  
   553  	b := bufio.NewWriter(f)
   554  	// Opening bracket for beginning of diffRecord list
   555  	_, err = b.Write([]byte("[\n"))
   556  	if err != nil {
   557  		return nil, err
   558  	}
   559  
   560  	return &jsonArrayFileWriter{
   561  		file:    f,
   562  		buffer:  b,
   563  		encoder: json.NewEncoder(b),
   564  	}, nil
   565  }
   566  
   567  func (w *jsonArrayFileWriter) addRecord(r interface{}) error {
   568  	// Add commas for records after the first in the list
   569  	if w.firstRecordWritten {
   570  		_, err := w.buffer.Write([]byte(",\n"))
   571  		if err != nil {
   572  			return err
   573  		}
   574  	} else {
   575  		w.firstRecordWritten = true
   576  	}
   577  
   578  	err := w.encoder.Encode(r)
   579  	if err != nil {
   580  		return err
   581  	}
   582  	w.count++
   583  
   584  	return nil
   585  }
   586  
   587  func (w *jsonArrayFileWriter) close() error {
   588  	_, err := w.buffer.Write([]byte("]\n"))
   589  	if err != nil {
   590  		return err
   591  	}
   592  
   593  	err = w.buffer.Flush()
   594  	if err != nil {
   595  		return err
   596  	}
   597  
   598  	err = w.file.Sync()
   599  	if err != nil {
   600  		return err
   601  	}
   602  
   603  	err = fileutil.SyncParentDir(w.file.Name())
   604  	if err != nil {
   605  		return err
   606  	}
   607  
   608  	return w.file.Close()
   609  }