Advertisement
Guest User

Untitled

a guest
Jul 17th, 2019
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.18 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. This script automatically re-writes sectors where
  5. ATA read errors occur. By re-writing the sectors
  6. (using hdparm), the HDD/SSD will be used to re-allocate
  7. the sectors.
  8.  
  9. **EXTREMELY DANGEROUS**
  10. This script will NOT ask before overwriting data
  11. and might DESTROY all your data. Use it under your
  12. own responsibility and only if you know EXACTLY
  13. what you're doing (or if you don't care).
  14. Expect fixhdd.py to contain critical bugs.
  15.  
  16. Runs on linux only. hdparm must be installed.
  17.  
  18. fixhdd.py must be run as root. It will only write to sectors
  19. if reading them using hdparm yields an error.
  20.  
  21. Use fixhdd.py --loop to watch the syslog for read errors
  22. and rewrite all sectors where errors occur. The script will
  23. check the log every five seconds and won't exit.
  24.  
  25. Use fixhdd.py -a -o <offset> to scan for bad blocks starting at
  26. LBA <offset>. Use this mode if a SMART selftest indicates an error
  27. at a specific LBA and select an offset smaller than the given LBA.
  28. Scanning a large number of LBAs takes a significant amount of time,
  29. especially if many LBAs yield errors.
  30.  
  31. Use fixhdd -s <sector> to rewrite a specific LBA, but only
  32. if reading it . Use this for correcting errors indicated by SMART
  33. if you don't see the need for actively scanning a significant number
  34. of blocks.
  35.  
  36. Use Ctrl+C to stop fixhdd.py.
  37.  
  38. Changelog:
  39. Revision 1.1: Fix --loop causing unary function to be called without arguments
  40. Revision 1.2: Fix hardcoded /dev/sda, various small improvements & fixes ; fix active scan
  41. Revision 1.3: Python3 ready
  42. Revision 1.4: Python3 fixes, fix bad/missing sense data & unusable logging
  43. """
  44. import subprocess
  45. import time
  46. import os
  47. import stat
  48. import sys
  49.  
  50. __author__ = "Uli Köhler"
  51. __copyright__ = "Copyright 2015-2016 Uli Koehler"
  52. __license__ = "Apache License v2.0"
  53. __version__ = "1.4"
  54. __maintainer__ = "Uli Köhler"
  55. __email__ = "ukoehler@techoverflow.net"
  56. __status__ = "Development"
  57.  
  58. #Get list of recent bad sectors via dmesg
  59. def getBadSectors(device):
  60. "Parse a list of recently read bad sectors from the syslog"
  61. #TODO this gets ALL bad sectors from ALL devices, not only the selected device
  62. try:
  63. out = subprocess.check_output('grep "end_request: I/O error" /var/log/syslog', shell=True)
  64. for line in out.split("\n"):
  65. line = line.strip()
  66. if not line: continue
  67. sector = int(line.rpartition(" ")[2])
  68. yield sector
  69. except subprocess.CalledProcessError:
  70. #usually this indicates grep has not found anything
  71. return
  72.  
  73.  
  74. def isSectorBad(device, sector):
  75. try:
  76. output = subprocess.check_output('hdparm --read-sector %d %s' % (sector, device), shell=True, stderr=subprocess.STDOUT)
  77. output = output.decode("utf-8")
  78. # Special case: process succeeds but with error message:
  79. # SG_IO: bad/missing sense data
  80. if "bad/missing sense data" in output:
  81. return True
  82. # Else: Success => sector is not bad
  83. return False
  84. except:
  85. return True
  86.  
  87.  
  88. def resetSectorHDParm(device, sector):
  89. """Write to a sector using hdparm only if reading it yields a HDD error"""
  90. #Will throw exception on non-zero exit code
  91. if isSectorBad(device, sector):
  92. print(("Sector %d is damaged, rewriting..." % sector))
  93. #Maaan, this is VERY DANGEROUS!
  94. #Really, no kidding. Might even make things worse.
  95. #It could work, but it probably doesn't. Ever.
  96. #Don't use if your data is worth a single dime to you.
  97. out = subprocess.check_output('hdparm --write-sector %d --yes-i-know-what-i-am-doing %s' % (sector, device), shell=True)
  98. out = out.decode("utf-8")
  99. if "succeeded" not in out:
  100. print (red(out.decode("utf-8").replace("\n")))
  101. else:
  102. print(("Sector %d is OK, ignoring" % sector))
  103.  
  104. def fixBadSectors(device, badSectors):
  105. "One-shot fixing of bad sectors"
  106. print(("Checking/Fixing %d sectors" % len(badSectors)))
  107. [resetSectorHDParm(device, sector) for sector in badSectors]
  108.  
  109. def checkDmesgBadSectors(device, knownGoodSectors):
  110. #Grab sector list from dmesg
  111. dmesgBadSectors = set(getBadSectors(device))
  112. dmesgBadSectors.difference_update(knownGoodSectors)
  113. if len(dmesgBadSectors) == 0:
  114. print ("No new sector errors found in syslog :-)")
  115. #Update set of sectors which are known to be good
  116. else:
  117. fixBadSectors(device, dmesgBadSectors)
  118. knownGoodSectors.update(dmesgBadSectors)
  119.  
  120. def loopCheckForBadSectors(device):
  121. knownGoodSectors = set()
  122. while True:
  123. print("Waiting 5 seconds (hit Ctrl+C to interrupt)...")
  124. time.sleep(5)
  125. #Try again after timeout
  126. checkDmesgBadSectors(device, knownGoodSectors)
  127.  
  128. def isBlockDevice(filename):
  129. "Return if the given filename represents a valid block device"
  130. return stat.S_ISBLK(os.stat(filename).st_mode)
  131.  
  132. def getNumberOfSectors(device):
  133. "Get the physical number of LBAs for the given device"
  134. #Line like: 255 heads, 63 sectors/track, 60801 cylinders, total 976773168 sectors
  135. sectorsLine = subprocess.check_output("LANG=C fdisk -l {0} 2>/dev/null | grep ^Disk | grep sectors".format(device), shell=True)
  136. print(sectorsLine)
  137. return int(sectorsLine.strip().split(b" ")[-2])
  138.  
  139. def performActiveSectorScan(device, offset=0, n=1000):
  140. "Check all sectors on the hard drive for errors and fix them."
  141. print(("Performing active sector scan of {0} starting at {1}").format(device, offset))
  142. print((getNumberOfSectors(device)))
  143. for i in range(offset, min(getNumberOfSectors(device), offset + n)):
  144. #Reset sector (only if it is damaged)
  145. resetSectorHDParm(device, i)
  146.  
  147. if __name__ == "__main__":
  148. # Parse arguments
  149. import argparse
  150. parser = argparse.ArgumentParser()
  151. parser.add_argument("-s", "--sector", nargs="*", default=[], type=int, help="A list of sectors to scan (beyond those listed in ")
  152. parser.add_argument("--loop", action="store_true", help="Loop and scan for bad sectors every few seconds")
  153. parser.add_argument("-a", "--active-scan", action="store_true", help="Actively scan all blocks for errors. Use --offset to start at a specific block.")
  154. parser.add_argument("-o", "--offset", default=0, type=int, help="For active scan, the block to start at")
  155. parser.add_argument("-n", default=1000, type=int, help="For active scan, the number of blocks to scan")
  156. parser.add_argument("device", default="/dev/sda", help="The device to use")
  157. args = parser.parse_args()
  158. #Check if the given device is a block device after all
  159. if not isBlockDevice(args.device):
  160. print("Error: device argument must be a block device")
  161. sys.exit(1)
  162. print(("Trying to fix bad sectors on %s" % args.device))
  163. # Always perform one-shot test
  164. checkDmesgBadSectors(args.device, set())
  165. # Fix manually added bad sector list
  166. fixBadSectors(args.device, args.sector)
  167. # Active sector scan
  168. if args.active_scan:
  169. performActiveSectorScan(args.device, args.offset, args.n)
  170. # If enabled, loop-check
  171. if args.loop: loopCheckForBadSectors(args.device)</sector></offset></offset>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement