| File src/io/ior.c changed (mode: 100644) (index 9cb067895..4a230de86) |
| ... |
... |
static int is_file(const char path[]); |
| 53 |
53 |
static VisitResult mv_visitor(const char full_path[], VisitAction action, |
static VisitResult mv_visitor(const char full_path[], VisitAction action, |
| 54 |
54 |
int deep, void *param); |
int deep, void *param); |
| 55 |
55 |
static VisitResult cp_mv_visitor(const char full_path[], VisitAction action, |
static VisitResult cp_mv_visitor(const char full_path[], VisitAction action, |
| 56 |
|
void *param, int cp); |
|
|
56 |
|
void *param, int cp, int deep); |
| 57 |
57 |
static VisitResult vr_from_io_res(IoRes result); |
static VisitResult vr_from_io_res(IoRes result); |
| 58 |
58 |
|
|
| 59 |
59 |
IoRes |
IoRes |
| |
| ... |
... |
ior_cp(io_args_t *args) |
| 163 |
163 |
static VisitResult |
static VisitResult |
| 164 |
164 |
cp_visitor(const char full_path[], VisitAction action, int deep, void *param) |
cp_visitor(const char full_path[], VisitAction action, int deep, void *param) |
| 165 |
165 |
{ |
{ |
| 166 |
|
return cp_mv_visitor(full_path, action, param, 1); |
|
|
166 |
|
return cp_mv_visitor(full_path, action, param, /*cp=*/1, deep); |
| 167 |
167 |
} |
} |
| 168 |
168 |
|
|
| 169 |
169 |
IoRes |
IoRes |
| |
| ... |
... |
is_file(const char path[]) |
| 378 |
378 |
static VisitResult |
static VisitResult |
| 379 |
379 |
mv_visitor(const char full_path[], VisitAction action, int deep, void *param) |
mv_visitor(const char full_path[], VisitAction action, int deep, void *param) |
| 380 |
380 |
{ |
{ |
| 381 |
|
return cp_mv_visitor(full_path, action, param, 0); |
|
|
381 |
|
return cp_mv_visitor(full_path, action, param, /*cp=*/0, /*deep=*/0); |
| 382 |
382 |
} |
} |
| 383 |
383 |
|
|
| 384 |
384 |
/* Generic implementation of traverse() visitor for subtree copying/moving. |
/* Generic implementation of traverse() visitor for subtree copying/moving. |
| 385 |
385 |
* Returns 0 on success, otherwise non-zero is returned. */ |
* Returns 0 on success, otherwise non-zero is returned. */ |
| 386 |
386 |
static VisitResult |
static VisitResult |
| 387 |
|
cp_mv_visitor(const char full_path[], VisitAction action, void *param, int cp) |
|
|
387 |
|
cp_mv_visitor(const char full_path[], VisitAction action, void *param, int cp, |
|
388 |
|
int deep) |
| 388 |
389 |
{ |
{ |
| 389 |
390 |
io_args_t *const cp_args = param; |
io_args_t *const cp_args = param; |
| 390 |
391 |
const char *dst_full_path; |
const char *dst_full_path; |
| |
| ... |
... |
cp_mv_visitor(const char full_path[], VisitAction action, void *param, int cp) |
| 433 |
434 |
/* It's safe to always use fast file cloning on moving files. */ |
/* It's safe to always use fast file cloning on moving files. */ |
| 434 |
435 |
.arg4.fast_file_cloning = cp ? cp_args->arg4.fast_file_cloning : 1, |
.arg4.fast_file_cloning = cp ? cp_args->arg4.fast_file_cloning : 1, |
| 435 |
436 |
.arg4.data_sync = cp_args->arg4.data_sync, |
.arg4.data_sync = cp_args->arg4.data_sync, |
| 436 |
|
.arg4.deep_copying = cp ? cp_args->arg4.deep_copying : 0, |
|
|
437 |
|
/* Deep copying may be suppressed for links that can't be copied. */ |
|
438 |
|
.arg4.deep_copying = cp ? deep && cp_args->arg4.deep_copying : 0, |
| 437 |
439 |
|
|
| 438 |
440 |
.cancellation = cp_args->cancellation, |
.cancellation = cp_args->cancellation, |
| 439 |
441 |
.confirm = cp_args->confirm, |
.confirm = cp_args->confirm, |
| File src/io/private/traverser.c changed (mode: 100644) (index d9e2526c9..3a2e15397) |
| 19 |
19 |
#include "traverser.h" |
#include "traverser.h" |
| 20 |
20 |
|
|
| 21 |
21 |
#include <stddef.h> /* NULL */ |
#include <stddef.h> /* NULL */ |
|
22 |
|
#include <stdio.h> /* snprintf() */ |
| 22 |
23 |
#include <stdlib.h> /* free() */ |
#include <stdlib.h> /* free() */ |
| 23 |
24 |
|
|
| 24 |
25 |
#include "../../compat/os.h" |
#include "../../compat/os.h" |
| 25 |
26 |
#include "../../utils/fs.h" |
#include "../../utils/fs.h" |
| 26 |
27 |
#include "../../utils/path.h" |
#include "../../utils/path.h" |
| 27 |
28 |
#include "../../utils/str.h" |
#include "../../utils/str.h" |
|
29 |
|
#include "../../utils/trie.h" |
| 28 |
30 |
|
|
| 29 |
|
static VisitResult traverse_subtree(const char path[], int deep, |
|
| 30 |
|
subtree_visitor visitor, void *param); |
|
|
31 |
|
/* Data used by traverse_subtree(). */ |
|
32 |
|
typedef struct |
|
33 |
|
{ |
|
34 |
|
subtree_visitor visitor; /* Callback to invoke for directories and files. */ |
|
35 |
|
void *param; /* Parameter to pass to the visitor. */ |
|
36 |
|
trie_t *parents; /* "List" of parents to detect symlink cycles. NULL |
|
37 |
|
if traversal isn't deep. */ |
|
38 |
|
int deep; /* Whether symlinks in source path are resolved. */ |
|
39 |
|
} |
|
40 |
|
traverse_data_t; |
|
41 |
|
|
|
42 |
|
static VisitResult traverse_subtree(const char path[], traverse_data_t *data); |
|
43 |
|
static int add_parent(traverse_data_t *data, const char path[], |
|
44 |
|
const struct stat *st); |
|
45 |
|
static int remove_parent(traverse_data_t *data, const char path[], |
|
46 |
|
const struct stat *st); |
| 31 |
47 |
|
|
| 32 |
48 |
IoRes |
IoRes |
| 33 |
49 |
traverse(const char path[], int deep, subtree_visitor visitor, void *param) |
traverse(const char path[], int deep, subtree_visitor visitor, void *param) |
| |
| ... |
... |
traverse(const char path[], int deep, subtree_visitor visitor, void *param) |
| 44 |
60 |
} |
} |
| 45 |
61 |
else |
else |
| 46 |
62 |
{ |
{ |
| 47 |
|
visit_result = traverse_subtree(path, deep, visitor, param); |
|
|
63 |
|
traverse_data_t data = { |
|
64 |
|
.deep = deep, |
|
65 |
|
.visitor = visitor, |
|
66 |
|
.param = param, |
|
67 |
|
}; |
|
68 |
|
|
|
69 |
|
if(deep) |
|
70 |
|
{ |
|
71 |
|
data.parents = trie_create(/*free_func=*/NULL); |
|
72 |
|
if(data.parents == NULL) |
|
73 |
|
{ |
|
74 |
|
return IO_RES_FAILED; |
|
75 |
|
} |
|
76 |
|
} |
|
77 |
|
|
|
78 |
|
visit_result = traverse_subtree(path, &data); |
|
79 |
|
trie_free(data.parents); |
| 48 |
80 |
} |
} |
| 49 |
81 |
|
|
| 50 |
82 |
switch(visit_result) |
switch(visit_result) |
| |
| ... |
... |
traverse(const char path[], int deep, subtree_visitor visitor, void *param) |
| 58 |
90 |
|
|
| 59 |
91 |
/* A generic subtree traversing. Returns status of visitation. */ |
/* A generic subtree traversing. Returns status of visitation. */ |
| 60 |
92 |
static VisitResult |
static VisitResult |
| 61 |
|
traverse_subtree(const char path[], int deep, subtree_visitor visitor, |
|
| 62 |
|
void *param) |
|
|
93 |
|
traverse_subtree(const char path[], traverse_data_t *data) |
| 63 |
94 |
{ |
{ |
|
95 |
|
int deep = data->deep; |
|
96 |
|
subtree_visitor visitor = data->visitor; |
|
97 |
|
void *param = data->param; |
|
98 |
|
|
|
99 |
|
struct stat dir_st; |
|
100 |
|
if(deep) |
|
101 |
|
{ |
|
102 |
|
/* Save the result of stat() both as a tiny optimization and to make sure we |
|
103 |
|
* remove the same entry even if path ends up pointing to a different |
|
104 |
|
* location at the end of the function. */ |
|
105 |
|
if(os_stat(path, &dir_st) != 0) |
|
106 |
|
{ |
|
107 |
|
return VR_ERROR; |
|
108 |
|
} |
|
109 |
|
|
|
110 |
|
if(add_parent(data, path, &dir_st) != 0) |
|
111 |
|
{ |
|
112 |
|
/* Copy this symbolic link to a directory which makes a cycle as a |
|
113 |
|
* file. */ |
|
114 |
|
return visitor(path, VA_FILE, /*deep=*/0, param); |
|
115 |
|
} |
|
116 |
|
} |
|
117 |
|
|
| 64 |
118 |
DIR *dir; |
DIR *dir; |
| 65 |
119 |
struct dirent *d; |
struct dirent *d; |
| 66 |
120 |
VisitResult enter_result; |
VisitResult enter_result; |
| |
| ... |
... |
traverse_subtree(const char path[], int deep, subtree_visitor visitor, |
| 92 |
146 |
/* Optionally treat symbolic links to directories as files as well. */ |
/* Optionally treat symbolic links to directories as files as well. */ |
| 93 |
147 |
if(deep ? is_dirent_targets_dir(full_path, d) : entry_is_dir(full_path, d)) |
if(deep ? is_dirent_targets_dir(full_path, d) : entry_is_dir(full_path, d)) |
| 94 |
148 |
{ |
{ |
| 95 |
|
result = traverse_subtree(full_path, deep, visitor, param); |
|
|
149 |
|
result = traverse_subtree(full_path, data); |
| 96 |
150 |
} |
} |
| 97 |
151 |
else |
else |
| 98 |
152 |
{ |
{ |
| |
| ... |
... |
traverse_subtree(const char path[], int deep, subtree_visitor visitor, |
| 112 |
166 |
result = visitor(path, VA_DIR_LEAVE, deep, param); |
result = visitor(path, VA_DIR_LEAVE, deep, param); |
| 113 |
167 |
} |
} |
| 114 |
168 |
|
|
|
169 |
|
if(deep && remove_parent(data, path, &dir_st) != 0) |
|
170 |
|
{ |
|
171 |
|
result = VR_ERROR; |
|
172 |
|
} |
|
173 |
|
|
| 115 |
174 |
return result; |
return result; |
| 116 |
175 |
} |
} |
| 117 |
176 |
|
|
|
177 |
|
/* Adds path to the list of active parents. Returns zero unless hit an |
|
178 |
|
* insertion error or the parent is already present. */ |
|
179 |
|
static int |
|
180 |
|
add_parent(traverse_data_t *data, const char path[], const struct stat *st) |
|
181 |
|
{ |
|
182 |
|
static char mark; |
|
183 |
|
|
|
184 |
|
#ifndef _WIN32 |
|
185 |
|
char key[40]; |
|
186 |
|
snprintf(key, sizeof(key), "%llx:%llx", (unsigned long long)st->st_dev, |
|
187 |
|
(unsigned long long)st->st_ino); |
|
188 |
|
#else |
|
189 |
|
const char *key = path; |
|
190 |
|
#endif |
|
191 |
|
|
|
192 |
|
void *node_data; |
|
193 |
|
if(trie_get(data->parents, key, &node_data) == 0 && node_data != NULL) |
|
194 |
|
{ |
|
195 |
|
return 1; |
|
196 |
|
} |
|
197 |
|
|
|
198 |
|
/* The value of the data doesn't matter, just setting it to non-NULL. */ |
|
199 |
|
if(trie_set(data->parents, key, &mark) < 0) |
|
200 |
|
{ |
|
201 |
|
return 1; |
|
202 |
|
} |
|
203 |
|
|
|
204 |
|
return 0; |
|
205 |
|
} |
|
206 |
|
|
|
207 |
|
/* Removes path from the list of active parents. Returns zero if the parent was |
|
208 |
|
* in the list and got successfully removed. */ |
|
209 |
|
static int |
|
210 |
|
remove_parent(traverse_data_t *data, const char path[], const struct stat *st) |
|
211 |
|
{ |
|
212 |
|
#ifndef _WIN32 |
|
213 |
|
char key[40]; |
|
214 |
|
snprintf(key, sizeof(key), "%llx:%llx", (unsigned long long)st->st_dev, |
|
215 |
|
(unsigned long long)st->st_ino); |
|
216 |
|
#else |
|
217 |
|
const char *key = path; |
|
218 |
|
#endif |
|
219 |
|
|
|
220 |
|
if(trie_set(data->parents, key, /*data=*/NULL) <= 0) |
|
221 |
|
{ |
|
222 |
|
return 1; |
|
223 |
|
} |
|
224 |
|
|
|
225 |
|
return 0; |
|
226 |
|
} |
|
227 |
|
|
| 118 |
228 |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
| 119 |
229 |
/* vim: set cinoptions+=t0 filetype=c : */ |
/* vim: set cinoptions+=t0 filetype=c : */ |
| File tests/ioeta/calculate.c changed (mode: 100644) (index b899f5696..38af3cf38) |
| ... |
... |
TEST(symlink_to_files_can_be_followed, IF(not_windows)) |
| 162 |
162 |
ioeta_free(estim); |
ioeta_free(estim); |
| 163 |
163 |
} |
} |
| 164 |
164 |
|
|
|
165 |
|
TEST(dir_link_loop_is_avoided, IF(not_windows)) |
|
166 |
|
{ |
|
167 |
|
ioeta_estim_t *const estim = ioeta_alloc(NULL, no_cancellation); |
|
168 |
|
|
|
169 |
|
{ |
|
170 |
|
io_args_t args = { |
|
171 |
|
.arg1.path = SANDBOX_PATH "/dir", |
|
172 |
|
.arg3.mode = 0700, |
|
173 |
|
}; |
|
174 |
|
ioe_errlst_init(&args.result.errors); |
|
175 |
|
|
|
176 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_mkdir(&args)); |
|
177 |
|
assert_int_equal(0, args.result.errors.error_count); |
|
178 |
|
} |
|
179 |
|
|
|
180 |
|
{ |
|
181 |
|
io_args_t args = { |
|
182 |
|
.arg1.path = ".", |
|
183 |
|
.arg2.target = SANDBOX_PATH "/dir/parent", |
|
184 |
|
}; |
|
185 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_ln(&args)); |
|
186 |
|
} |
|
187 |
|
|
|
188 |
|
ioeta_calculate(estim, SANDBOX_PATH "/dir", /*shallow=*/0, /*deep=*/1); |
|
189 |
|
|
|
190 |
|
assert_int_equal(2, estim->total_items); |
|
191 |
|
assert_int_equal(0, estim->current_item); |
|
192 |
|
assert_int_equal(0, estim->total_bytes); |
|
193 |
|
assert_int_equal(0, estim->current_byte); |
|
194 |
|
|
|
195 |
|
{ |
|
196 |
|
io_args_t args = { |
|
197 |
|
.arg1.path = SANDBOX_PATH "/dir/parent", |
|
198 |
|
}; |
|
199 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_rmfile(&args)); |
|
200 |
|
} |
|
201 |
|
|
|
202 |
|
{ |
|
203 |
|
io_args_t args = { |
|
204 |
|
.arg1.path = SANDBOX_PATH "/dir", |
|
205 |
|
}; |
|
206 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_rmdir(&args)); |
|
207 |
|
} |
|
208 |
|
|
|
209 |
|
ioeta_free(estim); |
|
210 |
|
} |
|
211 |
|
|
| 165 |
212 |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
| 166 |
213 |
/* vim: set cinoptions+=t0 filetype=c : */ |
/* vim: set cinoptions+=t0 filetype=c : */ |
| File tests/ior/cp-symlinks.c changed (mode: 100644) (index cdf3de7d5..9a198a083) |
| ... |
... |
TEST(nested_symlink_to_dir_is_dir_after_copy, IF(not_windows)) |
| 240 |
240 |
} |
} |
| 241 |
241 |
} |
} |
| 242 |
242 |
|
|
|
243 |
|
TEST(dir_link_loop_is_avoided_on_deep_copy, IF(not_windows)) |
|
244 |
|
{ |
|
245 |
|
{ |
|
246 |
|
io_args_t args = { |
|
247 |
|
.arg1.path = SANDBOX_PATH "/dir", |
|
248 |
|
.arg3.mode = 0700, |
|
249 |
|
}; |
|
250 |
|
ioe_errlst_init(&args.result.errors); |
|
251 |
|
|
|
252 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_mkdir(&args)); |
|
253 |
|
assert_int_equal(0, args.result.errors.error_count); |
|
254 |
|
} |
|
255 |
|
{ |
|
256 |
|
io_args_t args = { |
|
257 |
|
.arg1.path = ".", |
|
258 |
|
.arg2.target = SANDBOX_PATH "/dir/parent", |
|
259 |
|
}; |
|
260 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_ln(&args)); |
|
261 |
|
} |
|
262 |
|
|
|
263 |
|
{ |
|
264 |
|
io_args_t args = { |
|
265 |
|
.arg1.src = SANDBOX_PATH "/dir", |
|
266 |
|
.arg2.dst = SANDBOX_PATH "/dir-copy", |
|
267 |
|
.arg4.deep_copying = 1, |
|
268 |
|
}; |
|
269 |
|
ioe_errlst_init(&args.result.errors); |
|
270 |
|
|
|
271 |
|
assert_int_equal(IO_RES_SUCCEEDED, ior_cp(&args)); |
|
272 |
|
assert_int_equal(0, args.result.errors.error_count); |
|
273 |
|
} |
|
274 |
|
|
|
275 |
|
{ |
|
276 |
|
io_args_t args = { |
|
277 |
|
.arg1.path = SANDBOX_PATH "/dir", |
|
278 |
|
}; |
|
279 |
|
ioe_errlst_init(&args.result.errors); |
|
280 |
|
|
|
281 |
|
assert_int_equal(IO_RES_SUCCEEDED, ior_rm(&args)); |
|
282 |
|
assert_int_equal(0, args.result.errors.error_count); |
|
283 |
|
} |
|
284 |
|
{ |
|
285 |
|
io_args_t args = { |
|
286 |
|
.arg1.path = SANDBOX_PATH "/dir-copy/parent", |
|
287 |
|
}; |
|
288 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_rmfile(&args)); |
|
289 |
|
} |
|
290 |
|
{ |
|
291 |
|
io_args_t args = { |
|
292 |
|
.arg1.path = SANDBOX_PATH "/dir-copy", |
|
293 |
|
}; |
|
294 |
|
assert_int_equal(IO_RES_SUCCEEDED, iop_rmdir(&args)); |
|
295 |
|
} |
|
296 |
|
} |
|
297 |
|
|
| 243 |
298 |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
| 244 |
299 |
/* vim: set cinoptions+=t0 filetype=c : */ |
/* vim: set cinoptions+=t0 filetype=c : */ |