예제 #1
0
static gboolean _fm_file_ops_job_check_paths(FmFileOpsJob* job, GFile* src, GFileInfo* src_inf, GFile* dest)
{
    GError* err = NULL;
    FmJob* fmjob = FM_JOB(job);
    if(job->type == FM_FILE_OP_MOVE && g_file_equal(src, dest))
    {
        err = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED,
            _("Source and destination are the same."));
    }
    else if(g_file_info_get_file_type(src_inf) == G_FILE_TYPE_DIRECTORY
            && g_file_has_prefix(dest, src) )
    {
        const char* msg = NULL;
        if(job->type == FM_FILE_OP_MOVE)
            msg = _("Cannot move a folder into its sub folder");
        else if(job->type == FM_FILE_OP_COPY)
            msg = _("Cannot copy a folder into its sub folder");
        else
            msg = _("Destination is a sub folder of source");
        err = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, msg);
    }
    if(err)
    {
        if(!fm_job_is_cancelled(fmjob))
        {
            fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(src_inf));
            fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL);
        }
        g_error_free(err);
    }
    return (err == NULL);
}
예제 #2
0
static inline FmFileInfo *_new_info_for_native_file(FmDirListJob* job, FmPath* path, const char* path_str, GError** err)
{
    FmFileInfo *fi;

    if (fm_job_is_cancelled(FM_JOB(job)))
        return NULL;
    if (!(job->flags & FM_DIR_LIST_JOB_DETAILED))
        return fm_file_info_new_from_native_file(path, path_str, err);
    fi = fm_file_info_new();
    fm_file_info_set_path(fi, path);
    if (fm_file_info_set_from_native_file(fi, path_str, err))
        return fi;
    fm_file_info_unref(fi);
    return NULL;
}
예제 #3
0
gboolean fm_deep_count_job_run(FmJob* job)
{
    FmDeepCountJob* dc = (FmDeepCountJob*)job;
    GList* l;

    l = fm_list_peek_head_link(dc->paths);
    for(; !fm_job_is_cancelled(job) && l; l=l->next)
    {
        FmPath* path = FM_PATH(l->data);
        if(fm_path_is_native(path)) /* if it's a native file, use posix APIs */
            deep_count_posix( dc, path );
        else
        {
            GFile* gf = fm_path_to_gfile(path);
            deep_count_gio( dc, NULL, gf );
            g_object_unref(gf);
        }
    }
    return TRUE;
}
예제 #4
0
void FilePropsDialog::onFileSizeTimerTimeout() {
  if(deepCountJob && !fm_job_is_cancelled(FM_JOB(deepCountJob))) {
    char size_str[128];
    fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_size,
                        fm_config->si_unit);
    // FIXME:
    // OMG! It's really unbelievable that Qt developers only implement
    // QObject::tr(... int n). GNU gettext developers are smarter and
    // they use unsigned long instead of int.
    // We cannot use Qt here to handle plural forms. So sad. :-(
    QString str = QString::fromUtf8(size_str) %
      QString(" (%1 B)").arg(deepCountJob->total_size);
      // tr(" (%n) byte(s)", "", deepCountJob->total_size);
    ui->fileSize->setText(str);

    fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_ondisk_size,
                        fm_config->si_unit);
    str = QString::fromUtf8(size_str) %
      QString(" (%1 B)").arg(deepCountJob->total_ondisk_size);
      // tr(" (%n) byte(s)", "", deepCountJob->total_ondisk_size);
    ui->onDiskSize->setText(str);
  }
}
예제 #5
0
static gboolean on_timeout (FmFilePropData *data)
{
    char size_str[128];
    FmDeepCountJob *dc = (FmDeepCountJob*) data->dc_job;

    //gdk_threads_enter ();

    if (G_LIKELY (dc && !fm_job_is_cancelled (FM_JOB (dc))))
    {
        char *str;
        fm_file_size_to_str (size_str, dc->total_size, TRUE);
        str = g_strdup_printf ("%s  (%'llu %s)", size_str, dc->total_size, ngettext ("byte", "bytes", dc->total_size));
        gtk_label_set_text (GTK_LABEL (data->total_size), str);
        g_free (str);

        fm_file_size_to_str (size_str, dc->total_block_size, TRUE);
        str = g_strdup_printf ("%s  (%'llu %s)", size_str, dc->total_block_size, ngettext ("byte", "bytes", dc->total_block_size));
        gtk_label_set_text (GTK_LABEL (data->size_on_disk), str);
        g_free (str);
    }
    //gdk_threads_leave ();
    return TRUE;
}
예제 #6
0
static void on_finished (FmFileOpsJob *job, FmProgressDisplay *data)
{
    GtkWidget *parent;
    if (data->update_timeout)
    {
        g_source_remove (data->update_timeout);
        data->update_timeout = 0;
    }

    parent = data->parent;
    
    if (data->dlg)
    {
        // errors happened
        if (data->has_error && data->dlg)
        {
            gtk_label_set_text (GTK_LABEL (data->current), "");
            gtk_label_set_text (GTK_LABEL (data->remaining_time), "00:00:00");
            gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dlg), GTK_RESPONSE_CANCEL, FALSE);
            gtk_dialog_add_button (GTK_DIALOG (data->dlg), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);

            gtk_image_set_from_stock (GTK_IMAGE (data->icon), GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);

            gtk_widget_show (data->msg);
            if (fm_job_is_cancelled (FM_JOB (data->job)))
            {
                gtk_label_set_text (GTK_LABEL (data->msg), _("The file operation is cancelled and there are some errors."));
                gtk_window_set_title (GTK_WINDOW (data->dlg),
                                     _("Cancelled"));
            }
            else
            {
                gtk_label_set_text (GTK_LABEL (data->msg), _("The file operation is finished, but there are some errors."));
                gtk_window_set_title (GTK_WINDOW (data->dlg),
                                     _("Finished"));
            }
        }
        else
            fm_progress_display_destroy (data);
        
        g_debug ("file operation is finished!");
    }
    else
        fm_progress_display_destroy (data);

    /* sepcial handling for trash
     * FIXME_pcm: need to refactor this to use a more elegant way later. */
    
    if (job->type == FM_FILE_OP_TRASH)
    {
        FmPathList *unsupported =  (FmPathList*)g_object_get_data (G_OBJECT (job), "trash-unsupported");
        // some files cannot be trashed because underlying filesystems don't support it.
        // delete them instead
        if (unsupported)
        {
            if (fm_yes_no (GTK_WINDOW (parent), NULL,
                        _("Some files cannot be moved to trash can because "
                        "the underlying file systems don't support this operation.\n"
                        "Do you want to delete them instead?"), TRUE))
            {
                FmJob *job = fm_file_ops_job_new (FM_FILE_OP_DELETE, unsupported);
                fm_file_ops_job_run_with_progress (GTK_WINDOW (data->parent), (FmFileOpsJob*) job);
            }
        }
    }
}
예제 #7
0
static gboolean fm_file_info_job_run(FmJob* fmjob)
{
    GList* l;
    FmFileInfoJob* job = (FmFileInfoJob*)fmjob;
    GError* err = NULL;

    if(job->file_infos == NULL)
        return FALSE;

    for(l = fm_file_info_list_peek_head_link(job->file_infos); !fm_job_is_cancelled(fmjob) && l;)
    {
        FmFileInfo* fi = (FmFileInfo*)l->data;
        GList* next = l->next;
        FmPath* path = fm_file_info_get_path(fi);

        if(job->current)
            fm_path_unref(job->current);
        job->current = fm_path_ref(path);

        if(fm_path_is_native(path))
        {
            char* path_str = fm_path_to_str(path);
            if(!_fm_file_info_job_get_info_for_native_file(fmjob, fi, path_str, &err))
            {
                FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
                g_error_free(err);
                err = NULL;
                if(act == FM_JOB_RETRY)
                {
                    g_free(path_str);
                    continue; /* retry */
                }

                fm_file_info_list_delete_link(job->file_infos, l); /* also calls unref */
            }
            else if(G_UNLIKELY(job->flags & FM_FILE_INFO_JOB_EMIT_FOR_EACH_FILE))
                fm_job_call_main_thread(fmjob, _emit_current_file, fi);
            g_free(path_str);
            /* recursively set display names for path parents */
            _check_native_display_names(fm_path_get_parent(path));
        }
        else
        {
            GFile* gf;

            gf = fm_path_to_gfile(path);
            if(!_fm_file_info_job_get_info_for_gfile(fmjob, fi, gf, &err))
            {
              if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_NOT_MOUNTED)
              {
                GFileInfo *inf;
                /* location by link isn't mounted; unfortunately we cannot
                   launch a target if we don't know what kind of target we
                   have; lets make a simplest directory-kind GFIleInfo */
                /* FIXME: this may be dirty a bit */
                g_error_free(err);
                err = NULL;
                inf = g_file_info_new();
                g_file_info_set_file_type(inf, G_FILE_TYPE_DIRECTORY);
                g_file_info_set_name(inf, fm_path_get_basename(path));
                g_file_info_set_display_name(inf, fm_path_get_basename(path));
                fm_file_info_set_from_g_file_data(fi, gf, inf);
                g_object_unref(inf);
              }
              else
              {
                FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
                g_error_free(err);
                err = NULL;
                if(act == FM_JOB_RETRY)
                {
                    g_object_unref(gf);
                    continue; /* retry */
                }

                fm_file_info_list_delete_link(job->file_infos, l); /* also calls unref */
                goto _next;
              }
            }
            else if(G_UNLIKELY(job->flags & FM_FILE_INFO_JOB_EMIT_FOR_EACH_FILE))
                    fm_job_call_main_thread(fmjob, _emit_current_file, fi);
            /* recursively set display names for path parents */
            _check_gfile_display_names(fm_path_get_parent(path), gf);
_next:
            g_object_unref(gf);
        }
        l = next;
    }
    return TRUE;
}
예제 #8
0
static void on_finished(FmFileOpsJob* job, FmProgressDisplay* data)
{
    GtkWindow* parent = NULL;

    /* preserve pointers that fm_progress_display_destroy() will unreference
       as they may be requested by trash support below */
    if(data->parent)
        parent = g_object_ref(data->parent);
    g_object_ref(job);

    if(data->dlg)
    {
        /* errors happened */
        if(data->has_error)
        {
            gtk_label_set_text(data->current, "");
            gtk_label_set_text(data->remaining_time, "00:00:00");
            gtk_dialog_set_response_sensitive(data->dlg, GTK_RESPONSE_CANCEL, FALSE);
            gtk_dialog_add_button(data->dlg, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);

            gtk_image_set_from_stock(data->icon, GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);

            gtk_widget_show(GTK_WIDGET(data->msg));
            if(fm_job_is_cancelled(FM_JOB(job)))
            {
                gtk_label_set_text(data->msg, _("The file operation is cancelled and there are some errors."));
                gtk_window_set_title(GTK_WINDOW(data->dlg),
                                     _("Cancelled"));
            }
            else
            {
                gtk_label_set_text(data->msg, _("The file operation is finished, but there are some errors."));
                gtk_window_set_title(GTK_WINDOW(data->dlg),
                                     _("Finished"));
            }
        }
        else
            fm_progress_display_destroy(data);
        g_debug("file operation is finished!");
    }
    else
        fm_progress_display_destroy(data);
    /* if it's not destroyed yet then it will be destroyed with dialog window */

    /* sepcial handling for trash
     * FIXME: need to refactor this to use a more elegant way later. */
    if(job->type == FM_FILE_OP_TRASH) /* FIXME: direct access to job struct! */
    {
        FmPathList* unsupported = (FmPathList*)g_object_get_data(G_OBJECT(job), "trash-unsupported");
        /* some files cannot be trashed because underlying filesystems don't support it. */
        g_object_unref(job);
        if(unsupported) /* delete them instead */
        {
            /* FIXME: parent window might be already destroyed! */
            if(fm_yes_no(parent, NULL,
                        _("Some files cannot be moved to trash can because "
                        "the underlying file systems don't support this operation.\n"
                        "Do you want to delete them instead?"), TRUE))
            {
                job = fm_file_ops_job_new(FM_FILE_OP_DELETE, unsupported);
                fm_file_ops_job_run_with_progress(parent, job);
                                                        /* it eats reference! */
            }
        }
    }
    else
        g_object_unref(job);
    if(parent)
        g_object_unref(parent);
}
예제 #9
0
static gboolean fm_dir_list_job_run_gio(FmDirListJob* job)
{
    GFileEnumerator *enu;
    GFileInfo *inf;
    FmFileInfo* fi;
    GError *err = NULL;
    FmJob* fmjob = FM_JOB(job);
    GFile* gf;
    const char* query;

    gf = fm_path_to_gfile(job->dir_path);
_retry:
    inf = g_file_query_info(gf, gfile_info_query_attribs, 0, fm_job_get_cancellable(fmjob), &err);
    if(!inf )
    {
        FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
        g_error_free(err);
        if( act == FM_JOB_RETRY )
        {
            err = NULL;
            goto _retry;
        }
        else
        {
            g_object_unref(gf);
            return FALSE;
        }
    }

    if( g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY)
    {
        char *path_str = fm_path_to_str(job->dir_path);
        err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
                          _("The specified directory '%s' is not valid"),
                          path_str);
        fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL);
        g_free(path_str);
        g_error_free(err);
        g_object_unref(gf);
        g_object_unref(inf);
        return FALSE;
    }

    /* check if FS is R/O and set attr. into inf */
    _fm_file_info_job_update_fs_readonly(gf, inf, NULL, NULL);

    job->dir_fi = fm_file_info_new_from_g_file_data(gf, inf, job->dir_path);
    g_object_unref(inf);

    if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY))
    {
        query = G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_NAME","
                G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN","G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP","
                G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK","G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL","
                G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME","
                G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","G_FILE_ATTRIBUTE_STANDARD_ICON","
                G_FILE_ATTRIBUTE_STANDARD_SIZE","G_FILE_ATTRIBUTE_STANDARD_TARGET_URI","
                "unix::*,time::*,access::*,id::filesystem";
    }
    else
        query = gfile_info_query_attribs;

    enu = g_file_enumerate_children (gf, query, 0, fm_job_get_cancellable(fmjob), &err);
    g_object_unref(gf);
    if(enu)
    {
        while( ! fm_job_is_cancelled(fmjob) )
        {
            inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err);
            if(inf)
            {
                FmPath *dir, *sub;
                GFile *child;
                if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY))
                {
                    /* FIXME: handle symlinks */
                    if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY)
                    {
                        g_object_unref(inf);
                        continue;
                    }
                }

                /* virtual folders may return children not within them */
                dir = fm_path_new_for_gfile(g_file_enumerator_get_container(enu));
                if (fm_path_equal(job->dir_path, dir))
                    sub = fm_path_new_child(job->dir_path, g_file_info_get_name(inf));
                else
                    sub = fm_path_new_child(dir, g_file_info_get_name(inf));
                child = g_file_get_child(g_file_enumerator_get_container(enu),
                                         g_file_info_get_name(inf));
                if (g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY)
                    /* for dir: check if its FS is R/O and set attr. into inf */
                    _fm_file_info_job_update_fs_readonly(child, inf, NULL, NULL);
                fi = fm_file_info_new_from_g_file_data(child, inf, sub);
                fm_path_unref(sub);
                fm_path_unref(dir);
                g_object_unref(child);
                fm_dir_list_job_add_found_file(job, fi);
                fm_file_info_unref(fi);
            }
            else
            {
                if(err)
                {
                    FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
                    g_error_free(err);
                    /* FM_JOB_RETRY is not supported. */
                    if(act == FM_JOB_ABORT)
                        fm_job_cancel(fmjob);
                }
                /* otherwise it's EOL */
                break;
            }
            g_object_unref(inf);
        }
        g_file_enumerator_close(enu, NULL, &err);
        g_object_unref(enu);
    }
    else
    {
        fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL);
        g_error_free(err);
        return FALSE;
    }
    return TRUE;
}
예제 #10
0
static gboolean fm_dir_list_job_run_posix(FmDirListJob* job)
{
    FmJob* fmjob = FM_JOB(job);
    FmFileInfo* fi;
    GError *err = NULL;
    char* path_str;
    GDir* dir;

    path_str = fm_path_to_str(job->dir_path);

    fi = _new_info_for_native_file(job, job->dir_path, path_str, NULL);
    if(fi)
    {
        if(! fm_file_info_is_dir(fi))
        {
            err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
                              _("The specified directory '%s' is not valid"),
                              path_str);
            fm_file_info_unref(fi);
            fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL);
            g_error_free(err);
            g_free(path_str);
            return FALSE;
        }
        job->dir_fi = fi;
    }
    else
    {
        err = g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
                          _("The specified directory '%s' is not valid"),
                          path_str);
        fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL);
        g_error_free(err);
        g_free(path_str);
        return FALSE;
    }

    dir = g_dir_open(path_str, 0, &err);
    if( dir )
    {
        const char* name;
        GString* fpath = g_string_sized_new(4096);
        int dir_len = strlen(path_str);
        g_string_append_len(fpath, path_str, dir_len);
        if(fpath->str[dir_len-1] != '/')
        {
            g_string_append_c(fpath, '/');
            ++dir_len;
        }
        while( ! fm_job_is_cancelled(fmjob) && (name = g_dir_read_name(dir)) )
        {
            FmPath* new_path;
            g_string_truncate(fpath, dir_len);
            g_string_append(fpath, name);

            if(job->flags & FM_DIR_LIST_JOB_DIR_ONLY) /* if we only want directories */
            {
                struct stat st;
                /* FIXME: this results in an additional stat() call, which is inefficient */
                if(stat(fpath->str, &st) == -1 || !S_ISDIR(st.st_mode))
                    continue;
            }

            new_path = fm_path_new_child(job->dir_path, name);

        _retry:
            fi = _new_info_for_native_file(job, new_path, fpath->str, &err);
            if (fi == NULL) /* we got a damaged file */
            {
                FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
                GFile *gf;
                GFileInfo *inf;
                gchar *disp_basename;

                g_error_free(err);
                err = NULL;
                if(act == FM_JOB_RETRY)
                    goto _retry;
                /* bug #3615271: Damaged mountpoint isn't shown
                   let make a simple file info then */
                inf = g_file_info_new();
                gf = fm_path_to_gfile(new_path);
                g_file_info_set_file_type(inf, G_FILE_TYPE_UNKNOWN);
                g_file_info_set_name(inf, name);
                disp_basename = g_filename_display_basename(fpath->str);
                g_file_info_set_display_name(inf, disp_basename);
                g_free(disp_basename);
                g_file_info_set_content_type(inf, "inode/x-corrupted");
                fi = fm_file_info_new_from_g_file_data(gf, inf, new_path);
                g_object_unref(inf);
                g_object_unref(gf);
            }
            fm_dir_list_job_add_found_file(job, fi);
            fm_file_info_unref(fi);
            fm_path_unref(new_path);
        }
        g_string_free(fpath, TRUE);
        g_dir_close(dir);
    }
    else
    {
        fm_job_emit_error(fmjob, err, FM_JOB_ERROR_CRITICAL);
        g_error_free(err);
    }
    g_free(path_str);
    return TRUE;
}
예제 #11
0
/*********************************************************************
 * ...
 * 
 * 
 ********************************************************************/
