Skip to content

Commit

Permalink
Added a readonly option to verify that allows you to run it while the…
Browse files Browse the repository at this point in the history
… server is up
  • Loading branch information
fredli74 committed Jan 5, 2017
1 parent c9d699a commit 48c2f9e
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 41 deletions.
6 changes: 3 additions & 3 deletions server/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ func appendDatasetTx(accountNameH core.Byte128, datasetName core.String, tx dbTx
func updateInfoFile(accountNameH core.Byte128, datasetName core.String) {
collection := readDBFile(accountNameH, datasetName)
if collection == nil {
panic("updateInfoFile was called on a DB file which cannot be opened")
abort("updateInfoFile was called on a DB file which cannot be opened")
}

// Now also update account info
Expand Down Expand Up @@ -522,7 +522,7 @@ func (handler *AccountHandler) CollectAllRootBlocks(skipInvalid bool) (rootBlock
datasetName := getDatasetNameFromFile(name)
collection := readDBFile(accountNameH, datasetName)
if collection == nil {
panic("CollectAllRootBlocks was called on a DB file which cannot be opened")
abort("CollectAllRootBlocks was called on a DB file which cannot be opened")
}
for _, e := range collection.States {
if e.StateFlags&core.StateFlagInvalid == core.StateFlagInvalid {
Expand Down Expand Up @@ -603,7 +603,7 @@ func (handler *AccountHandler) RebuildAccountFiles() (rootBlocks []BlockSource)
func (handler *AccountHandler) InvalidateDatasetState(accountNameH core.Byte128, datasetName core.String, stateID core.Byte128) {
collection := readDBFile(accountNameH, datasetName)
if collection == nil {
panic("InvalidateDatasetState was called on a DB file which cannot be opened")
abort("InvalidateDatasetState was called on a DB file which cannot be opened")
}
for i, s := range collection.States {
if s.State.StateID.Compare(stateID) == 0 {
Expand Down
60 changes: 38 additions & 22 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func handleConnection(conn net.Conn) {
reply.Data = &core.MsgServerError{"Unable to verify blockID"}
}
default:
panic(errors.New("ASSERT: Well if we reach this point, we have not implemented everything correctly (missing " + incoming.String() + ")"))
ASSERT(false, incoming.String()) // Well if we reach this point, we have not implemented everything correctly
}
}
}
Expand Down Expand Up @@ -302,14 +302,14 @@ func run() (returnValue int) {
serverAddr := net.TCPAddr{nil, int(serverPort), ""}

if lock, err := lockfile.Lock(filepath.Join(datDirectory, "hashbox.lck")); err != nil {
panic(err)
abort("%v", err)
} else {
defer lock.Unlock()
}

var listener *net.TCPListener
if listener, err = net.ListenTCP("tcp", &serverAddr); err != nil {
panic(errors.New(fmt.Sprintf("Error listening: %v", err.Error())))
abort("Error listening: %v", err.Error())
}
core.Log(core.LogInfo, "%s is listening on %s", cmd.Title, listener.Addr().String())

Expand Down Expand Up @@ -357,17 +357,17 @@ func run() (returnValue int) {
// It should be an interface to an adminsitrative tool instead
cmd.Command("adduser", "<username> <password>", func() {
if lock, err := lockfile.Lock(filepath.Join(datDirectory, "hashbox.lck")); err != nil {
panic(err)
abort("%v", err)
} else {
defer lock.Unlock()
}

if len(cmd.Args) < 4 {
panic(errors.New("Missing argument to adduser command"))
abort("Missing argument to adduser command")
}

if (!accountHandler.SetInfo(AccountInfo{AccountName: core.String(cmd.Args[2]), AccessKey: core.GenerateAccessKey(cmd.Args[2], cmd.Args[3])})) {
panic(errors.New("Error creating account"))
abort("Error creating account")
}
accountNameH := core.Hash([]byte(cmd.Args[2]))
dataEncryptionKey := core.GenerateDataEncryptionKey()
Expand All @@ -378,7 +378,7 @@ func run() (returnValue int) {
blockData.Write(dataEncryptionKey[:])
block := core.NewHashboxBlock(core.BlockDataTypeRaw, blockData, nil)
if !storageHandler.writeBlock(block) {
panic(errors.New("Error writing key block"))
abort("Error writing key block")
}
err := accountHandler.AddDatasetState(accountNameH, core.String("\x07HASHBACK_DEK"), core.DatasetState{BlockID: block.BlockID})
abortOn(err)
Expand All @@ -397,7 +397,7 @@ func run() (returnValue int) {
cmd.IntOption("threshold", "gc", "<percentage>", "Compact minimum dead space threshold", &optGcDeadSkip, cmd.Standard)
cmd.Command("gc", "", func() {
if lock, err := lockfile.Lock(filepath.Join(datDirectory, "hashbox.lck")); err != nil {
panic(err)
abort("%v", err)
} else {
defer lock.Unlock()
}
Expand Down Expand Up @@ -427,23 +427,19 @@ func run() (returnValue int) {
endfile := int32(-1)
if len(cmd.Args) > 2 {
i, err := strconv.ParseInt(cmd.Args[2], 0, 32)
if err != nil {
panic(err)
}
abortOn(err)
startfile = int32(i)
core.Log(core.LogInfo, "Starting from file #%d (%08x)", startfile, startfile)
}
if len(cmd.Args) > 3 {
i, err := strconv.ParseInt(cmd.Args[3], 0, 32)
if err != nil {
panic(err)
}
abortOn(err)
endfile = int32(i)
core.Log(core.LogInfo, "Stopping after file #%d (%08x)", endfile, endfile)
}

if lock, err := lockfile.Lock(filepath.Join(datDirectory, "hashbox.lck")); err != nil {
panic(err)
abort("%v", err)
} else {
defer lock.Unlock()
}
Expand All @@ -467,30 +463,50 @@ func run() (returnValue int) {
})

optVerifyContent := false
optReadOnly := true
cmd.BoolOption("content", "verify", "Uncompress and verifying block content", &optVerifyContent, cmd.Standard)
cmd.BoolOption("readonly", "verify", "Do not invalidate broken block trees", &optReadOnly, cmd.Standard)
cmd.Command("verify", "", func() {
if !optReadOnly {
if lock, err := lockfile.Lock(filepath.Join(datDirectory, "hashbox.lck")); err != nil {
abort("%v", err)
} else {
defer lock.Unlock()
}
}

errorCount := 0
start := time.Now()

core.Log(core.LogInfo, "Verifying dataset storage")

verifiedBlocks := make(map[core.Byte128]bool) // Keep track of verifiedBlocks blocks
rootlist := []BlockSource{}
if optReadOnly {
rootlist = accountHandler.CollectAllRootBlocks(false)
} else {
rootlist = accountHandler.RebuildAccountFiles()
}

core.Log(core.LogInfo, "Verifying dataset storage")
rootlist := accountHandler.RebuildAccountFiles()
for i, r := range rootlist {
tag := fmt.Sprintf("%s.%s.%x", r.AccountName, r.DatasetName, r.StateID[:])
core.Log(core.LogDebug, "Verify data referenced by %s", tag)
if err := storageHandler.CheckBlockTree(r.BlockID, verifiedBlocks, optVerifyContent); err != nil {
core.Log(core.LogWarning, "Dataset %s is marked as invalid: %v", tag, err)
accountHandler.InvalidateDatasetState(r.AccountNameH, r.DatasetName, r.StateID)
errorCount++
if err := storageHandler.CheckBlockTree(r.BlockID, verifiedBlocks, optVerifyContent, optReadOnly); err != nil {
if optReadOnly {
abort("%v", err)
} else {
core.Log(core.LogWarning, "Dataset %s is marked as invalid: %v", tag, err)
accountHandler.InvalidateDatasetState(r.AccountNameH, r.DatasetName, r.StateID)
errorCount++
}
}

p := int(i * 100 / len(rootlist))
fmt.Printf("%d%%\r", p)
}

core.Log(core.LogInfo, "Verifying unreferenced index entries")
storageHandler.CheckIndexes(verifiedBlocks, optVerifyContent)
storageHandler.CheckIndexes(verifiedBlocks, optVerifyContent, optReadOnly)

core.Log(core.LogInfo, "Verify completed in %.1f minutes", time.Since(start).Minutes())
if errorCount > 0 {
Expand Down
34 changes: 18 additions & 16 deletions server/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ func (handler *StorageHandler) dispatcher() {
}
}()
case _, ok := <-handler.signal: // Signal is closed?
if ok {
panic(errors.New("We should not reach this point, it means someone outside this goroutine sent a signal on the channel"))
}
ASSERT(!ok, ok) // We should not reach this point with "ok", it means someone outside this goroutine sent a signal on the channel
return
}
}
Expand All @@ -99,12 +97,12 @@ func (handler *StorageHandler) doCommand(q ChannelCommand) interface{} {
select {
case err := <-handler.signal:
if err != nil {
panic(errors.New("StorageHandler panic: " + err.Error()))
abort("StorageHandler panic: %v", err.Error())
}
default:
switch t := r.(type) {
case error:
panic(errors.New("StorageHandler panic: " + t.Error()))
abort("StorageHandler panic: %v", t.Error())
}
}
}()
Expand Down Expand Up @@ -234,7 +232,7 @@ func (h *storageFileHeader) Unserialize(r io.Reader) (size int) {
size += core.ReadUint32(r, &h.filetype)
size += core.ReadUint32(r, &h.version)
if h.version != storageVersion {
panic(errors.New("Invalid version in dbFileHeader"))
abort("Invalid version in dbFileHeader")
}
size += core.ReadInt64(r, &h.deadspace)
return
Expand Down Expand Up @@ -462,7 +460,7 @@ func (handler *StorageHandler) getNumberedFile(fileType int, fileNumber int32, c
f, err := core.OpenBufferedFile(filename, storageFileTypeInfo[fileType].BufferSize, flag, 0666)
if err != nil {
if create {
panic(err)
abort("%v", err)
} else {
return nil
}
Expand Down Expand Up @@ -1028,7 +1026,7 @@ func (handler *StorageHandler) CompactFile(fileType int, fileNumber int32, lowes
file.Writer.Flush()

entry.ChangeLocation(handler, fileNumber, newOffset)
} else if free, _ := core.FreeSpace(datDirectory); free < int64(entrySize)*2 {
} else if free, _ := core.FreeSpace(datDirectory); free < int64(entrySize)+MINIMUM_DAT_FREE {
core.Log(core.LogWarning, "Unable to move block %x (%d bytes) because there is not enough free space on data path", entryBlockID[:], entrySize)
writeOffset = offset // make sure we point the writer pointer after this block so we do not overwrite it
} else { // found space in a different file, move the block
Expand Down Expand Up @@ -1083,7 +1081,7 @@ func (handler *StorageHandler) CompactAll(fileType int, threshold int) {
core.Log(core.LogInfo, "All %s files compacted, %s released", storageFileTypeInfo[fileType].Extension, core.HumanSize(compacted))
}

func (handler *StorageHandler) checkBlockFromIXEntry(ixEntry *storageIXEntry, verifiedBlocks map[core.Byte128]bool, fullVerify bool) error {
func (handler *StorageHandler) checkBlockFromIXEntry(ixEntry *storageIXEntry, verifiedBlocks map[core.Byte128]bool, fullVerify bool, readOnly bool) error {
err := (func() error {
metaFileNumber, metaOffset := ixEntry.location.Get()
metaEntry, err := handler.readMetaEntry(metaFileNumber, metaOffset)
Expand Down Expand Up @@ -1146,7 +1144,7 @@ func (handler *StorageHandler) checkBlockFromIXEntry(ixEntry *storageIXEntry, ve
if rIX.flags&entryFlagInvalid == entryFlagInvalid {
return errors.New(fmt.Sprintf("Error in block %x, link %x is invalid", ixEntry.blockID[:], r[:]))
}
if err := handler.checkBlockFromIXEntry(rIX, verifiedBlocks, fullVerify); err != nil {
if err := handler.checkBlockFromIXEntry(rIX, verifiedBlocks, fullVerify, readOnly); err != nil {
return err
}
} else if !v {
Expand All @@ -1159,21 +1157,25 @@ func (handler *StorageHandler) checkBlockFromIXEntry(ixEntry *storageIXEntry, ve
verifiedBlocks[ixEntry.blockID] = true
} else {
verifiedBlocks[ixEntry.blockID] = false
core.Log(core.LogDebug, "%v", err)
handler.InvalidateIXEntry(ixEntry.blockID)
if readOnly {
abort("%V", err)
} else {
core.Log(core.LogDebug, "%v", err)
handler.InvalidateIXEntry(ixEntry.blockID)
}
}
return err
}

func (handler *StorageHandler) CheckBlockTree(blockID core.Byte128, verifiedBlocks map[core.Byte128]bool, fullVerify bool) error {
func (handler *StorageHandler) CheckBlockTree(blockID core.Byte128, verifiedBlocks map[core.Byte128]bool, fullVerify bool, readOnly bool) error {
ixEntry, _, _, err := handler.readIXEntry(blockID)
if err != nil {
return err
}
return handler.checkBlockFromIXEntry(ixEntry, verifiedBlocks, fullVerify)
return handler.checkBlockFromIXEntry(ixEntry, verifiedBlocks, fullVerify, readOnly)
}

func (handler *StorageHandler) CheckIndexes(verifiedBlocks map[core.Byte128]bool, fullVerify bool) {
func (handler *StorageHandler) CheckIndexes(verifiedBlocks map[core.Byte128]bool, fullVerify bool, readOnly bool) {
var ixEntry storageIXEntry

for ixFileNumber := int32(0); ; ixFileNumber++ {
Expand Down Expand Up @@ -1208,7 +1210,7 @@ func (handler *StorageHandler) CheckIndexes(verifiedBlocks map[core.Byte128]bool

v, checked := verifiedBlocks[ixEntry.blockID]
if !checked {
if err := handler.checkBlockFromIXEntry(&ixEntry, verifiedBlocks, fullVerify); err != nil {
if err := handler.checkBlockFromIXEntry(&ixEntry, verifiedBlocks, fullVerify, readOnly); err != nil {
core.Log(core.LogWarning, "Block tree for %x was marked invalid: %v", ixEntry.blockID[:], err)
}
} else {
Expand Down

0 comments on commit 48c2f9e

Please sign in to comment.