When binding and listening on some socket id, the accept call on that socket creates a new descriptor.
That descriptor is not the same as the original socket id.
Add a new id_data to the socket structure to represent the data transfer file descriptor that is created via the accept() call.
Add a new f_file_close_id() to close the file by the id in case the f_file_t is not available or in use.
if (file->id == -1) return F_file_descriptor_not;
- return private_f_file_close(file);
+ return private_f_file_close(&file->id);
}
#endif // _di_f_file_close_
+#ifndef _di_f_file_close_id_
+ f_status_t f_file_close_id(int * const id) {
+ #ifndef _di_level_0_parameter_checking_
+ if (!id) return F_status_set_error(F_parameter);
+ #endif // _di_level_0_parameter_checking_
+
+ if (*id == -1) return F_file_descriptor_not;
+
+ return private_f_file_close(id);
+ }
+#endif // _di_f_file_close_id_
+
#ifndef _di_f_file_copy_
f_status_t f_file_copy(const f_string_static_t source, const f_string_static_t destination, const f_mode_t mode, const f_number_unsigned_t size_block, const uint8_t flag) {
#endif // _di_f_file_close_
/**
+ * Close an open file directly by the file descriptor.
+ *
+ * Will not flush before closing.
+ *
+ * @param id
+ * The file descriptor.
+ *
+ * @return
+ * F_none on success.
+ * F_file_descriptor_not if id is -1.
+ *
+ * F_file_close (with error bit) if fclose() failed for any other reason.
+ * F_file_descriptor (with error bit) if file descriptor is invalid.
+ * F_file_synchronize (with error bit) on flush failure.
+ * F_filesystem_quota_block (with error bit) if file system's disk blocks or inodes are exhausted.
+ * F_input_output (with error bit) on I/O error.
+ * F_interrupt (with error bit) when program received an interrupt signal, halting operation.
+ * F_space_not (with error bit) if file system is out of space (or file system quota is reached).
+ *
+ * @see fclose()
+ */
+#ifndef _di_f_file_close_id_
+ extern f_status_t f_file_close_id(int * const id);
+#endif // _di_f_file_close_id_
+
+/**
* Copy a file.
*
* The paths must not contain NULL except for the terminating NULL.
#endif // _di_f_file_size_at_
/**
- * Read size of a file relative to the path represented by the file descriptor id.
+ * Read size of a file relative to the path represented by the file descriptor.
*
* @param file
* The file.
#endif // _di_f_file_stat_
/**
- * Read statistics of a file relative to the path represented by the file descriptor id.
+ * Read statistics of a file relative to the path represented by the file descriptor.
*
* @param directory
* The parent directory, via an open directory file descriptor, in which path is relative to.
#endif // _di_f_file_stat_at_
/**
- * Read statistics of a file using a file descriptor id.
+ * Read statistics of a file using a file descriptor.
*
* @param file
* The file.
#endif
#if !defined(_di_f_file_clone_) || !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) || !defined(_di_f_file_create_) || !defined(_di_f_file_create_at_) || !defined(_di_f_file_stream_close_)
- f_status_t private_f_file_close(f_file_t * const file) {
+ f_status_t private_f_file_close(int * const id) {
- if (close(file->id) < 0) {
+ if (close(*id) < 0) {
// According to man pages, retrying close() after another close on error is invalid on Linux because Linux releases the descriptor before stages that cause failures.
if (errno != EBADF && errno != EINTR) {
- file->id = -1;
+ *id = -1;
}
if (errno == EBADF) return F_status_set_error(F_file_descriptor);
return F_status_set_error(F_file_close);
}
- file->id = -1;
+ *id = -1;
return F_none;
}
if (F_status_is_error(status)) {
private_f_file_flush(file_source);
- private_f_file_close(&file_source);
+ private_f_file_close(&file_source.id);
return status;
}
private_f_file_flush(file_destination);
private_f_file_flush(file_source);
- private_f_file_close(&file_destination);
- private_f_file_close(&file_source);
+ private_f_file_close(&file_destination.id);
+ private_f_file_close(&file_source.id);
return F_status_set_error(F_file_write);
}
private_f_file_flush(file_destination);
private_f_file_flush(file_source);
- private_f_file_close(&file_destination);
- private_f_file_close(&file_source);
+ private_f_file_close(&file_destination.id);
+ private_f_file_close(&file_source.id);
if (size_read < 0) return F_status_set_error(F_file_read);
if (F_status_is_error_not(status) && file.id != -1) {
private_f_file_flush(file);
- status = private_f_file_close(&file);
+ status = private_f_file_close(&file.id);
}
if (F_status_is_error(status)) return status;
if (F_status_is_error_not(status) && file_internal.id != -1) {
private_f_file_flush(file_internal);
- status = private_f_file_close(&file_internal);
+ status = private_f_file_close(&file_internal.id);
}
if (F_status_is_error(status)) return status;
*
* Intended to be shared to each of the different implementation variations.
*
- * @param file
- * The file to close.
+ * @param id
+ * The file descriptor to close.
* The file descriptor gets set to -1.
*
* @return
* @see f_file_stream_close()
*/
#if !defined(_di_f_file_clone_) || !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) || !defined(_di_f_file_create_) || !defined(_di_f_file_create_at_) || !defined(_di_f_file_stream_close_)
- extern f_status_t private_f_file_close(f_file_t * const file) F_attribute_visibility_internal_d;
+ extern f_status_t private_f_file_close(int * const id) F_attribute_visibility_internal_d;
#endif // !defined(_di_f_file_clone_) || !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) || !defined(_di_f_file_create_) || !defined(_di_f_file_create_at_) || !defined(_di_f_file_stream_close_)
/**
build_libraries -lc -lcmocka
build_libraries-individual -lf_memory -lf_string -lf_file
-build_sources_program test-file-access.c test-file-access_at.c test-file-clone.c test-file-close.c test-file-copy.c test-file-create.c test-file-create_at.c test-file-create_device.c test-file-create_device_at.c test-file-create_fifo.c test-file-create_fifo_at.c test-file-create_node.c test-file-create_node_at.c test-file-descriptor.c test-file-exists.c test-file-exists_at.c test-file-flush.c test-file-group_read.c test-file-is.c test-file-is_at.c test-file-is_stat.c test-file-link.c test-file-link_at.c test-file-link_hard.c test-file-link_hard_at.c test-file-link_read.c test-file-link_read_at.c test-file-manipulate.c test-file-mode_determine.c test-file-mode_from_string.c test-file-mode_read.c test-file-mode_read_at.c test-file-mode_set.c test-file-mode_set_at.c test-file-mode_to_mode.c test-file-name_base.c test-file-name_directory.c test-file-open.c test-file-open_at.c test-file-owner_read.c test-file-read.c test-file-read_block.c test-file-read_until.c test-file-remove.c test-file-remove_at.c test-file-rename.c test-file-rename_at.c test-file-role_change.c test-file-role_change_at.c test-file-poll.c test-file-seek.c test-file-select.c test-file-select_signal.c test-file-size.c test-file-size_at.c test-file-size_by_id.c test-file-stat.c test-file-stat_at.c test-file-stat_by_id.c test-file-stream_close.c test-file-stream_open_descriptor.c test-file-stream_open.c test-file-stream_read.c test-file-stream_read_block.c test-file-stream_read_until.c test-file-stream_reopen.c test-file-stream_write.c test-file-stream_write_block.c test-file-stream_write_until.c test-file-stream_write_range.c test-file-touch.c test-file-touch_at.c test-file-type.c test-file-type_at.c test-file-umask_get.c test-file-umask_set.c test-file-write.c test-file-write_block.c test-file-write_until.c test-file-write_range.c
+build_sources_program test-file-access.c test-file-access_at.c test-file-clone.c test-file-close.c test-file-close_id.c test-file-copy.c test-file-create.c test-file-create_at.c test-file-create_device.c test-file-create_device_at.c test-file-create_fifo.c test-file-create_fifo_at.c test-file-create_node.c test-file-create_node_at.c test-file-descriptor.c test-file-exists.c test-file-exists_at.c test-file-flush.c test-file-group_read.c test-file-is.c test-file-is_at.c test-file-is_stat.c test-file-link.c test-file-link_at.c test-file-link_hard.c test-file-link_hard_at.c test-file-link_read.c test-file-link_read_at.c test-file-manipulate.c test-file-mode_determine.c test-file-mode_from_string.c test-file-mode_read.c test-file-mode_read_at.c test-file-mode_set.c test-file-mode_set_at.c test-file-mode_to_mode.c test-file-name_base.c test-file-name_directory.c test-file-open.c test-file-open_at.c test-file-owner_read.c test-file-read.c test-file-read_block.c test-file-read_until.c test-file-remove.c test-file-remove_at.c test-file-rename.c test-file-rename_at.c test-file-role_change.c test-file-role_change_at.c test-file-poll.c test-file-seek.c test-file-select.c test-file-select_signal.c test-file-size.c test-file-size_at.c test-file-size_by_id.c test-file-stat.c test-file-stat_at.c test-file-stat_by_id.c test-file-stream_close.c test-file-stream_open_descriptor.c test-file-stream_open.c test-file-stream_read.c test-file-stream_read_block.c test-file-stream_read_until.c test-file-stream_reopen.c test-file-stream_write.c test-file-stream_write_block.c test-file-stream_write_until.c test-file-stream_write_range.c test-file-touch.c test-file-touch_at.c test-file-type.c test-file-type_at.c test-file-umask_get.c test-file-umask_set.c test-file-write.c test-file-write_block.c test-file-write_until.c test-file-write_range.c
build_sources_program test-file.c
build_script no
--- /dev/null
+#include "test-file.h"
+#include "test-file-close.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_file_close_id__fails(void **state) {
+
+ {
+ int errnos[] = {
+ EBADF,
+ EINTR,
+ EIO,
+ ENOSPC,
+ EDQUOT,
+ mock_errno_generic,
+ };
+
+ f_status_t statuss[] = {
+ F_file_descriptor,
+ F_interrupt,
+ F_input_output,
+ F_space_not,
+ F_filesystem_quota_block,
+ F_file_close,
+ };
+
+ for (int i = 0; i < 6; ++i) {
+
+ int id = F_type_descriptor_output_d;
+
+ will_return(__wrap_close, true);
+ will_return(__wrap_close, errnos[i]);
+
+ const f_status_t status = f_file_close_id(&id);
+
+ assert_int_equal(status, F_status_set_error(statuss[i]));
+
+ if (errnos[i] == EBADF || errnos[i] == EINTR) {
+ assert_int_equal(id, F_type_descriptor_output_d);
+ }
+ else {
+ assert_int_equal(id, -1);
+ }
+ } // for
+ }
+}
+
+void test__f_file_close_id__parameter_checking(void **state) {
+
+ {
+ const f_status_t status = f_file_close_id(0);
+
+ assert_int_equal(status, F_status_set_error(F_parameter));
+ }
+}
+
+void test__f_file_close_id__returns_file_descriptor_not(void **state) {
+
+ int id = -1;
+
+ {
+ const f_status_t status = f_file_close_id(&id);
+
+ assert_int_equal(status, F_file_descriptor_not);
+ }
+}
+
+void test__f_file_close_id__works(void **state) {
+
+ int id = F_type_descriptor_output_d;
+
+ {
+
+ will_return(__wrap_close, false);
+ will_return(__wrap_close, 0);
+
+ const f_status_t status = f_file_close_id(&id);
+
+ assert_int_equal(status, F_none);
+ assert_int_equal(id, -1);
+ }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/**
+ * FLL - Level 0
+ *
+ * Project: File
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the file project.
+ */
+#ifndef _TEST__F_file_close_id_h
+#define _TEST__F_file_close_id_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_file_close_id()
+ */
+extern void test__f_file_close_id__fails(void **state);
+
+/**
+ * Test that parameter checking works as expected.
+ *
+ * @see f_file_close_id()
+ */
+extern void test__f_file_close_id__parameter_checking(void **state);
+
+/**
+ * Test that function works but the descriptor is not valid.
+ *
+ * @see f_file_close_id()
+ */
+extern void test__f_file_close_id__returns_file_descriptor_not(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_file_close_id()
+ */
+extern void test__f_file_close_id__works(void **state);
+
+#endif // _TEST__F_file_close_id_h
cmocka_unit_test(test__f_file_close__returns_file_descriptor_not),
cmocka_unit_test(test__f_file_close__works),
+ cmocka_unit_test(test__f_file_close_id__fails),
+ cmocka_unit_test(test__f_file_close_id__returns_file_descriptor_not),
+ cmocka_unit_test(test__f_file_close_id__works),
+
cmocka_unit_test(test__f_file_copy__fails_during_read_write),
cmocka_unit_test(test__f_file_copy__fails_for_block),
cmocka_unit_test(test__f_file_copy__fails_for_character),
// f_file_access_at() doesn't use parameter checking.
// f_file_clone() doesn't use parameter checking.
cmocka_unit_test(test__f_file_close__parameter_checking),
+ cmocka_unit_test(test__f_file_close_id__parameter_checking),
// f_file_copy() doesn't use parameter checking.
// f_file_create() doesn't use parameter checking.
// f_file_create_at() doesn't use parameter checking.
#include "test-file-access_at.h"
#include "test-file-clone.h"
#include "test-file-close.h"
+#include "test-file-close_id.h"
#include "test-file-copy.h"
#include "test-file-create.h"
#include "test-file-create_at.h"
#endif
#ifndef _di_f_socket_accept_
- f_status_t f_socket_accept(f_socket_t * const socket, const int id) {
+ f_status_t f_socket_accept(f_socket_t * const socket) {
#ifndef _di_level_0_parameter_checking_
if (!socket) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
- const int result = accept(id, (struct sockaddr *) &socket->address, &socket->length);
+ const int result = accept(socket->id, (struct sockaddr *) &socket->address, &socket->length);
if (result == -1) {
if (errno == EACCES) return F_status_set_error(F_access_denied);
return F_status_set_error(F_failure);
}
- socket->id = result;
+ socket->id_data = result;
return F_none;
}
if (!buffer) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
- const ssize_t result = recvfrom(socket->id, buffer, socket->size_read, flags, (struct sockaddr *) &socket->address, &socket->length);
+ const ssize_t result = recvfrom(socket->id_data, buffer, socket->size_read, flags, (struct sockaddr *) &socket->address, &socket->length);
if (result < 0) {
if (errno == EACCES) return F_status_set_error(F_access_denied);
if (!header) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
- const ssize_t result = recvmsg(socket->id, header, flags);
+ const ssize_t result = recvmsg(socket->id_data, header, flags);
if (result < 0) {
if (errno == EACCES) return F_status_set_error(F_access_denied);
if (!buffer) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
- const ssize_t result = recv(socket->id, buffer, socket->size_read, flags);
+ const ssize_t result = recv(socket->id_data, buffer, socket->size_read, flags);
if (result < 0) {
if (errno == EACCES) return F_status_set_error(F_access_denied);
if (!buffer) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
- const ssize_t result = sendto(socket->id, buffer, socket->size_write, flags, (struct sockaddr *) &socket->address, socket->length);
+ const ssize_t result = sendto(socket->id_data, buffer, socket->size_write, flags, (struct sockaddr *) &socket->address, socket->length);
if (result < 0) {
if (errno == EACCES) return F_status_set_error(F_access_denied);
if (!header) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
- const ssize_t result = sendmsg(socket->id, header, flags);
+ const ssize_t result = sendmsg(socket->id_data, header, flags);
if (result < 0) {
if (errno == EACCES) return F_status_set_error(F_access_denied);
if (!buffer) return F_status_set_error(F_parameter);
#endif // _di_level_0_parameter_checking_
- const ssize_t result = send(socket->id, buffer, socket->size_write, flags);
+ const ssize_t result = send(socket->id_data, buffer, socket->size_write, flags);
if (result < 0) {
if (errno == EACCES) return F_status_set_error(F_access_denied);
* @param socket
* The client socket structure.
* The structure should be memset as appropriate before calling this.
- * The properties of the structure, namely socket.id, socket.address, and socket.length, are updated upon a successful return.
- * @param id
- * The socket file descriptor representing an actively listening socket to retrieve from.
+ * The socket.id is the socket file descriptor used to establish the data file descriptor socket.id_data.
+ * The socket.id_data, socket.address, and socket.length are updated upon a successful return.
*
* @return
* F_none on success.
* @see accept()
*/
#ifndef _di_f_socket_accept_
- extern f_status_t f_socket_accept(f_socket_t * const socket, const int id);
+ extern f_status_t f_socket_accept(f_socket_t * const socket);
#endif // _di_f_socket_accept_
/**
*
* This is the recommneded way to read UDP streams.
*
+ * This uses the socket.id_data and not the socket.id for processing the data.
+ *
* @param socket
* The socket structure.
- * The socket.id must represent a valid socket file descriptor.
+ * The socket.id_data must represent a valid file descriptor.
* The socket.size_read is used to represent the buffer size in buffer and must not be larger than the actual size of the buffer.
* @param flags
* Read flags.
/**
* Read a message from a socket.
*
+ * This uses the socket.id_data and not the socket.id for processing the data.
+ *
* @param socket
* The socket structure.
- * The socket.id must represent a valid socket file descriptor.
+ * The socket.id_data must represent a valid file descriptor.
* @param flags
* Read flags.
* @param header
*
* This is the recommneded way to read TCP streams.
*
+ * This uses the socket.id_data and not the socket.id for processing the data.
+ *
* @param socket
* The socket structure.
- * The socket.id must represent a valid socket file descriptor.
+ * The socket.id_data must represent a valid file descriptor.
* The socket.size_read is used to represent the buffer size in buffer and must not be larger than the actual size of the buffer.
* @param flags
* Read flags.
*
* This is the recommneded way to write UDP streams.
*
+ * This uses the socket.id_data and not the socket.id for processing the data.
+ *
* @param socket
* The socket structure.
- * The socket.id must represent a valid socket file descriptor.
+ * The socket.id_data must represent a valid socket file descriptor.
* The socket.size_write is used to represent the buffer size in buffer and must not be larger than the actual size of the buffer.
* @param flags
* Read flags.
/**
* Send a message to a socket.
*
+ * This uses the socket.id_data and not the socket.id for processing the data.
+ *
* @param socket
* The socket structure.
- * The socket.id must represent a valid socket file descriptor.
+ * The socket.id_data must represent a valid file descriptor.
* @param flags
* Read flags.
* @param header
*
* This is the recommneded way to write TCP streams.
*
+ * This uses the socket.id_data and not the socket.id for processing the data.
+ *
* @param socket
* The socket structure.
- * The socket.id must represent a valid socket file descriptor.
+ * The socket.id_data must represent a valid socket file descriptor.
* The socket.size_write is used to represent the buffer size in buffer and must not be larger than the actual size of the buffer.
* @param flags
* Read flags.
* Commonly used socket related properties, loosely based off of f_file_t.
*
* Properties:
- * - id: File descriptor, with a value of -1 represents a closed file.
+ * - id: Socket file descriptor, used for binding and listening.
+ * - id_data: Data file descriptor, used for reading and writing data from or to the socket.
* - domain: The socket domain (protocol family, such as f_socket_protocol_family_local_e).
* - protocol: The socket protocol (such as f_socket_protocol_tcp_e).
* - type: The socket type (address family, such as f_socket_address_family_local_e).
#ifndef _di_f_socket_t_
typedef struct {
int id;
+ int id_data;
int domain;
int protocol;
int type;
f_string_static_t name;
} f_socket_t;
- #define f_socket_t_initialize { -1, 0, 0, 0, F_socket_default_read_size_d, F_socket_default_write_size_d, f_socket_address_t_initialize, 0, f_string_static_t_initialize }
+ #define f_socket_t_initialize { -1, -1, 0, 0, 0, F_socket_default_read_size_d, F_socket_default_write_size_d, f_socket_address_t_initialize, 0, f_string_static_t_initialize }
#define macro_f_socket_t_initialize_1(address, length) { \
-1, \
+ -1, \
0, \
0, \
0, \
#define macro_f_socket_t_initialize_2(address, length, name) { \
-1, \
+ -1, \
0, \
0, \
0, \
#define macro_f_socket_t_initialize_3(id, domain, protocol, type, address, length, name) { \
id, \
+ -1, \
domain, \
protocol, \
type, \
#define macro_f_socket_t_initialize_4(id, domain, protocol, type, size_read, size_write, address, length, name) { \
id, \
+ -1, \
+ domain, \
+ protocol, \
+ type, \
+ size_read, \
+ size_write, \
+ address, \
+ length, \
+ name \
+ }
+
+ #define macro_f_socket_t_initialize_5(id, id_data, domain, protocol, type, size_read, size_write, address, length, name) { \
+ id, \
+ id_data, \
domain, \
protocol, \
type, \
#define macro_f_socket_t_clear(file) \
file.id = -1; \
+ file.id_data = -1; \
file.domain = 0; \
file.protocol = 0; \
file.type = 0; \
#define macro_f_socket_t_reset(file) \
file.id = -1; \
+ file.id_data = -1; \
file.domain = 0; \
file.protocol = 0; \
file.type = 0; \
will_return(__wrap_accept, true);
will_return(__wrap_accept, errnos[i]);
- const f_status_t status = f_socket_accept(&socket, 0);
+ const f_status_t status = f_socket_accept(&socket);
assert_int_equal(status, F_status_set_error(statuss[i]));
} // for
void test__f_socket_accept__parameter_checking(void **state) {
{
- const f_status_t status = f_socket_accept(0, 0);
+ const f_status_t status = f_socket_accept(0);
assert_int_equal(status, F_status_set_error(F_parameter));
}
f_socket_t socket = f_socket_t_initialize;
+ socket.id = 1;
+
{
- const int id = 1;
+ const int id = 2;
will_return(__wrap_accept, false);
will_return(__wrap_accept, id);
- const f_status_t status = f_socket_accept(&socket, 0);
+ const f_status_t status = f_socket_accept(&socket);
assert_int_equal(status, F_none);
- assert_int_equal(socket.id, id);
+ assert_int_equal(socket.id_data, id);
+ assert_int_not_equal(socket.id, id);
}
}