使用Python腳本檢驗文件系統數據完整性

    今天是又一年度的2月14日西方情人節,先說點與情人節有關的,但這毫不是狗糧,能夠放心閱讀。python

    講真,若是你是單身狗,沒事時仍是要靜下心學習以提高本身;若是你不是單身狗,沒事時除了上一條還要多陪陪媳婦和家人。由於沒有什麼是比親人和提高本身更重要的事了!不管是提高本身仍是陪伴家人,不要浮於表面,就像今天過情人節同樣,向對方表達愛並非只有這一天和那幾天,而是男女雙方長久的堅持和包容。git

    用之前有人用過的句子說:github

    當你的才華撐不起你的野心,那你就應該靜下心來學習;當你的金錢趕不上你的夢想,那你就應該坐下來好好工做;當你的能力還駕馭不了你的目標,那就應該沉下心來歷練!json

正文開始:app

    本文爲使用Python腳本檢驗文件系統數據完整性和防止數據篡改提供一種簡單且容易實現的思路(其實很簡單,只須要了解Python基礎+hashlib+文件操做等)。ide

    雖然校驗數據完整性這個話題已經由不少更加完美的解決辦法,但依然能夠做爲Python新手練手內容之一,培養一下邏輯思惟,防止「老年癡呆」。函數

    目前已經在Windows 10以及Ubuntu(Python 2.7)下測試經過,其餘的平臺應該也能夠,歡迎幫忙測試。oop

編寫的思路和執行過程簡要以下:學習

1.輸入要檢查數據完整性的目錄的路徑(也支持單個文件)和要保存文件hash值的校驗文件的路徑,若是路徑不存在,則拋出異常或者建立,取決於具體狀況;測試

參數傳入(最新版本將參數傳入經過命令行的方式傳入了,下面圖片中是老版本中的參數傳入):

p_w_picpath 

在剛更新的版本中,參數傳入和命令幫助經過docopt模塊實現,方便使用。

Python script to check data integrity on UNIX/Linux or Windows
accept options using 'docopt' module, using 'docopt' to accept parameters and command switch

Usage:
  checkDataIntegrity.py [-g FILE HASH_FILE]
  checkDataIntegrity.py [-c FILE HASH_FILE]
  checkDataIntegrity.py [-r HASH_FILE]
  checkDataIntegrity.py generate FILE HASH_FILE
  checkDataIntegrity.py validate FILE HASH_FILE
  checkDataIntegrity.py reset HASH_FILE
  checkDataIntegrity.py (--version | -v)
  checkDataIntegrity.py --help | -h | -?

Arguments:
  FILE                  the path to single file or directory to data protect
  HASH_FILE             the path to hash data saved

Options:
  -? -h --help          show this help message and exit
  -v --version          show version and exit

Example, try:
  checkDataIntegrity.py generate /tmp /tmp/data.json
  checkDataIntegrity.py validate /tmp /tmp/data.json
  checkDataIntegrity.py reset /tmp/data.json
  checkDataIntegrity.py -g /tmp /tmp/data.json
  checkDataIntegrity.py -c /tmp /tmp/data.json
  checkDataIntegrity.py -r /tmp/data.json
  checkDataIntegrity.py --help


合法的參數和路徑:

2017-02-14_192303

路徑不存在時拋出異常:

2017-02-14_192455

其餘異常處理能夠經過腳本內容看到。

2.首次執行保存須要校驗hash值的校驗文件的內容,再次執行讀取原先的文件與如今的待校驗的目錄中的文件的hash值作比對,若是hash值不同,則顯示出該文件路徑,若是所有一致,則輸出提示信息

首次執行:

2017-02-14_192303

再次執行(檢驗經過):

2017-02-14_192322

校驗不經過:

2017-02-14_192355 

3.當文件發生變動而且想更新校驗文件數據時,能夠使用remakeDataIntegrity()函數將已保存的校驗文件刪除

2017-02-14_192224

Linux上的測試:

p_w_picpath

