]>
Commit | Line | Data |
---|---|---|
23896e53 A |
1 | // |
2 | // stat_test.c | |
3 | // copyfile_test | |
4 | // | |
5 | ||
6 | #include <stdbool.h> | |
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <unistd.h> | |
11 | #include <removefile.h> | |
12 | #include <sandbox/rootless.h> | |
13 | #include <sys/fcntl.h> | |
14 | #include <sys/stat.h> | |
15 | #include <sys/types.h> | |
16 | ||
17 | #include "../copyfile.h" | |
18 | #include "stat_test.h" | |
19 | #include "test_utils.h" | |
20 | ||
21 | #define STORAGE_CLASS "copyfile_test" | |
22 | #define SPECIAL_DIR_NAME "special_dir/" | |
23 | #define REGULAR_DIR_NAME "regular_dir" | |
24 | #define TEST_FILE_NAME "almighty_tallest" | |
25 | ||
26 | typedef int (*special_mkdir_func)(const char *, mode_t, const char *); | |
27 | ||
28 | static bool test_special_dir_with_flag(const char *source_directory, const char *test_directory, | |
29 | special_mkdir_func create_func, uint32_t flag_to_test) { | |
30 | char special_dir[BSIZE_B] = {0}, regular_dir[BSIZE_B] = {0}, test_file[BSIZE_B] = {0}; | |
31 | struct stat sb = {0}; | |
32 | bool success = true; | |
33 | ||
34 | // The plan here is as follows: | |
35 | // | |
36 | // (1) Create a special directory using 'create_func', and verify that its bsdflags | |
37 | // have `flag_to_test` set. | |
38 | // | |
39 | // (2) Copy the contents of `source_directory` into that special directory, | |
40 | // and verify that the directory and, optionally, a well-known file inside it | |
41 | // have `flag_to_test` set. | |
42 | // | |
43 | // (3) Copy the contents of the special directory into a directory that is not a child | |
44 | // of our special directory (a 'regular' directory), and verify that the directory and | |
45 | // a well-known file inside it do *NOT* have `flag_to_test` set. | |
46 | ||
47 | // Create path names. | |
48 | assert_with_errno(snprintf(special_dir, BSIZE_B, "%s/" SPECIAL_DIR_NAME, test_directory) > 0); | |
49 | assert_with_errno(snprintf(regular_dir, BSIZE_B, "%s/" REGULAR_DIR_NAME, test_directory) > 0); | |
50 | assert_with_errno(snprintf(test_file, BSIZE_B, "%s/" TEST_FILE_NAME, special_dir) > 0); | |
51 | ||
52 | // Create our regular directory. | |
53 | assert_no_err(mkdir(regular_dir, DEFAULT_MKDIR_PERM)); | |
54 | ||
55 | // Create our special directory. | |
56 | assert_no_err(create_func(special_dir, DEFAULT_MKDIR_PERM, STORAGE_CLASS)); | |
57 | ||
58 | // (1) Make sure the special directory has the specified bit set. | |
59 | assert_no_err(stat(special_dir, &sb)); | |
60 | assert(sb.st_flags & flag_to_test); | |
61 | ||
62 | // Now, copy the source directory's into the special directory. | |
63 | assert_no_err(copyfile(source_directory, special_dir, NULL, COPYFILE_ALL|COPYFILE_RECURSIVE)); | |
64 | ||
65 | // (2) Make sure that the resulting folder (and optionally, its well-known subfile) | |
66 | // have the specified bit set. | |
67 | assert_no_err(stat(special_dir, &sb)); | |
68 | success &= verify_st_flags(&sb, flag_to_test); | |
69 | ||
70 | if (flag_to_test != SF_NOUNLINK) { | |
71 | assert_no_err(stat(test_file, &sb)); | |
72 | success &= verify_st_flags(&sb, flag_to_test); | |
73 | } | |
74 | ||
75 | // Finally, copy the contents of the special directory into our regular directory. | |
76 | // Since at least one of the files in this directory will have a rootless xattr, | |
77 | // which cannot be copied here, we do not attempt to copy extended attributes here. | |
78 | assert_no_err(copyfile(special_dir, regular_dir, NULL, | |
79 | COPYFILE_DATA|COPYFILE_SECURITY|COPYFILE_RECURSIVE)); | |
80 | ||
81 | // (3) Make sure that the regular directory (and optionally, its well-known subfile) | |
82 | // do *NOT* have the specified bit set. | |
83 | assert_no_err(stat(regular_dir, &sb)); | |
84 | success &= ((sb.st_flags & flag_to_test) == 0); | |
85 | ||
86 | if (flag_to_test != SF_NOUNLINK) { | |
87 | // Rebuild the path to the subfile, as our original path is relative | |
88 | // to the special directory. | |
89 | memset(test_file, 0, BSIZE_B); | |
90 | assert_with_errno(snprintf(test_file, BSIZE_B, "%s/" TEST_FILE_NAME, regular_dir) > 0); | |
91 | ||
92 | assert_no_err(stat(test_file, &sb)); | |
93 | success &= verify_st_flags(&sb, 0); | |
94 | } | |
95 | ||
96 | // Clean up after the test. | |
97 | assert_no_err(removefile(special_dir, NULL, REMOVEFILE_RECURSIVE)); | |
98 | assert_no_err(removefile(regular_dir, NULL, REMOVEFILE_RECURSIVE)); | |
99 | ||
100 | return success; | |
101 | } | |
102 | ||
103 | bool do_preserve_dst_flags_test(const char *test_directory, __unused size_t block_size) { | |
104 | int interior_file_src_fd, test_file_id; | |
105 | char exterior_dir_src[BSIZE_B] = {0}, interior_file_src[BSIZE_B] = {0}; | |
106 | uid_t euid = geteuid(); | |
107 | bool success = true; | |
108 | ||
109 | printf("START [preserve_dst_flags]\n"); | |
110 | ||
111 | // Construct our source layout. | |
112 | assert_with_errno(snprintf(exterior_dir_src, BSIZE_B, "%s/" TEST_FILE_NAME, test_directory) > 0); | |
113 | ||
114 | assert_no_err(mkdir(exterior_dir_src, DEFAULT_MKDIR_PERM)); | |
115 | ||
116 | test_file_id = rand() % DEFAULT_NAME_MOD; | |
117 | create_test_file_name(exterior_dir_src, TEST_FILE_NAME, test_file_id, interior_file_src); | |
118 | ||
119 | // Create our interior test file. | |
120 | interior_file_src_fd = open(interior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); | |
121 | assert_with_errno(interior_file_src_fd >= 0); | |
122 | assert_no_err(close(interior_file_src_fd)); // file only needs to exist | |
123 | ||
124 | if (euid == 0) { | |
125 | // Try to copy our directory into a restricted environment. | |
126 | success &= test_special_dir_with_flag(exterior_dir_src, test_directory, | |
127 | rootless_mkdir_restricted, SF_RESTRICTED); | |
128 | ||
129 | // Make sure SF_NOUNLINK works as well. | |
130 | success &= test_special_dir_with_flag(exterior_dir_src, test_directory, | |
131 | rootless_mkdir_nounlink, SF_NOUNLINK); | |
132 | } else { | |
133 | printf("Skipping SF_RESTRICTED and SF_NOUNLINK tests, because we are not root.\n"); | |
134 | } | |
135 | ||
136 | // Try to copy our directory into a datavault. | |
137 | success &= test_special_dir_with_flag(exterior_dir_src, test_directory, | |
138 | rootless_mkdir_datavault, UF_DATAVAULT); | |
139 | ||
140 | if (success) { | |
141 | printf("PASS [preserve_dst_flags]\n"); | |
142 | } else { | |
143 | printf("FAIL [preserve_dst_flags]\n"); | |
144 | } | |
145 | ||
146 | (void)removefile(exterior_dir_src, NULL, REMOVEFILE_RECURSIVE); | |
147 | ||
148 | return success ? EXIT_SUCCESS : EXIT_FAILURE; | |
149 | } | |
62b275d9 A |
150 | |
151 | bool do_preserve_dst_tracked_test(const char *test_directory, __unused size_t block_size) { | |
152 | char file_src[BSIZE_B] = {0}, file_dst[BSIZE_B] = {0}; | |
153 | off_t src_fsize = 0x1000; | |
154 | int test_file_id; | |
155 | struct stat dst_stb; | |
156 | bool success = true; | |
157 | ||
158 | printf("START [preserve_dst_tracked]\n"); | |
159 | ||
160 | // Create source file | |
161 | assert_with_errno(snprintf(file_src, BSIZE_B, "%s/" TEST_FILE_NAME, test_directory) > 0); | |
162 | assert_no_err(close(open(file_src, O_CREAT|O_EXCL, 0644))); | |
163 | assert_no_err(truncate(file_src, src_fsize)); | |
164 | ||
165 | // Create destination file | |
166 | test_file_id = rand() % DEFAULT_NAME_MOD; | |
167 | assert_with_errno(snprintf(file_dst, BSIZE_B, "%s/%s.%d", test_directory, TEST_FILE_NAME, test_file_id) > 0); | |
168 | assert_no_err(close(open(file_dst, O_CREAT|O_EXCL, 0644))); | |
169 | ||
170 | // Track destination file | |
171 | assert_no_err(chflags(file_dst, UF_TRACKED)); | |
172 | ||
173 | // Try to copy src onto destination | |
174 | assert_no_err(copyfile(file_src, file_dst, NULL, COPYFILE_DATA|COPYFILE_STAT|COPYFILE_PRESERVE_DST_TRACKED)); | |
175 | ||
176 | assert_no_err(stat(file_dst, &dst_stb)); | |
85b8a2cb A |
177 | success = success && (dst_stb.st_size == src_fsize); |
178 | success = success && (dst_stb.st_flags & UF_TRACKED); | |
62b275d9 A |
179 | if (success) { |
180 | printf("PASS [preserve_dst_tracked]\n"); | |
181 | } else { | |
182 | printf("FAIL [preserve_dst_tracked]\n"); | |
183 | } | |
184 | ||
185 | (void)unlink(file_src); | |
186 | (void)unlink(file_dst); | |
187 | ||
188 | return success ? EXIT_SUCCESS : EXIT_FAILURE; | |
189 | } |