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