最新的代碼能夠從GitHub得到,連接:https://github.com/DingGuodong/LinuxBashShellScriptForOps/blob/master/functions/security/checkDataIntegrity.py

代碼以下:

#!/usr/bin/python
# encoding: utf-8
# -*- coding: utf8 -*-
"""
Created by PyCharm.
File:               LinuxBashShellScriptForOps:checkDataIntegrity.py
User:               Guodong
Create Date:        2017/2/14
Create Time:        14:45

Python script to check data integrity on UNIX/Linux or Windows
accept options using 'docopt' module, using 'docopt' to accept parameters and command switch

Usage:
  checkDataIntegrity.py [-g FILE HASH_FILE]
  checkDataIntegrity.py [-c FILE HASH_FILE]
  checkDataIntegrity.py [-r HASH_FILE]
  checkDataIntegrity.py generate FILE HASH_FILE
  checkDataIntegrity.py validate FILE HASH_FILE
  checkDataIntegrity.py reset HASH_FILE
  checkDataIntegrity.py (--version | -v)
  checkDataIntegrity.py --help | -h | -?

Arguments:
  FILE                  the path to single file or directory to data protect
  HASH_FILE             the path to hash data saved

Options:
  -? -h --help          show this help message and exit
  -v --version          show version and exit

Example, try:
  checkDataIntegrity.py generate /tmp /tmp/data.json
  checkDataIntegrity.py validate /tmp /tmp/data.json
  checkDataIntegrity.py reset /tmp/data.json
  checkDataIntegrity.py -g /tmp /tmp/data.json
  checkDataIntegrity.py -c /tmp /tmp/data.json
  checkDataIntegrity.py -r /tmp/data.json
  checkDataIntegrity.py --help
"""
from docopt import docopt
import os
import sys
import hashlib


def get_hash_sum(filename, method="sha256", block_size=65536):
    if not os.path.exists(filename):
        raise RuntimeError("cannot open '%s' (No such file or directory)" % filename)
    if not os.path.isfile(filename):
        raise RuntimeError("'%s' :not a regular file" % filename)

    if "md5" in method:
        checksum = hashlib.md5()
    elif "sha1" in method:
        checksum = hashlib.sha1()
    elif "sha256" in method:
        checksum = hashlib.sha256()
    elif "sha384" in method:
        checksum = hashlib.sha384()
    elif "sha512" in method:
        checksum = hashlib.sha512()
    else:
        raise RuntimeError("unsupported method %s" % method)

    with open(filename, 'rb') as f:
        buf = f.read(block_size)
        while len(buf) > 0:
            checksum.update(buf)
            buf = f.read(block_size)
        if checksum is not None:
            return checksum.hexdigest()
        else:
            return checksum


def makeDataIntegrity(path):
    path = unicode(path, 'utf8')  # For Chinese Non-ASCII character

    if not os.path.exists(path):
        raise RuntimeError("Error: cannot access %s: No such file or directory" % path)
    elif os.path.isfile(path):
        dict_all = dict()
        dict_all[os.path.abspath(path)] = get_hash_sum(path)
        return dict_all
    elif os.path.isdir(path):
        dict_nondirs = dict()
        dict_dirs = dict()
        for top, dirs, nondirs in os.walk(path, followlinks=True):
            for item in nondirs:
                # Do NOT use os.path.abspath(item) here, else it will make a serious bug because of
                # os.path.abspath(item) return "os.getcwd()" + "filename" in some case.
                dict_nondirs[os.path.join(top, item)] = get_hash_sum(os.path.join(top, item))
            for item in dirs:
                dict_dirs[os.path.join(top, item)] = r""
        dict_all = dict(dict_dirs, **dict_nondirs)
        return dict_all


def saveDataIntegrity(data, filename):
    import json
    data_to_save = json.dumps(data, encoding='utf-8')
    if not os.path.exists(os.path.dirname(filename)):
        os.makedirs(os.path.dirname(filename))
    with open(filename, 'wb') as f:
        f.write(data_to_save)


