Ejemplo n.º 1
0
static ERL_NIF_TERM pwrite_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    ErlNifIOVec vec, *input = &vec;
    Sint64 bytes_written, offset;
    ERL_NIF_TERM tail;

    if(argc != 2 || !enif_is_number(env, argv[0])
                 || !enif_inspect_iovec(env, 64, argv[1], &tail, &input)) {
        return enif_make_badarg(env);
    }

    if(!enif_get_int64(env, argv[0], &offset) || offset < 0) {
        return posix_error_to_tuple(env, EINVAL);
    }

    bytes_written = efile_pwritev(d, offset, input->iov, input->iovcnt);

    if(bytes_written < 0) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    if(!enif_is_empty_list(env, tail)) {
        ASSERT(bytes_written > 0);
        return enif_make_tuple3(env, am_continue,
            enif_make_int64(env, bytes_written), tail);
    }

    return am_ok;
}
Ejemplo n.º 2
0
static ERL_NIF_TERM close_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    enum efile_state_t previous_state;
    efile_data_t *d;

    ASSERT(argc == 1);
    if(!get_file_data(env, argv[0], &d)) {
        return enif_make_badarg(env);
    }

    previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
        EFILE_STATE_CLOSED, EFILE_STATE_IDLE);

    if(previous_state == EFILE_STATE_IDLE) {
        posix_errno_t error;

        enif_demonitor_process(env, d, &d->monitor);

        if(!efile_close(d, &error)) {
            return posix_error_to_tuple(env, error);
        }

        return am_ok;
    } else {
        /* CLOSE_PENDING should be impossible at this point since it requires
         * a transition from BUSY; the only valid state here is CLOSED. */
        ASSERT(previous_state == EFILE_STATE_CLOSED);

        return posix_error_to_tuple(env, EINVAL);
    }
}
Ejemplo n.º 3
0
static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_fileinfo_t info = {0};
    efile_path_t path;
    int follow_links;

    if(argc != 2 || !enif_get_int(env, argv[1], &follow_links)) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_read_info(&path, follow_links, &info))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    /* #file_info as declared in file.hrl */
    return enif_make_tuple(env, 14,
        am_file_info,
        enif_make_uint64(env, info.size),
        efile_filetype_to_atom(info.type),
        efile_access_to_atom(info.access),
        enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.a_time)),
        enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.m_time)),
        enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.c_time)),
        enif_make_uint(env, info.mode),
        enif_make_uint(env, info.links),
        enif_make_uint(env, info.major_device),
        enif_make_uint(env, info.minor_device),
        enif_make_uint(env, info.inode),
        enif_make_uint(env, info.uid),
        enif_make_uint(env, info.gid)
    );
}
Ejemplo n.º 4
0
static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno, ignored;

    efile_fileinfo_t info = {0};
    efile_path_t path;
    efile_data_t *d;

    ErlNifBinary result;

    ASSERT(argc == 1);

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_read_info(&path, 1, &info))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_open(&path, EFILE_MODE_READ, efile_resource_type, &d))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    posix_errno = read_file(d, info.size, &result);

    erts_atomic32_set_acqb(&d->state, EFILE_STATE_CLOSED);
    efile_close(d, &ignored);

    if(posix_errno) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return enif_make_tuple2(env, am_ok, enif_make_binary(env, &result));
}
Ejemplo n.º 5
0
static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;
    efile_data_t *d;

    ErlNifPid controlling_process;
    enum efile_modes_t modes;
    ERL_NIF_TERM result;
    efile_path_t path;

    if(argc != 2 || !enif_is_list(env, argv[1])) {
        return enif_make_badarg(env);
    }

    modes = efile_translate_modelist(env, argv[1]);

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_open(&path, modes, efile_resource_type, &d))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    result = enif_make_resource(env, d);
    enif_release_resource(d);

    enif_self(env, &controlling_process);

    if(enif_monitor_process(env, d, &controlling_process, &d->monitor)) {
        return posix_error_to_tuple(env, EINVAL);
    }

    return enif_make_tuple2(env, am_ok, result);
}
Ejemplo n.º 6
0
static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_fileinfo_t info = {0};
    efile_path_t path;
    efile_data_t *d;

    ErlNifBinary result;

    if(argc != 1) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_read_info(&path, 1, &info))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_open(&path, EFILE_MODE_READ, efile_resource_type, &d))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    posix_errno = read_file(d, info.size, &result);
    enif_release_resource(d);

    if(posix_errno) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return enif_make_tuple2(env, am_ok, enif_make_binary(env, &result));
}
Ejemplo n.º 7
0
static ERL_NIF_TERM set_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t path;

    ASSERT(argc == 1);

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_set_cwd(&path))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 8
0
static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;
    efile_data_t *d;

    ErlNifPid controlling_process;
    enum efile_modes_t modes;
    ERL_NIF_TERM result;
    efile_path_t path;

    ASSERT(argc == 2);
    if(!enif_is_list(env, argv[1])) {
        return enif_make_badarg(env);
    }

    modes = efile_translate_modelist(env, argv[1]);

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_open(&path, modes, efile_resource_type, &d))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    enif_self(env, &controlling_process);

    if(enif_monitor_process(env, d, &controlling_process, &d->monitor)) {
        /* We need to close the file manually as we haven't registered a
         * destructor. */
        posix_errno_t ignored;

        erts_atomic32_set_acqb(&d->state, EFILE_STATE_CLOSED);
        efile_close(d, &ignored);

        return posix_error_to_tuple(env, EINVAL);
    }

    /* Note that we do not call enif_release_resource at this point. While it's
     * normally safe to leave resource management to the GC, efile_close is a
     * blocking operation which must not be done in the GC callback, and we
     * can't defer it as the resource is gone as soon as it returns.
     *
     * We instead keep the resource alive until efile_close is called, after
     * which it's safe to leave things to the GC. If the controlling process
     * were to die before the user had a chance to close their file, the above
     * monitor will tell the erts_prim_file process to close it for them. */
    result = enif_make_resource(env, d);

    return enif_make_tuple2(env, am_ok, result);
}
Ejemplo n.º 9
0
static ERL_NIF_TERM read_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t path;
    ERL_NIF_TERM result;

    ASSERT(argc == 1);

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_read_link(env, &path, &result))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return enif_make_tuple2(env, am_ok, result);
}
Ejemplo n.º 10
0
static ERL_NIF_TERM seek_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    Sint64 new_position, offset;
    enum efile_seek_t seek;

    ASSERT(argc == 2);
    if(!enif_get_int64(env, argv[1], &offset)) {
        return enif_make_badarg(env);
    }

    if(enif_is_identical(argv[0], am_bof)) {
        seek = EFILE_SEEK_BOF;
    } else if(enif_is_identical(argv[0], am_cur)) {
        seek = EFILE_SEEK_CUR;
    } else if(enif_is_identical(argv[0], am_eof)) {
        seek = EFILE_SEEK_EOF;
    } else {
        return enif_make_badarg(env);
    }

    if(!efile_seek(d, seek, offset, &new_position)) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    return enif_make_tuple2(env, am_ok, enif_make_uint64(env, new_position));
}
Ejemplo n.º 11
0
static ERL_NIF_TERM make_soft_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t existing_path, new_path;

    ASSERT(argc == 2);

    if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_marshal_path(env, argv[1], &new_path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_make_soft_link(&existing_path, &new_path))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 12
