]> git.saurik.com Git - apple/copyfile.git/blame - copyfile_test/stat_test.c
copyfile-173.40.2.tar.gz
[apple/copyfile.git] / copyfile_test / stat_test.c
CommitLineData
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
26typedef int (*special_mkdir_func)(const char *, mode_t, const char *);
27
28static 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
103bool 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
151bool 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}