def readDataIntegrity(filename):
    import json
    if not os.path.exists(filename):
        raise RuntimeError("cannot open '%s' (No such file or directory)" % filename)
    with open(filename, 'rb') as f:
        data = json.loads(f.read())
    if data:
        return data


def remakeDataIntegrity(filename):
    def confirm(question, default=True):
        """
        Ask user a yes/no question and return their response as True or False.

        :parameter question:
        ``question`` should be a simple, grammatically complete question such as
        "Do you wish to continue?", and will have a string similar to " [Y/n] "
        appended automatically. This function will *not* append a question mark for
        you.
        The prompt string, if given,is printed without a trailing newline before reading.

        :parameter default:
        By default, when the user presses Enter without typing anything, "yes" is
        assumed. This can be changed by specifying ``default=False``.

        :return True or False
        """
        # Set up suffix
        if default:
            # suffix = "Y/n, default=True"
            suffix = "Y/n"
        else:
            # suffix = "y/N, default=False"
            suffix = "y/N"
        # Loop till we get something we like
        while True:
            response = raw_input("%s [%s] " % (question, suffix)).lower()
            # Default
            if not response:
                return default
            # Yes
            if response in ['y', 'yes']:
                return True
            # No
            if response in ['n', 'no']:
                return False
            # Didn't get empty, yes or no, so complain and loop
            print("I didn't understand you. Please specify '(y)es' or '(n)o'.")

    if os.path.exists(filename):
        if confirm("[warning] remake data integrity file \'%s\'?" % filename):
            os.remove(filename)
            print "[successful] data integrity file \'%s\' has been remade." % filename
            sys.exit(0)
        else:
            print "[warning] data integrity file \'%s\'is not remade." % filename
            sys.exit(0)
    else:
        print >> sys.stderr, "[error] data integrity file \'%s\'is not exist." % filename


def checkDataIntegrity(path_to_check, file_to_save):
    from time import sleep

    if not os.path.exists(file_to_save):
        print "[info] data integrity file \'%s\' is not exist." % file_to_save
        print "[info] make a data integrity file to \'%s\'" % file_to_save
        data = makeDataIntegrity(path_to_check)
        saveDataIntegrity(data, file_to_save)
        print "[successful] make a data integrity file to \'%s\', finished!" % file_to_save,
        print "Now you can use this script later to check data integrity."
    else:
        old_data = readDataIntegrity(file_to_save)
        new_data = makeDataIntegrity(path_to_check)
        error_flag = True
        for item in old_data.keys():
            try:
                if not old_data[item] == new_data[item]:
                    print>> sys.stderr, new_data[item], item
                    sleep(0.01)
                    print "\told hash data is %s" % old_data[item], item
                    error_flag = False
            except KeyError as e:
                print >> sys.stderr, "[error]", e.message, "Not Exist!"
                error_flag = False
        if error_flag:
            print "[ successful ] passed, All files integrity is ok!"


if __name__ == '__main__':
    arguments = docopt(__doc__, version='1.0.0rc2')
    if arguments['-r'] or arguments['reset']:
        if arguments['HASH_FILE']:
            remakeDataIntegrity(arguments['HASH_FILE'])
    elif arguments['-g'] or arguments['generate']:
        if arguments['FILE'] and arguments['HASH_FILE']:
            checkDataIntegrity(arguments['FILE'], arguments['HASH_FILE'])
    elif arguments['-c'] or arguments['validate']:
        if arguments['FILE'] and arguments['HASH_FILE']:
            checkDataIntegrity(arguments['FILE'], arguments['HASH_FILE'])
    else:
        print >> sys.stderr, "bad parameters"
        sys.stderr.flush()
        print docopt(__doc__, argv="--help")

tag:Python校驗文件完整性,文件完整性,哈希校驗

這個世界屬於有天賦的人,    
也屬於認真的人,      
更屬於那些      
在有天賦的領域認真鑽研的人。

加油,together!

--end--

相關文章
相關標籤/搜索