gboolean fm_file_info_job_run (FmJob *fmjob)
{
	FmFileInfoJob *job = (FmFileInfoJob*) fmjob;
    GError *err = NULL;

	GList *l;
	for (l = fm_list_peek_head_link (job->file_infos); !fm_job_is_cancelled (fmjob) && l; )
	{
		FmFileInfo *file_info = (FmFileInfo*) l->data;
        GList *next = l->next;

        job->current = file_info->path;

		if (fm_path_is_native (file_info->path))
		{
			char *path_str = fm_path_to_str (file_info->path);
			
            
            // FileInfo rework: new function for testing...
            // this one is not cancellable and doesn't handle errors...
            // if (!fm_file_info_job_get_info_for_native_file (FM_JOB (job), file_info, path_str, &err))
            if (!fm_file_info_set_for_native_file (file_info, path_str))
            {
                //~ FmJobErrorAction act = fm_job_emit_error (FM_JOB(job), err, FM_JOB_ERROR_MILD);
                //~ 
                //~ g_error_free (err);
                //~ err = NULL;
                //~ 
                //~ if (act == FM_JOB_RETRY)
                    //~ continue;

                DEBUG ("fm_file_info_set_for_native_file: error reading %s\n", path_str);
                
                next = l->next;
                fm_list_delete_link (job->file_infos, l); // Also calls unref...
            }
			
            g_free (path_str);
		}
		else
		{
            GFile *gf;
            
            if (fm_path_is_virtual (file_info->path))
            {
                // This is a xdg menu
                if (fm_path_is_xdg_menu (file_info->path))
                {
                    MenuCache *mc;
                    MenuCacheDir *dir;
                    
                    char *path_str = fm_path_to_str (file_info->path);
                    char *menu_name = path_str + 5, ch;
                    char *dir_name;
                    
                    while (*menu_name == '/')
                        ++menu_name;
                    
                    dir_name = menu_name;
                    
                    while (*dir_name && *dir_name != '/')
                        ++dir_name;
                    
                    ch = *dir_name;
                    *dir_name = '\0';
                    
                    menu_name = g_strconcat (menu_name, ".menu", NULL);
                    mc = menu_cache_lookup_sync (menu_name);
                    g_free (menu_name);

                    if (*dir_name && !(*dir_name == '/' && dir_name[1] == '\0'))
                    {
                        char *tmp = g_strconcat ("/",
                                                 menu_cache_item_get_id (MENU_CACHE_ITEM(menu_cache_get_root_dir (mc))),
                                                 dir_name, NULL);
                        
                        dir = menu_cache_get_dir_from_path (mc, tmp);
                        
                        g_free (tmp);
                    }
                    else
                    {
                        dir = menu_cache_get_root_dir (mc);
                    }
                    
                    if (dir)
                    {
                        fm_file_info_set_from_menu_cache_item (file_info, (MenuCacheItem*) dir);
                    }
                    else
                    {
                        next = l->next;
                        fm_list_delete_link (job->file_infos, l); // Also calls unref...
                    }
                    
                    g_free (path_str);
                    menu_cache_unref (mc);
                    
                    l = l->next;
                    continue;
                }
            }

			gf = fm_path_to_gfile (file_info->path);
			
            if (!fm_file_info_job_get_info_for_gfile (FM_JOB (job), file_info, gf, &err))
            {
                FmJobErrorAction act = fm_job_emit_error (FM_JOB (job), err, FM_JOB_ERROR_MILD);
                
                g_error_free (err);
                err = NULL;
                
                if (act == FM_JOB_RETRY)
                    continue;

                next = l->next;
                
                fm_list_delete_link (job->file_infos, l);   // Also calls unref...
            }
			
            g_object_unref (gf);
		}
        
        l = next;
	}
	
    return TRUE;
}
예제 #12
0
static gboolean deep_count_gio(FmDeepCountJob* job, GFileInfo* inf, GFile* gf)
{
    FmJob* fmjob = FM_JOB(job);
    GError* err = NULL;
    GFileType type;
    const char* fs_id;
    gboolean descend;

    if(inf)
        g_object_ref(inf);
    else
    {
_retry_query_info:
        inf = g_file_query_info(gf, query_str,
                    (job->flags & FM_DC_JOB_FOLLOW_LINKS) ? 0 : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                    fm_job_get_cancellable(fmjob), &err);
        if(!inf)
        {
            FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
            g_error_free(err);
            err = NULL;
            if(act == FM_JOB_RETRY)
                goto _retry_query_info;
            return FALSE;
        }
    }
    if(fm_job_is_cancelled(fmjob))
    {
        g_object_unref(inf);
        return FALSE;
    }

    type = g_file_info_get_file_type(inf);
    descend = TRUE;

    ++job->count;
    job->total_size += g_file_info_get_size(inf);
    job->total_ondisk_size += g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);

    /* prepare for moving across different devices */
    if( job->flags & FM_DC_JOB_PREPARE_MOVE )
    {
        fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
        if( g_strcmp0(fs_id, job->dest_fs_id) != 0 )
        {
            /* files on different device requires an additional 'delete' for the source file. */
            ++job->total_size; /* this is for the additional delete */
            ++job->total_ondisk_size;
            ++job->count;
        }
        else
            descend = FALSE;
    }

    if( type == G_FILE_TYPE_DIRECTORY )
    {
        FmPath* fm_path = fm_path_new_for_gfile(gf);
        /* check if we need to decends into the dir. */
        /* trash:/// doesn't support deleting files recursively */
        if(job->flags & FM_DC_JOB_PREPARE_DELETE && fm_path_is_trash(fm_path) && ! fm_path_is_trash_root(fm_path))
            descend = FALSE;
        else
        {
            /* only descends into files on the same filesystem */
            if( job->flags & FM_DC_JOB_SAME_FS )
            {
                fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
                descend = (g_strcmp0(fs_id, job->dest_fs_id) == 0);
            }
        }
        fm_path_unref(fm_path);
        g_object_unref(inf);
        inf = NULL;

        if(descend)
        {
            GFileEnumerator* enu;
        _retry_enum_children:
            enu = g_file_enumerate_children(gf, query_str,
                                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                fm_job_get_cancellable(fmjob), &err);
            if(enu)
            {
                while( !fm_job_is_cancelled(fmjob) )
                {
                    inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err);
                    if(inf)
                    {
                        GFile* child = g_file_get_child(gf, g_file_info_get_name(inf));
                        deep_count_gio(job, inf, child);
                        g_object_unref(child);
                        g_object_unref(inf);
                        inf = NULL;
                    }
                    else
                    {
                        if(err) /* error! */
                        {
                            /* FM_JOB_RETRY is not supported */
                            /*FmJobErrorAction act = */
                            fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
                            g_error_free(err);
                            err = NULL;
                        }
                        else
                        {
                            /* EOF is reached, do nothing. */
                            break;
                        }
                    }
                }
                g_file_enumerator_close(enu, NULL, NULL);
                g_object_unref(enu);
            }
            else
            {
                FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
                g_error_free(err);
                err = NULL;
                if(act == FM_JOB_RETRY)
                    goto _retry_enum_children;
            }
        }
    }
    else
        g_object_unref(inf);

    return TRUE;
}
예제 #13
0
static gboolean deep_count_posix(FmDeepCountJob* job, const char *path)
{
    FmJob* fmjob = FM_JOB(job);
    struct stat st;
    int ret;

_retry_stat:
    if( G_UNLIKELY(job->flags & FM_DC_JOB_FOLLOW_LINKS) )
        ret = stat(path, &st);
    else
        ret = lstat(path, &st);

    if( ret == 0 )
    {
        ++job->count;
        job->total_size += (goffset)st.st_size;
        job->total_ondisk_size += (st.st_blocks * 512);

        /* NOTE: if job->dest_dev is 0, that means our destination
         * folder is not on native UNIX filesystem. Hence it's not
         * on the same device. Our st.st_dev will always be non-zero
         * since our file is on a native UNIX filesystem. */

        /* only descends into files on the same filesystem */
        if( job->flags & FM_DC_JOB_SAME_FS )
        {
            if( st.st_dev != job->dest_dev )
                return TRUE;
        }
        /* only descends into files on the different filesystem */
        else if( job->flags & FM_DC_JOB_PREPARE_MOVE )
        {
            if( st.st_dev == job->dest_dev )
                return TRUE;
        }
    }
    else
    {
        GError* err = g_error_new(G_IO_ERROR, g_io_error_from_errno(errno), "%s", g_strerror(errno));
        FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MILD);
        g_error_free(err);
        err = NULL;
        if(act == FM_JOB_RETRY)
            goto _retry_stat;
        return FALSE;
    }
    if(fm_job_is_cancelled(fmjob))
        return FALSE;

    if( S_ISDIR(st.st_mode) ) /* if it's a dir */
    {
        GDir* dir_ent = g_dir_open(path, 0, NULL);
        if(dir_ent)
        {
            const char* basename;
            while( !fm_job_is_cancelled(fmjob)
                && (basename = g_dir_read_name(dir_ent)) )
            {
                char *sub = g_build_filename(path, basename, NULL);
                if(!fm_job_is_cancelled(fmjob))
                {
                    if(deep_count_posix(job, sub))
                    {
                        /* for moving across different devices, an additional 'delete'
                         * for source file is needed. so let's +1 for the delete.*/
                        if(job->flags & FM_DC_JOB_PREPARE_MOVE)
                        {
                            ++job->total_size;
                            ++job->total_ondisk_size;
                            ++job->count;
                        }
                    }
                }
                g_free(sub);
            }
            g_dir_close(dir_ent);
        }
    }
    return TRUE;
}
예제 #14
0
static gboolean _fm_file_ops_job_copy_file(FmFileOpsJob* job, GFile* src,
                                           GFileInfo* inf, GFile* dest,
                                           FmFolder *src_folder, /* if move */
                                           FmFolder *dest_folder)
{
    gboolean ret = FALSE;
    gboolean delete_src = FALSE;
    GError* err = NULL;
    GFileType type;
    guint64 size;
    GFile* new_dest = NULL;
    GFileCopyFlags flags;
    FmJob* fmjob = FM_JOB(job);
    FmPath *fm_dest;
    guint32 mode;
    gboolean skip_dir_content = FALSE;

    /* FIXME: g_file_get_child() failed? generate error! */
    g_return_val_if_fail(dest != NULL, FALSE);

    job->supported_options = FM_FILE_OP_RENAME | FM_FILE_OP_SKIP | FM_FILE_OP_OVERWRITE;
    if( G_LIKELY(inf) )
        g_object_ref(inf);
    else
    {
_retry_query_src_info:
        inf = g_file_query_info(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err);
        if( !inf )
        {
            FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
            g_error_free(err);
            err = NULL;
            if(act == FM_JOB_RETRY)
                goto _retry_query_src_info;
            return FALSE;
        }
    }

    if(!_fm_file_ops_job_check_paths(job, src, inf, dest))
    {
        g_object_unref(inf);
        return FALSE;
    }

    /* if this is a cross-device move operation, delete source files. */
    if( job->type == FM_FILE_OP_MOVE )
        delete_src = TRUE;

    /* showing currently processed file. */
    fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(inf));

    type = g_file_info_get_file_type(inf);

    size = g_file_info_get_size(inf);
    mode = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_MODE);

    g_object_unref(inf);
    inf = NULL;

    switch(type)
    {
    case G_FILE_TYPE_DIRECTORY:
        {
            GFileEnumerator* enu;
            gboolean dir_created = FALSE;
_retry_mkdir:
            if( !fm_job_is_cancelled(fmjob) && !job->skip_dir_content &&
                !g_file_make_directory(dest, fm_job_get_cancellable(fmjob), &err) )
            {
                if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS ||
                                                 err->code == G_IO_ERROR_INVALID_FILENAME ||
                                                 err->code == G_IO_ERROR_FILENAME_TOO_LONG))
                {
                    GFile* dest_cp = new_dest;
                    gboolean dest_exists = (err->code == G_IO_ERROR_EXISTS);
                    FmFileOpOption opt = 0;
                    g_error_free(err);
                    err = NULL;

                    new_dest = NULL;
                    opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, dest_exists);
                    if(!new_dest) /* restoring status quo */
                        new_dest = dest_cp;
                    else if(dest_cp) /* we got new new_dest, forget old one */
                        g_object_unref(dest_cp);
                    switch(opt)
                    {
                    case FM_FILE_OP_RENAME:
                        dest = new_dest;
                        goto _retry_mkdir;
                        break;
                    case FM_FILE_OP_SKIP:
                        /* when a dir is skipped, we need to know its total size to calculate correct progress */
                        job->finished += size;
                        fm_file_ops_job_emit_percent(job);
                        job->skip_dir_content = skip_dir_content = TRUE;
                        dir_created = TRUE; /* pretend that dir creation succeeded */
                        break;
                    case FM_FILE_OP_OVERWRITE:
                        dir_created = TRUE; /* pretend that dir creation succeeded */
                        break;
                    case FM_FILE_OP_CANCEL:
                        fm_job_cancel(fmjob);
                        break;
                    case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */
                    }
                }
                else if(!fm_job_is_cancelled(fmjob))
                {
                    FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                    g_error_free(err);
                    err = NULL;
                    if(act == FM_JOB_RETRY)
                        goto _retry_mkdir;
                }
                job->finished += size;
                fm_file_ops_job_emit_percent(job);
            }
            else
            {
                /* chmod the newly created dir properly */
                if(!fm_job_is_cancelled(fmjob) && !job->skip_dir_content)
                {
                    if(mode)
                    {
_retry_chmod_for_dir:
                        mode |= (S_IRUSR|S_IWUSR); /* ensure we have rw permission to this file. */
                        if( !g_file_set_attribute_uint32(dest, G_FILE_ATTRIBUTE_UNIX_MODE,
                                                         mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                                         fm_job_get_cancellable(fmjob), &err) )
                        {
                            FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                            g_error_free(err);
                            err = NULL;
                            if(act == FM_JOB_RETRY)
                                goto _retry_chmod_for_dir;
                            /* FIXME: some filesystems may not support this. */
                        }
                    }
                    dir_created = TRUE;
                }
                job->finished += size;
                fm_file_ops_job_emit_percent(job);
            }

            if(!dir_created) /* if target dir is not created, don't copy dir content */
            {
                if(!job->skip_dir_content)
                    job->skip_dir_content = skip_dir_content = TRUE;
            }

            /* the dest dir is created. let's copy its content. */
            /* FIXME: handle the case when the dir cannot be created. */
            else if(!fm_job_is_cancelled(fmjob))
            {
                FmFolder *sub_folder;
                FmFolder *sub_src = NULL;

                if (delete_src)
                {
                    FmPath *src_path = fm_path_new_for_gfile(src);
                    sub_src = fm_folder_find_by_path(src_path);
                    fm_path_unref(src_path);
                }
                fm_dest = fm_path_new_for_gfile(dest);
                sub_folder = fm_folder_find_by_path(fm_dest);
                /* inform folder we created directory */
                if (!dest_folder || !_fm_folder_event_file_added(dest_folder, fm_dest))
                    fm_path_unref(fm_dest);
_retry_enum_children:
                enu = g_file_enumerate_children(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                            fm_job_get_cancellable(fmjob), &err);
                if(enu)
                {
                    int n_children = 0;
                    int n_copied = 0;
                    ret = TRUE;
                    while( !fm_job_is_cancelled(fmjob) )
                    {
                        inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err);
                        if( inf )
                        {
                            ++n_children;
                            /* don't overwrite dir content, only calculate progress. */
                            if(G_UNLIKELY(job->skip_dir_content))
                            {
                                /* FIXME: this is incorrect as we don't do the calculation recursively. */
                                job->finished += g_file_info_get_size(inf);
                                fm_file_ops_job_emit_percent(job);
                            }
                            else
                            {
                                gboolean ret2;
                                GFile* sub = g_file_get_child(src, g_file_info_get_name(inf));
                                GFile* sub_dest;
                                char* tmp_basename;

                                if(g_file_is_native(src) == g_file_is_native(dest))
                                    /* both are native or both are virtual */
                                    tmp_basename = NULL;
                                else if(g_file_is_native(src)) /* copy from native to virtual */
                                    tmp_basename = g_filename_to_utf8(g_file_info_get_name(inf),
                                                                      -1, NULL, NULL, NULL);
                                    /* gvfs escapes it itself */
                                else /* copy from virtual to native */
                                    tmp_basename = fm_uri_subpath_to_native_subpath(g_file_info_get_name(inf), NULL);
                                sub_dest = g_file_get_child(dest,
                                        tmp_basename ? tmp_basename : g_file_info_get_name(inf));
                                g_free(tmp_basename);

                                ret2 = _fm_file_ops_job_copy_file(job, sub, inf, sub_dest, sub_src, sub_folder);
                                g_object_unref(sub);
                                g_object_unref(sub_dest);

                                if(ret2)
                                    ++n_copied;
                                else
                                    ret = FALSE;
                            }
                            g_object_unref(inf);
                        }
                        else
                        {
                            if(err)
                            {
                                fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                                g_error_free(err);
                                err = NULL;
                                /* FM_JOB_RETRY is not supported here */
                                ret = FALSE;
                            }
                            else /* EOF is reached */
                            {
                                /* all files are successfully copied. */
                                if(fm_job_is_cancelled(fmjob))
                                    ret = FALSE;
                                else
                                {
                                    if(dir_created) /* target dir is created */
                                    {
                                        /* some files are not copied */
                                        if(n_children != n_copied)
                                        {
                                            /* if the copy actions are skipped deliberately, it's ok */
                                            if(!job->skip_dir_content)
                                                ret = FALSE;
                                        }
                                    }
                                    /* else job->skip_dir_content is TRUE */
                                }
                                break;
                            }
                        }
                    }
                    g_file_enumerator_close(enu, NULL, &err);
                    g_object_unref(enu);
                }
                else
                {
                    FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                    g_error_free(err);
                    err = NULL;
                    if(act == FM_JOB_RETRY)
                        goto _retry_enum_children;
                }
                if (sub_src)
                    g_object_unref(sub_src);
                if (sub_folder)
                    g_object_unref(sub_folder);
            }
            if(job->skip_dir_content)
                delete_src = FALSE;
            if(skip_dir_content)
                job->skip_dir_content = FALSE;
        }
        break;

    case G_FILE_TYPE_SPECIAL:
        /* only handle FIFO for local files */
        if(g_file_is_native(src) && g_file_is_native(dest))
        {
            char* src_path = g_file_get_path(src);
            struct stat src_st;
            int r;
            r = lstat(src_path, &src_st);
            g_free(src_path);
            if(r == 0)
            {
                /* Handle FIFO on native file systems. */
                if(S_ISFIFO(src_st.st_mode))
                {
                    char* dest_path = g_file_get_path(dest);
                    int r = mkfifo(dest_path, src_st.st_mode);
                    g_free(dest_path);
                    if( r == 0)
                        ret = TRUE;
                }
                /* FIXME: how about block device, char device, and socket? */
            }
        }
        if (!ret)
        {
            g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED,
                        _("Cannot copy file '%s': not supported"),
                        g_file_info_get_display_name(inf));
            fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
            g_clear_error(&err);
        }
        goto _file_copied;

    default:
        flags = G_FILE_COPY_ALL_METADATA|G_FILE_COPY_NOFOLLOW_SYMLINKS;
_retry_copy:
        if( !g_file_copy(src, dest, flags, fm_job_get_cancellable(fmjob),
                         progress_cb, fmjob, &err) )
        {
            flags &= ~G_FILE_COPY_OVERWRITE;

            /* handle existing files or file name conflict */
            if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS ||
                                             err->code == G_IO_ERROR_INVALID_FILENAME ||
                                             err->code == G_IO_ERROR_FILENAME_TOO_LONG))
            {
                GFile* dest_cp = new_dest;
                gboolean dest_exists = (err->code == G_IO_ERROR_EXISTS);
                FmFileOpOption opt = 0;
                g_error_free(err);
                err = NULL;

                new_dest = NULL;
                opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, dest_exists);
                if(!new_dest) /* restoring status quo */
                    new_dest = dest_cp;
                else if(dest_cp) /* we got new new_dest, forget old one */
                    g_object_unref(dest_cp);
                switch(opt)
                {
                case FM_FILE_OP_RENAME:
                    dest = new_dest;
                    goto _retry_copy;
                    break;
                case FM_FILE_OP_OVERWRITE:
                    flags |= G_FILE_COPY_OVERWRITE;
                    goto _retry_copy;
                    break;
                case FM_FILE_OP_CANCEL:
                    fm_job_cancel(fmjob);
                    break;
                case FM_FILE_OP_SKIP:
                    ret = TRUE;
                    delete_src = FALSE; /* don't delete source file. */
                    break;
                case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */
                }
            }
            else
            {
                gboolean is_no_space = (err->domain == G_IO_ERROR &&
                                        err->code == G_IO_ERROR_NO_SPACE);
                FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                g_error_free(err);
                err = NULL;
                if(act == FM_JOB_RETRY)
                {
                    job->current_file_finished = 0;
                    goto _retry_copy;
                }
                /* FIXME: ask to leave partial content? */
                if(is_no_space)
                    g_file_delete(dest, fm_job_get_cancellable(fmjob), NULL);
                ret = FALSE;
                delete_src = FALSE;
            }
        }
        else
            ret = TRUE;

