11 #include <removefile.h>
12 #include <sandbox/rootless.h>
13 #include <sys/fcntl.h>
15 #include <sys/types.h>
17 #include "../copyfile.h"
18 #include "stat_test.h"
19 #include "test_utils.h"
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"
26 typedef int (*special_mkdir_func
)(const char *, mode_t
, const char *);
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};
34 // The plan here is as follows:
36 // (1) Create a special directory using 'create_func', and verify that its bsdflags
37 // have `flag_to_test` set.
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.
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.
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);
52 // Create our regular directory.
53 assert_no_err(mkdir(regular_dir
, DEFAULT_MKDIR_PERM
));
55 // Create our special directory.
56 assert_no_err(create_func(special_dir
, DEFAULT_MKDIR_PERM
, STORAGE_CLASS
));
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
);
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
));
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
);
70 if (flag_to_test
!= SF_NOUNLINK
) {
71 assert_no_err(stat(test_file
, &sb
));
72 success
&= verify_st_flags(&sb
, flag_to_test
);
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
));
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);
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);
92 assert_no_err(stat(test_file
, &sb
));
93 success
&= verify_st_flags(&sb
, 0);
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
));
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();
109 printf("START [preserve_dst_flags]\n");
111 // Construct our source layout.
112 assert_with_errno(snprintf(exterior_dir_src
, BSIZE_B
, "%s/" TEST_FILE_NAME
, test_directory
) > 0);
114 assert_no_err(mkdir(exterior_dir_src
, DEFAULT_MKDIR_PERM
));
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
);
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
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
);
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
);
133 printf("Skipping SF_RESTRICTED and SF_NOUNLINK tests, because we are not root.\n");
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
);
141 printf("PASS [preserve_dst_flags]\n");
143 printf("FAIL [preserve_dst_flags]\n");
146 (void)removefile(exterior_dir_src
, NULL
, REMOVEFILE_RECURSIVE
);
148 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;