@@ -23,6 +23,7 @@ import (
2323 "fmt"
2424 "hash/crc32"
2525 "io"
26+ "math"
2627 "os"
2728 "path/filepath"
2829 "sync"
@@ -79,6 +80,10 @@ type TableManifest struct {
7980type manifestFile struct {
8081 fp * os.File
8182 directory string
83+
84+ // The external magic number used by the application running badger.
85+ externalMagic uint16
86+
8287 // We make this configurable so that unit tests can hit rewrite() code quickly
8388 deletionsRewriteThreshold int
8489
@@ -124,11 +129,12 @@ func openOrCreateManifestFile(opt Options) (
124129 if opt .InMemory {
125130 return & manifestFile {inMemory : true }, Manifest {}, nil
126131 }
127- return helpOpenOrCreateManifestFile (opt .Dir , opt .ReadOnly , manifestDeletionsRewriteThreshold )
132+ return helpOpenOrCreateManifestFile (opt .Dir , opt .ReadOnly , opt .ExternalMagicVersion ,
133+ manifestDeletionsRewriteThreshold )
128134}
129135
130- func helpOpenOrCreateManifestFile (dir string , readOnly bool , deletionsThreshold int ) (
131- * manifestFile , Manifest , error ) {
136+ func helpOpenOrCreateManifestFile (dir string , readOnly bool , extMagic uint16 ,
137+ deletionsThreshold int ) ( * manifestFile , Manifest , error ) {
132138
133139 path := filepath .Join (dir , ManifestFilename )
134140 var flags y.Flags
@@ -144,21 +150,22 @@ func helpOpenOrCreateManifestFile(dir string, readOnly bool, deletionsThreshold
144150 return nil , Manifest {}, fmt .Errorf ("no manifest found, required for read-only db" )
145151 }
146152 m := createManifest ()
147- fp , netCreations , err := helpRewrite (dir , & m )
153+ fp , netCreations , err := helpRewrite (dir , & m , extMagic )
148154 if err != nil {
149155 return nil , Manifest {}, err
150156 }
151157 y .AssertTrue (netCreations == 0 )
152158 mf := & manifestFile {
153159 fp : fp ,
154160 directory : dir ,
161+ externalMagic : extMagic ,
155162 manifest : m .clone (),
156163 deletionsRewriteThreshold : deletionsThreshold ,
157164 }
158165 return mf , m , nil
159166 }
160167
161- manifest , truncOffset , err := ReplayManifestFile (fp )
168+ manifest , truncOffset , err := ReplayManifestFile (fp , extMagic )
162169 if err != nil {
163170 _ = fp .Close ()
164171 return nil , Manifest {}, err
@@ -179,6 +186,7 @@ func helpOpenOrCreateManifestFile(dir string, readOnly bool, deletionsThreshold
179186 mf := & manifestFile {
180187 fp : fp ,
181188 directory : dir ,
189+ externalMagic : extMagic ,
182190 manifest : manifest .clone (),
183191 deletionsRewriteThreshold : deletionsThreshold ,
184192 }
@@ -237,20 +245,27 @@ var syncFunc = func(f *os.File) error { return f.Sync() }
237245// Has to be 4 bytes. The value can never change, ever, anyway.
238246var magicText = [4 ]byte {'B' , 'd' , 'g' , 'r' }
239247
240- // The magic version number.
241- const magicVersion = 8
248+ // The magic version number. It is allocated 2 bytes, so it's value must be <= math.MaxUint16
249+ const badgerMagicVersion = 8
242250
243- func helpRewrite (dir string , m * Manifest ) (* os.File , int , error ) {
251+ func helpRewrite (dir string , m * Manifest , extMagic uint16 ) (* os.File , int , error ) {
244252 rewritePath := filepath .Join (dir , manifestRewriteFilename )
245253 // We explicitly sync.
246254 fp , err := y .OpenTruncFile (rewritePath , false )
247255 if err != nil {
248256 return nil , 0 , err
249257 }
250258
259+ // magic bytes are structured as
260+ // +---------------------+-------------------------+-----------------------+
261+ // | magicText (4 bytes) | externalMagic (2 bytes) | badgerMagic (2 bytes) |
262+ // +---------------------+-------------------------+-----------------------+
263+
264+ y .AssertTrue (badgerMagicVersion <= math .MaxUint16 )
251265 buf := make ([]byte , 8 )
252266 copy (buf [0 :4 ], magicText [:])
253- binary .BigEndian .PutUint32 (buf [4 :8 ], magicVersion )
267+ binary .BigEndian .PutUint16 (buf [4 :6 ], extMagic )
268+ binary .BigEndian .PutUint16 (buf [6 :8 ], badgerMagicVersion )
254269
255270 netCreations := len (m .Tables )
256271 changes := m .asChanges ()
@@ -305,7 +320,7 @@ func (mf *manifestFile) rewrite() error {
305320 if err := mf .fp .Close (); err != nil {
306321 return err
307322 }
308- fp , netCreations , err := helpRewrite (mf .directory , & mf .manifest )
323+ fp , netCreations , err := helpRewrite (mf .directory , & mf .manifest , mf . externalMagic )
309324 if err != nil {
310325 return err
311326 }
@@ -345,7 +360,7 @@ var (
345360// Also, returns the last offset after a completely read manifest entry -- the file must be
346361// truncated at that point before further appends are made (if there is a partial entry after
347362// that). In normal conditions, truncOffset is the file size.
348- func ReplayManifestFile (fp * os.File ) (Manifest , int64 , error ) {
363+ func ReplayManifestFile (fp * os.File , extMagic uint16 ) (Manifest , int64 , error ) {
349364 r := countingReader {wrapped : bufio .NewReader (fp )}
350365
351366 var magicBuf [8 ]byte
@@ -355,14 +370,22 @@ func ReplayManifestFile(fp *os.File) (Manifest, int64, error) {
355370 if ! bytes .Equal (magicBuf [0 :4 ], magicText [:]) {
356371 return Manifest {}, 0 , errBadMagic
357372 }
358- version := y .BytesToU32 (magicBuf [4 :8 ])
359- if version != magicVersion {
373+
374+ extVersion := y .BytesToU16 (magicBuf [4 :6 ])
375+ version := y .BytesToU16 (magicBuf [6 :8 ])
376+
377+ if version != badgerMagicVersion {
360378 return Manifest {}, 0 ,
361379 //nolint:lll
362380 fmt .Errorf ("manifest has unsupported version: %d (we support %d).\n " +
363381 "Please see https://github.com/dgraph-io/badger/blob/master/README.md#i-see-manifest-has-unsupported-version-x-we-support-y-error" +
364382 " on how to fix this." ,
365- version , magicVersion )
383+ version , badgerMagicVersion )
384+ }
385+ if extVersion != extMagic {
386+ return Manifest {}, 0 ,
387+ fmt .Errorf ("Cannot open DB because the external magic number doesn't match. " +
388+ "Expected: %d, version present in manifest: %d\n " , extMagic , extVersion )
366389 }
367390
368391 stat , err := fp .Stat ()
0 commit comments