Пример #1
0
static void
vfs_file_task_link( char* src_file, VFSFileTask* task )
{
    struct stat src_stat;
    int result;
    gchar* dest_file;
    gchar* file_name = g_path_get_basename( src_file );

    if ( should_abort( task ) )
        return ;
    file_name = g_path_get_basename( src_file );
    dest_file = g_build_filename( task->dest_dir, file_name, NULL );
    g_free( file_name );
    call_progress_callback( task );
    if ( stat( src_file, &src_stat ) == -1 )
    {
        task->error = errno;
        call_state_callback( task, VFS_FILE_TASK_ERROR );
        if ( should_abort( task ) )
            return ;
    }

    /* FIXME: Check overwrite!! */
    result = symlink( src_file, dest_file );
    g_free( dest_file );
    if ( result )
    {
        task->error = errno;
        call_state_callback( task, VFS_FILE_TASK_ERROR );
        if ( should_abort( task ) )
            return ;
    }
    task->progress += src_stat.st_size;
    call_progress_callback( task );
}
Пример #2
0
static void
vfs_file_task_do_move ( VFSFileTask* task,
                        const char* src_file,
                        const char* dest_file )
{
    gchar* new_dest_file = NULL;
    gboolean dest_exists;
    struct stat file_stat;
    int result;

    if ( should_abort( task ) )
        return ;
    /* g_debug( "move \"%s\" to \"%s\"\n", src_file, dest_file ); */
    if ( lstat( src_file, &file_stat ) == -1 )
    {
        task->error = errno;    /* Error occurred */
        call_state_callback( task, VFS_FILE_TASK_ERROR );
        if ( should_abort( task ) )
            return ;
    }

    task->current_file = src_file;
    task->current_dest = dest_file;

    if ( should_abort( task ) )
        return ;

    call_progress_callback( task );

    if ( ! check_overwrite( task, dest_file,
           &dest_exists, &new_dest_file ) )
        return ;

    if ( new_dest_file )
        task->current_dest = dest_file = new_dest_file;

    result = rename( src_file, dest_file );

    if ( result != 0 )
    {
        task->error = errno;
        call_state_callback( task, VFS_FILE_TASK_ERROR );
        if ( should_abort( task ) )
        {
            g_free( new_dest_file );
            return ;
        }
    }
    else
        chmod( dest_file, file_stat.st_mode );

    task->progress += file_stat.st_size;
    call_progress_callback( task );

    g_free( new_dest_file );
}
Пример #3
0
void rendez_sleep(struct rendez *rv, int (*cond)(void*), void *arg)
{
	int8_t irq_state = 0;
	struct cv_lookup_elm cle;
	/* Do a quick check before registering and sleeping.  this is the 'check,
	 * signal, check again' pattern, where the first check is an optimization.
	 * Many rendezes will already be satisfied, so we want to avoid excessive
	 * locking associated with reg/dereg. */
	cv_lock_irqsave(&rv->cv, &irq_state);
	if (cond(arg)) {
		cv_unlock_irqsave(&rv->cv, &irq_state);
		return;
	}
	__reg_abortable_cv(&cle, &rv->cv);
	/* Mesa-style semantics, which is definitely what you want.  See the
	 * discussion at the end of the URL above. */
	while (!cond(arg)) {
		/* it's okay if we miss the ABORT flag; we hold the cv lock, so an
		 * aborter's broadcast is waiting until we unlock. */
		if (should_abort(&cle)) {
			cv_unlock_irqsave(&rv->cv, &irq_state);
			dereg_abortable_cv(&cle);
			error(EINTR, "syscall aborted");
		}
		cv_wait(&rv->cv);
		cpu_relax();
	}
	cv_unlock_irqsave(&rv->cv, &irq_state);
	dereg_abortable_cv(&cle);
}
Пример #4
0
static void
vfs_file_task_move( char* src_file, VFSFileTask* task )
{
    struct stat src_stat;
    struct stat dest_stat;
    gchar* file_name;
    gchar* dest_file;
    GKeyFile* kf;   /* for trash info */
    int tmpfd = -1;

    if ( should_abort( task ) )
        return ;

    task->current_file = src_file;

    file_name = g_path_get_basename( src_file );
    if( task->type == VFS_FILE_TASK_TRASH )
    {
        dest_file = g_strconcat( task->dest_dir, "/",  file_name, "XXXXXX", NULL );
        tmpfd = mkstemp( dest_file );
        if( tmpfd < 0 )
        {
            goto on_error;
        }
        g_debug( dest_file );
    }
    else
        dest_file = g_build_filename( task->dest_dir, file_name, NULL );

    g_free( file_name );

    if ( lstat( src_file, &src_stat ) == 0
            && lstat( task->dest_dir, &dest_stat ) == 0 )
    {
        /* Not on the same device */
        if ( src_stat.st_dev != dest_stat.st_dev )
        {
            /* g_print("not on the same dev: %s\n", src_file); */
            vfs_file_task_do_copy( task, src_file, dest_file );
        }
        else
        {
            /* g_print("on the same dev: %s\n", src_file); */
            vfs_file_task_do_move( task, src_file, dest_file );
        }
    }
    else
    {
        task->error = errno;
        call_state_callback( task, VFS_FILE_TASK_ERROR );
    }
on_error:
    if( tmpfd >= 0 )
    {
        close( tmpfd );
        unlink( dest_file );
    }
    g_free( dest_file );
}
Пример #5
0
/*
* void get_total_size_of_dir( const char* path, off_t* size )
* Recursively count total size of all files in the specified directory.
* If the path specified is a file, the size of the file is directly returned.
* cancel is used to cancel the operation. This function will check the value
* pointed by cancel in every iteration. If cancel is set to TRUE, the
* calculation is cancelled.
* NOTE: *size should be set to zero before calling this function.
*/
void get_total_size_of_dir( VFSFileTask* task,
                            const char* path,
                            off_t* size )
{
    GDir * dir;
    const char* name;
    char* full_path;
    struct stat file_stat;

    if ( should_abort( task ) )
        return;

    lstat( path, &file_stat );

    *size += file_stat.st_size;
    if ( S_ISLNK( file_stat.st_mode ) )             /* Don't follow symlinks */
        return ;

    dir = g_dir_open( path, 0, NULL );
    if ( dir )
    {
        while ( (name = g_dir_read_name( dir )) )
        {
            if ( should_abort( task ) )
                break;
            full_path = g_build_filename( path, name, NULL );
            lstat( full_path, &file_stat );
            if ( S_ISDIR( file_stat.st_mode ) )
            {
                get_total_size_of_dir( task, full_path, size );
            }
            else
            {
                *size += file_stat.st_size;
            }
            g_free( full_path );
        }
        g_dir_close( dir );
    }
}
Пример #6
0
/* Like sleep, but it will timeout in 'usec' microseconds. */
void rendez_sleep_timeout(struct rendez *rv, int (*cond)(void*), void *arg,
                          uint64_t usec)
{
	int8_t irq_state = 0;
	struct alarm_waiter awaiter;
	struct cv_lookup_elm cle;
	struct timer_chain *pcpui_tchain = &per_cpu_info[core_id()].tchain;

	if (!usec)
		return;
	/* Doing this cond check early, but then unlocking again.  Mostly just to
	 * avoid weird issues with the CV lock and the alarm tchain lock. */
	cv_lock_irqsave(&rv->cv, &irq_state);
	if (cond(arg)) {
		cv_unlock_irqsave(&rv->cv, &irq_state);
		return;
	}
	cv_unlock_irqsave(&rv->cv, &irq_state);
	/* The handler will call rendez_wake, but won't mess with the condition
	 * state.  It's enough to break us out of cv_wait() to see .on_tchain. */
	init_awaiter(&awaiter, rendez_alarm_handler);
	awaiter.data = rv;
	set_awaiter_rel(&awaiter, usec);
	/* Set our alarm on this cpu's tchain.  Note that when we sleep in cv_wait,
	 * we could be migrated, and later on we could be unsetting the alarm
	 * remotely. */
	set_alarm(pcpui_tchain, &awaiter);
	cv_lock_irqsave(&rv->cv, &irq_state);
	__reg_abortable_cv(&cle, &rv->cv);
	/* We could wake early for a few reasons.  Legit wakeups after a changed
	 * condition (and we should exit), other alarms with different timeouts (and
	 * we should go back to sleep), etc.  Note it is possible for our alarm to
	 * fire immediately upon setting it: before we even cv_lock. */
	while (!cond(arg) && awaiter.on_tchain) {
		if (should_abort(&cle)) {
			cv_unlock_irqsave(&rv->cv, &irq_state);
			unset_alarm(pcpui_tchain, &awaiter);
			dereg_abortable_cv(&cle);
			error(EINTR, "syscall aborted");
		}
		cv_wait(&rv->cv);
		cpu_relax();
	}
	cv_unlock_irqsave(&rv->cv, &irq_state);
	dereg_abortable_cv(&cle);
	/* Turn off our alarm.  If it already fired, this is a no-op.  Note this
	 * could be cross-core. */
	unset_alarm(pcpui_tchain, &awaiter);
}
Пример #7
0
static gpointer vfs_file_task_thread ( VFSFileTask* task )
{
    GList * l;
    struct stat file_stat;
    dev_t dest_dev = 0;
    GFunc funcs[] = {( GFunc ) vfs_file_task_move,
                     ( GFunc ) vfs_file_task_copy,
                     ( GFunc ) vfs_file_task_move,  /* trash */
                     ( GFunc ) vfs_file_task_delete,
                     ( GFunc ) vfs_file_task_link,
                     ( GFunc ) vfs_file_task_chown_chmod};

    if ( task->type < VFS_FILE_TASK_MOVE
            || task->type >= VFS_FILE_TASK_LAST )
        goto _exit_thread;

    task->state = VFS_FILE_TASK_RUNNING;
    task->current_file = ( char* ) task->src_paths->data;
    task->total_size = 0;

    if( task->type == VFS_FILE_TASK_TRASH )
    {
        task->dest_dir = g_build_filename( vfs_get_trash_dir(), "files", NULL );
        /* Create the trash dir if it doesn't exist */
        if( ! g_file_test( task->dest_dir, G_FILE_TEST_EXISTS ) )
            g_mkdir_with_parents( task->dest_dir, 0700 );
    }

    call_progress_callback( task );

    /* Calculate total size of all files */
    if ( task->recursive )
    {
        for ( l = task->src_paths; l; l = l->next )
        {
            if( task->dest_dir && g_str_has_prefix( task->dest_dir, (char*)l->data ) )
            {
                /* FIXME: This has serious problems */
                /* Check if source file is contained in destination dir */
                /* The src file is already in dest dir */
                if( lstat( (char*)l->data, &file_stat ) == 0
                    && S_ISDIR(file_stat.st_mode) )
                {
                    /* It's a dir */
                    if ( task->state_cb )
                    {
                        char* err;
                        char* disp_src;
                        char* disp_dest;
                        disp_src = g_filename_display_name( (char*)l->data );
                        disp_dest = g_filename_display_name( task->dest_dir );
                        err = g_strdup_printf( _("Destination directory \"%1$s\" is contained in source \"%2$s\""), disp_dest, disp_src );
                        if ( task->state_cb( task, VFS_FILE_TASK_ERROR,
                             err, task->state_cb_data ) )
                            task->state = VFS_FILE_TASK_RUNNING;
                        else
                            task->state = VFS_FILE_TASK_ABORTED;
                        g_free( disp_src );
                        g_free( disp_dest );
                        g_free( err );
                    }
                    else
                        task->state = VFS_FILE_TASK_ABORTED;
                    if ( should_abort( task ) )
                        goto _exit_thread;
                }
            }
            get_total_size_of_dir( task, ( char* ) l->data, &task->total_size );
        }
    }
    else
    {
        if ( task->type != VFS_FILE_TASK_CHMOD_CHOWN )
        {
            if ( task->dest_dir &&
                    lstat( task->dest_dir, &file_stat ) < 0 )
            {
                task->error = errno;
                call_state_callback( task, VFS_FILE_TASK_ERROR );
                if ( should_abort( task ) )
                    goto _exit_thread;
            }
            dest_dev = file_stat.st_dev;
        }

        for ( l = task->src_paths; l; l = l->next )
        {
            if ( lstat( ( char* ) l->data, &file_stat ) == -1 )
            {
                task->error = errno;
                call_state_callback( task, VFS_FILE_TASK_ERROR );
                if ( should_abort( task ) )
                    goto _exit_thread;
            }
            if ( S_ISLNK( file_stat.st_mode ) )      /* Don't do deep copy for symlinks */
                task->recursive = FALSE;
            else if ( task->type == VFS_FILE_TASK_MOVE || task->type == VFS_FILE_TASK_TRASH )
                task->recursive = ( file_stat.st_dev != dest_dev );

            if ( task->recursive )
            {
                get_total_size_of_dir( task, ( char* ) l->data,
                                       &task->total_size );
            }
            else
            {
                task->total_size += file_stat.st_size;
            }
        }
    }

    g_list_foreach( task->src_paths,
                    funcs[ task->type ],
                    task );

_exit_thread:
    if ( task->state_cb )
        call_state_callback( task, VFS_FILE_TASK_FINISH );
    else
    {
        /* FIXME: Will this cause any problem? */
        task->thread = NULL;
        vfs_file_task_free( task );
    }
    return NULL;
}
Пример #8
0
static void
vfs_file_task_chown_chmod( char* src_file, VFSFileTask* task )
{
    struct stat src_stat;
    int i;
    GDir* dir;
    gchar* sub_src_file;
    const gchar* file_name;
    mode_t new_mode;

    int result;

    if( should_abort( task ) )
        return ;
    task->current_file = src_file;
    /* g_debug("chmod_chown: %s\n", src_file); */
    call_progress_callback( task );

    if ( lstat( src_file, &src_stat ) == 0 )
    {
        /* chown */
        if ( task->uid != -1 || task->gid != -1 )
        {
            result = chown( src_file, task->uid, task->gid );
            if ( result != 0 )
            {
                task->error = errno;
                call_state_callback( task, VFS_FILE_TASK_ERROR );
                if ( should_abort( task ) )
                    return ;
            }
        }

        /* chmod */
        if ( task->chmod_actions )
        {
            new_mode = src_stat.st_mode;
            for ( i = 0; i < N_CHMOD_ACTIONS; ++i )
            {
                if ( task->chmod_actions[ i ] == 2 )            /* Don't change */
                    continue;
                if ( task->chmod_actions[ i ] == 0 )            /* Remove this bit */
                    new_mode &= ~chmod_flags[ i ];
                else  /* Add this bit */
                    new_mode |= chmod_flags[ i ];
            }
            if ( new_mode != src_stat.st_mode )
            {
                result = chmod( src_file, new_mode );
                if ( result != 0 )
                {
                    task->error = errno;
                    call_state_callback( task, VFS_FILE_TASK_ERROR );
                    if ( should_abort( task ) )
                        return ;
                }
            }
        }

        task->progress += src_stat.st_size;
        call_progress_callback( task );

        if ( S_ISDIR( src_stat.st_mode ) && task->recursive )
        {
            if ( (dir = g_dir_open( src_file, 0, NULL )) )
            {
                while ( (file_name = g_dir_read_name( dir )) )
                {
                    if ( should_abort( task ) )
                        break;
                    sub_src_file = g_build_filename( src_file, file_name, NULL );
                    vfs_file_task_chown_chmod( sub_src_file, task );
                    g_free( sub_src_file );
                }
                g_dir_close( dir );
            }
            else
            {
                task->error = errno;
                call_state_callback( task, VFS_FILE_TASK_ERROR );
                if ( should_abort( task ) )
                    return ;
            }
        }
    }
}
Пример #9
0
static void
vfs_file_task_delete( char* src_file, VFSFileTask* task )
{
    GDir * dir;
    const gchar* file_name;
    gchar* sub_src_file;
    struct stat file_stat;
    int result;

    task->current_file = src_file;

    if ( should_abort( task ) )
        return ;

    if ( lstat( src_file, &file_stat ) == -1 )
    {
        task->error = errno;
        call_state_callback( task, VFS_FILE_TASK_ERROR );
        return ; /* Error occurred */
    }

    task->current_file = src_file;
    call_progress_callback( task );

    if ( S_ISDIR( file_stat.st_mode ) )
    {
        dir = g_dir_open( src_file, 0, NULL );
        while ( (file_name = g_dir_read_name( dir )) )
        {
            if ( should_abort( task ) )
                break;
            sub_src_file = g_build_filename( src_file, file_name, NULL );
            vfs_file_task_delete( sub_src_file, task );
            g_free( sub_src_file );
        }
        g_dir_close( dir );
        if ( should_abort( task ) )
            return ;
        result = rmdir( src_file );
        if ( result != 0 )
        {
            task->error = errno;
            call_state_callback( task, VFS_FILE_TASK_ERROR );
            return ;
        }

        task->progress += file_stat.st_size;
        call_progress_callback( task );
    }
    else
    {
        result = unlink( src_file );
        if ( result != 0 )
        {
            task->error = errno;
            call_state_callback( task, VFS_FILE_TASK_ERROR );
            return ;
        }

        task->progress += file_stat.st_size;
        call_progress_callback( task );
    }
}
Пример #10
0
static void
vfs_file_task_do_copy( VFSFileTask* task,
                       const char* src_file,
                       const char* dest_file )
{
    GDir * dir;
    const gchar* file_name;
    gchar* sub_src_file;
    gchar* sub_dest_file;
    struct stat file_stat;
    char buffer[ 4096 ];
    int rfd;
    int wfd;
    ssize_t rsize;
    char* new_dest_file = NULL;
    gboolean dest_exists;
    int result;

    if ( should_abort( task ) )
        return ;
    if ( lstat( src_file, &file_stat ) == -1 )
    {
        /* Error occurred */
        call_state_callback( task, VFS_FILE_TASK_ERROR );
        if ( should_abort( task ) )
            return ;
    }

    task->current_file = src_file;
    task->current_dest = dest_file;
    call_progress_callback( task );

    result = 0;
    if ( S_ISDIR( file_stat.st_mode ) )
    {
        if ( ! check_overwrite( task, dest_file,
                                &dest_exists, &new_dest_file ) )
            goto _return_;

        if ( new_dest_file )
            task->current_dest = dest_file = new_dest_file;

        if ( ! dest_exists )
            result = mkdir( dest_file, file_stat.st_mode | 0700 );

        if ( result == 0 )
        {
            struct utimbuf times;
            task->progress += file_stat.st_size;
            call_progress_callback( task );

            dir = g_dir_open( src_file, 0, NULL );
            while ( (file_name = g_dir_read_name( dir )) )
            {
                if ( should_abort( task ) )
                    break;
                sub_src_file = g_build_filename( src_file, file_name, NULL );
                sub_dest_file = g_build_filename( dest_file, file_name, NULL );
                vfs_file_task_do_copy( task, sub_src_file, sub_dest_file );
                g_free( sub_dest_file );
                g_free( sub_src_file );
            }
            g_dir_close( dir );
            chmod( dest_file, file_stat.st_mode );
            times.actime = file_stat.st_atime;
            times.modtime = file_stat.st_mtime;
            utime( dest_file, &times );
            /* Move files to different device: Need to delete source files */
            if ( ( task->type == VFS_FILE_TASK_MOVE
                 || task->type == VFS_FILE_TASK_TRASH )
                 && !should_abort( task ) )
            {
                if ( (result = rmdir( src_file )) )
                {
                    task->error = errno;
                    call_state_callback( task, VFS_FILE_TASK_ERROR );
                    if ( should_abort( task ) )
                        goto _return_;
                }
            }
        }
        else
        {  /* result != 0, error occurred */
            task->error = errno;
            call_state_callback( task, VFS_FILE_TASK_ERROR );
        }
    }
    else if ( S_ISLNK( file_stat.st_mode ) )
    {
        if ( ( rfd = readlink( src_file, buffer, sizeof( buffer ) ) ) > 0 )
        {
            if ( ! check_overwrite( task, dest_file,
                                    &dest_exists, &new_dest_file ) )
                goto _return_;

            if ( new_dest_file )
                task->current_dest = dest_file = new_dest_file;

            if ( ( wfd = symlink( buffer, dest_file ) ) == 0 )
            {
                chmod( dest_file, file_stat.st_mode );
                /* Move files to different device: Need to delete source files */
                if( task->type == VFS_FILE_TASK_MOVE || task->type == VFS_FILE_TASK_TRASH )
                {
                    result = unlink( src_file );
                    if ( result )
                    {
                        task->error = errno;
                        call_state_callback( task, VFS_FILE_TASK_ERROR );
                    }
                }
                task->progress += file_stat.st_size;
                call_progress_callback( task );
            }
            else
            {
                task->error = errno;
                call_state_callback( task, VFS_FILE_TASK_ERROR );
            }
        }
        else
        {
            task->error = errno;
            call_state_callback( task, VFS_FILE_TASK_ERROR );
        }
    }
    else
    {
        if ( ( rfd = open( src_file, O_RDONLY ) ) >= 0 )
        {
            if ( ! check_overwrite( task, dest_file,
                                    &dest_exists, &new_dest_file ) )
                goto _return_;

            if ( new_dest_file )
                task->current_dest = dest_file = new_dest_file;

            if ( ( wfd = creat( dest_file,
                                file_stat.st_mode | S_IWUSR ) ) >= 0 )
            {
                struct utimbuf times;
                while ( ( rsize = read( rfd, buffer, sizeof( buffer ) ) ) > 0 )
                {
                    if ( should_abort( task ) )
                        break;

                    if ( write( wfd, buffer, rsize ) > 0 )
                        task->progress += rsize;
                    else
                    {
                        task->error = errno;
                        call_state_callback( task, VFS_FILE_TASK_ERROR );
                        close( wfd );
                    }
                    call_progress_callback( task );
                }
                close( wfd );
                chmod( dest_file, file_stat.st_mode );
                times.actime = file_stat.st_atime;
                times.modtime = file_stat.st_mtime;
                utime( dest_file, &times );

                /* Move files to different device: Need to delete source files */
                if ( (task->type == VFS_FILE_TASK_MOVE || task->type == VFS_FILE_TASK_TRASH)
                     && !should_abort( task ) )
                {
                    result = unlink( src_file );
                    if ( result )
                    {
                        task->error = errno;
                        call_state_callback( task, VFS_FILE_TASK_ERROR );
                    }
                }
            }
            else
            {
                task->error = errno;
                call_state_callback( task, VFS_FILE_TASK_ERROR );
            }
            close( rfd );
        }
        else
        {
            task->error = errno;
            call_state_callback( task, VFS_FILE_TASK_ERROR );
        }
    }
_return_:
    g_free( new_dest_file );
}
Пример #11
0
/*
* Check if the destination file exists.
* If the dest_file exists, let the user choose a new destination,
* skip this file, overwrite existing file, or cancel all file operation.
* The returned string is the new destination file choosed by the user
*/
static
gboolean check_overwrite( VFSFileTask* task,
                          const gchar* dest_file,
                          gboolean* dest_exists,
                          char** new_dest_file )
{
    char * new_dest;
    new_dest = *new_dest_file = NULL;
    struct stat dest_stat;

    if ( task->overwrite_mode == VFS_FILE_TASK_OVERWRITE_ALL )
    {
        *dest_exists = !lstat( dest_file, &dest_stat );
        return TRUE;
    }
    if ( task->overwrite_mode == VFS_FILE_TASK_SKIP_ALL )
    {
        *dest_exists = !lstat( dest_file, &dest_stat );
        return FALSE;
    }

    *dest_exists = FALSE;
    if ( task->state_cb )
    {
        while ( lstat( dest_file, &dest_stat ) != -1 )
        {
            *dest_exists = TRUE;
            /* destination file exists */
            task->state = VFS_FILE_TASK_QUERY_OVERWRITE;

            if ( ! task->state_cb( task, VFS_FILE_TASK_QUERY_OVERWRITE,
                                   &new_dest, task->state_cb_data ) )
            {
                task->state = VFS_FILE_TASK_ABORTED;
            }
            else
            {
                task->state = VFS_FILE_TASK_RUNNING;
            }

            if ( should_abort( task ) )
                return FALSE;
            if( task->overwrite_mode != VFS_FILE_TASK_RENAME )
            {
                g_free( new_dest );
                *new_dest_file = NULL;
                if( task->overwrite_mode == VFS_FILE_TASK_OVERWRITE ||
                    task->overwrite_mode == VFS_FILE_TASK_OVERWRITE_ALL )
                {
                    return TRUE;
                }
                else
                    return FALSE;
            }
            if ( new_dest )
                dest_file = new_dest;
        }
        *dest_exists = FALSE;
        *new_dest_file = new_dest;
    }
    return ! should_abort( task );
}