1/*
2 * Copyright (C) 2022 Huawei Technologies Co., Ltd.
3 * Licensed under the Mulan PSL v2.
4 * You can use this software according to the terms and conditions of the Mulan PSL v2.
5 * You may obtain a copy of Mulan PSL v2 at:
6 *     http://license.coscl.org.cn/MulanPSL2
7 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9 * PURPOSE.
10 * See the Mulan PSL v2 for more details.
11 */
12#include "tarzip.h"
13
14#include <stdio.h>
15#include <string.h>
16#include <stdlib.h>
17#include <stdbool.h>
18#include <unistd.h>
19#include <fcntl.h>
20#include <zlib.h>
21#include <dirent.h>
22#include <sys/stat.h>
23#include <pwd.h>
24#include <errno.h>
25#include <securec.h>
26
27#include "tee_log.h"
28#include "tlogcat.h"
29
30#ifdef LOG_TAG
31#undef LOG_TAG
32#endif
33#define LOG_TAG "tlogcat"
34
35#define HEADER_NUM           512
36#define CHECK_SUM_APPEND     256
37#define NAME_LEN             100U
38#define FILE_MODE_LEN        8U
39#define UID_LEN              8U
40#define GID_LEN              8U
41#define FILE_SIZE            12U
42#define UNIX_TIME_LEN        12U
43#define CHECK_SUM_LEN        8U
44#define HEADER_RESVD_LEN     356U
45
46/* tar file header struct */
47struct TagHeader { /* byte offset */
48    char name[NAME_LEN];  /* 0 */
49    char mode[FILE_MODE_LEN]; /* 100 */
50    char uid[UID_LEN]; /* 108 */
51    char gid[GID_LEN]; /* 116 */
52    char size[FILE_SIZE]; /* 124 */
53    char unixTime[UNIX_TIME_LEN]; /* 136 */
54    char checkSum[CHECK_SUM_LEN]; /* 148 */
55    char reserved[HEADER_RESVD_LEN]; /* 156 */
56};
57
58/* tar file header mode permissions data */
59static const char FILE_MODE[] = { 0x31, 0x30, 0x30, 0x36, 0x36, 0x36, 0x20, 0 };
60
61/* tar file haeder UID and GID date */
62static const char ID_BYTE[] = {
63    0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20, 0x00,
64};
65
66static int32_t WriteOneUint(size_t startIndex, size_t unitLen, const char *input, char *ouput)
67{
68    size_t i;
69    int32_t sum = 0;
70
71    for (i = 0; i < unitLen; ++i) {
72        ouput[i + startIndex] = input[i];
73        sum += (int32_t)input[i];
74    }
75
76    return sum;
77}
78
79/* write the tar file harder to header struct */
80static void WriteHeader(struct TagHeader *header, const char *fileName, long fileSize)
81{
82    int32_t sumAppend = 0;
83    size_t nameLen;
84    size_t i;
85    size_t j = 0;
86    char *index = (char *)header;
87    char buf[FILE_SIZE] = {0};
88    errno_t rc;
89
90    if (fileName == NULL || fileSize == 0) {
91        return;
92    }
93
94    nameLen = strlen(fileName);
95    nameLen = ((nameLen >= NAME_LEN) ? (NAME_LEN - 1) : nameLen);
96
97    sumAppend += WriteOneUint(j, nameLen, fileName, index);
98    j += NAME_LEN;
99
100    sumAppend += WriteOneUint(j, FILE_MODE_LEN, FILE_MODE, index);
101    j += FILE_MODE_LEN;
102
103    sumAppend += WriteOneUint(j, (UID_LEN + GID_LEN), ID_BYTE, index);
104    j += (UID_LEN + GID_LEN);
105
106    rc = snprintf_s(buf, FILE_SIZE, FILE_SIZE - 1, "%o", (unsigned int)fileSize);
107    if (rc == -1) {
108        tloge("snprintf_s failed: %d\n", rc);
109        return;
110    }
111
112    sumAppend += WriteOneUint(j, FILE_SIZE, buf, index);
113    j += (FILE_SIZE + UNIX_TIME_LEN);
114
115    sumAppend += CHECK_SUM_APPEND;
116    rc = snprintf_s(buf, FILE_SIZE, FILE_SIZE - 1, "%o", sumAppend);
117    if (rc == -1) {
118        tloge("snprintf_s failed: %d\n", rc);
119        return;
120    }
121    for (i = 0; i < CHECK_SUM_LEN; ++i) {
122        index[j + i] = buf[i];
123    }
124}
125
126#define ZIP_OPEN_MODE 0400U
127
128/* write file content to tar zip file */
129static void WriteZipContent(gzFile gzFd, const char *fileName, long fileSize)
130{
131    char buf[HEADER_NUM];
132    ssize_t ret;
133    int32_t iret;
134    long temFileSize = fileSize;
135    bool cond = (gzFd == NULL || fileName == NULL || fileSize == 0);
136
137    if (cond) {
138        tloge("fd or fileName or fileSize invalid\n");
139        return;
140    }
141
142    int32_t fileFd = open(fileName, O_CREAT | O_RDWR, ZIP_OPEN_MODE);
143    if (fileFd < 0) {
144        return;
145    }
146    while (temFileSize > 0) {
147        (void)memset_s(buf, HEADER_NUM, 0, HEADER_NUM);
148        ret = read(fileFd, buf, HEADER_NUM);
149        if (ret < 0) {
150            tloge("read failed\n");
151            goto CLOSE_FD;
152        }
153
154        iret = gzwrite(gzFd, buf, HEADER_NUM);
155        if (iret < 0) {
156            tloge("gzwrite failed\n");
157            goto CLOSE_FD;
158        } else if (iret < HEADER_NUM) {
159            tloge("incomplete gzwrite\n");
160            goto CLOSE_FD;
161        }
162
163        temFileSize -= HEADER_NUM;
164    }
165
166CLOSE_FD:
167    close(fileFd);
168}
169
170static int32_t OpenZipFile(const char *outputName, gzFile *outFile, gid_t pathGroup)
171{
172    int32_t ret;
173
174    *outFile = NULL;
175
176    int32_t fd = open(outputName, O_CREAT | O_WRONLY, ZIP_OPEN_MODE);
177    if (fd < 0) {
178        tloge("open file failed\n");
179        return -1;
180    }
181    gzFile out = gzdopen(fd, "w");
182    if (out == NULL) {
183        tloge("change fd to file failed\n");
184        close(fd);
185        return -1;
186    }
187    ret = fchown(fd, (uid_t)-1, pathGroup);
188    if (ret < 0) {
189        tloge("chown failed\n");
190        gzclose(out);
191        return -1;
192    }
193    ret = fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
194    if (ret < 0) {
195        tloge("chmod failed\n");
196        gzclose(out);
197        return -1;
198    }
199
200    *outFile = out;
201    return 0;
202}
203
204static bool JudgeFileValidite(const char *index, struct stat *fileAttr)
205{
206    if (index == NULL) {
207        return false;
208    }
209
210    if (lstat(index, fileAttr) < 0) {
211        return false;
212    }
213
214    if (!S_ISREG(fileAttr->st_mode)) {
215        return false;
216    }
217    return true;
218}
219
220#define FILE_NAME_INVALID_LEN  8U
221static int32_t WriteSingleFile(const char *fileName, gzFile out)
222{
223    struct stat fileAttr = {0};
224    struct TagHeader header;
225    char *s1 = NULL;
226    int32_t ret;
227
228    if (!JudgeFileValidite(fileName, &fileAttr)) {
229        return 0;
230    }
231
232    /* fileName contain file path and file name, search for the file name from fileName. */
233    s1 = strrchr(fileName, '/');
234    if (s1 == NULL || strlen(s1) < FILE_NAME_INVALID_LEN) {
235        return -1;
236    }
237
238    (void)memset_s(&header, sizeof(header), 0, sizeof(header));
239    WriteHeader(&header, (const char *)(s1 + 1), fileAttr.st_size);
240    ret = gzwrite(out, &header, sizeof(struct TagHeader));
241    if (ret < 0) {
242        tloge("gzwrite failed\n");
243        return -1;
244    }
245    WriteZipContent(out, fileName, fileAttr.st_size);
246    return 0;
247}
248
249/* tar and zip input files to output file */
250void TarZipFiles(uint32_t nameCount, const char **inputNames, const char *outputName, gid_t pathGroup)
251{
252    gzFile out = NULL;
253    int32_t ret;
254    struct TagHeader endHeader;
255    const char **fileName = NULL;
256    uint32_t i;
257    bool cond = (inputNames == NULL || outputName == NULL || nameCount != LOG_FILE_INDEX_MAX);
258
259    if (cond) {
260        return;
261    }
262
263    ret = OpenZipFile(outputName, &out, pathGroup);
264    if (ret != 0) {
265        return;
266    }
267
268    fileName = inputNames;
269    for (i = 0; i < nameCount; ++i) {
270        if (*fileName == NULL) {
271            ++fileName;
272            continue;
273        }
274        ret = WriteSingleFile(*fileName, out);
275        if (ret != 0) {
276            goto GZ_CLOSE;
277        }
278
279        ++fileName;
280    }
281
282    (void)memset_s(&endHeader, sizeof(endHeader), 0, sizeof(endHeader));
283    ret = gzwrite(out, &endHeader, sizeof(endHeader));
284    if (ret < 0) {
285        tloge("gzwrite failed\n");
286    }
287
288GZ_CLOSE:
289    gzclose(out);
290    return;
291}
292