0
static ERL_NIF_TERM del_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t path;

    if(argc != 1) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_del_dir(&path))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 13
0
static ERL_NIF_TERM truncate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    ASSERT(argc == 0);

    if(!efile_truncate(d)) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 14
0
static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t path;
    Uint32 permissions;

    if(argc != 2 || !enif_get_uint(env, argv[1], &permissions)) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_set_permissions(&path, permissions))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 15
0
static ERL_NIF_TERM list_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t path;
    ERL_NIF_TERM result;

    if(argc != 1) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_list_dir(env, &path, &result))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return enif_make_tuple2(env, am_ok, result);
}
Ejemplo n.º 16
0
static ERL_NIF_TERM rename_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t existing_path, new_path;

    if(argc != 2) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_marshal_path(env, argv[1], &new_path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_rename(&existing_path, &new_path))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 17
0
static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    efile_path_t path;
    Sint32 uid, gid;

    if(argc != 3 || !enif_get_int(env, argv[1], &uid)
                 || !enif_get_int(env, argv[2], &gid)) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_set_owner(&path, uid, gid))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 18
0
static ERL_NIF_TERM set_time_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    Sint64 accessed, modified, created;
    efile_path_t path;

    if(argc != 4 || !enif_get_int64(env, argv[1], &accessed)
                 || !enif_get_int64(env, argv[2], &modified)
                 || !enif_get_int64(env, argv[3], &created)) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_marshal_path(env, argv[0], &path))) {
        return posix_error_to_tuple(env, posix_errno);
    } else if((posix_errno = efile_set_time(&path, accessed, modified, created))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 19
0
static ERL_NIF_TERM allocate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    Sint64 offset, length;

    if(argc != 2 || !enif_is_number(env, argv[0])
                 || !enif_is_number(env, argv[1])) {
        return enif_make_badarg(env);
    }

    if(!enif_get_int64(env, argv[0], &offset) ||
       !enif_get_int64(env, argv[1], &length) ||
       (offset < 0 || length < 0)) {
        return posix_error_to_tuple(env, EINVAL);
    }

    if(!efile_allocate(d, offset, length)) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 20
0
static ERL_NIF_TERM truncate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    if(argc != 0) {
        return enif_make_badarg(env);
    }

    if(!efile_truncate(d)) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 21
0
static ERL_NIF_TERM get_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;
    ERL_NIF_TERM result;

    ASSERT(argc == 0);

    if((posix_errno = efile_get_cwd(env, &result))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return enif_make_tuple2(env, am_ok, result);
}
Ejemplo n.º 22
0
static ERL_NIF_TERM pread_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    Sint64 bytes_read, block_size, offset;
    SysIOVec read_vec[1];
    ErlNifBinary result;

    ASSERT(argc == 2);
    if(!enif_is_number(env, argv[0]) || !enif_is_number(env, argv[1])) {
        return enif_make_badarg(env);
    }

    if(!enif_get_int64(env, argv[0], &offset) ||
       !enif_get_int64(env, argv[1], &block_size) ||
       (offset < 0 || block_size < 0)) {
        return posix_error_to_tuple(env, EINVAL);
    }

    if(!enif_alloc_binary(block_size, &result)) {
        return posix_error_to_tuple(env, ENOMEM);
    }

    read_vec[0].iov_base = result.data;
    read_vec[0].iov_len = result.size;

    bytes_read = efile_preadv(d, offset, read_vec, 1);

    if(bytes_read < 0) {
        enif_release_binary(&result);
        return posix_error_to_tuple(env, d->posix_errno);
    } else if(bytes_read == 0) {
        enif_release_binary(&result);
        return am_eof;
    }

    if(bytes_read < block_size && !enif_realloc_binary(&result, bytes_read)) {
        ERTS_INTERNAL_ERROR("Failed to shrink pread result.");
    }

    return enif_make_tuple2(env, am_ok, enif_make_binary(env, &result));
}
Ejemplo n.º 23
0
static ERL_NIF_TERM sync_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    int data_only;

    if(argc != 1 || !enif_get_int(env, argv[0], &data_only)) {
        return enif_make_badarg(env);
    }

    if(!efile_sync(d, data_only)) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 24
