1/**
2 * Copyright (c) 2022 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#include <errno.h>
17#include <ftw.h>
18#include <limits.h>
19#include <fcntl.h>
20#include <unistd.h>
21#include <stdlib.h>
22#include <dirent.h>
23#include <pthread.h>
24#include <sys/stat.h>
25#include "functionalext.h"
26
27#define TEST_FD_LIMIT 128
28#define TEST_FLAG_SIZE 4
29#define TEST_DIGIT_TWO 2
30#define TEST_PATH_DEPTH 5
31#define TEST_NFTW_PATH "/data/local/tmp/nftwPath"
32
33pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
34
35static int nftw_callback(const char *pathname, const struct stat *sb, int flag, struct FTW *ftw)
36{
37    EXPECT_TRUE("nftw_callback", pathname != NULL);
38    EXPECT_TRUE("nftw_callback", sb != NULL);
39    if (flag == FTW_NS) {
40        struct stat st;
41        EXPECT_EQ("nftw_callback", stat(pathname, &st), -1);
42        return 0;
43    }
44
45    if (S_ISDIR(sb->st_mode)) {
46        if (access(pathname, R_OK) == 0) {
47            EXPECT_TRUE("nftw_callback", flag == FTW_D || flag == FTW_DP);
48        } else {
49            EXPECT_EQ("nftw_callback", flag, FTW_DNR);
50        }
51    } else if (S_ISLNK(sb->st_mode)) {
52        EXPECT_TRUE("nftw_callback", flag == FTW_SL || flag == FTW_SLN);
53    } else {
54        EXPECT_EQ("nftw_callback", flag, FTW_F);
55    }
56    return 0;
57}
58
59void remove_directory(const char *path)
60{
61    DIR *dir;
62    struct dirent *entry;
63    char filepath[PATH_MAX];
64
65    if (!(dir = opendir(path))) {
66        return;
67    }
68
69    while ((entry = readdir(dir)) != NULL) {
70        if (entry->d_type == DT_DIR) {
71            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
72                continue;
73            }
74
75            int result = snprintf(filepath, sizeof(filepath), "%s/%s", path, entry->d_name);
76            if (result >= sizeof(filepath)) {
77                t_error("%s error in snprintf! \n", __func__);
78            }
79            remove_directory(filepath);
80        } else {
81            int result = snprintf(filepath, sizeof(filepath), "%s/%s", path, entry->d_name);
82            if (result >= sizeof(filepath)) {
83                t_error("%s error in snprintf! \n", __func__);
84            }
85            if (remove(filepath) == -1) {
86                t_error("%s error in remove test nftw filepath! \n", __func__);
87            }
88        }
89    }
90
91    closedir(dir);
92
93    // Now we can remove the empty directory
94    if (rmdir(path) == -1) {
95        t_error("%s error in rmdir test nftw path! \n", __func__);
96    }
97}
98
99void nftw_build_testfile(const char *path)
100{
101    // Create directory
102    if (mkdir(path, 0755) == -1) {
103        t_error("%s error in mkdir test nftw path! %s \n", __func__, path);
104        return;
105    }
106
107    char file[PATH_MAX];
108    int result = snprintf(file, sizeof(file), "%s/normal_file.txt", path);
109    if (result >= sizeof(file)) {
110        t_error("%s error in snprintf! \n", __func__);
111    }
112    // Create plain file
113    int fd = open(file, O_WRONLY | O_CREAT, 0644);
114    if (fd == -1) {
115        t_error("%s error in open normal_file.txt! \n", __func__);
116        return;
117    }
118    close(fd);
119
120    result = snprintf(file, sizeof(file), "%s/non-executable_file.txt", path);
121    if (result >= sizeof(file)) {
122        t_error("%s error in snprintf! \n", __func__);
123    }
124    // Create non-executable file
125    fd = open(file, O_WRONLY | O_CREAT, 0666);
126    if (fd == -1) {
127        t_error("%s error in open normal_file.txt! \n", __func__);
128        return;
129    }
130    close(fd);
131
132    result = snprintf(file, sizeof(file), "%s/unauthorized_file.txt", path);
133    if (result >= sizeof(file)) {
134        t_error("%s error in snprintf! \n", __func__);
135    }
136    // Create unauthorized file
137    fd = open(file, O_WRONLY | O_CREAT, 0000);
138    if (fd == -1) {
139        t_error("%s error in open normal_file.txt! \n", __func__);
140        return;
141    }
142    close(fd);
143
144    result = snprintf(file, sizeof(file), "%s/.hidden_file.txt", path);
145    if (result >= sizeof(file)) {
146        t_error("%s error in snprintf! \n", __func__);
147    }
148    // Create Hidden Files
149    fd = open(file, O_WRONLY | O_CREAT, 0644);
150    if (fd == -1) {
151        t_error("%s error in open hidden_file.txt! \n", __func__);
152        return;
153    }
154    close(fd);
155
156    result = snprintf(file, sizeof(file), "%s/read_only_file.txt", path);
157    if (result >= sizeof(file)) {
158        t_error("%s error in snprintf! \n", __func__);
159    }
160    //Create Read-only files
161    fd = open(file, O_WRONLY | O_CREAT, 0444);
162    if (fd == -1) {
163        t_error("%s error in open read_only_file.txt! \n", __func__);
164        return;
165    }
166    close(fd);
167
168    result = snprintf(file, sizeof(file), "%s/symlink_to_normal_file", path);
169    if (result >= sizeof(file)) {
170        t_error("%s error in snprintf! \n", __func__);
171    }
172    // Create Symbolic links
173    if (symlink("normal_file.txt", file) == -1) {
174        t_error("%s error in open symlink_to_normal_file.txt! \n", __func__);
175        return;
176    }
177}
178
179void nftw_build_testDir()
180{
181    nftw_build_testfile(TEST_NFTW_PATH);
182    char path[PATH_MAX];
183    int result = snprintf(path, sizeof(path), "%s", TEST_NFTW_PATH);
184    if (result >= sizeof(path)) {
185        t_error("%s error in snprintf! \n", __func__);
186    }
187    for (int i = 0 ; i < TEST_PATH_DEPTH ; i++) {
188        result = snprintf(path, sizeof(path), "%s/data", path);
189        if (result >= sizeof(path)) {
190            t_error("%s error in snprintf! \n", __func__);
191        }
192        nftw_build_testfile(path);
193    }
194}
195
196/**
197 * @tc.name      : nftw_0100
198 * @tc.desc      : Traverse directory /data
199 * @tc.level     : Level 0
200 */
201void nftw_0100(void)
202{
203    int flag[TEST_FLAG_SIZE] = {FTW_PHYS, FTW_MOUNT, FTW_CHDIR, FTW_DEPTH};
204    int i;
205    for (i = 0; i < TEST_FLAG_SIZE; i++) {
206        int ret = nftw(TEST_NFTW_PATH, nftw_callback, TEST_FD_LIMIT, flag[i]);
207        EXPECT_EQ("nftw_0100", ret, 0);
208    }
209}
210
211/**
212 * @tc.name      : nftw_0200
213 * @tc.desc      : Traverse directory /data, but the maximum number of file descriptors is 0
214 * @tc.level     : Level 0
215 */
216void nftw_0200(void)
217{
218    int ret = nftw(TEST_NFTW_PATH, nftw_callback, 0, FTW_PHYS);
219    EXPECT_EQ("nftw_0200", ret, 0);
220}
221
222/**
223 * @tc.name      : nftw_0300
224 * @tc.desc      : The file path length exceeds PATH_MAX, traverse the directory
225 * @tc.level     : Level 2
226 */
227void nftw_0300(void)
228{
229    char path[PATH_MAX * TEST_DIGIT_TWO];
230    memset(path, 'a', sizeof(path));
231    path[PATH_MAX * TEST_DIGIT_TWO - 1] = 0;
232    int ret = nftw(path, nftw_callback, TEST_FD_LIMIT, FTW_PHYS);
233    EXPECT_EQ("nftw_0300", ret, -1);
234    EXPECT_EQ("nftw_0300", errno, ENAMETOOLONG);
235}
236
237int main(void)
238{
239    pthread_mutex_lock(&g_mutex);
240    nftw_build_testDir();
241    nftw_0100();
242    nftw_0200();
243    nftw_0300();
244    remove_directory(TEST_NFTW_PATH);
245    pthread_mutex_unlock(&g_mutex);
246    return t_status;
247}