1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ |
---|
2 | /* gnome-vfs-xfer.c - File transfers in the GNOME Virtual File System. |
---|
3 | |
---|
4 | Copyright (C) 1999 Free Software Foundation |
---|
5 | |
---|
6 | The Gnome Library is free software; you can redistribute it and/or |
---|
7 | modify it under the terms of the GNU Library General Public License as |
---|
8 | published by the Free Software Foundation; either version 2 of the |
---|
9 | License, or (at your option) any later version. |
---|
10 | |
---|
11 | The Gnome Library is distributed in the hope that it will be useful, |
---|
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
14 | Library General Public License for more details. |
---|
15 | |
---|
16 | You should have received a copy of the GNU Library General Public |
---|
17 | License along with the Gnome Library; see the file COPYING.LIB. If not, |
---|
18 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
19 | Boston, MA 02111-1307, USA. |
---|
20 | |
---|
21 | Authors: |
---|
22 | Ettore Perazzoli <ettore@comm2000.it> |
---|
23 | Pavel Cisler <pavel@eazel.com> |
---|
24 | */ |
---|
25 | |
---|
26 | /* FIXME bugzilla.eazel.com 1197: |
---|
27 | Check that all the flags passed by address are set at least once by |
---|
28 | functions that are expected to set them. */ |
---|
29 | /* FIXME bugzilla.eazel.com 1198: |
---|
30 | There should be a concept of a `context' in the file methods that would |
---|
31 | allow us to set a prefix URI for all the operations. This way we can |
---|
32 | greatly optimize access to "strange" file systems. */ |
---|
33 | |
---|
34 | #ifdef HAVE_CONFIG_H |
---|
35 | #include <config.h> |
---|
36 | #endif |
---|
37 | |
---|
38 | #include <sys/time.h> |
---|
39 | #include <string.h> |
---|
40 | |
---|
41 | #include "gnome-vfs.h" |
---|
42 | #include "gnome-vfs-private.h" |
---|
43 | #include "gnome-vfs-private-types.h" |
---|
44 | |
---|
45 | /* Implementation of file transfers (`gnome_vfs_xfer*()' functions). */ |
---|
46 | |
---|
47 | static GnomeVFSResult remove_directory (GnomeVFSURI *uri, |
---|
48 | gboolean recursive, |
---|
49 | GnomeVFSProgressCallbackState *progress, |
---|
50 | GnomeVFSXferOptions xfer_options, |
---|
51 | GnomeVFSXferErrorMode *error_mode, |
---|
52 | gboolean *skip); |
---|
53 | |
---|
54 | |
---|
55 | enum { |
---|
56 | /* size used for accounting for the expense of copying directories, |
---|
57 | * doing a move operation, etc. in a progress callback |
---|
58 | * (during a move the file size is used for a regular file). |
---|
59 | */ |
---|
60 | DEFAULT_SIZE_OVERHEAD = 1024 |
---|
61 | }; |
---|
62 | |
---|
63 | /* in asynch mode the progress callback does a context switch every time |
---|
64 | * it gets called. We'll only call it every now and then to not loose a |
---|
65 | * lot of performance |
---|
66 | */ |
---|
67 | #define UPDATE_PERIOD ((gint64) (100 * 1000)) |
---|
68 | |
---|
69 | static gint64 |
---|
70 | system_time (void) |
---|
71 | { |
---|
72 | struct timeval time_of_day; |
---|
73 | |
---|
74 | gettimeofday (&time_of_day, NULL); |
---|
75 | return (gint64) time_of_day.tv_usec + ((gint64) time_of_day.tv_sec) * 1000000; |
---|
76 | } |
---|
77 | |
---|
78 | static void |
---|
79 | init_progress (GnomeVFSProgressCallbackState *progress_state, |
---|
80 | GnomeVFSXferProgressInfo *progress_info) |
---|
81 | { |
---|
82 | progress_info->source_name = NULL; |
---|
83 | progress_info->target_name = NULL; |
---|
84 | progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
85 | progress_info->vfs_status = GNOME_VFS_OK; |
---|
86 | progress_info->phase = GNOME_VFS_XFER_PHASE_INITIAL; |
---|
87 | progress_info->file_index = 1; |
---|
88 | progress_info->files_total = 0; |
---|
89 | progress_info->bytes_total = 0; |
---|
90 | progress_info->file_size = 0; |
---|
91 | progress_info->bytes_copied = 0; |
---|
92 | progress_info->total_bytes_copied = 0; |
---|
93 | progress_info->duplicate_name = NULL; |
---|
94 | progress_info->top_level_item = FALSE; |
---|
95 | |
---|
96 | progress_state->progress_info = progress_info; |
---|
97 | progress_state->sync_callback = NULL; |
---|
98 | progress_state->update_callback = NULL; |
---|
99 | progress_state->async_job_data = NULL; |
---|
100 | progress_state->next_update_callback_time = 0; |
---|
101 | progress_state->next_text_update_callback_time = 0; |
---|
102 | progress_state->update_callback_period = UPDATE_PERIOD; |
---|
103 | } |
---|
104 | |
---|
105 | static void |
---|
106 | free_progress (GnomeVFSXferProgressInfo *progress_info) |
---|
107 | { |
---|
108 | g_free (progress_info->source_name); |
---|
109 | progress_info->source_name = NULL; |
---|
110 | g_free (progress_info->target_name); |
---|
111 | progress_info->target_name = NULL; |
---|
112 | } |
---|
113 | |
---|
114 | static void |
---|
115 | progress_set_source_target_uris (GnomeVFSProgressCallbackState *progress, |
---|
116 | const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri) |
---|
117 | { |
---|
118 | g_free (progress->progress_info->source_name); |
---|
119 | progress->progress_info->source_name = source_uri ? gnome_vfs_uri_to_string (source_uri, |
---|
120 | GNOME_VFS_URI_HIDE_NONE) : NULL; |
---|
121 | g_free (progress->progress_info->target_name); |
---|
122 | progress->progress_info->target_name = dest_uri ? gnome_vfs_uri_to_string (dest_uri, |
---|
123 | GNOME_VFS_URI_HIDE_NONE) : NULL; |
---|
124 | |
---|
125 | } |
---|
126 | |
---|
127 | static int |
---|
128 | call_progress (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase) |
---|
129 | { |
---|
130 | int result; |
---|
131 | |
---|
132 | /* FIXME bugzilla.eazel.com 1199: should use proper progress result values from an enum here */ |
---|
133 | |
---|
134 | result = 0; |
---|
135 | progress_set_source_target_uris (progress, NULL, NULL); |
---|
136 | |
---|
137 | progress->next_update_callback_time = system_time () + progress->update_callback_period; |
---|
138 | |
---|
139 | progress->progress_info->phase = phase; |
---|
140 | |
---|
141 | if (progress->sync_callback != NULL) |
---|
142 | result = (* progress->sync_callback) (progress->progress_info, progress->user_data); |
---|
143 | |
---|
144 | if (progress->update_callback != NULL) |
---|
145 | result = (* progress->update_callback) (progress->progress_info, |
---|
146 | progress->async_job_data); |
---|
147 | |
---|
148 | return result; |
---|
149 | } |
---|
150 | |
---|
151 | static GnomeVFSXferErrorAction |
---|
152 | call_progress_with_current_names (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase) |
---|
153 | { |
---|
154 | int result; |
---|
155 | |
---|
156 | result = GNOME_VFS_XFER_ERROR_ACTION_ABORT; |
---|
157 | |
---|
158 | progress->next_update_callback_time = system_time () + progress->update_callback_period; |
---|
159 | progress->next_update_callback_time = progress->next_text_update_callback_time; |
---|
160 | |
---|
161 | progress->progress_info->phase = phase; |
---|
162 | |
---|
163 | if (progress->sync_callback != NULL) |
---|
164 | result = (* progress->sync_callback) (progress->progress_info, progress->user_data); |
---|
165 | |
---|
166 | if (progress->update_callback != NULL) |
---|
167 | result = (* progress->update_callback) (progress->progress_info, |
---|
168 | progress->async_job_data); |
---|
169 | |
---|
170 | return result; |
---|
171 | } |
---|
172 | |
---|
173 | static int |
---|
174 | call_progress_uri (GnomeVFSProgressCallbackState *progress, |
---|
175 | const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri, |
---|
176 | GnomeVFSXferPhase phase) |
---|
177 | { |
---|
178 | int result; |
---|
179 | |
---|
180 | result = 0; |
---|
181 | progress_set_source_target_uris (progress, source_uri, dest_uri); |
---|
182 | |
---|
183 | progress->next_text_update_callback_time = system_time () + progress->update_callback_period; |
---|
184 | progress->next_update_callback_time = progress->next_text_update_callback_time; |
---|
185 | |
---|
186 | progress->progress_info->phase = phase; |
---|
187 | |
---|
188 | if (progress->sync_callback != NULL) |
---|
189 | result = (* progress->sync_callback) (progress->progress_info, progress->user_data); |
---|
190 | |
---|
191 | if (progress->update_callback != NULL) |
---|
192 | result = (* progress->update_callback) (progress->progress_info, progress->async_job_data); |
---|
193 | |
---|
194 | return result; |
---|
195 | } |
---|
196 | |
---|
197 | static int |
---|
198 | call_progress_often (GnomeVFSProgressCallbackState *progress, GnomeVFSXferPhase phase) |
---|
199 | { |
---|
200 | int result; |
---|
201 | gint64 now; |
---|
202 | |
---|
203 | result = 1; |
---|
204 | now = system_time (); |
---|
205 | |
---|
206 | progress->progress_info->phase = phase; |
---|
207 | |
---|
208 | if (progress->sync_callback != NULL) { |
---|
209 | result = (* progress->sync_callback) (progress->progress_info, progress->user_data); |
---|
210 | } |
---|
211 | |
---|
212 | if (now < progress->next_update_callback_time) { |
---|
213 | return result; |
---|
214 | } |
---|
215 | |
---|
216 | progress->next_update_callback_time = now + progress->update_callback_period; |
---|
217 | if (progress->update_callback != NULL) { |
---|
218 | result = (* progress->update_callback) (progress->progress_info, progress->async_job_data); |
---|
219 | } |
---|
220 | |
---|
221 | return result; |
---|
222 | } |
---|
223 | |
---|
224 | static int |
---|
225 | call_progress_with_uris_often (GnomeVFSProgressCallbackState *progress, |
---|
226 | const GnomeVFSURI *source_uri, const GnomeVFSURI *dest_uri, |
---|
227 | GnomeVFSXferPhase phase) |
---|
228 | { |
---|
229 | int result; |
---|
230 | gint64 now; |
---|
231 | |
---|
232 | progress_set_source_target_uris (progress, source_uri, dest_uri); |
---|
233 | result = 1; |
---|
234 | now = system_time (); |
---|
235 | |
---|
236 | progress->progress_info->phase = phase; |
---|
237 | |
---|
238 | if (progress->sync_callback != NULL) |
---|
239 | result = (* progress->sync_callback) (progress->progress_info, progress->user_data); |
---|
240 | |
---|
241 | if (now < progress->next_text_update_callback_time) |
---|
242 | return result; |
---|
243 | |
---|
244 | progress->next_text_update_callback_time = now + progress->update_callback_period; |
---|
245 | |
---|
246 | if (progress->update_callback != NULL) |
---|
247 | result = (* progress->update_callback) (progress->progress_info, progress->async_job_data); |
---|
248 | |
---|
249 | return result; |
---|
250 | } |
---|
251 | |
---|
252 | /* Handle an error condition according to `error_mode'. Returns `TRUE' if the |
---|
253 | * operation should be retried, `FALSE' otherwise. `*result' will be set to |
---|
254 | * the result value we want to be returned to the caller of the xfer |
---|
255 | * function. |
---|
256 | */ |
---|
257 | static gboolean |
---|
258 | handle_error (GnomeVFSResult *result, |
---|
259 | GnomeVFSProgressCallbackState *progress, |
---|
260 | GnomeVFSXferErrorMode *error_mode, |
---|
261 | gboolean *skip) |
---|
262 | { |
---|
263 | GnomeVFSXferErrorAction action; |
---|
264 | |
---|
265 | *skip = FALSE; |
---|
266 | |
---|
267 | switch (*error_mode) { |
---|
268 | case GNOME_VFS_XFER_ERROR_MODE_ABORT: |
---|
269 | return FALSE; |
---|
270 | |
---|
271 | case GNOME_VFS_XFER_ERROR_MODE_QUERY: |
---|
272 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR; |
---|
273 | progress->progress_info->vfs_status = *result; |
---|
274 | action = call_progress_with_current_names (progress, progress->progress_info->phase); |
---|
275 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
276 | |
---|
277 | switch (action) { |
---|
278 | case GNOME_VFS_XFER_ERROR_ACTION_RETRY: |
---|
279 | return TRUE; |
---|
280 | case GNOME_VFS_XFER_ERROR_ACTION_ABORT: |
---|
281 | *result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
282 | return FALSE; |
---|
283 | case GNOME_VFS_XFER_ERROR_ACTION_SKIP: |
---|
284 | *result = GNOME_VFS_OK; |
---|
285 | *skip = TRUE; |
---|
286 | return FALSE; |
---|
287 | } |
---|
288 | break; |
---|
289 | } |
---|
290 | |
---|
291 | *skip = FALSE; |
---|
292 | return FALSE; |
---|
293 | } |
---|
294 | |
---|
295 | /* This is conceptually similiar to the previous `handle_error()' function, but |
---|
296 | * handles the overwrite case. |
---|
297 | */ |
---|
298 | static gboolean |
---|
299 | handle_overwrite (GnomeVFSResult *result, |
---|
300 | GnomeVFSProgressCallbackState *progress, |
---|
301 | GnomeVFSXferErrorMode *error_mode, |
---|
302 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
303 | gboolean *replace, |
---|
304 | gboolean *skip) |
---|
305 | { |
---|
306 | GnomeVFSXferOverwriteAction action; |
---|
307 | |
---|
308 | switch (*overwrite_mode) { |
---|
309 | case GNOME_VFS_XFER_OVERWRITE_MODE_ABORT: |
---|
310 | *replace = FALSE; |
---|
311 | *result = GNOME_VFS_ERROR_FILE_EXISTS; |
---|
312 | *skip = FALSE; |
---|
313 | return FALSE; |
---|
314 | case GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE: |
---|
315 | *replace = TRUE; |
---|
316 | *skip = FALSE; |
---|
317 | return TRUE; |
---|
318 | case GNOME_VFS_XFER_OVERWRITE_MODE_SKIP: |
---|
319 | *replace = FALSE; |
---|
320 | *skip = TRUE; |
---|
321 | return FALSE; |
---|
322 | case GNOME_VFS_XFER_OVERWRITE_MODE_QUERY: |
---|
323 | progress->progress_info->vfs_status = *result; |
---|
324 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE; |
---|
325 | action = call_progress_with_current_names (progress, progress->progress_info->phase); |
---|
326 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
327 | |
---|
328 | switch (action) { |
---|
329 | case GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT: |
---|
330 | *replace = FALSE; |
---|
331 | *result = GNOME_VFS_ERROR_FILE_EXISTS; |
---|
332 | *skip = FALSE; |
---|
333 | return FALSE; |
---|
334 | case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE: |
---|
335 | *replace = TRUE; |
---|
336 | *skip = FALSE; |
---|
337 | return TRUE; |
---|
338 | case GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE_ALL: |
---|
339 | *replace = TRUE; |
---|
340 | *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE; |
---|
341 | *skip = FALSE; |
---|
342 | return TRUE; |
---|
343 | case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP: |
---|
344 | *replace = FALSE; |
---|
345 | *skip = TRUE; |
---|
346 | return FALSE; |
---|
347 | case GNOME_VFS_XFER_OVERWRITE_ACTION_SKIP_ALL: |
---|
348 | *replace = FALSE; |
---|
349 | *skip = TRUE; |
---|
350 | *overwrite_mode = GNOME_VFS_XFER_OVERWRITE_MODE_SKIP; |
---|
351 | return FALSE; |
---|
352 | } |
---|
353 | } |
---|
354 | |
---|
355 | *replace = FALSE; |
---|
356 | *skip = FALSE; |
---|
357 | return FALSE; |
---|
358 | } |
---|
359 | |
---|
360 | static GnomeVFSResult |
---|
361 | remove_file (GnomeVFSURI *uri, |
---|
362 | GnomeVFSProgressCallbackState *progress, |
---|
363 | GnomeVFSXferOptions xfer_options, |
---|
364 | GnomeVFSXferErrorMode *error_mode, |
---|
365 | gboolean *skip) |
---|
366 | { |
---|
367 | GnomeVFSResult result; |
---|
368 | gboolean retry; |
---|
369 | |
---|
370 | do { |
---|
371 | retry = FALSE; |
---|
372 | |
---|
373 | result = gnome_vfs_unlink_from_uri (uri); |
---|
374 | if (result != GNOME_VFS_OK) |
---|
375 | retry = handle_error (&result, progress, |
---|
376 | error_mode, skip); |
---|
377 | else { |
---|
378 | progress->progress_info->file_index++; |
---|
379 | /* add some small size for each deleted item because delete overhead |
---|
380 | * does not depend on file/directory size |
---|
381 | */ |
---|
382 | progress->progress_info->bytes_copied += DEFAULT_SIZE_OVERHEAD; |
---|
383 | if (call_progress_with_uris_often (progress, uri, NULL, |
---|
384 | GNOME_VFS_XFER_PHASE_DELETESOURCE) |
---|
385 | == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
386 | result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
387 | break; |
---|
388 | } |
---|
389 | } |
---|
390 | |
---|
391 | |
---|
392 | } while (retry); |
---|
393 | |
---|
394 | return result; |
---|
395 | } |
---|
396 | |
---|
397 | static GnomeVFSResult |
---|
398 | empty_directory (GnomeVFSURI *uri, |
---|
399 | GnomeVFSProgressCallbackState *progress, |
---|
400 | GnomeVFSXferOptions xfer_options, |
---|
401 | GnomeVFSXferErrorMode *error_mode, |
---|
402 | gboolean *skip) |
---|
403 | { |
---|
404 | GnomeVFSResult result; |
---|
405 | GnomeVFSDirectoryHandle *directory_handle; |
---|
406 | gboolean retry; |
---|
407 | |
---|
408 | |
---|
409 | *skip = FALSE; |
---|
410 | do { |
---|
411 | result = gnome_vfs_directory_open_from_uri (&directory_handle, uri, |
---|
412 | GNOME_VFS_FILE_INFO_DEFAULT, |
---|
413 | NULL); |
---|
414 | retry = FALSE; |
---|
415 | if (result != GNOME_VFS_OK) { |
---|
416 | retry = handle_error (&result, progress, |
---|
417 | error_mode, skip); |
---|
418 | } |
---|
419 | } while (retry); |
---|
420 | |
---|
421 | if (result != GNOME_VFS_OK || *skip) { |
---|
422 | return result; |
---|
423 | } |
---|
424 | |
---|
425 | for (;;) { |
---|
426 | GnomeVFSFileInfo info; |
---|
427 | GnomeVFSURI *item_uri; |
---|
428 | |
---|
429 | gnome_vfs_file_info_init (&info); |
---|
430 | result = gnome_vfs_directory_read_next (directory_handle, &info); |
---|
431 | if (result != GNOME_VFS_OK) |
---|
432 | break; |
---|
433 | |
---|
434 | /* Skip "." and "..". */ |
---|
435 | if (strcmp (info.name, ".") == 0 |
---|
436 | || strcmp (info.name, "..") == 0) { |
---|
437 | continue; |
---|
438 | } |
---|
439 | |
---|
440 | item_uri = gnome_vfs_uri_append_file_name (uri, info.name); |
---|
441 | |
---|
442 | if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
443 | result = remove_directory (item_uri, TRUE, |
---|
444 | progress, xfer_options, error_mode, |
---|
445 | skip); |
---|
446 | } else { |
---|
447 | result = remove_file (item_uri, progress, |
---|
448 | xfer_options, error_mode, |
---|
449 | skip); |
---|
450 | } |
---|
451 | |
---|
452 | gnome_vfs_uri_unref (item_uri); |
---|
453 | |
---|
454 | if (result != GNOME_VFS_OK) |
---|
455 | break; |
---|
456 | } |
---|
457 | gnome_vfs_directory_close (directory_handle); |
---|
458 | |
---|
459 | if (result == GNOME_VFS_ERROR_EOF) |
---|
460 | result = GNOME_VFS_OK; |
---|
461 | |
---|
462 | return result; |
---|
463 | } |
---|
464 | |
---|
465 | typedef struct { |
---|
466 | const GnomeVFSURI *base_uri; |
---|
467 | GList *uri_list; |
---|
468 | } PrependOneURIParams; |
---|
469 | |
---|
470 | static gboolean |
---|
471 | PrependOneURIToList (const gchar *rel_path, GnomeVFSFileInfo *info, |
---|
472 | gboolean recursing_will_loop, gpointer cast_to_params, gboolean *recurse) |
---|
473 | { |
---|
474 | PrependOneURIParams *params; |
---|
475 | |
---|
476 | params = (PrependOneURIParams *)cast_to_params; |
---|
477 | params->uri_list = g_list_prepend (params->uri_list, gnome_vfs_uri_append_string ( |
---|
478 | params->base_uri, rel_path)); |
---|
479 | |
---|
480 | if (recursing_will_loop) { |
---|
481 | return FALSE; |
---|
482 | } |
---|
483 | *recurse = TRUE; |
---|
484 | return TRUE; |
---|
485 | } |
---|
486 | |
---|
487 | static GnomeVFSResult |
---|
488 | non_recursive_empty_directory (GnomeVFSURI *directory_uri, |
---|
489 | GnomeVFSProgressCallbackState *progress, |
---|
490 | GnomeVFSXferOptions xfer_options, |
---|
491 | GnomeVFSXferErrorMode *error_mode, |
---|
492 | gboolean *skip) |
---|
493 | { |
---|
494 | /* Used as a fallback when we run out of file descriptors during |
---|
495 | * a deep recursive deletion. |
---|
496 | * We instead compile a flat list of URIs, doing a recursion that does not |
---|
497 | * keep directories open. |
---|
498 | */ |
---|
499 | |
---|
500 | GList *uri_list; |
---|
501 | GList *p; |
---|
502 | GnomeVFSURI *uri; |
---|
503 | GnomeVFSResult result; |
---|
504 | GnomeVFSFileInfo info; |
---|
505 | PrependOneURIParams visit_params; |
---|
506 | |
---|
507 | /* Build up the list so that deep items appear before their parents |
---|
508 | * so that we can delete directories, children first. |
---|
509 | */ |
---|
510 | visit_params.base_uri = directory_uri; |
---|
511 | visit_params.uri_list = NULL; |
---|
512 | result = gnome_vfs_directory_visit_uri (directory_uri, GNOME_VFS_FILE_INFO_DEFAULT, |
---|
513 | NULL, GNOME_VFS_DIRECTORY_VISIT_SAMEFS | GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK, |
---|
514 | PrependOneURIToList, &visit_params); |
---|
515 | |
---|
516 | uri_list = visit_params.uri_list; |
---|
517 | |
---|
518 | if (result == GNOME_VFS_OK) { |
---|
519 | for (p = uri_list; p != NULL; p = p->next) { |
---|
520 | |
---|
521 | uri = (GnomeVFSURI *)p->data; |
---|
522 | gnome_vfs_file_info_init (&info); |
---|
523 | result = gnome_vfs_get_file_info_uri (uri, &info, GNOME_VFS_FILE_INFO_DEFAULT); |
---|
524 | if (result == GNOME_VFS_OK) { |
---|
525 | if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
526 | result = remove_directory (uri, FALSE, |
---|
527 | progress, xfer_options, error_mode, skip); |
---|
528 | } else { |
---|
529 | result = remove_file (uri, progress, |
---|
530 | xfer_options, error_mode, skip); |
---|
531 | } |
---|
532 | } |
---|
533 | gnome_vfs_file_info_clear (&info); |
---|
534 | } |
---|
535 | } |
---|
536 | |
---|
537 | gnome_vfs_uri_list_free (uri_list); |
---|
538 | |
---|
539 | return result; |
---|
540 | } |
---|
541 | |
---|
542 | static GnomeVFSResult |
---|
543 | remove_directory (GnomeVFSURI *uri, |
---|
544 | gboolean recursive, |
---|
545 | GnomeVFSProgressCallbackState *progress, |
---|
546 | GnomeVFSXferOptions xfer_options, |
---|
547 | GnomeVFSXferErrorMode *error_mode, |
---|
548 | gboolean *skip) |
---|
549 | { |
---|
550 | GnomeVFSResult result; |
---|
551 | gboolean retry; |
---|
552 | |
---|
553 | result = GNOME_VFS_OK; |
---|
554 | |
---|
555 | if (recursive) { |
---|
556 | result = empty_directory (uri, progress, xfer_options, error_mode, skip); |
---|
557 | if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) { |
---|
558 | result = non_recursive_empty_directory (uri, progress, xfer_options, |
---|
559 | error_mode, skip); |
---|
560 | } |
---|
561 | } |
---|
562 | |
---|
563 | if (result == GNOME_VFS_ERROR_EOF) |
---|
564 | result = GNOME_VFS_OK; |
---|
565 | |
---|
566 | if (result == GNOME_VFS_OK) { |
---|
567 | do { |
---|
568 | retry = FALSE; |
---|
569 | |
---|
570 | result = gnome_vfs_remove_directory_from_uri (uri); |
---|
571 | if (result != GNOME_VFS_OK) { |
---|
572 | retry = handle_error (&result, progress, |
---|
573 | error_mode, skip); |
---|
574 | } else { |
---|
575 | progress->progress_info->file_index++; |
---|
576 | /* add some small size for each deleted item */ |
---|
577 | progress->progress_info->bytes_total += DEFAULT_SIZE_OVERHEAD; |
---|
578 | progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD; |
---|
579 | |
---|
580 | if (call_progress_with_uris_often (progress, uri, NULL, |
---|
581 | GNOME_VFS_XFER_PHASE_DELETESOURCE) |
---|
582 | == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
583 | result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
584 | break; |
---|
585 | } |
---|
586 | } |
---|
587 | |
---|
588 | } while (retry); |
---|
589 | } |
---|
590 | |
---|
591 | return result; |
---|
592 | } |
---|
593 | |
---|
594 | /* iterates the list of names in a given directory, applies @callback on each, |
---|
595 | * optionaly recurses into directories |
---|
596 | */ |
---|
597 | static GnomeVFSResult |
---|
598 | gnome_vfs_visit_list (const GList *name_uri_list, |
---|
599 | GnomeVFSFileInfoOptions info_options, |
---|
600 | GnomeVFSDirectoryVisitOptions visit_options, |
---|
601 | gboolean recursive, |
---|
602 | GnomeVFSDirectoryVisitFunc callback, |
---|
603 | gpointer data) |
---|
604 | { |
---|
605 | GnomeVFSResult result; |
---|
606 | const GList *p; |
---|
607 | GnomeVFSURI *uri; |
---|
608 | GnomeVFSFileInfo info; |
---|
609 | |
---|
610 | result = GNOME_VFS_OK; |
---|
611 | |
---|
612 | /* go through our list of items */ |
---|
613 | for (p = name_uri_list; p != NULL; p = p->next) { |
---|
614 | |
---|
615 | /* get the URI and VFSFileInfo for each */ |
---|
616 | uri = p->data; |
---|
617 | gnome_vfs_file_info_init (&info); |
---|
618 | result = gnome_vfs_get_file_info_uri (uri, &info, info_options); |
---|
619 | |
---|
620 | if (result == GNOME_VFS_OK) { |
---|
621 | gboolean tmp_recurse; |
---|
622 | |
---|
623 | tmp_recurse = TRUE; |
---|
624 | |
---|
625 | /* call our callback on each item*/ |
---|
626 | if (!callback (p->data, &info, FALSE, data, &tmp_recurse)) |
---|
627 | result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
628 | |
---|
629 | if (result == GNOME_VFS_OK |
---|
630 | && recursive |
---|
631 | && info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
632 | /* let gnome_vfs_directory_visit_uri call our callback |
---|
633 | * recursively |
---|
634 | */ |
---|
635 | result = gnome_vfs_directory_visit_uri (uri, info_options, |
---|
636 | NULL, visit_options, callback, data); |
---|
637 | } |
---|
638 | } |
---|
639 | if (result != GNOME_VFS_OK) |
---|
640 | break; |
---|
641 | } |
---|
642 | return result; |
---|
643 | } |
---|
644 | |
---|
645 | typedef struct { |
---|
646 | GnomeVFSProgressCallbackState *progress; |
---|
647 | GnomeVFSResult result; |
---|
648 | } CountEachFileSizeParams; |
---|
649 | |
---|
650 | /* iteratee for count_items_and_size */ |
---|
651 | static gboolean |
---|
652 | count_each_file_size_one (const gchar *rel_path, |
---|
653 | GnomeVFSFileInfo *info, |
---|
654 | gboolean recursing_will_loop, |
---|
655 | gpointer data, |
---|
656 | gboolean *recurse) |
---|
657 | { |
---|
658 | CountEachFileSizeParams *params; |
---|
659 | |
---|
660 | params = (CountEachFileSizeParams *)data; |
---|
661 | |
---|
662 | if (call_progress_often (params->progress, params->progress->progress_info->phase) == 0) { |
---|
663 | /* progress callback requested to stop */ |
---|
664 | params->result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
665 | *recurse = FALSE; |
---|
666 | return FALSE; |
---|
667 | } |
---|
668 | |
---|
669 | /* count each file, folder, symlink */ |
---|
670 | params->progress->progress_info->files_total++; |
---|
671 | if (info->type == GNOME_VFS_FILE_TYPE_REGULAR) { |
---|
672 | /* add each file size */ |
---|
673 | params->progress->progress_info->bytes_total += info->size; |
---|
674 | } else if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
675 | /* add some small size for each directory */ |
---|
676 | params->progress->progress_info->bytes_total += DEFAULT_SIZE_OVERHEAD; |
---|
677 | params->progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD; |
---|
678 | } |
---|
679 | |
---|
680 | /* watch out for infinite recursion*/ |
---|
681 | if (recursing_will_loop) { |
---|
682 | params->result = GNOME_VFS_ERROR_LOOP; |
---|
683 | return FALSE; |
---|
684 | } |
---|
685 | |
---|
686 | *recurse = TRUE; |
---|
687 | |
---|
688 | return TRUE; |
---|
689 | } |
---|
690 | |
---|
691 | /* calculate the number of items and their total size; used as a preflight |
---|
692 | * before the transfer operation starts |
---|
693 | */ |
---|
694 | static GnomeVFSResult |
---|
695 | count_items_and_size (const GList *name_uri_list, |
---|
696 | GnomeVFSXferOptions xfer_options, |
---|
697 | GnomeVFSProgressCallbackState *progress, |
---|
698 | gboolean move, |
---|
699 | gboolean link) |
---|
700 | { |
---|
701 | /* |
---|
702 | * FIXME bugzilla.eazel.com 1200: |
---|
703 | * Deal with errors here, respond to skip by pulling items from the name list |
---|
704 | */ |
---|
705 | GnomeVFSFileInfoOptions info_options; |
---|
706 | GnomeVFSDirectoryVisitOptions visit_options; |
---|
707 | CountEachFileSizeParams each_params; |
---|
708 | |
---|
709 | /* initialize the results */ |
---|
710 | progress->progress_info->files_total = 0; |
---|
711 | progress->progress_info->bytes_total = 0; |
---|
712 | |
---|
713 | /* set up the params for recursion */ |
---|
714 | visit_options = GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK; |
---|
715 | if (xfer_options & GNOME_VFS_XFER_SAMEFS) |
---|
716 | visit_options |= GNOME_VFS_DIRECTORY_VISIT_SAMEFS; |
---|
717 | each_params.progress = progress; |
---|
718 | each_params.result = GNOME_VFS_OK; |
---|
719 | |
---|
720 | if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) { |
---|
721 | info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS; |
---|
722 | } else { |
---|
723 | info_options = GNOME_VFS_FILE_INFO_DEFAULT; |
---|
724 | } |
---|
725 | |
---|
726 | return gnome_vfs_visit_list (name_uri_list, info_options, |
---|
727 | visit_options, !link && !move && (xfer_options & GNOME_VFS_XFER_RECURSIVE) != 0, |
---|
728 | count_each_file_size_one, &each_params); |
---|
729 | } |
---|
730 | |
---|
731 | /* calculate the number of items and their total size; used as a preflight |
---|
732 | * before the transfer operation starts |
---|
733 | */ |
---|
734 | static GnomeVFSResult |
---|
735 | directory_add_items_and_size (GnomeVFSURI *dir_uri, |
---|
736 | GnomeVFSXferOptions xfer_options, |
---|
737 | GnomeVFSProgressCallbackState *progress) |
---|
738 | { |
---|
739 | GnomeVFSFileInfoOptions info_options; |
---|
740 | GnomeVFSDirectoryVisitOptions visit_options; |
---|
741 | CountEachFileSizeParams each_params; |
---|
742 | |
---|
743 | /* set up the params for recursion */ |
---|
744 | visit_options = GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK; |
---|
745 | if (xfer_options & GNOME_VFS_XFER_SAMEFS) |
---|
746 | visit_options |= GNOME_VFS_DIRECTORY_VISIT_SAMEFS; |
---|
747 | each_params.progress = progress; |
---|
748 | each_params.result = GNOME_VFS_OK; |
---|
749 | |
---|
750 | if (xfer_options & GNOME_VFS_XFER_FOLLOW_LINKS) { |
---|
751 | info_options = GNOME_VFS_FILE_INFO_FOLLOW_LINKS; |
---|
752 | } else { |
---|
753 | info_options = GNOME_VFS_FILE_INFO_DEFAULT; |
---|
754 | } |
---|
755 | |
---|
756 | return gnome_vfs_directory_visit_uri (dir_uri, info_options, NULL, |
---|
757 | visit_options, count_each_file_size_one, &each_params); |
---|
758 | |
---|
759 | } |
---|
760 | |
---|
761 | /* Compares the list of files about to be created by a transfer with |
---|
762 | * any possible existing files with conflicting names in the target directory. |
---|
763 | * Handles conflicts, optionaly removing the conflicting file/directory |
---|
764 | */ |
---|
765 | static GnomeVFSResult |
---|
766 | handle_name_conflicts (GList **source_uri_list, |
---|
767 | GList **target_uri_list, |
---|
768 | GnomeVFSXferOptions xfer_options, |
---|
769 | GnomeVFSXferErrorMode *error_mode, |
---|
770 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
771 | GnomeVFSProgressCallbackState *progress) |
---|
772 | { |
---|
773 | GnomeVFSResult result; |
---|
774 | GList *source_item; |
---|
775 | GList *target_item; |
---|
776 | |
---|
777 | int conflict_count; /* values are 0, 1, many */ |
---|
778 | |
---|
779 | result = GNOME_VFS_OK; |
---|
780 | conflict_count = 0; |
---|
781 | |
---|
782 | /* Go through the list of names, find out if there is 0, 1 or more conflicts. */ |
---|
783 | for (target_item = *target_uri_list; target_item != NULL; |
---|
784 | target_item = target_item->next) { |
---|
785 | if (gnome_vfs_uri_exists ((GnomeVFSURI *)target_item->data)) { |
---|
786 | conflict_count++; |
---|
787 | if (conflict_count > 1) |
---|
788 | break; |
---|
789 | } |
---|
790 | } |
---|
791 | |
---|
792 | if (conflict_count == 0) { |
---|
793 | /* No conflicts, we are done. */ |
---|
794 | return GNOME_VFS_OK; |
---|
795 | } |
---|
796 | |
---|
797 | /* Pass in the conflict count so that we can decide to present the Replace All |
---|
798 | * for multiple conflicts. |
---|
799 | */ |
---|
800 | progress->progress_info->duplicate_count = conflict_count; |
---|
801 | |
---|
802 | |
---|
803 | /* Go through the list of names again, present overwrite alerts for each. */ |
---|
804 | for (target_item = *target_uri_list, source_item = *source_uri_list; |
---|
805 | target_item != NULL;) { |
---|
806 | gboolean replace; |
---|
807 | gboolean skip; |
---|
808 | GnomeVFSURI *uri; |
---|
809 | GnomeVFSFileInfo info; |
---|
810 | |
---|
811 | skip = FALSE; |
---|
812 | uri = (GnomeVFSURI *)target_item->data; |
---|
813 | if (gnome_vfs_uri_exists (uri)) { |
---|
814 | progress_set_source_target_uris (progress, NULL, uri); |
---|
815 | |
---|
816 | /* no error getting info -- file exists, ask what to do */ |
---|
817 | replace = handle_overwrite (&result, progress, error_mode, |
---|
818 | overwrite_mode, &replace, &skip); |
---|
819 | |
---|
820 | /* FIXME bugzilla.eazel.com 1207: |
---|
821 | * move items to Trash here |
---|
822 | */ |
---|
823 | |
---|
824 | /* get rid of the conflicting file */ |
---|
825 | if (replace) { |
---|
826 | gnome_vfs_file_info_init (&info); |
---|
827 | gnome_vfs_get_file_info_uri (uri, &info, GNOME_VFS_FILE_INFO_DEFAULT); |
---|
828 | if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
829 | remove_directory (uri, TRUE, progress, |
---|
830 | xfer_options, error_mode, &skip); |
---|
831 | } else { |
---|
832 | remove_file (uri, progress, |
---|
833 | xfer_options, error_mode, &skip); |
---|
834 | } |
---|
835 | gnome_vfs_file_info_clear (&info); |
---|
836 | } |
---|
837 | } |
---|
838 | |
---|
839 | |
---|
840 | if (result != GNOME_VFS_OK) { |
---|
841 | break; |
---|
842 | } |
---|
843 | |
---|
844 | if (skip) { |
---|
845 | /* skipping a file, remove it's name from the source and target name |
---|
846 | * lists. |
---|
847 | */ |
---|
848 | GList *source_item_to_remove; |
---|
849 | GList *target_item_to_remove; |
---|
850 | |
---|
851 | source_item_to_remove = source_item; |
---|
852 | target_item_to_remove = target_item; |
---|
853 | |
---|
854 | source_item = source_item->next; |
---|
855 | target_item = target_item->next; |
---|
856 | |
---|
857 | gnome_vfs_uri_unref ((GnomeVFSURI *)source_item_to_remove->data); |
---|
858 | gnome_vfs_uri_unref ((GnomeVFSURI *)target_item_to_remove->data); |
---|
859 | *source_uri_list = g_list_remove_link (*source_uri_list, source_item_to_remove); |
---|
860 | *target_uri_list = g_list_remove_link (*target_uri_list, target_item_to_remove); |
---|
861 | |
---|
862 | continue; |
---|
863 | } |
---|
864 | |
---|
865 | |
---|
866 | target_item = target_item->next; |
---|
867 | source_item = source_item->next; |
---|
868 | } |
---|
869 | |
---|
870 | return result; |
---|
871 | } |
---|
872 | |
---|
873 | /* Create new directory. If GNOME_VFS_XFER_USE_UNIQUE_NAMES is set, |
---|
874 | * return with an error if a name conflict occurs, else |
---|
875 | * handle the overwrite. |
---|
876 | * On success, opens the new directory |
---|
877 | */ |
---|
878 | static GnomeVFSResult |
---|
879 | create_directory (GnomeVFSURI *dir_uri, |
---|
880 | GnomeVFSDirectoryHandle **return_handle, |
---|
881 | GnomeVFSXferOptions xfer_options, |
---|
882 | GnomeVFSXferErrorMode *error_mode, |
---|
883 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
884 | GnomeVFSProgressCallbackState *progress, |
---|
885 | gboolean *skip) |
---|
886 | { |
---|
887 | GnomeVFSResult result; |
---|
888 | gboolean retry; |
---|
889 | |
---|
890 | *skip = FALSE; |
---|
891 | do { |
---|
892 | retry = FALSE; |
---|
893 | |
---|
894 | result = gnome_vfs_make_directory_for_uri (dir_uri, 0700); |
---|
895 | |
---|
896 | if (result == GNOME_VFS_ERROR_FILE_EXISTS) { |
---|
897 | gboolean force_replace; |
---|
898 | |
---|
899 | if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) != 0) { |
---|
900 | /* just let the caller pass a unique name*/ |
---|
901 | return result; |
---|
902 | } |
---|
903 | |
---|
904 | retry = handle_overwrite (&result, |
---|
905 | progress, |
---|
906 | error_mode, |
---|
907 | overwrite_mode, |
---|
908 | &force_replace, |
---|
909 | skip); |
---|
910 | |
---|
911 | if (*skip) { |
---|
912 | return GNOME_VFS_OK; |
---|
913 | } |
---|
914 | if (force_replace) { |
---|
915 | result = remove_directory (dir_uri, TRUE, progress, |
---|
916 | xfer_options, error_mode, |
---|
917 | skip); |
---|
918 | } else { |
---|
919 | result = GNOME_VFS_OK; |
---|
920 | } |
---|
921 | } |
---|
922 | |
---|
923 | if (result == GNOME_VFS_OK) { |
---|
924 | return gnome_vfs_directory_open_from_uri (return_handle, |
---|
925 | dir_uri, |
---|
926 | GNOME_VFS_FILE_INFO_DEFAULT, |
---|
927 | NULL); |
---|
928 | } |
---|
929 | /* handle the error case */ |
---|
930 | retry = handle_error (&result, progress, |
---|
931 | error_mode, skip); |
---|
932 | |
---|
933 | if (*skip) { |
---|
934 | return GNOME_VFS_OK; |
---|
935 | } |
---|
936 | |
---|
937 | } while (retry); |
---|
938 | |
---|
939 | return result; |
---|
940 | } |
---|
941 | |
---|
942 | /* Copy the data of a single file. */ |
---|
943 | static GnomeVFSResult |
---|
944 | copy_file_data (GnomeVFSHandle *target_handle, |
---|
945 | GnomeVFSHandle *source_handle, |
---|
946 | GnomeVFSProgressCallbackState *progress, |
---|
947 | GnomeVFSXferOptions xfer_options, |
---|
948 | GnomeVFSXferErrorMode *error_mode, |
---|
949 | guint block_size, |
---|
950 | gboolean *skip) |
---|
951 | { |
---|
952 | GnomeVFSResult result; |
---|
953 | gpointer buffer; |
---|
954 | |
---|
955 | *skip = FALSE; |
---|
956 | |
---|
957 | if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) { |
---|
958 | return GNOME_VFS_ERROR_INTERRUPTED; |
---|
959 | } |
---|
960 | |
---|
961 | buffer = g_malloc (block_size); |
---|
962 | |
---|
963 | do { |
---|
964 | GnomeVFSFileSize bytes_read; |
---|
965 | GnomeVFSFileSize bytes_to_write; |
---|
966 | GnomeVFSFileSize bytes_written; |
---|
967 | gboolean retry; |
---|
968 | |
---|
969 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
970 | progress->progress_info->vfs_status = GNOME_VFS_OK; |
---|
971 | |
---|
972 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_READSOURCE; |
---|
973 | |
---|
974 | do { |
---|
975 | retry = FALSE; |
---|
976 | |
---|
977 | result = gnome_vfs_read (source_handle, buffer, |
---|
978 | block_size, &bytes_read); |
---|
979 | if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) |
---|
980 | retry = handle_error (&result, progress, |
---|
981 | error_mode, skip); |
---|
982 | } while (retry && bytes_read > 0 && result != GNOME_VFS_ERROR_EOF); |
---|
983 | |
---|
984 | if (result != GNOME_VFS_OK || bytes_read == 0 || *skip) { |
---|
985 | break; |
---|
986 | } |
---|
987 | |
---|
988 | bytes_to_write = bytes_read; |
---|
989 | |
---|
990 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_WRITETARGET; |
---|
991 | |
---|
992 | do { |
---|
993 | retry = FALSE; |
---|
994 | |
---|
995 | result = gnome_vfs_write (target_handle, buffer, |
---|
996 | bytes_to_write, |
---|
997 | &bytes_written); |
---|
998 | |
---|
999 | if (result != GNOME_VFS_OK) { |
---|
1000 | retry = handle_error (&result, progress, error_mode, skip); |
---|
1001 | } |
---|
1002 | |
---|
1003 | bytes_to_write -= bytes_written; |
---|
1004 | } while (retry || bytes_to_write > 0); |
---|
1005 | |
---|
1006 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COPYING; |
---|
1007 | progress->progress_info->bytes_copied += bytes_read; |
---|
1008 | progress->progress_info->total_bytes_copied += bytes_read; |
---|
1009 | |
---|
1010 | if (call_progress_often (progress, GNOME_VFS_XFER_PHASE_COPYING) == 0) { |
---|
1011 | g_free (buffer); |
---|
1012 | return GNOME_VFS_ERROR_INTERRUPTED; |
---|
1013 | } |
---|
1014 | |
---|
1015 | if (*skip) { |
---|
1016 | break; |
---|
1017 | } |
---|
1018 | |
---|
1019 | } while (result == GNOME_VFS_OK); |
---|
1020 | |
---|
1021 | g_free (buffer); |
---|
1022 | |
---|
1023 | if (result == GNOME_VFS_ERROR_EOF) { |
---|
1024 | return GNOME_VFS_OK; |
---|
1025 | } |
---|
1026 | |
---|
1027 | return result; |
---|
1028 | } |
---|
1029 | |
---|
1030 | static GnomeVFSResult |
---|
1031 | xfer_open_source (GnomeVFSHandle **source_handle, |
---|
1032 | GnomeVFSURI *source_uri, |
---|
1033 | GnomeVFSProgressCallbackState *progress, |
---|
1034 | GnomeVFSXferOptions xfer_options, |
---|
1035 | GnomeVFSXferErrorMode *error_mode, |
---|
1036 | gboolean *skip) |
---|
1037 | { |
---|
1038 | GnomeVFSResult result; |
---|
1039 | gboolean retry; |
---|
1040 | |
---|
1041 | *skip = FALSE; |
---|
1042 | do { |
---|
1043 | retry = FALSE; |
---|
1044 | |
---|
1045 | result = gnome_vfs_open_uri (source_handle, source_uri, |
---|
1046 | GNOME_VFS_OPEN_READ); |
---|
1047 | |
---|
1048 | if (result != GNOME_VFS_OK) { |
---|
1049 | retry = handle_error (&result, progress, error_mode, skip); |
---|
1050 | } |
---|
1051 | } while (retry); |
---|
1052 | |
---|
1053 | return result; |
---|
1054 | } |
---|
1055 | |
---|
1056 | static GnomeVFSResult |
---|
1057 | xfer_create_target (GnomeVFSHandle **target_handle, |
---|
1058 | GnomeVFSURI *target_uri, |
---|
1059 | GnomeVFSProgressCallbackState *progress, |
---|
1060 | GnomeVFSXferOptions xfer_options, |
---|
1061 | GnomeVFSXferErrorMode *error_mode, |
---|
1062 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
1063 | gboolean *skip) |
---|
1064 | { |
---|
1065 | GnomeVFSResult result; |
---|
1066 | gboolean retry; |
---|
1067 | gboolean exclusive; |
---|
1068 | |
---|
1069 | exclusive = (*overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE); |
---|
1070 | |
---|
1071 | *skip = FALSE; |
---|
1072 | do { |
---|
1073 | retry = FALSE; |
---|
1074 | |
---|
1075 | result = gnome_vfs_create_uri (target_handle, target_uri, |
---|
1076 | GNOME_VFS_OPEN_WRITE, |
---|
1077 | exclusive, 0600); |
---|
1078 | |
---|
1079 | if (result == GNOME_VFS_ERROR_FILE_EXISTS) { |
---|
1080 | gboolean replace; |
---|
1081 | |
---|
1082 | retry = handle_overwrite (&result, |
---|
1083 | progress, |
---|
1084 | error_mode, |
---|
1085 | overwrite_mode, |
---|
1086 | &replace, |
---|
1087 | skip); |
---|
1088 | |
---|
1089 | if (replace) { |
---|
1090 | exclusive = FALSE; |
---|
1091 | } |
---|
1092 | |
---|
1093 | } else if (result != GNOME_VFS_OK) { |
---|
1094 | retry = handle_error (&result, progress, error_mode, skip); |
---|
1095 | } |
---|
1096 | } while (retry); |
---|
1097 | |
---|
1098 | return result; |
---|
1099 | } |
---|
1100 | |
---|
1101 | static GnomeVFSResult |
---|
1102 | copy_file (GnomeVFSFileInfo *info, |
---|
1103 | GnomeVFSURI *source_uri, |
---|
1104 | GnomeVFSURI *target_uri, |
---|
1105 | GnomeVFSXferOptions xfer_options, |
---|
1106 | GnomeVFSXferErrorMode *error_mode, |
---|
1107 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
1108 | GnomeVFSProgressCallbackState *progress, |
---|
1109 | gboolean *skip) |
---|
1110 | { |
---|
1111 | GnomeVFSResult close_result, result; |
---|
1112 | GnomeVFSHandle *source_handle, *target_handle; |
---|
1113 | |
---|
1114 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_OPENSOURCE; |
---|
1115 | progress->progress_info->bytes_copied = 0; |
---|
1116 | result = xfer_open_source (&source_handle, source_uri, |
---|
1117 | progress, xfer_options, |
---|
1118 | error_mode, skip); |
---|
1119 | if (*skip) { |
---|
1120 | return GNOME_VFS_OK; |
---|
1121 | } |
---|
1122 | |
---|
1123 | if (result != GNOME_VFS_OK) { |
---|
1124 | return result; |
---|
1125 | } |
---|
1126 | |
---|
1127 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_OPENTARGET; |
---|
1128 | result = xfer_create_target (&target_handle, target_uri, |
---|
1129 | progress, xfer_options, |
---|
1130 | error_mode, overwrite_mode, |
---|
1131 | skip); |
---|
1132 | |
---|
1133 | |
---|
1134 | if (*skip) { |
---|
1135 | gnome_vfs_close (source_handle); |
---|
1136 | return GNOME_VFS_OK; |
---|
1137 | } |
---|
1138 | if (result != GNOME_VFS_OK) { |
---|
1139 | gnome_vfs_close (source_handle); |
---|
1140 | return result; |
---|
1141 | } |
---|
1142 | |
---|
1143 | if (call_progress_with_uris_often (progress, source_uri, target_uri, |
---|
1144 | GNOME_VFS_XFER_PHASE_OPENTARGET) != GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
1145 | |
---|
1146 | |
---|
1147 | result = copy_file_data (target_handle, source_handle, |
---|
1148 | progress, xfer_options, error_mode, |
---|
1149 | /* use an arbitrary default block size of 4096 |
---|
1150 | * if one isn't available for this file system |
---|
1151 | */ |
---|
1152 | (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE) |
---|
1153 | ? info->io_block_size : 4096, |
---|
1154 | skip); |
---|
1155 | } |
---|
1156 | |
---|
1157 | progress->progress_info->file_index++; |
---|
1158 | if (result == GNOME_VFS_OK |
---|
1159 | && call_progress_often (progress, GNOME_VFS_XFER_PHASE_CLOSETARGET) == 0) { |
---|
1160 | result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
1161 | } |
---|
1162 | |
---|
1163 | close_result = gnome_vfs_close (source_handle); |
---|
1164 | if (result == GNOME_VFS_OK && |
---|
1165 | close_result != GNOME_VFS_OK) { |
---|
1166 | handle_error (&close_result, progress, error_mode, skip); |
---|
1167 | return close_result; |
---|
1168 | } |
---|
1169 | close_result = gnome_vfs_close (target_handle); |
---|
1170 | if (result == GNOME_VFS_OK && |
---|
1171 | close_result != GNOME_VFS_OK) { |
---|
1172 | handle_error (&close_result, progress, error_mode, skip); |
---|
1173 | return close_result; |
---|
1174 | } |
---|
1175 | |
---|
1176 | if (*skip) { |
---|
1177 | return GNOME_VFS_OK; |
---|
1178 | } |
---|
1179 | |
---|
1180 | return result; |
---|
1181 | } |
---|
1182 | |
---|
1183 | static GnomeVFSResult |
---|
1184 | copy_directory (GnomeVFSURI *source_dir_uri, |
---|
1185 | GnomeVFSURI *target_dir_uri, |
---|
1186 | GnomeVFSXferOptions xfer_options, |
---|
1187 | GnomeVFSXferErrorMode *error_mode, |
---|
1188 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
1189 | GnomeVFSProgressCallbackState *progress, |
---|
1190 | gboolean *skip) |
---|
1191 | { |
---|
1192 | GnomeVFSResult result; |
---|
1193 | GnomeVFSDirectoryHandle *source_directory_handle; |
---|
1194 | GnomeVFSDirectoryHandle *dest_directory_handle; |
---|
1195 | |
---|
1196 | source_directory_handle = NULL; |
---|
1197 | dest_directory_handle = NULL; |
---|
1198 | |
---|
1199 | result = gnome_vfs_directory_open_from_uri (&source_directory_handle, source_dir_uri, |
---|
1200 | GNOME_VFS_FILE_INFO_DEFAULT, NULL); |
---|
1201 | |
---|
1202 | if (result != GNOME_VFS_OK) { |
---|
1203 | return result; |
---|
1204 | } |
---|
1205 | |
---|
1206 | progress->progress_info->bytes_copied = 0; |
---|
1207 | if (call_progress_with_uris_often (progress, source_dir_uri, target_dir_uri, |
---|
1208 | GNOME_VFS_XFER_PHASE_COPYING) |
---|
1209 | == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
1210 | return GNOME_VFS_ERROR_INTERRUPTED; |
---|
1211 | } |
---|
1212 | |
---|
1213 | result = create_directory (target_dir_uri, |
---|
1214 | &dest_directory_handle, |
---|
1215 | xfer_options, |
---|
1216 | error_mode, |
---|
1217 | overwrite_mode, |
---|
1218 | progress, |
---|
1219 | skip); |
---|
1220 | |
---|
1221 | if (*skip) { |
---|
1222 | gnome_vfs_directory_close (source_directory_handle); |
---|
1223 | return GNOME_VFS_OK; |
---|
1224 | } |
---|
1225 | if (result != GNOME_VFS_OK) { |
---|
1226 | gnome_vfs_directory_close (source_directory_handle); |
---|
1227 | return result; |
---|
1228 | } |
---|
1229 | |
---|
1230 | if (call_progress_with_uris_often (progress, source_dir_uri, target_dir_uri, |
---|
1231 | GNOME_VFS_XFER_PHASE_OPENTARGET) != 0) { |
---|
1232 | |
---|
1233 | progress->progress_info->file_index++; |
---|
1234 | progress->progress_info->total_bytes_copied += DEFAULT_SIZE_OVERHEAD; |
---|
1235 | progress->progress_info->top_level_item = FALSE; |
---|
1236 | |
---|
1237 | /* We do not deal with symlink loops here. |
---|
1238 | * That's OK because we don't follow symlinks. |
---|
1239 | */ |
---|
1240 | do { |
---|
1241 | GnomeVFSURI *source_uri; |
---|
1242 | GnomeVFSURI *dest_uri; |
---|
1243 | GnomeVFSFileInfo info; |
---|
1244 | |
---|
1245 | gnome_vfs_file_info_init (&info); |
---|
1246 | |
---|
1247 | result = gnome_vfs_directory_read_next (source_directory_handle, &info); |
---|
1248 | if (result != GNOME_VFS_OK) { |
---|
1249 | break; |
---|
1250 | } |
---|
1251 | |
---|
1252 | /* Skip "." and "..". */ |
---|
1253 | if (strcmp (info.name, ".") == 0 || strcmp (info.name, "..") == 0) { |
---|
1254 | continue; |
---|
1255 | } |
---|
1256 | |
---|
1257 | source_uri = gnome_vfs_uri_append_file_name (source_dir_uri, info.name); |
---|
1258 | dest_uri = gnome_vfs_uri_append_file_name (target_dir_uri, info.name); |
---|
1259 | |
---|
1260 | if (info.type == GNOME_VFS_FILE_TYPE_REGULAR) { |
---|
1261 | result = copy_file (&info, source_uri, dest_uri, |
---|
1262 | xfer_options, error_mode, overwrite_mode, |
---|
1263 | progress, skip); |
---|
1264 | } else if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
1265 | result = copy_directory (source_uri, dest_uri, |
---|
1266 | xfer_options, error_mode, overwrite_mode, |
---|
1267 | progress, skip); |
---|
1268 | } else if (info.type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) { |
---|
1269 | result = gnome_vfs_create_symbolic_link (dest_uri, info.symlink_name); |
---|
1270 | } else { |
---|
1271 | g_assert_not_reached (); |
---|
1272 | } |
---|
1273 | |
---|
1274 | gnome_vfs_uri_unref (dest_uri); |
---|
1275 | gnome_vfs_uri_unref (source_uri); |
---|
1276 | |
---|
1277 | } while (result == GNOME_VFS_OK); |
---|
1278 | } |
---|
1279 | |
---|
1280 | if (result == GNOME_VFS_ERROR_EOF) { |
---|
1281 | /* all is well, we just finished iterating the directory */ |
---|
1282 | result = GNOME_VFS_OK; |
---|
1283 | } |
---|
1284 | |
---|
1285 | gnome_vfs_directory_close (dest_directory_handle); |
---|
1286 | gnome_vfs_directory_close (source_directory_handle); |
---|
1287 | |
---|
1288 | return result; |
---|
1289 | } |
---|
1290 | |
---|
1291 | static GnomeVFSResult |
---|
1292 | copy_items (const GList *source_uri_list, |
---|
1293 | const GList *target_uri_list, |
---|
1294 | GnomeVFSXferOptions xfer_options, |
---|
1295 | GnomeVFSXferErrorMode *error_mode, |
---|
1296 | GnomeVFSXferOverwriteMode overwrite_mode, |
---|
1297 | GnomeVFSProgressCallbackState *progress) |
---|
1298 | { |
---|
1299 | GnomeVFSResult result; |
---|
1300 | const GList *source_item, *target_item; |
---|
1301 | |
---|
1302 | result = GNOME_VFS_OK; |
---|
1303 | |
---|
1304 | /* go through the list of names */ |
---|
1305 | for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) { |
---|
1306 | |
---|
1307 | GnomeVFSURI *source_uri; |
---|
1308 | GnomeVFSURI *target_uri; |
---|
1309 | GnomeVFSURI *target_dir_uri; |
---|
1310 | |
---|
1311 | GnomeVFSFileInfo info; |
---|
1312 | gboolean skip; |
---|
1313 | int count; |
---|
1314 | int progress_result; |
---|
1315 | |
---|
1316 | skip = FALSE; |
---|
1317 | target_uri = NULL; |
---|
1318 | |
---|
1319 | source_uri = (GnomeVFSURI *)source_item->data; |
---|
1320 | target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data); |
---|
1321 | |
---|
1322 | /* get source URI and file info */ |
---|
1323 | gnome_vfs_file_info_init (&info); |
---|
1324 | result = gnome_vfs_get_file_info_uri (source_uri, &info, |
---|
1325 | GNOME_VFS_FILE_INFO_DEFAULT); |
---|
1326 | |
---|
1327 | progress->progress_info->duplicate_name = |
---|
1328 | gnome_vfs_uri_extract_short_path_name |
---|
1329 | ((GnomeVFSURI *)target_item->data); |
---|
1330 | |
---|
1331 | if (result == GNOME_VFS_OK) { |
---|
1332 | /* optionally keep trying until we hit a unique target name */ |
---|
1333 | for (count = 1; ; count++) { |
---|
1334 | GnomeVFSXferOverwriteMode overwrite_mode_abort; |
---|
1335 | |
---|
1336 | target_uri = gnome_vfs_uri_append_string |
---|
1337 | (target_dir_uri, |
---|
1338 | progress->progress_info->duplicate_name); |
---|
1339 | |
---|
1340 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
1341 | progress->progress_info->file_size = info.size; |
---|
1342 | progress->progress_info->bytes_copied = 0; |
---|
1343 | progress->progress_info->top_level_item = TRUE; |
---|
1344 | |
---|
1345 | if (call_progress_with_uris_often (progress, source_uri, target_uri, |
---|
1346 | GNOME_VFS_XFER_PHASE_COPYING) == 0) { |
---|
1347 | result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
1348 | } |
---|
1349 | |
---|
1350 | overwrite_mode_abort = GNOME_VFS_XFER_OVERWRITE_MODE_ABORT; |
---|
1351 | |
---|
1352 | |
---|
1353 | if (info.type == GNOME_VFS_FILE_TYPE_REGULAR) { |
---|
1354 | result = copy_file (&info, source_uri, target_uri, |
---|
1355 | xfer_options, error_mode, |
---|
1356 | &overwrite_mode_abort, |
---|
1357 | progress, &skip); |
---|
1358 | } else if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
1359 | result = copy_directory (source_uri, target_uri, |
---|
1360 | xfer_options, error_mode, |
---|
1361 | &overwrite_mode_abort, |
---|
1362 | progress, &skip); |
---|
1363 | } else if (info.type == GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK) { |
---|
1364 | result = gnome_vfs_create_symbolic_link (target_uri, info.symlink_name); |
---|
1365 | } else { |
---|
1366 | g_message ("type: %d", info.type); |
---|
1367 | g_assert_not_reached (); |
---|
1368 | } |
---|
1369 | |
---|
1370 | if (result != GNOME_VFS_ERROR_FILE_EXISTS) { |
---|
1371 | /* whatever happened, it wasn't a name conflict */ |
---|
1372 | break; |
---|
1373 | } |
---|
1374 | |
---|
1375 | if (overwrite_mode != GNOME_VFS_XFER_OVERWRITE_MODE_QUERY |
---|
1376 | || (xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) |
---|
1377 | break; |
---|
1378 | |
---|
1379 | /* pass in the current target name, progress will update it to |
---|
1380 | * a new unique name such as 'foo (copy)' or 'bar (copy 2)' |
---|
1381 | */ |
---|
1382 | g_free (progress->progress_info->duplicate_name); |
---|
1383 | progress->progress_info->duplicate_name = |
---|
1384 | gnome_vfs_uri_extract_short_path_name |
---|
1385 | ((GnomeVFSURI *)target_item->data); |
---|
1386 | progress->progress_info->duplicate_count = count; |
---|
1387 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE; |
---|
1388 | progress->progress_info->vfs_status = result; |
---|
1389 | progress_result = call_progress_uri (progress, source_uri, target_uri, |
---|
1390 | GNOME_VFS_XFER_PHASE_COPYING); |
---|
1391 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
1392 | |
---|
1393 | if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
1394 | break; |
---|
1395 | } |
---|
1396 | |
---|
1397 | if (skip) { |
---|
1398 | break; |
---|
1399 | } |
---|
1400 | |
---|
1401 | /* try again with new uri */ |
---|
1402 | gnome_vfs_uri_unref (target_uri); |
---|
1403 | |
---|
1404 | } |
---|
1405 | } |
---|
1406 | |
---|
1407 | g_free (progress->progress_info->duplicate_name); |
---|
1408 | |
---|
1409 | if (result != GNOME_VFS_OK) { |
---|
1410 | break; |
---|
1411 | } |
---|
1412 | |
---|
1413 | gnome_vfs_uri_unref (target_dir_uri); |
---|
1414 | |
---|
1415 | source_item = source_item->next; |
---|
1416 | target_item = target_item->next; |
---|
1417 | |
---|
1418 | g_assert ((source_item != NULL) == (target_item != NULL)); |
---|
1419 | } |
---|
1420 | |
---|
1421 | return result; |
---|
1422 | } |
---|
1423 | |
---|
1424 | static GnomeVFSResult |
---|
1425 | move_items (const GList *source_uri_list, |
---|
1426 | const GList *target_uri_list, |
---|
1427 | GnomeVFSXferOptions xfer_options, |
---|
1428 | GnomeVFSXferErrorMode *error_mode, |
---|
1429 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
1430 | GnomeVFSProgressCallbackState *progress) |
---|
1431 | { |
---|
1432 | GnomeVFSResult result; |
---|
1433 | const GList *source_item, *target_item; |
---|
1434 | |
---|
1435 | result = GNOME_VFS_OK; |
---|
1436 | |
---|
1437 | /* go through the list of names */ |
---|
1438 | for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) { |
---|
1439 | |
---|
1440 | GnomeVFSURI *source_uri; |
---|
1441 | GnomeVFSURI *target_uri; |
---|
1442 | GnomeVFSURI *target_dir_uri; |
---|
1443 | gboolean retry; |
---|
1444 | gboolean skip; |
---|
1445 | int conflict_count; |
---|
1446 | int progress_result; |
---|
1447 | |
---|
1448 | source_uri = (GnomeVFSURI *)source_item->data; |
---|
1449 | target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data); |
---|
1450 | |
---|
1451 | progress->progress_info->duplicate_name = |
---|
1452 | gnome_vfs_uri_extract_short_path_name |
---|
1453 | ((GnomeVFSURI *)target_item->data); |
---|
1454 | |
---|
1455 | skip = FALSE; |
---|
1456 | conflict_count = 1; |
---|
1457 | |
---|
1458 | do { |
---|
1459 | retry = FALSE; |
---|
1460 | target_uri = gnome_vfs_uri_append_string (target_dir_uri, |
---|
1461 | progress->progress_info->duplicate_name); |
---|
1462 | |
---|
1463 | progress->progress_info->file_size = DEFAULT_SIZE_OVERHEAD; |
---|
1464 | progress->progress_info->bytes_copied = 0; |
---|
1465 | progress_set_source_target_uris (progress, source_uri, target_uri); |
---|
1466 | progress->progress_info->top_level_item = TRUE; |
---|
1467 | |
---|
1468 | /* no matter what the replace mode, just overwrite the destination |
---|
1469 | * handle_name_conflicts took care of conflicting files |
---|
1470 | */ |
---|
1471 | result = gnome_vfs_move_uri (source_uri, target_uri, |
---|
1472 | (xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) != 0 |
---|
1473 | ? GNOME_VFS_XFER_OVERWRITE_MODE_ABORT |
---|
1474 | : GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE); |
---|
1475 | |
---|
1476 | |
---|
1477 | if (result == GNOME_VFS_ERROR_FILE_EXISTS) { |
---|
1478 | /* deal with a name conflict -- ask the progress_callback for a better name */ |
---|
1479 | g_free (progress->progress_info->duplicate_name); |
---|
1480 | progress->progress_info->duplicate_name = |
---|
1481 | gnome_vfs_uri_extract_short_path_name |
---|
1482 | ((GnomeVFSURI *)target_item->data); |
---|
1483 | progress->progress_info->duplicate_count = conflict_count; |
---|
1484 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE; |
---|
1485 | progress->progress_info->vfs_status = result; |
---|
1486 | progress_result = call_progress_uri (progress, source_uri, target_uri, |
---|
1487 | GNOME_VFS_XFER_PHASE_COPYING); |
---|
1488 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
1489 | |
---|
1490 | if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
1491 | gnome_vfs_uri_unref (target_uri); |
---|
1492 | break; |
---|
1493 | } |
---|
1494 | conflict_count++; |
---|
1495 | result = GNOME_VFS_OK; |
---|
1496 | retry = TRUE; |
---|
1497 | continue; |
---|
1498 | } |
---|
1499 | |
---|
1500 | if (result != GNOME_VFS_OK) { |
---|
1501 | retry = handle_error (&result, progress, error_mode, &skip); |
---|
1502 | } |
---|
1503 | |
---|
1504 | if (result == GNOME_VFS_OK |
---|
1505 | && call_progress_with_uris_often (progress, source_uri, |
---|
1506 | target_uri, GNOME_VFS_XFER_PHASE_MOVING) == 0) { |
---|
1507 | result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
1508 | gnome_vfs_uri_unref (target_uri); |
---|
1509 | break; |
---|
1510 | } |
---|
1511 | gnome_vfs_uri_unref (target_uri); |
---|
1512 | } while (retry); |
---|
1513 | |
---|
1514 | gnome_vfs_uri_unref (target_dir_uri); |
---|
1515 | |
---|
1516 | if (result != GNOME_VFS_OK && !skip) |
---|
1517 | break; |
---|
1518 | |
---|
1519 | source_item = source_item->next; |
---|
1520 | target_item = target_item->next; |
---|
1521 | g_assert ((source_item != NULL) == (target_item != NULL)); |
---|
1522 | } |
---|
1523 | |
---|
1524 | return result; |
---|
1525 | } |
---|
1526 | |
---|
1527 | static GnomeVFSResult |
---|
1528 | link_items (const GList *source_uri_list, |
---|
1529 | const GList *target_uri_list, |
---|
1530 | GnomeVFSXferOptions xfer_options, |
---|
1531 | GnomeVFSXferErrorMode *error_mode, |
---|
1532 | GnomeVFSXferOverwriteMode *overwrite_mode, |
---|
1533 | GnomeVFSProgressCallbackState *progress) |
---|
1534 | { |
---|
1535 | GnomeVFSResult result; |
---|
1536 | const GList *source_item, *target_item; |
---|
1537 | GnomeVFSURI *source_uri; |
---|
1538 | GnomeVFSURI *target_dir_uri; |
---|
1539 | GnomeVFSURI *target_uri; |
---|
1540 | gboolean retry; |
---|
1541 | gboolean skip; |
---|
1542 | int conflict_count; |
---|
1543 | int progress_result; |
---|
1544 | char *source_reference; |
---|
1545 | |
---|
1546 | result = GNOME_VFS_OK; |
---|
1547 | |
---|
1548 | /* go through the list of names, create a link to each item */ |
---|
1549 | for (source_item = source_uri_list, target_item = target_uri_list; source_item != NULL;) { |
---|
1550 | |
---|
1551 | source_uri = (GnomeVFSURI *)source_item->data; |
---|
1552 | source_reference = gnome_vfs_uri_to_string (source_uri, GNOME_VFS_URI_HIDE_NONE); |
---|
1553 | |
---|
1554 | target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_item->data); |
---|
1555 | progress->progress_info->duplicate_name = |
---|
1556 | gnome_vfs_uri_extract_short_path_name |
---|
1557 | ((GnomeVFSURI *)target_item->data); |
---|
1558 | |
---|
1559 | skip = FALSE; |
---|
1560 | conflict_count = 1; |
---|
1561 | |
---|
1562 | do { |
---|
1563 | retry = FALSE; |
---|
1564 | target_uri = gnome_vfs_uri_append_string |
---|
1565 | (target_dir_uri, |
---|
1566 | progress->progress_info->duplicate_name); |
---|
1567 | |
---|
1568 | progress->progress_info->file_size = DEFAULT_SIZE_OVERHEAD; |
---|
1569 | progress->progress_info->bytes_copied = 0; |
---|
1570 | progress->progress_info->top_level_item = TRUE; |
---|
1571 | progress_set_source_target_uris (progress, source_uri, target_uri); |
---|
1572 | |
---|
1573 | /* no matter what the replace mode, just overwrite the destination |
---|
1574 | * handle_name_conflicts took care of conflicting files |
---|
1575 | */ |
---|
1576 | result = gnome_vfs_create_symbolic_link (target_uri, source_reference); |
---|
1577 | if (result == GNOME_VFS_ERROR_FILE_EXISTS) { |
---|
1578 | /* deal with a name conflict -- ask the progress_callback for a better name */ |
---|
1579 | g_free (progress->progress_info->duplicate_name); |
---|
1580 | progress->progress_info->duplicate_name = |
---|
1581 | gnome_vfs_uri_extract_short_path_name |
---|
1582 | ((GnomeVFSURI *)target_item->data); |
---|
1583 | progress->progress_info->duplicate_count = conflict_count; |
---|
1584 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE; |
---|
1585 | progress->progress_info->vfs_status = result; |
---|
1586 | progress_result = call_progress_uri (progress, source_uri, target_uri, |
---|
1587 | GNOME_VFS_XFER_PHASE_COPYING); |
---|
1588 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
1589 | |
---|
1590 | if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
1591 | gnome_vfs_uri_unref (target_uri); |
---|
1592 | break; |
---|
1593 | } |
---|
1594 | conflict_count++; |
---|
1595 | result = GNOME_VFS_OK; |
---|
1596 | retry = TRUE; |
---|
1597 | continue; |
---|
1598 | } |
---|
1599 | |
---|
1600 | if (result != GNOME_VFS_OK) { |
---|
1601 | retry = handle_error (&result, progress, error_mode, &skip); |
---|
1602 | } |
---|
1603 | |
---|
1604 | if (result == GNOME_VFS_OK |
---|
1605 | && call_progress_with_uris_often (progress, source_uri, |
---|
1606 | target_uri, GNOME_VFS_XFER_PHASE_OPENTARGET) == 0) { |
---|
1607 | result = GNOME_VFS_ERROR_INTERRUPTED; |
---|
1608 | gnome_vfs_uri_unref (target_uri); |
---|
1609 | break; |
---|
1610 | } |
---|
1611 | gnome_vfs_uri_unref (target_uri); |
---|
1612 | } while (retry); |
---|
1613 | |
---|
1614 | gnome_vfs_uri_unref (target_dir_uri); |
---|
1615 | g_free (source_reference); |
---|
1616 | |
---|
1617 | if (result != GNOME_VFS_OK && !skip) |
---|
1618 | break; |
---|
1619 | |
---|
1620 | source_item = source_item->next; |
---|
1621 | target_item = target_item->next; |
---|
1622 | g_assert ((source_item != NULL) == (target_item != NULL)); |
---|
1623 | } |
---|
1624 | |
---|
1625 | return result; |
---|
1626 | } |
---|
1627 | |
---|
1628 | static GnomeVFSResult |
---|
1629 | gnome_vfs_xfer_empty_trash (const GList *trash_dir_uris, |
---|
1630 | GnomeVFSXferErrorMode error_mode, |
---|
1631 | GnomeVFSProgressCallbackState *progress) |
---|
1632 | { |
---|
1633 | GnomeVFSResult result; |
---|
1634 | const GList *p; |
---|
1635 | gboolean skip; |
---|
1636 | |
---|
1637 | result = GNOME_VFS_OK; |
---|
1638 | |
---|
1639 | call_progress (progress, GNOME_VFS_XFER_PHASE_INITIAL); |
---|
1640 | |
---|
1641 | /* initialize the results */ |
---|
1642 | progress->progress_info->files_total = 0; |
---|
1643 | progress->progress_info->bytes_total = 0; |
---|
1644 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COLLECTING; |
---|
1645 | |
---|
1646 | |
---|
1647 | for (p = trash_dir_uris; p != NULL; p = p->next) { |
---|
1648 | result = directory_add_items_and_size (p->data, |
---|
1649 | GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE, |
---|
1650 | progress); |
---|
1651 | if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) { |
---|
1652 | /* out of file descriptors -- we will deal with that */ |
---|
1653 | result = GNOME_VFS_OK; |
---|
1654 | break; |
---|
1655 | } |
---|
1656 | if (result != GNOME_VFS_OK) |
---|
1657 | break; |
---|
1658 | /* set up a fake total size to represent the bulk of the operation |
---|
1659 | * -- we'll subtract a proportional value for every deletion |
---|
1660 | */ |
---|
1661 | progress->progress_info->bytes_total |
---|
1662 | = progress->progress_info->files_total * DEFAULT_SIZE_OVERHEAD; |
---|
1663 | } |
---|
1664 | if (result == GNOME_VFS_OK) { |
---|
1665 | call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO); |
---|
1666 | for (p = trash_dir_uris; p != NULL; p = p->next) { |
---|
1667 | result = empty_directory ((GnomeVFSURI *)p->data, progress, |
---|
1668 | GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE, |
---|
1669 | &error_mode, &skip); |
---|
1670 | if (result == GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES) { |
---|
1671 | result = non_recursive_empty_directory ((GnomeVFSURI *)p->data, |
---|
1672 | progress, GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE, |
---|
1673 | &error_mode, &skip); |
---|
1674 | } |
---|
1675 | if (result != GNOME_VFS_OK) { |
---|
1676 | break; |
---|
1677 | } |
---|
1678 | } |
---|
1679 | } |
---|
1680 | |
---|
1681 | call_progress (progress, GNOME_VFS_XFER_PHASE_COMPLETED); |
---|
1682 | |
---|
1683 | return result; |
---|
1684 | } |
---|
1685 | |
---|
1686 | static GnomeVFSResult |
---|
1687 | gnome_vfs_xfer_delete_items_common (const GList *source_uri_list, |
---|
1688 | GnomeVFSXferErrorMode error_mode, |
---|
1689 | GnomeVFSXferOptions xfer_options, |
---|
1690 | GnomeVFSProgressCallbackState *progress) |
---|
1691 | { |
---|
1692 | GnomeVFSFileInfo info; |
---|
1693 | GnomeVFSResult result; |
---|
1694 | GnomeVFSURI *uri; |
---|
1695 | const GList *p; |
---|
1696 | gboolean skip; |
---|
1697 | |
---|
1698 | result = GNOME_VFS_OK; |
---|
1699 | |
---|
1700 | for (p = source_uri_list; p != NULL; p = p->next) { |
---|
1701 | |
---|
1702 | skip = FALSE; |
---|
1703 | /* get the URI and VFSFileInfo for each */ |
---|
1704 | uri = p->data; |
---|
1705 | |
---|
1706 | gnome_vfs_file_info_init (&info); |
---|
1707 | result = gnome_vfs_get_file_info_uri (uri, &info, |
---|
1708 | GNOME_VFS_FILE_INFO_DEFAULT); |
---|
1709 | |
---|
1710 | if (result != GNOME_VFS_OK) { |
---|
1711 | break; |
---|
1712 | } |
---|
1713 | |
---|
1714 | if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY) { |
---|
1715 | remove_directory (uri, TRUE, progress, xfer_options, |
---|
1716 | &error_mode, &skip); |
---|
1717 | } else { |
---|
1718 | remove_file (uri, progress, xfer_options, &error_mode, |
---|
1719 | &skip); |
---|
1720 | } |
---|
1721 | } |
---|
1722 | |
---|
1723 | return result; |
---|
1724 | } |
---|
1725 | |
---|
1726 | static GnomeVFSResult |
---|
1727 | gnome_vfs_xfer_delete_items (const GList *source_uri_list, |
---|
1728 | GnomeVFSXferErrorMode error_mode, |
---|
1729 | GnomeVFSXferOptions xfer_options, |
---|
1730 | GnomeVFSProgressCallbackState *progress) |
---|
1731 | { |
---|
1732 | |
---|
1733 | GnomeVFSResult result; |
---|
1734 | |
---|
1735 | call_progress (progress, GNOME_VFS_XFER_PHASE_INITIAL); |
---|
1736 | |
---|
1737 | /* initialize the results */ |
---|
1738 | progress->progress_info->files_total = 0; |
---|
1739 | progress->progress_info->bytes_total = 0; |
---|
1740 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COLLECTING; |
---|
1741 | |
---|
1742 | |
---|
1743 | result = count_items_and_size (source_uri_list, |
---|
1744 | GNOME_VFS_XFER_REMOVESOURCE | GNOME_VFS_XFER_RECURSIVE, progress, TRUE, FALSE); |
---|
1745 | |
---|
1746 | if (result == GNOME_VFS_OK) { |
---|
1747 | call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO); |
---|
1748 | result = gnome_vfs_xfer_delete_items_common (source_uri_list, |
---|
1749 | error_mode, xfer_options, progress); |
---|
1750 | } |
---|
1751 | |
---|
1752 | call_progress (progress, GNOME_VFS_XFER_PHASE_COMPLETED); |
---|
1753 | |
---|
1754 | return result; |
---|
1755 | } |
---|
1756 | |
---|
1757 | static GnomeVFSResult |
---|
1758 | gnome_vfs_new_directory_with_unique_name (const GnomeVFSURI *target_dir_uri, |
---|
1759 | const char *name, |
---|
1760 | GnomeVFSXferErrorMode error_mode, |
---|
1761 | GnomeVFSXferOverwriteMode overwrite_mode, |
---|
1762 | GnomeVFSProgressCallbackState *progress) |
---|
1763 | { |
---|
1764 | GnomeVFSResult result; |
---|
1765 | GnomeVFSURI *target_uri; |
---|
1766 | GnomeVFSDirectoryHandle *dest_directory_handle; |
---|
1767 | gboolean dummy; |
---|
1768 | int progress_result; |
---|
1769 | int conflict_count; |
---|
1770 | |
---|
1771 | dest_directory_handle = NULL; |
---|
1772 | progress->progress_info->duplicate_name = g_strdup (name); |
---|
1773 | |
---|
1774 | for (conflict_count = 1; ; conflict_count++) { |
---|
1775 | |
---|
1776 | target_uri = gnome_vfs_uri_append_string |
---|
1777 | (target_dir_uri, |
---|
1778 | progress->progress_info->duplicate_name); |
---|
1779 | result = create_directory (target_uri, |
---|
1780 | &dest_directory_handle, |
---|
1781 | GNOME_VFS_XFER_USE_UNIQUE_NAMES, |
---|
1782 | &error_mode, |
---|
1783 | &overwrite_mode, |
---|
1784 | progress, |
---|
1785 | &dummy); |
---|
1786 | |
---|
1787 | if (result != GNOME_VFS_ERROR_FILE_EXISTS |
---|
1788 | && result != GNOME_VFS_ERROR_NAME_TOO_LONG) { |
---|
1789 | break; |
---|
1790 | } |
---|
1791 | |
---|
1792 | /* deal with a name conflict -- ask the progress_callback for a better name */ |
---|
1793 | g_free (progress->progress_info->duplicate_name); |
---|
1794 | progress->progress_info->duplicate_name = g_strdup (name); |
---|
1795 | progress->progress_info->duplicate_count = conflict_count; |
---|
1796 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_DUPLICATE; |
---|
1797 | progress->progress_info->vfs_status = result; |
---|
1798 | progress_result = call_progress_uri (progress, NULL, target_uri, |
---|
1799 | GNOME_VFS_XFER_PHASE_COPYING); |
---|
1800 | progress->progress_info->status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; |
---|
1801 | |
---|
1802 | if (progress_result == GNOME_VFS_XFER_OVERWRITE_ACTION_ABORT) { |
---|
1803 | break; |
---|
1804 | } |
---|
1805 | gnome_vfs_uri_unref (target_uri); |
---|
1806 | result = GNOME_VFS_OK; |
---|
1807 | } |
---|
1808 | |
---|
1809 | call_progress_uri (progress, NULL, target_uri, |
---|
1810 | GNOME_VFS_XFER_PHASE_OPENTARGET); |
---|
1811 | |
---|
1812 | if (dest_directory_handle != NULL) { |
---|
1813 | gnome_vfs_directory_close (dest_directory_handle); |
---|
1814 | } |
---|
1815 | |
---|
1816 | gnome_vfs_uri_unref (target_uri); |
---|
1817 | g_free (progress->progress_info->duplicate_name); |
---|
1818 | |
---|
1819 | return result; |
---|
1820 | } |
---|
1821 | |
---|
1822 | static GnomeVFSResult |
---|
1823 | gnome_vfs_destination_is_writable (const GnomeVFSURI *uri) |
---|
1824 | { |
---|
1825 | GnomeVFSURI *test_uri; |
---|
1826 | GnomeVFSResult result; |
---|
1827 | GnomeVFSHandle *handle; |
---|
1828 | |
---|
1829 | if (!gnome_vfs_uri_is_local (uri)) { |
---|
1830 | /* if destination is not local, do not test it for writability, just |
---|
1831 | * assume it's writable |
---|
1832 | */ |
---|
1833 | return GNOME_VFS_OK; |
---|
1834 | } |
---|
1835 | |
---|
1836 | /* test writability by creating and erasing a temporary file */ |
---|
1837 | test_uri = gnome_vfs_uri_append_file_name (uri, ".vfs-write.tmp"); |
---|
1838 | result = gnome_vfs_create_uri (&handle, test_uri, GNOME_VFS_OPEN_WRITE, TRUE, 0600); |
---|
1839 | |
---|
1840 | if (result == GNOME_VFS_OK) { |
---|
1841 | gnome_vfs_close (handle); |
---|
1842 | } |
---|
1843 | |
---|
1844 | if (result == GNOME_VFS_OK || result == GNOME_VFS_ERROR_FILE_EXISTS) { |
---|
1845 | gnome_vfs_unlink_from_uri (test_uri); |
---|
1846 | result = GNOME_VFS_OK; |
---|
1847 | } |
---|
1848 | |
---|
1849 | gnome_vfs_uri_unref (test_uri); |
---|
1850 | return result; |
---|
1851 | } |
---|
1852 | |
---|
1853 | static GnomeVFSResult |
---|
1854 | gnome_vfs_xfer_uri_internal (const GList *source_uris, |
---|
1855 | const GList *target_uris, |
---|
1856 | GnomeVFSXferOptions xfer_options, |
---|
1857 | GnomeVFSXferErrorMode error_mode, |
---|
1858 | GnomeVFSXferOverwriteMode overwrite_mode, |
---|
1859 | GnomeVFSProgressCallbackState *progress) |
---|
1860 | { |
---|
1861 | GnomeVFSResult result; |
---|
1862 | GList *source_uri_list, *target_uri_list; |
---|
1863 | GList *source_uri, *target_uri; |
---|
1864 | GnomeVFSURI *target_dir_uri; |
---|
1865 | gboolean move, link; |
---|
1866 | GnomeVFSFileSize free_bytes; |
---|
1867 | gboolean skip; |
---|
1868 | |
---|
1869 | result = GNOME_VFS_OK; |
---|
1870 | move = FALSE; |
---|
1871 | link = FALSE; |
---|
1872 | target_dir_uri = NULL; |
---|
1873 | |
---|
1874 | /* Check and see if target is writable. Return error if it is not. */ |
---|
1875 | target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)((GList *)target_uris)->data); |
---|
1876 | result = gnome_vfs_destination_is_writable (target_dir_uri); |
---|
1877 | progress_set_source_target_uris (progress, NULL, target_dir_uri); |
---|
1878 | if (result != GNOME_VFS_OK) { |
---|
1879 | handle_error (&result, progress, &error_mode, &skip); |
---|
1880 | gnome_vfs_uri_unref (target_dir_uri); |
---|
1881 | return result; |
---|
1882 | } |
---|
1883 | |
---|
1884 | /* Create an owning list of source and destination uris. |
---|
1885 | * We want to be able to remove items that we decide to skip during |
---|
1886 | * name conflict check. |
---|
1887 | */ |
---|
1888 | source_uri_list = gnome_vfs_uri_list_copy ((GList *)source_uris); |
---|
1889 | target_uri_list = gnome_vfs_uri_list_copy ((GList *)target_uris); |
---|
1890 | |
---|
1891 | move = (xfer_options & GNOME_VFS_XFER_REMOVESOURCE) != 0; |
---|
1892 | link = (xfer_options & GNOME_VFS_XFER_LINK_ITEMS) != 0; |
---|
1893 | |
---|
1894 | if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) { |
---|
1895 | /* see if moved files are on the same file system so that we decide to do |
---|
1896 | * a simple move or have to do a copy/remove |
---|
1897 | */ |
---|
1898 | for (source_uri = source_uri_list, target_uri = target_uri_list; |
---|
1899 | source_uri != NULL; |
---|
1900 | source_uri = source_uri->next, target_uri = target_uri->next) { |
---|
1901 | gboolean same_fs; |
---|
1902 | |
---|
1903 | g_assert (target_dir_uri != NULL); |
---|
1904 | |
---|
1905 | result = gnome_vfs_check_same_fs_uris ((GnomeVFSURI *)source_uri->data, |
---|
1906 | target_dir_uri, &same_fs); |
---|
1907 | |
---|
1908 | if (result != GNOME_VFS_OK) { |
---|
1909 | break; |
---|
1910 | } |
---|
1911 | |
---|
1912 | move &= same_fs; |
---|
1913 | } |
---|
1914 | } |
---|
1915 | |
---|
1916 | if (target_dir_uri != NULL) { |
---|
1917 | gnome_vfs_uri_unref (target_dir_uri); |
---|
1918 | target_dir_uri = NULL; |
---|
1919 | } |
---|
1920 | |
---|
1921 | if (result == GNOME_VFS_OK) { |
---|
1922 | |
---|
1923 | call_progress (progress, GNOME_VFS_XFER_PHASE_INITIAL); |
---|
1924 | |
---|
1925 | progress->progress_info->phase = GNOME_VFS_XFER_PHASE_COLLECTING; |
---|
1926 | result = count_items_and_size (source_uri_list, xfer_options, progress, move, link); |
---|
1927 | if (result == GNOME_VFS_OK) { |
---|
1928 | |
---|
1929 | /* Calculate free space on destination. If an error is returned, we have a non-local |
---|
1930 | * file system, so we just forge ahead and hope for the best |
---|
1931 | */ |
---|
1932 | target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_uri_list->data); |
---|
1933 | result = gnome_vfs_get_volume_free_space (target_dir_uri, &free_bytes); |
---|
1934 | if (target_dir_uri != NULL) { |
---|
1935 | gnome_vfs_uri_unref (target_dir_uri); |
---|
1936 | target_dir_uri = NULL; |
---|
1937 | } |
---|
1938 | |
---|
1939 | if (result == GNOME_VFS_OK) { |
---|
1940 | if (!move && progress->progress_info->bytes_total > free_bytes) { |
---|
1941 | result = GNOME_VFS_ERROR_NO_SPACE; |
---|
1942 | handle_error (&result, progress, &error_mode, &skip); |
---|
1943 | return result; |
---|
1944 | } |
---|
1945 | } |
---|
1946 | |
---|
1947 | /* Errors from gnome_vfs_get_volume_free_space should be ignored */ |
---|
1948 | result = GNOME_VFS_OK; |
---|
1949 | |
---|
1950 | if ((xfer_options & GNOME_VFS_XFER_USE_UNIQUE_NAMES) == 0) { |
---|
1951 | result = handle_name_conflicts (&source_uri_list, &target_uri_list, |
---|
1952 | xfer_options, &error_mode, &overwrite_mode, |
---|
1953 | progress); |
---|
1954 | } |
---|
1955 | /* reset the preflight numbers */ |
---|
1956 | progress->progress_info->file_index = 0; |
---|
1957 | progress->progress_info->total_bytes_copied = 0; |
---|
1958 | |
---|
1959 | if (result != GNOME_VFS_OK) { |
---|
1960 | /* don't care about any results from handle_error */ |
---|
1961 | handle_error (&result, progress, &error_mode, &skip); |
---|
1962 | |
---|
1963 | /* whatever error it was, we handled it */ |
---|
1964 | result = GNOME_VFS_OK; |
---|
1965 | } else { |
---|
1966 | call_progress (progress, GNOME_VFS_XFER_PHASE_READYTOGO); |
---|
1967 | |
---|
1968 | if (move) { |
---|
1969 | g_assert (!link); |
---|
1970 | result = move_items (source_uri_list, target_uri_list, |
---|
1971 | xfer_options, &error_mode, &overwrite_mode, |
---|
1972 | progress); |
---|
1973 | } else if (link) { |
---|
1974 | result = link_items (source_uri_list, target_uri_list, |
---|
1975 | xfer_options, &error_mode, &overwrite_mode, |
---|
1976 | progress); |
---|
1977 | } else { |
---|
1978 | result = copy_items (source_uri_list, target_uri_list, |
---|
1979 | xfer_options, &error_mode, overwrite_mode, |
---|
1980 | progress); |
---|
1981 | } |
---|
1982 | |
---|
1983 | if (result == GNOME_VFS_OK) { |
---|
1984 | if (xfer_options & GNOME_VFS_XFER_REMOVESOURCE) { |
---|
1985 | call_progress (progress, GNOME_VFS_XFER_PHASE_CLEANUP); |
---|
1986 | result = gnome_vfs_xfer_delete_items_common (source_uri_list, |
---|
1987 | error_mode, xfer_options, progress); |
---|
1988 | } |
---|
1989 | } |
---|
1990 | } |
---|
1991 | } |
---|
1992 | } |
---|
1993 | |
---|
1994 | /* Done, at last. At this point, there is no chance to interrupt the |
---|
1995 | operation anymore so we don't check the return value. */ |
---|
1996 | call_progress (progress, GNOME_VFS_XFER_PHASE_COMPLETED); |
---|
1997 | |
---|
1998 | free_progress (progress->progress_info); |
---|
1999 | gnome_vfs_uri_list_free (source_uri_list); |
---|
2000 | gnome_vfs_uri_list_free (target_uri_list); |
---|
2001 | |
---|
2002 | return result; |
---|
2003 | } |
---|
2004 | |
---|
2005 | GnomeVFSResult |
---|
2006 | gnome_vfs_xfer_private (const GList *source_uri_list, |
---|
2007 | const GList *target_uri_list, |
---|
2008 | GnomeVFSXferOptions xfer_options, |
---|
2009 | GnomeVFSXferErrorMode error_mode, |
---|
2010 | GnomeVFSXferOverwriteMode overwrite_mode, |
---|
2011 | GnomeVFSXferProgressCallback progress_callback, |
---|
2012 | gpointer data, |
---|
2013 | GnomeVFSXferProgressCallback sync_progress_callback, |
---|
2014 | gpointer sync_progress_data) |
---|
2015 | { |
---|
2016 | GnomeVFSProgressCallbackState progress_state; |
---|
2017 | GnomeVFSXferProgressInfo progress_info; |
---|
2018 | GnomeVFSURI *target_dir_uri; |
---|
2019 | GnomeVFSResult result; |
---|
2020 | |
---|
2021 | init_progress (&progress_state, &progress_info); |
---|
2022 | progress_state.sync_callback = sync_progress_callback; |
---|
2023 | progress_state.user_data = sync_progress_data; |
---|
2024 | progress_state.update_callback = progress_callback; |
---|
2025 | progress_state.async_job_data = data; |
---|
2026 | |
---|
2027 | if ((xfer_options & GNOME_VFS_XFER_EMPTY_DIRECTORIES) != 0) { |
---|
2028 | /* Directory empty operation (Empty Trash, etc.). */ |
---|
2029 | g_assert (source_uri_list != NULL); |
---|
2030 | g_assert (target_uri_list == NULL); |
---|
2031 | |
---|
2032 | result = gnome_vfs_xfer_empty_trash (source_uri_list, error_mode, &progress_state); |
---|
2033 | } else if ((xfer_options & GNOME_VFS_XFER_DELETE_ITEMS) != 0) { |
---|
2034 | /* Delete items operation */ |
---|
2035 | g_assert (source_uri_list != NULL); |
---|
2036 | g_assert (target_uri_list == NULL); |
---|
2037 | |
---|
2038 | result = gnome_vfs_xfer_delete_items (source_uri_list, |
---|
2039 | error_mode, xfer_options, &progress_state); |
---|
2040 | } else if ((xfer_options & GNOME_VFS_XFER_NEW_UNIQUE_DIRECTORY) != 0) { |
---|
2041 | /* New directory create operation */ |
---|
2042 | g_assert (source_uri_list == NULL); |
---|
2043 | g_assert (g_list_length ((GList *)target_uri_list) == 1); |
---|
2044 | |
---|
2045 | |
---|
2046 | target_dir_uri = gnome_vfs_uri_get_parent ((GnomeVFSURI *)target_uri_list->data); |
---|
2047 | if (target_dir_uri == NULL |
---|
2048 | || gnome_vfs_uri_get_basename ((GnomeVFSURI *)target_uri_list->data) == NULL) |
---|
2049 | return GNOME_VFS_ERROR_INVALID_URI; |
---|
2050 | |
---|
2051 | result = gnome_vfs_new_directory_with_unique_name (target_dir_uri, |
---|
2052 | gnome_vfs_uri_get_basename ((GnomeVFSURI *)target_uri_list->data), |
---|
2053 | error_mode, overwrite_mode, &progress_state); |
---|
2054 | gnome_vfs_uri_unref (target_dir_uri); |
---|
2055 | } else { |
---|
2056 | /* Copy items operation */ |
---|
2057 | g_assert (source_uri_list != NULL); |
---|
2058 | g_assert (target_uri_list != NULL); |
---|
2059 | g_assert (g_list_length ((GList *)source_uri_list) == g_list_length ((GList *)target_uri_list)); |
---|
2060 | |
---|
2061 | result = gnome_vfs_xfer_uri_internal (source_uri_list, target_uri_list, |
---|
2062 | xfer_options, error_mode, overwrite_mode, &progress_state); |
---|
2063 | } |
---|
2064 | |
---|
2065 | /* FIXME bugzilla.eazel.com 1218: |
---|
2066 | * |
---|
2067 | * The async job setup will try to call the callback function with the callback data |
---|
2068 | * even though they are usually dead at this point because the callback detected |
---|
2069 | * that we are giving up and cleaned up after itself. |
---|
2070 | * |
---|
2071 | * Should fix this in the async job call setup. |
---|
2072 | * |
---|
2073 | * For now just pretend everything worked well. |
---|
2074 | * |
---|
2075 | */ |
---|
2076 | result = GNOME_VFS_OK; |
---|
2077 | |
---|
2078 | return result; |
---|
2079 | } |
---|
2080 | |
---|
2081 | GnomeVFSResult |
---|
2082 | gnome_vfs_xfer_uri_list (const GList *source_uri_list, |
---|
2083 | const GList *target_uri_list, |
---|
2084 | GnomeVFSXferOptions xfer_options, |
---|
2085 | GnomeVFSXferErrorMode error_mode, |
---|
2086 | GnomeVFSXferOverwriteMode overwrite_mode, |
---|
2087 | GnomeVFSXferProgressCallback progress_callback, |
---|
2088 | gpointer data) |
---|
2089 | { |
---|
2090 | GnomeVFSProgressCallbackState progress_state; |
---|
2091 | GnomeVFSXferProgressInfo progress_info; |
---|
2092 | |
---|
2093 | g_return_val_if_fail (source_uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2094 | g_return_val_if_fail (target_uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2095 | g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY, |
---|
2096 | GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2097 | |
---|
2098 | init_progress (&progress_state, &progress_info); |
---|
2099 | progress_state.sync_callback = progress_callback; |
---|
2100 | progress_state.user_data = data; |
---|
2101 | |
---|
2102 | return gnome_vfs_xfer_uri_internal (source_uri_list, target_uri_list, |
---|
2103 | xfer_options, error_mode, overwrite_mode, &progress_state); |
---|
2104 | } |
---|
2105 | |
---|
2106 | GnomeVFSResult |
---|
2107 | gnome_vfs_xfer_uri (const GnomeVFSURI *source_uri, |
---|
2108 | const GnomeVFSURI *target_uri, |
---|
2109 | GnomeVFSXferOptions xfer_options, |
---|
2110 | GnomeVFSXferErrorMode error_mode, |
---|
2111 | GnomeVFSXferOverwriteMode overwrite_mode, |
---|
2112 | GnomeVFSXferProgressCallback progress_callback, |
---|
2113 | gpointer data) |
---|
2114 | { |
---|
2115 | GList *source_uri_list, *target_uri_list; |
---|
2116 | GnomeVFSResult result; |
---|
2117 | |
---|
2118 | g_return_val_if_fail (source_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2119 | g_return_val_if_fail (target_uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2120 | g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY, |
---|
2121 | GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2122 | |
---|
2123 | source_uri_list = g_list_append (NULL, (void *)source_uri); |
---|
2124 | target_uri_list = g_list_append (NULL, (void *)target_uri); |
---|
2125 | |
---|
2126 | result = gnome_vfs_xfer_uri_list (source_uri_list, target_uri_list, |
---|
2127 | xfer_options, error_mode, overwrite_mode, progress_callback, data); |
---|
2128 | |
---|
2129 | g_list_free (source_uri_list); |
---|
2130 | g_list_free (target_uri_list); |
---|
2131 | |
---|
2132 | return result; |
---|
2133 | } |
---|
2134 | |
---|
2135 | GnomeVFSResult |
---|
2136 | gnome_vfs_xfer_delete_list (const GList *uri_list, |
---|
2137 | GnomeVFSXferErrorMode error_mode, |
---|
2138 | GnomeVFSXferOptions xfer_options, |
---|
2139 | GnomeVFSXferProgressCallback |
---|
2140 | progress_callback, |
---|
2141 | gpointer data) |
---|
2142 | { |
---|
2143 | GnomeVFSProgressCallbackState progress_state; |
---|
2144 | GnomeVFSXferProgressInfo progress_info; |
---|
2145 | |
---|
2146 | g_return_val_if_fail (uri_list != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2147 | g_return_val_if_fail (progress_callback != NULL || error_mode != GNOME_VFS_XFER_ERROR_MODE_QUERY, |
---|
2148 | GNOME_VFS_ERROR_BAD_PARAMETERS); |
---|
2149 | |
---|
2150 | init_progress (&progress_state, &progress_info); |
---|
2151 | progress_state.sync_callback = progress_callback; |
---|
2152 | progress_state.user_data = data; |
---|
2153 | |
---|
2154 | return gnome_vfs_xfer_delete_items (uri_list, error_mode, xfer_options, |
---|
2155 | &progress_state); |
---|
2156 | } |
---|
2157 | |
---|