0
static ERL_NIF_TERM advise_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    enum efile_advise_t advise;
    Sint64 offset, length;

    if(argc != 3 || !enif_is_number(env, argv[0])
                 || !enif_is_number(env, argv[1])) {
        return enif_make_badarg(env);
    }

    if(!enif_get_int64(env, argv[0], &offset) ||
       !enif_get_int64(env, argv[1], &length) ||
       (offset < 0 || length < 0)) {
        return posix_error_to_tuple(env, EINVAL);
    }

    if(enif_is_identical(argv[2], am_normal)) {
        advise = EFILE_ADVISE_NORMAL;
    } else if(enif_is_identical(argv[2], am_random)) {
        advise = EFILE_ADVISE_RANDOM;
    } else if(enif_is_identical(argv[2], am_sequential)) {
        advise = EFILE_ADVISE_SEQUENTIAL;
    } else if(enif_is_identical(argv[2], am_will_need)) {
        advise = EFILE_ADVISE_WILL_NEED;
    } else if(enif_is_identical(argv[2], am_dont_need)) {
        advise = EFILE_ADVISE_DONT_NEED;
    } else if(enif_is_identical(argv[2], am_no_reuse)) {
        advise = EFILE_ADVISE_NO_REUSE;
    } else {
        /* The tests check for EINVAL instead of badarg. Sigh. */
        return posix_error_to_tuple(env, EINVAL);
    }

    if(!efile_advise(d, offset, length, advise)) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    return am_ok;
}
Ejemplo n.º 25
0
static ERL_NIF_TERM get_device_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    posix_errno_t posix_errno;

    ERL_NIF_TERM result;
    int device_index;

    if(argc != 1 || !enif_get_int(env, argv[0], &device_index)) {
        return enif_make_badarg(env);
    }

    if((posix_errno = efile_get_device_cwd(env, device_index, &result))) {
        return posix_error_to_tuple(env, posix_errno);
    }

    return enif_make_tuple2(env, am_ok, result);
}
Ejemplo n.º 26
0
static ERL_NIF_TERM file_handle_wrapper(file_op_impl_t operation, ErlNifEnv *env,
        int argc, const ERL_NIF_TERM argv[]) {

    efile_data_t *d;

    enum efile_state_t previous_state;
    ERL_NIF_TERM result;

    if(argc < 1 || !get_file_data(env, argv[0], &d)) {
        return enif_make_badarg(env);
    }

    previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
        EFILE_STATE_BUSY, EFILE_STATE_IDLE);

    if(previous_state == EFILE_STATE_IDLE) {
        result = operation(d, env, argc - 1, &argv[1]);

        previous_state = erts_atomic32_cmpxchg_relb(&d->state,
            EFILE_STATE_IDLE, EFILE_STATE_BUSY);

        ASSERT(previous_state != EFILE_STATE_IDLE);

        if(previous_state == EFILE_STATE_CLOSE_PENDING) {
            /* This is the only point where a change from CLOSE_PENDING is
             * possible, and we're running synchronously, so we can't race with
             * anything else here. */
            posix_errno_t ignored;

            erts_atomic32_set_acqb(&d->state, EFILE_STATE_CLOSED);
            efile_close(d, &ignored);
        }
    } else {
        /* CLOSE_PENDING should be impossible at this point since it requires
         * a transition from BUSY; the only valid state here is CLOSED. */
        ASSERT(previous_state == EFILE_STATE_CLOSED);

        result = posix_error_to_tuple(env, EINVAL);
    }

    return result;
}
Ejemplo n.º 27
0
static ERL_NIF_TERM close_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    enum efile_state_t previous_state;

    ASSERT(argc == 0);

    previous_state = erts_atomic32_cmpxchg_acqb(&d->state,
        EFILE_STATE_CLOSED, EFILE_STATE_BUSY);

    ASSERT(previous_state == EFILE_STATE_CLOSE_PENDING ||
        previous_state == EFILE_STATE_BUSY);

    if(previous_state == EFILE_STATE_BUSY) {
        enif_demonitor_process(env, d, &d->monitor);

        if(!efile_close(d)) {
            return posix_error_to_tuple(env, d->posix_errno);
        }
    }

    return am_ok;
}
Ejemplo n.º 28
0
static ERL_NIF_TERM write_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    ErlNifIOVec vec, *input = &vec;
    Sint64 bytes_written;
    ERL_NIF_TERM tail;

    if(argc != 1 || !enif_inspect_iovec(env, 64, argv[0], &tail, &input)) {
        return enif_make_badarg(env);
    }

    bytes_written = efile_writev(d, input->iov, input->iovcnt);

    if(bytes_written < 0) {
        return posix_error_to_tuple(env, d->posix_errno);
    }

    if(!enif_is_empty_list(env, tail)) {
        ASSERT(bytes_written > 0);
        return enif_make_tuple2(env, am_continue, tail);
    }

    return am_ok;
}
Ejemplo n.º 29
0
/* This undocumented function reads a pointer and then reads the data block
 * described by said pointer. It was reverse-engineered from the old
 * implementation so while all tests pass it may not be entirely correct. Our
 * current understanding is as follows:
 *
 * Pointer layout:
 *
 *     <<Size:1/integer-unit:32, Offset:1/integer-unit:32>>
 *
 * Where Offset is the -absolute- address to the data block.
 *
 * *) If we fail to read the pointer block in its entirety, we return eof.
 * *) If the provided max_payload_size is larger than Size, we return eof.
 * *) If we fail to read any data whatsoever at Offset, we return
 *    {ok, {Size, Offset, eof}}
 * *) Otherwise, we return {ok, {Size, Offset, Data}}. Note that the size
 *    of Data may be smaller than Size if we encounter EOF before we could
 *    read the entire block.
 *
 * On errors we'll return {error, posix()} regardless of whether they
 * happened before or after reading the pointer block. */
