xref: /test/xts/tools/lite/checksum/src/checksum_file.c (revision 31c75014)
1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16/*
17 * Description: 对单个文件或目录下多文件进行计算SHA256校验和
18 */
19
20#include "checksum_file.h"
21
22#include <dirent.h>
23#include <errno.h>
24#include <securec.h>
25#include <stdio.h>
26#include <string.h>
27
28#include "checksum_sha256.h"
29
30
31#define BUFFER_SIZE 4096
32int g_fileNums = 0;
33
34/**
35 * 该方法实现将十六进制的字节转成字符串
36 */
37void HexArrayToString(const unsigned char *hexarray, int length, unsigned char *string)
38{
39    int byte = 4;
40    unsigned char value = 0x0f;
41    const unsigned char num2CharTable[16] = "0123456789ABCDEF";
42
43    for (int i = 0; i < length; i++) {
44        *(string++) = num2CharTable[(hexarray[i] >> byte) & value];
45        *(string++) = num2CharTable[hexarray[i] & value];
46    }
47
48    *string = 0x0;
49}
50
51/**
52 * 获取文件校验和的最终结果
53 */
54unsigned char* GetChecksumResult(MesgDigest* mesgd)
55{
56    unsigned int dataLen = mesgd->dataLen;
57    unsigned int totalLen = 64;
58    unsigned int len = 8;
59    unsigned int groupNum = 4;
60    int num = 24;
61    unsigned char pad0 = 0x80;
62    unsigned char pad1 = 0x00;
63    static unsigned char hash[32];
64    unsigned char bitNum = 0x000000ff;
65    unsigned int i, j;
66    errno_t err;
67
68    // 0-dataLen之间元素保持不变,dataLen位填充1次1000 0000
69    mesgd->data[dataLen++] = pad0;
70
71    // 剩余部分填充0000 0000
72    if (mesgd->dataLen < totalLen - len) {
73        for (; dataLen < totalLen - len; dataLen++) {
74            mesgd->data[dataLen] = pad1;
75        }
76    }
77
78    if (mesgd->dataLen >= totalLen - len) {
79        for (; dataLen < totalLen; dataLen++) {
80            mesgd->data[dataLen] = pad1;
81        }
82
83        CalcSha256(mesgd, mesgd->data);
84        err = memset_s(mesgd->data, sizeof(mesgd->data), 0, totalLen - len);
85        if (err != EOK) {
86            printf("memset_s failed, err = %d\n", err);
87        }
88    }
89
90    mesgd->bitLen += mesgd->dataLen * len;
91
92    // 填充长度的0-63位
93    for (i = 1; i <= len; i++) {
94        mesgd->data[totalLen - i] = mesgd->bitLen >> ((i - 1) * len);
95    }
96
97    // 计算填充后数据的sha256
98    CalcSha256(mesgd, mesgd->data);
99
100    for (i = 0; i < groupNum; ++i) {
101        for (j = 0; j < len; j++) {
102            hash[i + groupNum * j] = (mesgd->hash[j] >> (num - i * len)) & bitNum;
103        }
104    }
105
106    return hash;
107}
108
109
110/**
111 * 该方法计算文件数据块的SHA256校验和
112 */
113void CalcFileChunkSha256(MesgDigest* mesgd, unsigned char data[], size_t len)
114{
115    unsigned int dataLen = 64;
116    unsigned int bitLen = 512;
117
118    for (size_t i = 0; i < len; i++) {
119        mesgd->data[mesgd->dataLen] = data[i];
120        mesgd->dataLen++;
121        if (mesgd->dataLen == dataLen) {
122            CalcSha256(mesgd, mesgd->data);
123            mesgd->bitLen += bitLen;
124            mesgd->dataLen = 0;
125        }
126    }
127}
128
129/**
130 * 该方法计算单个文件的SHA256校验和
131 */
132int CalcSingleFileSha256(char* fileName)
133{
134    unsigned int outputLen = 32;
135    unsigned char *output = NULL;
136    unsigned char outputStr[64];
137
138    FILE *fp = NULL;
139    if ((fp = fopen(fileName, "rb")) == NULL) {
140        printf("error: fail to open file %s: %s.\n", fileName, strerror(errno));
141    }
142
143    MesgDigest mesgd;
144    InitSha256(&mesgd);
145
146    unsigned char buffer[BUFFER_SIZE];
147    while (!feof(fp)) {
148        size_t size = fread(buffer, 1, BUFFER_SIZE, fp);
149        CalcFileChunkSha256(&mesgd, buffer, size);
150    }
151    fclose(fp);
152
153    output = GetChecksumResult(&mesgd);
154    HexArrayToString(output, outputLen, outputStr);
155
156    g_fileNums++;
157    printf("%s:%s\n", fileName, outputStr);
158    return RESULT_SUCCESS;
159}
160
161char* CreatePathName(char *base, int len, char *name0, char *name1, char *name2)
162{
163    errno_t err;
164
165    err = memset_s(base, len, '\0', len);
166    if (err != EOK) {
167        printf("memset_s failed, err = %d\n", err);
168    }
169
170    err = strcpy_s(base, len, name0);
171    if (err != EOK) {
172        printf("strcpy_s failed, err = %d\n", err);
173    }
174
175    err = strcat_s(base, len, name1);
176    if (err != EOK) {
177        printf("strcat_s failed, err = %d\n", err);
178    }
179
180    err = strcat_s(base, len, name2);
181    if (err != EOK) {
182        printf("strcat_s failed, err = %d\n", err);
183    }
184
185    return base;
186}
187
188/**
189 * 该方法计算当前目录下所有文件的SHA256校验和
190 */
191int CalcMultiFilesSha256(char* dirPathName)
192{
193    DIR *dir;
194    struct dirent *ptr = NULL;
195    int maxLen = 1000;
196    static char pathName[1000], fileName[1000];
197    char *fileNewName = NULL;
198    char *pathNewName = NULL;
199    int typeFile = 8;
200    int typeDir = 4;
201
202    if ((dir = opendir(dirPathName)) == NULL) {
203        printf("error:fail to open dir %s: %s.\n", dirPathName, strerror(errno));
204    }
205
206    while ((ptr = readdir(dir)) != NULL) {
207        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
208            continue;
209        } else if (ptr->d_type == typeFile) {
210            // 获取的类型为文件,计算文件的校验和
211            fileNewName = CreatePathName(fileName, maxLen, dirPathName, "/", ptr->d_name);
212            CalcSingleFileSha256(fileNewName);
213        } else if (ptr->d_type == typeDir) {
214            // 获取的类型为目录,递归方式遍历所有子目录
215            pathNewName = CreatePathName(pathName, maxLen, dirPathName, "/", ptr->d_name);
216            CalcMultiFilesSha256(pathNewName);
217        }
218    }
219    closedir(dir);
220    return RESULT_SUCCESS;
221}
222
223/**
224 * 该方法获取文件的个数
225 */
226int GetFileTotalNum(void)
227{
228    return g_fileNums;
229}