_file_copied:
        job->finished += size;
        job->current_file_finished = 0;

        if(ret && dest_folder)
        {
            fm_dest = fm_path_new_for_gfile(dest);
            if(!_fm_folder_event_file_added(dest_folder, fm_dest))
                fm_path_unref(fm_dest);
        }

        /* update progress */
        fm_file_ops_job_emit_percent(job);
        break;
    }
    /* if this is a cross-device move operation, delete source files. */
    /* ret == TRUE means the copy is successful. */
    if( !fm_job_is_cancelled(fmjob) && ret && delete_src )
        ret = _fm_file_ops_job_delete_file(fmjob, src, inf, src_folder); /* delete the source file. */

    if(new_dest)
        g_object_unref(new_dest);

    return ret;
}
예제 #15
0
gboolean _fm_file_ops_job_move_run(FmFileOpsJob* job)
{
    GFile *dest_dir;
    GFileInfo* inf;
    GList* l;
    GError* err = NULL;
    FmJob* fmjob = FM_JOB(job);
    dev_t dest_dev = 0;
    gboolean ret = TRUE;
    FmDeepCountJob* dc;
    FmPath *parent = NULL;
    FmFolder *df, *sf = NULL;

    /* get information of destination folder */
    g_return_val_if_fail(job->dest, FALSE);
    dest_dir = fm_path_to_gfile(job->dest);
_retry_query_dest_info:
    inf = g_file_query_info(dest_dir, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL","
                                  G_FILE_ATTRIBUTE_UNIX_DEVICE","
                                  G_FILE_ATTRIBUTE_ID_FILESYSTEM","
                                  G_FILE_ATTRIBUTE_UNIX_DEVICE, 0,
                                  fm_job_get_cancellable(fmjob), &err);
    if(inf)
    {
        job->dest_fs_id = g_intern_string(g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM));
        dest_dev = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_DEVICE); /* needed by deep count */
        g_object_unref(inf);
    }
    else
    {
        FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
        g_error_free(err);
        err = NULL;
        if(act == FM_JOB_RETRY)
            goto _retry_query_dest_info;
        else
        {
            g_object_unref(dest_dir);
            return FALSE;
        }
    }

    /* prepare the job, count total work needed with FmDeepCountJob */
    dc = fm_deep_count_job_new(job->srcs, FM_DC_JOB_PREPARE_MOVE);
    fm_deep_count_job_set_dest(dc, dest_dev, job->dest_fs_id);
    fm_job_run_sync(FM_JOB(dc));
    job->total = dc->total_size;

    if( fm_job_is_cancelled(FM_JOB(dc)) )
    {
        g_object_unref(dest_dir);
        g_object_unref(dc);
        return FALSE;
    }
    g_object_unref(dc);
    g_debug("total size to move: %llu, dest_fs: %s",
            (long long unsigned int)job->total, job->dest_fs_id);

    fm_file_ops_job_emit_prepared(job);
    /* suspend updates for destination */
    df = fm_folder_find_by_path(job->dest);
    if (df)
        fm_folder_block_updates(df);

    for(l = fm_path_list_peek_head_link(job->srcs); !fm_job_is_cancelled(fmjob) && l; l=l->next)
    {
        FmPath* path = FM_PATH(l->data);
        GFile* src = fm_path_to_gfile(path);
        GFile* dest;
        char* tmp_basename;

        /* do with updates for source */
        if (fm_path_get_parent(path) != parent && fm_path_get_parent(path) != NULL)
        {
            FmFolder *pf;

            pf = fm_folder_find_by_path(fm_path_get_parent(path));
            if (pf != sf)
            {
                if (sf)
                {
                    fm_folder_unblock_updates(sf);
                    g_object_unref(sf);
                }
                if (pf)
                    fm_folder_block_updates(pf);
                sf = pf;
            }
            else if (pf)
                g_object_unref(pf);
        }
        parent = fm_path_get_parent(path);
        if(g_file_is_native(src) && g_file_is_native(dest_dir))
            /* both are native */
            tmp_basename = NULL;
        else if(g_file_is_native(src)) /* move from native to virtual */
            tmp_basename = g_filename_to_utf8(fm_path_get_basename(path),
                                              -1, NULL, NULL, NULL);
            /* gvfs escapes it itself */
        else /* move from virtual to native/virtual */
            tmp_basename = fm_uri_subpath_to_native_subpath(fm_path_get_basename(path), NULL);
        dest = g_file_get_child(dest_dir,
                        tmp_basename ? tmp_basename : fm_path_get_basename(path));
        g_free(tmp_basename);

        if(!_fm_file_ops_job_move_file(job, src, NULL, dest, path, sf, df))
            ret = FALSE;
        g_object_unref(src);
        g_object_unref(dest);

        if(!ret)
            break;
    }
    /* restore updates for destination and source */
    if (df)
    {
        fm_folder_unblock_updates(df);
        g_object_unref(df);
    }
    if (sf)
    {
        fm_folder_unblock_updates(sf);
        g_object_unref(sf);
    }

    g_object_unref(dest_dir);
    return ret;
}
예제 #16
0
gboolean _fm_file_ops_job_copy_run(FmFileOpsJob* job)
{
    gboolean ret = TRUE;
    GFile *dest_dir;
    GList* l;
    FmJob* fmjob = FM_JOB(job);
    /* prepare the job, count total work needed with FmDeepCountJob */
    FmDeepCountJob* dc = fm_deep_count_job_new(job->srcs, FM_DC_JOB_DEFAULT);
    FmFolder *df;

    /* let the deep count job share the same cancellable object. */
    fm_job_set_cancellable(FM_JOB(dc), fm_job_get_cancellable(fmjob));
    fm_job_run_sync(FM_JOB(dc));
    job->total = dc->total_size;
    if(fm_job_is_cancelled(fmjob))
    {
        g_object_unref(dc);
        return FALSE;
    }
    g_object_unref(dc);
    g_debug("total size to copy: %llu", (long long unsigned int)job->total);

    dest_dir = fm_path_to_gfile(job->dest);
    /* suspend updates for destination */
    df = fm_folder_find_by_path(job->dest);
    if (df)
        fm_folder_block_updates(df);

    fm_file_ops_job_emit_prepared(job);

    for(l = fm_path_list_peek_head_link(job->srcs); !fm_job_is_cancelled(fmjob) && l; l=l->next)
    {
        FmPath* path = FM_PATH(l->data);
        GFile* src = fm_path_to_gfile(path);
        GFile* dest;
        char* tmp_basename;

        if(g_file_is_native(src) && g_file_is_native(dest_dir))
            /* both are native */
            tmp_basename = NULL;
        else if(g_file_is_native(src)) /* copy from native to virtual */
            tmp_basename = g_filename_to_utf8(fm_path_get_basename(path),
                                              -1, NULL, NULL, NULL);
            /* gvfs escapes it itself */
        else /* copy from virtual to native/virtual */
        {
            /* if we drop URI query onto native filesystem, omit query part */
            const char *basename = fm_path_get_basename(path);
            char *sub_name;

            sub_name = strchr(basename, '?');
            if (sub_name)
            {
                sub_name = g_strndup(basename, sub_name - basename);
                basename = strrchr(sub_name, G_DIR_SEPARATOR);
                if (basename)
                    basename++;
                else
                    basename = sub_name;
            }
            tmp_basename = fm_uri_subpath_to_native_subpath(basename, NULL);
            g_free(sub_name);
        }
        dest = g_file_get_child(dest_dir,
                        tmp_basename ? tmp_basename : fm_path_get_basename(path));
        g_free(tmp_basename);
        if(!_fm_file_ops_job_copy_file(job, src, NULL, dest, NULL, df))
            ret = FALSE;
        g_object_unref(src);
        g_object_unref(dest);
    }

    /* g_debug("finished: %llu, total: %llu", job->finished, job->total); */
    fm_file_ops_job_emit_percent(job);

    /* restore updates for destination */
    if (df)
    {
        fm_folder_unblock_updates(df);
        g_object_unref(df);
    }
    g_object_unref(dest_dir);
    return ret;
}
예제 #17
0
gboolean _fm_file_ops_job_move_file(FmFileOpsJob* job, GFile* src,
                                    GFileInfo* inf, GFile* dest, FmPath *src_path,
                                    FmFolder *src_folder, FmFolder *dest_folder)
{
    GError* err = NULL;
    FmJob* fmjob = FM_JOB(job);
    const char* src_fs_id;
    gboolean ret = TRUE;
    GFile* new_dest = NULL;

    job->supported_options = FM_FILE_OP_RENAME | FM_FILE_OP_SKIP | FM_FILE_OP_OVERWRITE;
    if( G_LIKELY(inf) )
        g_object_ref(inf);
    else
    {
_retry_query_src_info:
        inf = g_file_query_info(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err);
        if( !inf )
        {
            FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
            g_error_free(err);
            err = NULL;
            if(act == FM_JOB_RETRY)
                goto _retry_query_src_info;
            return FALSE;
        }
    }

    if(!_fm_file_ops_job_check_paths(job, src, inf, dest))
    {
        g_object_unref(inf);
        return FALSE;
    }

    src_fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
    /* Check if source and destination are on the same device */
    if( job->type == FM_FILE_OP_UNTRASH || g_strcmp0(src_fs_id, job->dest_fs_id) == 0 ) /* same device */
    {
        guint64 size;
        GFileCopyFlags flags = G_FILE_COPY_ALL_METADATA|G_FILE_COPY_NOFOLLOW_SYMLINKS;
        FmPath *fm_dest;

        fm_dest = fm_path_new_for_gfile(dest);
        /* showing currently processed file. */
        fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(inf));
_retry_move:
        if( !g_file_move(src, dest, flags, fm_job_get_cancellable(fmjob), progress_cb, job, &err))
        {
            flags &= ~G_FILE_COPY_OVERWRITE;
            if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
            {
                GFile* dest_cp = new_dest;
                FmFileOpOption opt = 0;

                new_dest = NULL;
                opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, TRUE);
                if(!new_dest) /* restoring status quo */
                    new_dest = dest_cp;
                else if(dest_cp) /* we got new new_dest, forget old one */
                    g_object_unref(dest_cp);

                g_error_free(err);
                err = NULL;

                switch(opt)
                {
                case FM_FILE_OP_RENAME:
                    dest = new_dest;
                    goto _retry_move;
                    break;
                case FM_FILE_OP_OVERWRITE:
                    flags |= G_FILE_COPY_OVERWRITE;
                    if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY) /* merge dirs */
                    {
                        GFileEnumerator* enu = g_file_enumerate_children(src, query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                                                        fm_job_get_cancellable(fmjob), &err);
                        if(enu)
                        {
                            GFileInfo* child_inf;
                            FmFolder *sub_src_folder = fm_folder_find_by_path(src_path);
                            FmFolder *sub_dest_folder = fm_folder_find_by_path(fm_dest);
                            while(!fm_job_is_cancelled(fmjob))
                            {
                                child_inf = g_file_enumerator_next_file(enu, fm_job_get_cancellable(fmjob), &err);
                                if(child_inf)
                                {
                                    GFile* child = g_file_get_child(src, g_file_info_get_name(child_inf));
                                    GFile* child_dest = g_file_get_child(dest, g_file_info_get_name(child_inf));
                                    FmPath *child_path = fm_path_new_for_gfile(child);
                                    _fm_file_ops_job_move_file(job, child, child_inf, child_dest, child_path, sub_src_folder, sub_dest_folder);
                                    g_object_unref(child);
                                    g_object_unref(child_dest);
                                    g_object_unref(child_inf);
                                    fm_path_unref(child_path);
                                }
                                else
                                {
                                    if(err) /* error */
                                    {
                                        fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                                        g_error_free(err);
                                        err = NULL;
                                    }
                                    else /* EOF */
                                    {
                                        break;
                                    }
                                }
                            }
                            if (sub_src_folder)
                                g_object_unref(sub_src_folder);
                            if (sub_dest_folder)
                                g_object_unref(sub_dest_folder);
                            g_object_unref(enu);
                        }
                        else
                        {
                            /*FmJobErrorAction act = */
                            fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                            g_error_free(err);
                            err = NULL;
                            /* if(act == FM_JOB_RETRY)
                                goto _retry_move; */
                        }

                        /* remove source dir after its content is merged with destination dir */
                        if(!g_file_delete(src, fm_job_get_cancellable(fmjob), &err))
                        {
                            fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                            g_error_free(err);
                            err = NULL;
                            /* FIXME: should this be recoverable? */
                        }
                    }
                    else /* the destination is a file, just overwrite it. */
                        goto _retry_move;
                    break;
                case FM_FILE_OP_CANCEL:
                    fm_job_cancel(fmjob);
                    ret = FALSE;
                    break;
                case FM_FILE_OP_SKIP:
                    ret = TRUE;
                    break;
                case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */
                }
            }
            if(err)
            {
                FmJobErrorAction act = fm_job_emit_error(fmjob, err, FM_JOB_ERROR_MODERATE);
                g_error_free(err);
                err = NULL;
                if(act == FM_JOB_RETRY)
                    goto _retry_move;
            }
            fm_path_unref(fm_dest);
        }
        else
        {
            if (src_folder)
                _fm_folder_event_file_deleted(src_folder, src_path);
            if (!dest_folder || !_fm_folder_event_file_added(dest_folder, fm_dest))
                fm_path_unref(fm_dest);
        }
/*
        size = g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_UNIX_BLOCKS);
        size *= g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE);
*/
        size = g_file_info_get_size(inf);

        job->finished += size;
        fm_file_ops_job_emit_percent(job);
    }
    else /* use copy if they are on different devices */
    {
        /* use copy & delete */
        /* source file will be deleted in _fm_file_ops_job_copy_file() */
        ret = _fm_file_ops_job_copy_file(job, src, inf, dest, src_folder, dest_folder);
    }

    if(new_dest)
        g_object_unref(new_dest);

    g_object_unref(inf);
    return ret;
}