static ERL_NIF_TERM ipread_s32bu_p32bu_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
    Sint64 payload_offset, payload_size;

    SysIOVec read_vec[1];
    Sint64 bytes_read;

    ErlNifBinary payload;

    if(argc != 2 || !enif_is_number(env, argv[0])
                 || !enif_is_number(env, argv[1])) {
        return enif_make_badarg(env);
    }

    {
        Sint64 max_payload_size, pointer_offset;
        unsigned char pointer_block[8];

        if(!enif_get_int64(env, argv[0], &pointer_offset) ||
           !enif_get_int64(env, argv[1], &max_payload_size) ||
           (pointer_offset < 0 || max_payload_size >= 1u << 31)) {
            return posix_error_to_tuple(env, EINVAL);
        }

        read_vec[0].iov_base = pointer_block;
        read_vec[0].iov_len = sizeof(pointer_block);

        bytes_read = efile_preadv(d, pointer_offset, read_vec, 1);

        if(bytes_read < 0) {
            return posix_error_to_tuple(env, d->posix_errno);
        } else if(bytes_read < sizeof(pointer_block)) {
            return am_eof;
        }

        payload_size = (Uint32)get_int32(&pointer_block[0]);
        payload_offset = (Uint32)get_int32(&pointer_block[4]);

        if(payload_size > max_payload_size) {
            return am_eof;
        }
    }

    if(!enif_alloc_binary(payload_size, &payload)) {
        return posix_error_to_tuple(env, ENOMEM);
    }

    read_vec[0].iov_base = payload.data;
    read_vec[0].iov_len = payload.size;

    bytes_read = efile_preadv(d, payload_offset, read_vec, 1);

    if(bytes_read < 0) {
        return posix_error_to_tuple(env, d->posix_errno);
    } else if(bytes_read == 0) {
        enif_release_binary(&payload);

        return enif_make_tuple2(env, am_ok,
                enif_make_tuple3(env,
                    enif_make_uint(env, payload_size),
                    enif_make_uint(env, payload_offset),
                    am_eof));
    }

    if(bytes_read < payload.size && !enif_realloc_binary(&payload, bytes_read)) {
        ERTS_INTERNAL_ERROR("Failed to shrink ipread payload.");
    }

    return enif_make_tuple2(env, am_ok,
        enif_make_tuple3(env,
            enif_make_uint(env, payload_size),
            enif_make_uint(env, payload_offset),
            enif_make_binary(env, &payload)));
}