Branch data Line data Source code
1 : : #include "sute.h"
2 : :
3 : : /**
4 : : * @brief Make a file sparse by extending logical size while keeping allocated blocks unchanged
5 : : *
6 : : */
7 : 2 : static Return make_sparse_size_change_without_allocated_block_growth(
8 : : const char *relative_path_to_tmpdir,
9 : : off_t *new_size_out,
10 : : blkcnt_t *blocks_after_change_out)
11 : : {
12 : : /* Status returned by this function through provide()
13 : : Default value assumes successful completion */
14 : 2 : Return status = SUCCESS;
15 : 2 : struct stat before_stat = {0};
16 : 2 : struct stat after_stat = {0};
17 : 2 : create(char,absolute_path);
18 : :
19 [ + - + - : 2 : if(relative_path_to_tmpdir == NULL || new_size_out == NULL || blocks_after_change_out == NULL)
- + ]
20 : : {
21 : 0 : status = FAILURE;
22 : : }
23 : :
24 [ + - ]: 2 : if(SUCCESS == status)
25 : : {
26 : 2 : status = construct_path(relative_path_to_tmpdir,absolute_path);
27 : : }
28 : :
29 [ + - ]: 2 : if(SUCCESS == status)
30 : : {
31 : 2 : status = get_file_stat(getcstring(absolute_path),&before_stat);
32 : : }
33 : :
34 [ + - ]: 2 : if(SUCCESS == status)
35 : : {
36 : 2 : const off_t grown_size = before_stat.st_size + (off_t)131072;
37 : :
38 : : // Grow logical size via truncate to create a sparse tail without writing payload bytes
39 [ - + ]: 2 : if(grown_size <= before_stat.st_size)
40 : : {
41 : 0 : status = FAILURE;
42 [ - + ]: 2 : } else if(truncate(getcstring(absolute_path),grown_size) != 0){
43 : 0 : status = FAILURE;
44 : : }
45 : : }
46 : :
47 [ + - ]: 2 : if(SUCCESS == status)
48 : : {
49 : 2 : status = get_file_stat(getcstring(absolute_path),&after_stat);
50 : : }
51 : :
52 [ + - - + ]: 2 : if(SUCCESS == status && after_stat.st_size <= before_stat.st_size)
53 : : {
54 : 0 : status = FAILURE;
55 : : }
56 : :
57 [ + - - + ]: 2 : if(SUCCESS == status && after_stat.st_blocks != before_stat.st_blocks)
58 : : {
59 : 0 : status = FAILURE;
60 : : }
61 : :
62 [ + - ]: 2 : if(SUCCESS == status)
63 : : {
64 : 2 : *new_size_out = after_stat.st_size;
65 : 2 : *blocks_after_change_out = after_stat.st_blocks;
66 : : }
67 : :
68 : 2 : del(absolute_path);
69 : :
70 : 2 : return(status);
71 : : }
72 : :
73 : : /**
74 : : * @brief Rewrite file content with dense bytes while preserving logical size
75 : : *
76 : : */
77 : 2 : static Return rewrite_file_dense_with_same_size(
78 : : const char *relative_path_to_tmpdir,
79 : : const off_t target_size,
80 : : const blkcnt_t blocks_before_rewrite)
81 : : {
82 : : /* Status returned by this function through provide()
83 : : Default value assumes successful completion */
84 : 2 : Return status = SUCCESS;
85 : 2 : FILE *file = NULL;
86 : 2 : struct stat after_stat = {0};
87 : : unsigned char buffer[4096];
88 : 2 : create(char,absolute_path);
89 : :
90 : 2 : memset(buffer,'X',sizeof(buffer));
91 : :
92 [ + - - + ]: 2 : if(relative_path_to_tmpdir == NULL || target_size <= 0)
93 : : {
94 : 0 : status = FAILURE;
95 : : }
96 : :
97 [ + - ]: 2 : if(SUCCESS == status)
98 : : {
99 : 2 : status = construct_path(relative_path_to_tmpdir,absolute_path);
100 : : }
101 : :
102 [ + - ]: 2 : if(SUCCESS == status)
103 : : {
104 : : // Rewrite the whole file with real bytes while keeping the same logical size
105 : 2 : file = fopen(getcstring(absolute_path),"wb");
106 : :
107 [ - + ]: 2 : if(file == NULL)
108 : : {
109 : 0 : status = FAILURE;
110 : : }
111 : : }
112 : :
113 : 2 : off_t written = 0;
114 : :
115 [ + - + + ]: 68 : while(SUCCESS == status && written < target_size)
116 : : {
117 : 66 : const off_t remaining = target_size - written;
118 : 66 : size_t chunk = sizeof(buffer);
119 : :
120 [ + + ]: 66 : if(remaining < (off_t)chunk)
121 : : {
122 : 2 : chunk = (size_t)remaining;
123 : : }
124 : :
125 [ - + ]: 66 : if(fwrite(buffer,sizeof(unsigned char),chunk,file) != chunk)
126 : : {
127 : 0 : status = FAILURE;
128 : : } else {
129 : 66 : written += (off_t)chunk;
130 : : }
131 : : }
132 : :
133 [ + - ]: 2 : if(file != NULL)
134 : : {
135 [ - + ]: 2 : if(fclose(file) != 0)
136 : : {
137 : 0 : status = FAILURE;
138 : : }
139 : : }
140 : :
141 [ + - ]: 2 : if(SUCCESS == status)
142 : : {
143 : 2 : status = get_file_stat(getcstring(absolute_path),&after_stat);
144 : : }
145 : :
146 [ + - - + ]: 2 : if(SUCCESS == status && after_stat.st_size != target_size)
147 : : {
148 : 0 : status = FAILURE;
149 : : }
150 : :
151 [ + - - + ]: 2 : if(SUCCESS == status && after_stat.st_blocks == blocks_before_rewrite)
152 : : {
153 : 0 : status = FAILURE;
154 : : }
155 : :
156 : 2 : del(absolute_path);
157 : :
158 : 2 : return(status);
159 : : }
160 : :
161 : : /**
162 : : * @brief Verify that lsize and asize flags are reported independently from on-disk file changes
163 : : *
164 : : */
165 : 2 : static Return test0034_1(void)
166 : : {
167 : 2 : INITTEST;
168 : :
169 : 2 : create(char,result);
170 : 2 : create(char,pattern);
171 : :
172 : 2 : const char *prepare_fixture_command = "cd ${TMPDIR};"
173 : : "rm -f 0034_lsize_vs_asize_flags.db;"
174 : : "rm -rf tests/fixtures/diff1_backup;"
175 : : "mv tests/fixtures/diffs/diff1 tests/fixtures/diff1_backup;"
176 : : "cp -a tests/fixtures/diff1_backup tests/fixtures/diffs/diff1;";
177 : :
178 : 2 : const char *cleanup_fixture_command = "cd ${TMPDIR};"
179 : : "rm -f 0034_lsize_vs_asize_flags.db;"
180 : : "rm -rf tests/fixtures/diffs/diff1;"
181 : : "mv tests/fixtures/diff1_backup tests/fixtures/diffs/diff1;";
182 : :
183 : 2 : off_t sparse_file_size = 0;
184 : 2 : blkcnt_t sparse_blocks = 0;
185 : :
186 : : // First pass runs in TESTING mode without verbose output
187 [ + - + - ]: 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
188 : :
189 : : // Prepare fixture copy and clean previous DB
190 [ + - + - ]: 2 : ASSERT(SUCCESS == external_call(prepare_fixture_command,NULL,NULL,COMPLETED,ALLOW_BOTH));
191 : :
192 : 2 : const char *arguments = "--database=0034_lsize_vs_asize_flags.db tests/fixtures/diffs/diff1";
193 [ + - + - ]: 2 : ASSERT(SUCCESS == runit(arguments,NULL,NULL,COMPLETED,ALLOW_BOTH));
194 : :
195 : : // Case 1: grow logical size as sparse extension and preserve allocated blocks
196 [ + - + - ]: 2 : ASSERT(SUCCESS == make_sparse_size_change_without_allocated_block_growth(
197 : : "tests/fixtures/diffs/diff1/path1/AAA/BCB/CCC/a.txt",
198 : : &sparse_file_size,
199 : : &sparse_blocks));
200 : :
201 : : // Second and third passes run in non-TESTING mode
202 : : // Second pass uses --verbose, third pass uses --watch-timestamps
203 [ + - + - ]: 2 : ASSERT(SUCCESS == set_environment_variable("TESTING","false"));
204 : :
205 : 2 : arguments = "--verbose --update --database=0034_lsize_vs_asize_flags.db tests/fixtures/diffs/diff1";
206 [ + - + - ]: 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
207 : :
208 : 2 : const char *filename = "templates/0034_001_1.txt";
209 [ + - + - ]: 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
210 [ + - + - ]: 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
211 : :
212 [ + - + - ]: 2 : ASSERT(NULL != strstr(getcstring(result),"changed: lsize"));
213 [ + - + - ]: 2 : ASSERT(NULL == strstr(getcstring(result),"changed: asize"));
214 : :
215 : : // Case 2: rewrite file densely with same logical size so only allocated blocks differ
216 [ + - + - ]: 2 : ASSERT(SUCCESS == rewrite_file_dense_with_same_size(
217 : : "tests/fixtures/diffs/diff1/path1/AAA/BCB/CCC/a.txt",
218 : : sparse_file_size,
219 : : sparse_blocks));
220 : :
221 : 2 : arguments = "--update --watch-timestamps --database=0034_lsize_vs_asize_flags.db tests/fixtures/diffs/diff1";
222 [ + - + - ]: 2 : ASSERT(SUCCESS == runit(arguments,result,NULL,COMPLETED,ALLOW_BOTH));
223 : :
224 : 2 : filename = "templates/0034_001_2.txt";
225 [ + - + - ]: 2 : ASSERT(SUCCESS == get_file_content(filename,pattern));
226 [ + - + - ]: 2 : ASSERT(SUCCESS == match_pattern(result,pattern,filename));
227 : :
228 [ + - + - ]: 2 : ASSERT(NULL == strstr(getcstring(result),"changed: lsize"));
229 [ + - + - ]: 2 : ASSERT(NULL != strstr(getcstring(result),"changed: asize"));
230 [ + - + - ]: 2 : ASSERT(NULL != strstr(getcstring(result),"update & rehash"));
231 [ + - + - ]: 2 : ASSERT(NULL != strstr(getcstring(result),"path1/AAA/BCB/CCC/a.txt"));
232 : :
233 [ + - + - ]: 2 : ASSERT(SUCCESS == external_call(cleanup_fixture_command,NULL,NULL,COMPLETED,ALLOW_BOTH));
234 : :
235 : 2 : del(pattern);
236 : 2 : del(result);
237 : :
238 [ + - - + : 2 : RETURN_STATUS;
- - - + +
- ]
239 : : }
240 : :
241 : 2 : Return test0034(void)
242 : : {
243 : 2 : INITTEST;
244 : :
245 [ + - ]: 2 : TEST(test0034_1,"Metadata diff flags: lsize-only and asize-only cases...");
246 : :
247 [ + - - + : 2 : RETURN_STATUS;
- - - + +
- ]
248 : : }
|