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; }
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); } }
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) ); }
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)); }
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); }
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)); }
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; }
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); }
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); }
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)); }
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; }
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; }
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; }
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; }
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); }
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; }
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; }
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; }
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; }
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; }
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); }
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)); }
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; }
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; }
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); }
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; }
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; }
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; }
/* 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))); }