Motr  M0
error_injection.py
Go to the documentation of this file.
1 #!/usr/bin/python3
2 #
3 # Copyright (c) 2020 Seagate Technology LLC and/or its Affiliates
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 # For any questions about this software or licensing,
18 # please email opensource@seagate.com or cortx-questions@seagate.com.
19 #
20 import binascii
21 import sys
22 import os
23 import random
24 import argparse
25 import time
26 import logging
27 
28 timestr = time.strftime("%Y%m%d-%H%M%S")
29 log_filename = "hole_creation_" + timestr + ".log"
30 
31 logger = logging.getLogger()
32 logger.setLevel(logging.DEBUG)
33 
34 fh = logging.FileHandler(log_filename)
35 fh.setLevel(logging.DEBUG)
36 
37 ch = logging.StreamHandler()
38 ch.setLevel(logging.DEBUG)
39 fformatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
40 cformatter = logging.Formatter('%(levelname)s : %(message)s')
41 fh.setFormatter(fformatter)
42 ch.setFormatter(cformatter)
43 logger.addHandler(fh)
44 logger.addHandler(ch)
45 
46 logger.info("***** Script Started *****")
47 
48 parser = argparse.ArgumentParser(description="Basic Arguments to run the script")
49 parser.add_argument('-rn', action='store_true', default=False, dest='random',
50  help='For inducing error at Random place')
51 parser.add_argument('-e', action='store', default=0, type=int, dest='noOfErr',
52  help='How Many number of error do you want to induce in Metadata')
53 parser.add_argument('-rt', action='store', dest='Record_Type',
54  help='Record Type For inducing error at particular record like:'
55  ' BE_BTREE, BE_EMAP, CAS_CTG etc')
56 parser.add_argument('-m', action='store', dest='mfile', help='Metadata Path')
57 parser.add_argument('-v', action='store_true', default=False, dest='verify',
58  help='Read full Metadata and Print all the Records entries counts')
59 parser.add_argument('-a', action='store_true', default=False, dest='allErr',
60  help='Induce Error in All Record at Random place')
61 parser.add_argument('-gmd', action='store_true', default=False, dest='allGMD',
62  help='Induce Error in All GMD type of Record at Random place')
63 parser.add_argument('-dmd', action='store_true', default=False, dest='allDMD',
64  help='Induce Error in All DMD type of Record at Random place')
65 parser.add_argument('-512k', action='store_true', default=False, dest='err512k',
66  help='Induce 512K bytes error in Metadata')
67 parser.add_argument('-huge', action='store_true', default=False, dest='hugeCorruption',
68  help='Induce Huge amount of corruption in Metadata')
69 parser.add_argument('-seed', action='store', default=0, type=float, dest='seed',
70  help='Seed is used to initialize the "random" library:'
71  ' to initialize the random generation')
72 parser.add_argument('-corrupt_emap', action='store', dest='corrupt_emap',
73  help='Induce Error in Emap specified by Cob Id')
74 parser.add_argument('-list_emap', action='store_true', default=False, dest='list_emap',
75  help='Display all Emap keys with device id')
76 parser.add_argument('-parse_size', action='store', dest='parse_size', type=int,
77  help='Limit for metadata parsing size in bytes for list_emap and verify option')
78 parser.add_argument('-offset', action='store', default=0, type=int, dest='seek_offset',
79  help='Starting offset of metadata file in multiple of 8 bytes')
80 
81 args = parser.parse_args()
82 
83 results = parser.parse_args()
84 logger.info('Induce Random Error = {!r}'.format(args.random))
85 logger.info('Number of Error induce = {!r}'.format(args.noOfErr))
86 logger.info('Record Type = {!r}'.format(args.Record_Type))
87 logger.info('Metadata file path = {!r}'.format(args.mfile))
88 logger.info('Verify Record entries = {!r}'.format(args.verify))
89 logger.info('Induce Error in All Record = {!r}'.format(args.allErr))
90 logger.info('Induce Error in GMD Record = {!r}'.format(args.allGMD))
91 logger.info('Induce Error in DMD Record = {!r}'.format(args.allDMD))
92 logger.info('Induce 512k errors = {!r}'.format(args.err512k))
93 logger.info('Induce huge errors = {!r}'.format(args.hugeCorruption))
94 logger.info('Seed for random number = {!r}'.format(args.seed))
95 logger.info('Induce Error in emap by Cob Id = {!r}'.format(args.corrupt_emap))
96 logger.info('List all Emap Keys and Records = {!r}'.format(args.list_emap))
97 logger.info('Limit for parsing size in bytes = {!r}'.format(args.parse_size))
98 logger.info('Metadata seek offset in bytes multiple of 8 bytes = {!r}'.format(args.seek_offset))
99 
100 filename = args.mfile
101 recordType = args.Record_Type
102 noOfErr = args.noOfErr
103 
104 if args.seed != 0:
105  seed = args.seed
106  logger.info("Seed used: {}".format(seed))
107 else:
108  seed = time.time()
109  logger.info("Seed used: {}".format(seed))
110 
111 random.seed(seed)
112 random_system=random.SystemRandom()
113 
114 if not os.walk(filename):
115  logger.error('Failed: The path specified does not exist or Missing file path')
116  sys.exit(1)
117 
118 # M0_FORMAT_HEADER_MAGIC = 0x33011ca5e511de77
119 header = b'33011ca5e511de77'
120 # M0_FORMAT_FOOTER_MAGIC = 0x33f007e7f007e777
121 footer = b'33f007e7f007e777'
122 
123 typeDict = {b'01': 'RPC_PACKET', b'02': 'RPC_ITEM', b'03': 'BE_BTREE',
124  b'04': 'BE_BNODE', b'05': 'BE_EMAP_KEY', b'06': 'BE_EMAP_REC',
125  b'07': 'BE_EMAP', b'08': 'BE_LIST', b'09': 'BE_SEG_HDR',
126  b'0a': 'BALLOC', b'0b': 'ADDB2_FRAME_HEADER', b'0c': 'STOB_AD_0TYPE_REC',
127  b'0d': 'STOB_AD_DOMAIN', b'0e': 'COB_DOMAIN', b'0f': 'COB_NSREC',
128  b'10': 'BALLOC_GROUP_DESC', b'11': 'EXT', b'12': 'CAS_INDEX',
129  b'13': 'POOLNODE', b'14': 'POOLDEV', b'15': 'POOL_SPARE_USAGE',
130  b'16': 'CAS_STATE', b'17': 'CAS_CTG', b'22': 'WRONG_ENTRY', b'44': 'WRONG_ENTRY'}
131 
132 recordDict = {'BE_BTREE': [], 'BE_BNODE': [], 'BE_EMAP_KEY': [], 'BE_EMAP_REC': [],
133  'BE_EMAP': [], 'BE_LIST': [], 'BE_SEG_HDR': [], 'BALLOC': [],
134  'STOB_AD_0TYPE_REC': [], 'STOB_AD_DOMAIN': [], 'COB_DOMAIN': [],
135  'COB_NSREC': [], 'BALLOC_GROUP_DESC': [], 'EXT': [], 'POOLNODE': [],
136  'POOLDEV': [], 'POOL_SPARE_USAGE': [], 'CAS_STATE': [], 'CAS_CTG': [], 'EXTRA': []}
137 
138 AllRecordList = ['BE_BTREE', 'BE_BNODE', 'BE_EMAP_KEY', 'BE_EMAP_REC', 'BE_EMAP', 'BE_LIST',
139  'BE_SEG_HDR', 'BALLOC', 'STOB_AD_0TYPE_REC', 'STOB_AD_DOMAIN', 'COB_DOMAIN',
140  'COB_NSREC', 'BALLOC_GROUP_DESC', 'EXT', 'POOLNODE',
141  'POOLDEV', 'POOL_SPARE_USAGE', 'CAS_STATE', 'CAS_CTG', 'EXTRA']
142 DMDList = ['BE_BNODE', 'BE_EMAP_KEY', 'BE_EMAP_REC', 'COB_NSREC', 'BALLOC_GROUP_DESC']
143 GMDList = ['BE_BTREE', 'BE_EMAP', 'BE_LIST', 'BE_SEG_HDR', 'BALLOC', 'STOB_AD_0TYPE_REC',
144  'STOB_AD_DOMAIN', 'COB_DOMAIN', 'CAS_STATE', 'CAS_CTG']
145 
146 btreeType = {b'01': 'M0_BBT_INVALID', b'02': 'M0_BBT_BALLOC_GROUP_EXTENTS',
147  b'03': 'M0_BBT_BALLOC_GROUP_DESC', b'04': 'M0_BBT_EMAP_EM_MAPPING',
148  b'05': 'M0_BBT_CAS_CTG', b'06': 'M0_BBT_COB_NAMESPACE',
149  b'07': 'M0_BBT_COB_OBJECT_INDEX', b'08': 'M0_BBT_COB_FILEATTR_BASIC',
150  b'09': 'M0_BBT_COB_FILEATTR_EA', b'0a': 'M0_BBT_COB_FILEATTR_OMG',
151  b'0b': 'M0_BBT_CONFDB', b'0c': 'M0_BBT_UT_KV_OPS', b'0d': 'M0_BBT_NR'}
152 
153 BeBnodeTypeKeys = {}
154 
155 def RecordOffset(record, i, size):
156  if record in recordDict.keys():
157  recordDict[record].append(i)
158  if record == "BE_BNODE":
159  bliType = i + 16 # bli_type offet
160  btNumActiveKey = i + 56 # active key count offset
161  BeBnodeTypeKeys[i] = [bliType, btNumActiveKey]
162  else:
163  recordDict['EXTRA'].append(i)
164 
165 def ReadTypeSize(byte): # Ex: 0001(ver) 0009(type) 00003dd8(size)
166  # ver = byte[:4] # .ot_version = src->hd_bits >> 48,
167  rtype = byte[6:8] # .ot_type = src->hd_bits >> 32 & 0x0000ffff,
168  size = byte[8:16] # .ot_size = src->hd_bits & 0xffffffff
169  # logger.info("Version {}, Type {}, Size {}".format(ver, rtype, size)) #debug print
170  return rtype, size
171 
172 
173 def EditMetadata(offset):
174  """Edit metadata with the fixed pattern of 0x1111222244443333."""
175  with open(filename, 'r+b') as wbfr:
176  logger.info("** Corrupting 8byte of Metadata at offset {}"
177  " with b'1111222244443333' **".format(offset))
178  wbfr.seek(offset)
179  wbfr.write(b'\x33\x33\x44\x44\x22\x22\x11\x11')
180  wbfr.seek(offset)
181  ReadMetadata(offset)
182 
183 
184 def ReadMetadata(offset):
185  """Verifies that meta-data contains the valid footer at the given offset."""
186  with open(filename, "rb") as mdata:
187  mdata.seek(offset)
188  data = binascii.hexlify((mdata.read(8))[::-1])
189  if data == footer:
190  return True, data
191  return False, data
192 
193 
194 def ReadCompleteRecord(offset):
195  """Function read complete record starting after header and until footer for record."""
196  curr_record = []
197  while 1:
198  footerFound, data=ReadMetadata(offset)
199  if footerFound:
200  break
201  curr_record.append(data.decode('utf-8'))
202  offset = offset + 8 # check next 8 bytes
203 
204  # Convert list to hex representation
205  curr_record = [ hex(int(i, 16)) for i in curr_record]
206  return curr_record, offset # Return record data and footer offset
207 
208 
209 def ReadBeBNode(offset):
210  """Reads BeNode data."""
211  llist = BeBnodeTypeKeys[offset]
212  with open(filename, "rb") as mdata:
213  mdata.seek(llist[0])
214  data = binascii.hexlify((mdata.read(8))[::-1])
215  data = data[14:16]
216  logger.info("bli_type of BE_BNODE is: {0}: {1}".format( data, btreeType[data]))
217 
218  mdata.seek(llist[1])
219  data = binascii.hexlify((mdata.read(8))[::-1])
220  data = data[8:16]
221  logger.info("Active key count of BE_BNODE is: {}".format( int(data,16)))
222 
223 
224 def InduceCorruption(recordType, noOfErr):
225  """Induces Corruption in a record with number of error."""
226  count = 0
228  logger.info(recordType)
229  logger.info("Number of Error want to induce: {}".format(noOfErr))
230  lookupList = recordDict[recordType]
231  if (len(lookupList) and noOfErr) == 0:
232  logger.error("Record List is empty. Please choose another Record")
233  count = 0
234  return count
235  elif len(lookupList) < noOfErr:
236  logger.error(
237  " Record List contains Less number of entries than input."
238  " Please reduce the number of Error Injection")
239  count = 0
240  return count
241  else:
242  logger.info(lookupList)
243  logger.info("**** Inducing {} Error in Record: {} ****".format(noOfErr, recordType))
244  for i in range(noOfErr):
245  offset = lookupList[i] # Please add offset here for starting from middle of offset list
246  ReadMetadata(offset + 8)
247  EditMetadata(offset + 8)
248  if recordType == "BE_BNODE":
249  ReadBeBNode(offset)
250  count = count + 1
251  return count
252 
253 
255  """Induces corruption in meta data at random offset."""
256  count = 0
258  while 1:
259  recType = random_system.choice(list(recordDict))
260  logger.info("+++ Picked a Random Record from Dictionary Record type:{}+++".format(recType))
261  logger.info("Number of Error want to induce: {}".format(noOfErr))
262  lookupList = recordDict[recType]
263  logger.info(lookupList)
264  if (len(lookupList) == 0) or (len(lookupList) < noOfErr):
265  logger.info("Record List is empty OR contains Less number of entries than input."
266  " Going to next Record")
267  else:
268  lookupList = random_system.sample(lookupList, noOfErr)
269  logger.info(lookupList)
270  for i in range(noOfErr):
271  offset = lookupList[i]
272  logger.info("**** Inducing RANDOM Error in Record at offsets: {}****"
273  .format(hex(offset + 8)))
274  ReadMetadata(offset + 8) # Read original
275  EditMetadata(offset + 8) # Modify
276  ReadMetadata(offset + 8) # Verify
277  count = count + 1
278  break
279  return count
280 
281 
282 def InduceErrInRecords(recList):
283  """Function which induces error in a particular type of record."""
284  count = 0
286  logger.info("++++ Induce Random number of errors in All Records ++++")
287  for recType in recList:
288  logger.info("Record Name: {}".format(recType))
289  lookupList = recordDict[recType]
290  length = len(lookupList)
291  if length == 0:
292  logger.info("Record List is empty. Moving to Next Record")
293  else:
294  lookupList = random_system.sample(lookupList,
295  random_system.randint(1, length))
296  logger.info("Inducing {} Error at these offsets".format(len(lookupList)))
297  logger.info(lookupList)
298  for offset in lookupList:
299  logger.info("**** Inducing Error in Record at offsets {}****"
300  .format(hex(offset + 8)))
301  ReadMetadata(offset + 8) # Read original
302  EditMetadata(offset + 8) # Modify
303  ReadMetadata(offset + 8) # Verify
304  count = count + 1
305  return count
306 
307 
309  """Corrupt Metadata file from random location till end of metadata file."""
310  count = 0
311  with open(filename, 'r+b') as wbfr:
312  logger.info("** Corrupting 8byte of Metadata with b'1111222244443333' all place")
313  wbfr.seek(-1, os.SEEK_END)
314  endOffset = wbfr.tell()
315  offset = random_system.randint(1, endOffset)
316  logger.info("Start offset is {}".format(offset))
317  while 1:
318  offset = offset + 8
319  wbfr.seek(offset)
320  byte = wbfr.read(8)
321  if not byte:
322  break
323  else:
324  EditMetadata(offset + 8)
325  count = count + 1
326  return count
327 
328 
330  """Corrupt 512k Metadata in Metadata file from random location."""
331  count = 0
332  j = 0
333  with open(filename, 'r+b') as wbfr:
334  wbfr.seek(-524400, os.SEEK_END) # Took a bigger number than 512k
335  endOffset = wbfr.tell()
336  offset = random_system.randint(1, endOffset)
337  logger.info("Start offset is {}".format(offset))
338  while 1:
339  offset = offset + 8
340  j = j + 8
341  wbfr.seek(offset)
342  byte = wbfr.read(8)
343  if not byte:
344  break
345  else:
346  if j > 524288:
347  break
348  else:
349  EditMetadata(offset)
350  count = count + 1
351  return count
352 
353 
354 def ConvertAdstob2Cob(stob_f_container, stob_f_key):
355  """Method to extract cob related data."""
356  M0_FID_DEVICE_ID_OFFSET = 32
357  M0_FID_DEVICE_ID_MASK = 72057589742960640
358  M0_FID_TYPE_MASK = 72057594037927935
359 
360  # m0_fid_tassume()
361  tid = int(67) # Char 'C' Ascii Value
362  cob_f_container = ((tid << (64 - 8 )) | (int(stob_f_container, 16) & M0_FID_TYPE_MASK))
363  cob_f_key = int(stob_f_key, 16)
364  device_id = (int(cob_f_container) & M0_FID_DEVICE_ID_MASK) >> M0_FID_DEVICE_ID_OFFSET
365 
366  return cob_f_container, cob_f_key, device_id
367 
368 
369 def ConvertCobAdstob(cob_f_container, cob_f_key):
370  """Method take cob_f_cotainer, cob_f_key and returns stob_f_container, stob_f_key."""
371  M0_FID_TYPE_MASK = 72057594037927935
372 
373  # m0_fid_tassume()
374  tid = 2 # STOB_TYPE_AD = 0x02
375  stob_f_container = ((tid << (64 - 8 )) | (int(cob_f_container, 16) & M0_FID_TYPE_MASK))
376  stob_f_key = int(cob_f_key, 16)
377 
378  return stob_f_container, stob_f_key
379 
380 
381 def CorruptEmap(recordType, stob_f_container, stob_f_key):
382  """Method corrupts EMAP record specified by Cob ID."""
383  count = 0
385  lookupList = recordDict[recordType]
386  # logger.info("Offset List of {} = {} ".format(recordType, lookupList))
387  logger.info("*****Corrupting BE_EMAP_KEY for Cob ID {}*****".format(args.corrupt_emap))
388 
389  for offset in lookupList:
390  emap_key_data, offset = ReadCompleteRecord(offset)
391  if (hex(stob_f_container) in emap_key_data) and\
392  (hex(stob_f_key) in emap_key_data) and \
393  ("0xffffffffffffffff" not in emap_key_data):
394  # 16 bytes of BE_EMAP_KEY (footer) + 16 bytes of BE_EMAP_REC(header)
395  # gives offset of corresponding BE_EMAP_REC
396  rec_offset = offset + 32
397  emap_rec_data, rec_offset = ReadCompleteRecord(rec_offset)
398 
399  # Check er_cs_nob and if it is not 0 then go and corrupt last checksum 8 bytes
400  if emap_rec_data[3] != "0x0":
401  logger.info("** Metadata at offset {},"
402  " BE_EMAP_KEY ek_prefix = {}:{},"
403  " ek_offset = {}".format(offset-24,
404  emap_key_data[0], emap_key_data[1], emap_key_data[2]))
405  logger.info("** Metadata at offset {},"
406  " BE_EMAP_REC er_start = {},"
407  " er_value = {}, er_unit_size = {},"
408  " er_cs_nob = {}, checksum = {}".format(
409  offset+32, emap_rec_data[0], emap_rec_data[1],
410  emap_rec_data[2], emap_rec_data[3], emap_rec_data[4:]))
411  EditMetadata(rec_offset-8)
412  count = count + 1
413  print()
414  return count
415 
416 
418  logger.info("*****Listing all emap keys and emap records with device id*****")
419  recordType = "BE_EMAP_KEY"
421  lookupList = recordDict[recordType]
422  # logger.info(lookupList)
423 
424  for offset in lookupList:
425  print()
426  emap_key_data , offset = ReadCompleteRecord(offset)
427  stob_f_container_hex = emap_key_data[0]
428  stob_f_key_hex = emap_key_data[1]
429  _, _, device_id = ConvertAdstob2Cob(stob_f_container_hex, stob_f_key_hex)
430  # 16 bytes of BE_EMAP_KEY (footer) + 16 bytes of BE_EMAP_REC(header)
431  # gives offset of Corresponding BE_EMAP_REC
432  emap_rec_offset = offset + 32
433  emap_rec_data, _ = ReadCompleteRecord(emap_rec_offset)
434 
435  logger.info("** Metadata at offset {},"
436  " BE_EMAP_KEY ek_prefix = {}:{},"
437  " ek_offset = {}, Device ID = {}".format(offset,
438  emap_key_data[0], emap_key_data[1], emap_key_data[2], device_id))
439  logger.info("** Metadata at offset {},"
440  " BE_EMAP_REC er_start = {},"
441  " er_value = {}, er_unit_size = {},"
442  " er_cs_nob = {}, checksum = {}"
443  .format(emap_rec_offset, emap_rec_data[0],
444  emap_rec_data[1], emap_rec_data[2],
445  emap_rec_data[3], emap_rec_data[4:]))
446 
447 
448 def VerifyLengthOfRecord(recordDict):
449  count = 0
451  logger.info("***********Record list will be print here************")
452  for record, items in recordDict.items():
453  logger.info(" {} : {}".format(record, len(items)))
454  count = count + 1
455  return count
456 
457 
459  with open(filename, "rb") as metadata:
460  i: int = 0
461  metadata.seek(args.seek_offset)
462  while 1:
463  byte = metadata.read(8)
464  i = i + 8
465  if not byte:
466  break
467  byte = binascii.hexlify(byte[::-1])
468  if byte == header:
469  byte = binascii.hexlify((metadata.read(8))[::-1]) # Read the Type Size Version
470  rtype, size = ReadTypeSize(byte)
471  if rtype not in typeDict.keys():
472  continue
473  record = typeDict[rtype]
474  i = i + 8
475  if size > b'00000000':
476  RecordOffset(record, i, size)
477  i = int(size, 16) + i - 16
478  metadata.seek(i)
479  # Not parsing the whole file for few test
480  # as It will take many hours, depending on metadata size
481  if (args.verify == True) or (args.list_emap == True):
482  if args.parse_size:
483  if i > args.parse_size: # This will parse metadata file util specified parse_size for list_emap and verify option
484  break
485  else:
486  pass # we will read complete metadata file in case of -v or -list_emap option
487  else:
488  if i > 111280000: # Increase this number for reading more location in metadata
489  break
490 
491 
492 noOfErrs = 0
493 
494 if args.err512k:
495  noOfErrs = Induce512kbError()
496 
497 elif args.hugeCorruption:
498  noOfErrs = InduceHugeError()
499 
500 elif args.random:
501  noOfErrs = InduceRandomCorruption(noOfErr)
502 
503 elif recordType:
504  noOfErrs = InduceCorruption(recordType, noOfErr)
505 
506 elif args.verify:
507  noOfErrs = VerifyLengthOfRecord(recordDict)
508 
509 elif args.allErr:
510  noOfErrs = InduceErrInRecords(AllRecordList) #InduceErrInAllRecord()
511 
512 elif args.allGMD:
513  noOfErrs = InduceErrInRecords(GMDList) #InduceErrInGMDRecords()
514 
515 elif args.allDMD:
516  noOfErrs = InduceErrInRecords(DMDList) #InduceErrInDMDRecords()
517 
518 elif args.corrupt_emap:
519  _f_container, _f_key = args.corrupt_emap.split(":")
520  cob_f_container = hex(int(_f_container, 16))
521  cob_f_key = hex(int(_f_key, 16))
522  stob_f_container, stob_f_key = ConvertCobAdstob(cob_f_container, cob_f_key)
523  noOfErrs = CorruptEmap("BE_EMAP_KEY", stob_f_container, stob_f_key)
524 
525 elif args.list_emap:
527 
528 if not args.verify:
529  logger.info("Number of errors induced by script: {}".format(noOfErrs))
530 
531 if noOfErrs > 0:
532  logger.info("**** Successfully injected holes in metadata ****")
533 else:
534  logger.error("**** Failed to inject holes in metadata ****")
static struct m0_list list
Definition: list.c:144
def RecordOffset(record, i, size)
def ReadTypeSize(byte)
static M0_UNUSED void print(struct m0_be_list *list)
Definition: list.c:186
def VerifyLengthOfRecord(recordDict)
def InduceCorruption(recordType, noOfErr)
def InduceErrInRecords(recList)
def ReadCompleteRecord(offset)
format
Definition: hist.py:128
def ConvertAdstob2Cob(stob_f_container, stob_f_key)
def EditMetadata(offset)
def ReadMetadata(offset)
def ConvertCobAdstob(cob_f_container, cob_f_key)
def CorruptEmap(recordType, stob_f_container, stob_f_key)
def InduceRandomCorruption(noOfErr)
def ReadBeBNode(offset)
static void hex(struct m0_addb2__context *ctx, const uint64_t *v, char *buf)
Definition: dump.c:413