From: Kevin Day Date: Thu, 21 Apr 2022 03:54:39 +0000 (-0500) Subject: Bugfix: Fixes for f_file exposed by unit tests. X-Git-Tag: 0.5.10~192 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=be99a3873f3b70d323013c5437a52ed09be5f76f;p=fll Bugfix: Fixes for f_file exposed by unit tests. Fix case where private_f_file_stat_at() is being passed F_true rather than the flag. Add missing path.used checks. When total is 0 in f_file_stream_read_until(), then immediately return as F_none_stop. Handle case where freopen() actually allows for the path to be NULL in which case the mode string is applied. In this case, return F_data_not only if both path and mode are not used. Always re-assign the file stream after calling freopen(). The return status' from private_f_file_stream_write_until() calls are not being processed. The code is checking the values but the value is never assigned. Add the missing return value assignment. Move file stream locking into private_f_file_stream_write_until() and make sure only unlocked functions are used. The f_file_type() and f_file_type_at() functions need to accept a dereference boolean for consistency with the rest of the project. Have these two functions call private_f_file_stat() and private_f_file_stat_at() respectively. When fwrite_unlocked() is called, be sure to set check if ferror_unlocked() returns an error rather than checking size_write. The previous behavior is incorrect because it is checking if size is less than 0 and the man pages claim that fwrite()/fwrite_unlocked() returns nothing smaller than 0 on failure. Have the fwrite_unlocked() unknown errnor codes return F_file_write rather than F_failure. Clean up function ordering. --- diff --git a/level_0/f_file/c/file.c b/level_0/f_file/c/file.c index 3c08186..d6bb583 100644 --- a/level_0/f_file/c/file.c +++ b/level_0/f_file/c/file.c @@ -1373,7 +1373,7 @@ extern "C" { memset(&stat_file, 0, sizeof(struct stat)); { - const f_status_t status = private_f_file_stat_at(at_id, path, F_true, &stat_file); + const f_status_t status = private_f_file_stat_at(at_id, path, flag, &stat_file); if (F_status_is_error(status)) return status; } @@ -2077,97 +2077,101 @@ extern "C" { } #endif // _di_f_file_stream_close_ -#ifndef _di_f_file_stream_open_descriptor_ - f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file) { +#ifndef _di_f_file_stream_open_ + f_status_t f_file_stream_open(const f_string_static_t path, const f_string_static_t mode, f_file_t * const file) { #ifndef _di_level_0_parameter_checking_ if (!file) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ - if (file->id == -1) { - return F_status_set_error(F_file_descriptor); + if (!path.used) { + return F_data_not; } if (mode.used) { - file->stream = fdopen(file->id, private_f_file_stream_open_mode_determine(file->flag)); + file->stream = fopen(path.string, mode.string); } else { - file->stream = fdopen(file->id, mode.string); + file->stream = fopen(path.string, private_f_file_stream_open_mode_determine(file->flag)); } if (!file->stream) { if (errno == EACCES) return F_status_set_error(F_access_denied); - if (errno == EAGAIN || errno == EWOULDBLOCK) return F_status_set_error(F_block); - if (errno == EBADF) return F_status_set_error(F_file_descriptor); - if (errno == EFBIG) return F_status_set_error(F_file_overflow); - if (errno == EDEADLK) return F_status_set_error(F_deadlock); - if (errno == EDESTADDRREQ) return F_status_set_error(F_socket_not); - if (errno == EDQUOT) return F_status_set_error(F_space_not); + if (errno == EDQUOT) return F_status_set_error(F_filesystem_quota_block); + if (errno == EEXIST) return F_status_set_error(F_file_found); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EFBIG || errno == EOVERFLOW) return F_status_set_error(F_number_overflow); if (errno == EINTR) return F_status_set_error(F_interrupt); if (errno == EINVAL) return F_status_set_error(F_parameter); - if (errno == EIO) return F_status_set_error(F_input_output); - if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max); - if (errno == ENODEV) return F_status_set_error(F_device_not); - if (errno == ENOLCK) return F_status_set_error(F_lock); + if (errno == ELOOP) return F_status_set_error(F_loop); + if (errno == ENFILE) return F_status_set_error(F_file_open_max); + if (errno == ENOENT) return F_status_set_error(F_file_found_not); + if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory); if (errno == ENOMEM) return F_status_set_error(F_memory_not); if (errno == ENOSPC) return F_status_set_error(F_space_not); - if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory); if (errno == EPERM) return F_status_set_error(F_prohibited); - if (errno == EPIPE) return F_status_set_error(F_pipe_not); + if (errno == EROFS) return F_status_set_error(F_read_only); + if (errno == ETXTBSY) return F_status_set_error(F_busy); + if (errno == EISDIR) return F_status_set_error(F_directory); + if (errno == EOPNOTSUPP) return F_status_set_error(F_supported_not); return F_status_set_error(F_failure); } + file->id = fileno(file->stream); + + if (file->id == -1) { + return F_status_set_error(F_file_descriptor); + } + return F_none; } -#endif // _di_f_file_stream_open_descriptor_ +#endif // _di_f_file_stream_open_ -#ifndef _di_f_file_stream_open_ - f_status_t f_file_stream_open(const f_string_static_t path, const f_string_static_t mode, f_file_t * const file) { +#ifndef _di_f_file_stream_open_descriptor_ + f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file) { #ifndef _di_level_0_parameter_checking_ if (!file) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ + if (file->id == -1) { + return F_status_set_error(F_file_descriptor); + } + if (mode.used) { - file->stream = fopen(path.string, mode.string); + file->stream = fdopen(file->id, private_f_file_stream_open_mode_determine(file->flag)); } else { - file->stream = fopen(path.string, private_f_file_stream_open_mode_determine(file->flag)); + file->stream = fdopen(file->id, mode.string); } if (!file->stream) { if (errno == EACCES) return F_status_set_error(F_access_denied); - if (errno == EDQUOT) return F_status_set_error(F_filesystem_quota_block); - if (errno == EEXIST) return F_status_set_error(F_file_found); - if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == EAGAIN || errno == EWOULDBLOCK) return F_status_set_error(F_block); + if (errno == EBADF) return F_status_set_error(F_file_descriptor); + if (errno == EFBIG) return F_status_set_error(F_file_overflow); + if (errno == EDEADLK) return F_status_set_error(F_deadlock); + if (errno == EDESTADDRREQ) return F_status_set_error(F_socket_not); + if (errno == EDQUOT) return F_status_set_error(F_space_not); if (errno == EFAULT) return F_status_set_error(F_buffer); - if (errno == EFBIG || errno == EOVERFLOW) return F_status_set_error(F_number_overflow); if (errno == EINTR) return F_status_set_error(F_interrupt); if (errno == EINVAL) return F_status_set_error(F_parameter); - if (errno == ELOOP) return F_status_set_error(F_loop); - if (errno == ENFILE) return F_status_set_error(F_file_open_max); - if (errno == ENOENT) return F_status_set_error(F_file_found_not); - if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory); + if (errno == EIO) return F_status_set_error(F_input_output); + if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max); + if (errno == ENODEV) return F_status_set_error(F_device_not); + if (errno == ENOLCK) return F_status_set_error(F_lock); if (errno == ENOMEM) return F_status_set_error(F_memory_not); if (errno == ENOSPC) return F_status_set_error(F_space_not); + if (errno == ENOTDIR) return F_status_set_error(F_file_type_not_directory); if (errno == EPERM) return F_status_set_error(F_prohibited); - if (errno == EROFS) return F_status_set_error(F_read_only); - if (errno == ETXTBSY) return F_status_set_error(F_busy); - if (errno == EISDIR) return F_status_set_error(F_directory); - if (errno == EOPNOTSUPP) return F_status_set_error(F_supported_not); + if (errno == EPIPE) return F_status_set_error(F_pipe_not); return F_status_set_error(F_failure); } - file->id = fileno(file->stream); - - if (file->id == -1) { - return F_status_set_error(F_file_descriptor); - } - return F_none; } -#endif // _di_f_file_stream_open_ +#endif // _di_f_file_stream_open_descriptor_ #ifndef _di_f_file_stream_read_ f_status_t f_file_stream_read(const f_file_t file, f_string_dynamic_t * const buffer) { @@ -2310,6 +2314,10 @@ extern "C" { return F_status_set_error(F_file_closed); } + if (!total) { + return F_none_stop; + } + flockfile(file.stream); if (feof_unlocked(file.stream)) { @@ -2386,17 +2394,17 @@ extern "C" { if (!file) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ - if (!path.used) { + if (!path.used && !mode.used) { return F_data_not; } FILE *result = 0; if (mode.used) { - result = freopen(path.string, mode.string, file->stream); + result = freopen(path.used ? path.string : 0, mode.string, file->stream); } else { - result = freopen(path.string, private_f_file_stream_open_mode_determine(file->flag), file->stream); + result = freopen(path.used ? path.string : 0, private_f_file_stream_open_mode_determine(file->flag), file->stream); } if (!result) { @@ -2421,6 +2429,7 @@ extern "C" { return F_status_set_error(F_failure); } + file->stream = result; file->id = fileno(file->stream); if (file->id == -1) { @@ -2452,7 +2461,7 @@ extern "C" { f_status_t status = F_none; if (written) { - private_f_file_stream_write_until(file, buffer, buffer.used, written); + status = private_f_file_stream_write_until(file, buffer, buffer.used, written); if (status == F_none && *written == buffer.used) { return F_none_eos; @@ -2461,17 +2470,13 @@ extern "C" { else { f_array_length_t written_local = 0; - private_f_file_stream_write_until(file, buffer, buffer.used, &written_local); + status = private_f_file_stream_write_until(file, buffer, buffer.used, &written_local); if (status == F_none && written_local == buffer.used) { return F_none_eos; } } - if (F_status_is_error(status)) { - return F_status_set_error(status); - } - return status; } #endif // _di_f_file_stream_write_ @@ -2503,7 +2508,7 @@ extern "C" { f_status_t status = F_none; if (written) { - private_f_file_stream_write_until(file, buffer, write_max, written); + status = private_f_file_stream_write_until(file, buffer, write_max, written); if (status == F_none) { if (*written == buffer.used) { @@ -2518,7 +2523,7 @@ extern "C" { else { f_array_length_t written_local = 0; - private_f_file_stream_write_until(file, buffer, write_max, &written_local); + status = private_f_file_stream_write_until(file, buffer, write_max, &written_local); if (status == F_none) { if (written_local == buffer.used) { @@ -2562,7 +2567,7 @@ extern "C" { f_status_t status = F_none; if (written) { - private_f_file_stream_write_until(file, buffer, write_max, written); + status = private_f_file_stream_write_until(file, buffer, write_max, written); if (status == F_none) { if (*written == buffer.used) { @@ -2577,7 +2582,7 @@ extern "C" { else { f_array_length_t written_local = 0; - private_f_file_stream_write_until(file, buffer, buffer.used, &written_local); + status = private_f_file_stream_write_until(file, buffer, buffer.used, &written_local); if (status == F_none) { if (written_local == buffer.used) { @@ -2624,11 +2629,7 @@ extern "C" { if (written) { const f_string_static_t buffer_adjusted = macro_f_string_static_t_initialize(buffer.string + range.start, 0, buffer.used - range.start); - flockfile(file.stream); - - private_f_file_stream_write_until(file, buffer_adjusted, write_max, written); - - funlockfile(file.stream); + status = private_f_file_stream_write_until(file, buffer_adjusted, write_max, written); if (status == F_none) { if (range.start + *written == buffer.used) { @@ -2644,11 +2645,7 @@ extern "C" { const f_string_static_t buffer_adjusted = macro_f_string_static_t_initialize(buffer.string + range.start, 0, buffer.used - range.start); f_array_length_t written_local = 0; - flockfile(file.stream); - - private_f_file_stream_write_until(file, buffer_adjusted, write_max, &written_local); - - funlockfile(file.stream); + status = private_f_file_stream_write_until(file, buffer_adjusted, write_max, &written_local); if (status == F_none) { if (range.start + written_local == buffer.used) { @@ -2677,7 +2674,7 @@ extern "C" { memset(&stat_file, 0, sizeof(struct stat)); - status = private_f_file_stat(path, F_false, &stat_file); + status = private_f_file_stat(path, dereference, &stat_file); if (F_status_set_fine(status) == F_file_found_not) { return private_f_file_create(path, mode, dereference); @@ -2748,7 +2745,7 @@ extern "C" { #endif // _di_f_file_touch_at_ #ifndef _di_f_file_type_ - f_status_t f_file_type(const f_string_static_t path, int * const type) { + f_status_t f_file_type(const f_string_static_t path, const bool dereference, int * const type) { #ifndef _di_level_0_parameter_checking_ if (!type) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ @@ -2761,17 +2758,9 @@ extern "C" { memset(&stat_file, 0, sizeof(struct stat)); - if (stat(path.string, &stat_file) < 0) { - if (errno == ENAMETOOLONG) return F_status_set_error(F_name); - if (errno == EFAULT) return F_status_set_error(F_buffer); - if (errno == ENOMEM) return F_status_set_error(F_memory_not); - if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow); - if (errno == ENOTDIR) return F_status_set_error(F_directory_not); - if (errno == ENOENT) return F_file_found_not; - if (errno == EACCES) return F_status_set_error(F_access_denied); - if (errno == ELOOP) return F_status_set_error(F_loop); - - return F_status_set_error(F_file_stat); + { + const f_status_t status = private_f_file_stat(path, dereference, &stat_file); + if (F_status_is_error(status)) return status; } *type = macro_f_file_type_get(stat_file.st_mode); @@ -2794,18 +2783,9 @@ extern "C" { memset(&stat_file, 0, sizeof(struct stat)); - if (fstatat(at_id, path.string, &stat_file, flag) < 0) { - if (errno == EACCES) return F_status_set_error(F_access_denied); - if (errno == EBADF) return F_status_set_error(F_directory_descriptor); - if (errno == EFAULT) return F_status_set_error(F_buffer); - if (errno == ENAMETOOLONG) return F_status_set_error(F_name); - if (errno == ENOENT) return F_file_found_not; - if (errno == ENOMEM) return F_status_set_error(F_memory_not); - if (errno == ENOTDIR) return F_status_set_error(F_directory_not); - if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow); - if (errno == ELOOP) return F_status_set_error(F_loop); - - return F_status_set_error(F_file_stat); + { + const f_status_t status = private_f_file_stat_at(at_id, path, flag, &stat_file); + if (F_status_is_error(status)) return status; } *type = macro_f_file_type_get(stat_file.st_mode); diff --git a/level_0/f_file/c/file.h b/level_0/f_file/c/file.h index f5b08a3..14319a4 100644 --- a/level_0/f_file/c/file.h +++ b/level_0/f_file/c/file.h @@ -2085,49 +2085,16 @@ extern "C" { #endif // _di_f_file_stream_close_ /** - * Open a file stream from a file descriptor. - * - * @param mode - * The file modes do use when opening. - * Set mode.used to 0 to determine mode from file.flags (falling back to read only as a failsafe). - * If neither truncate nor append are not specified in write only mode, then the failsafe is to append. - * This should match the modes used to open the file descriptor as it relates to the stream modes. - * @param file - * The file with a valid file descriptor (file.id). - * THe file stream (file.stream) is updated on success, but may be set to NULL on error. - * - * @return - * F_none is returned on success. - * - * F_access_denied (with error bit) on access denied. - * F_block (with error bit) if the action would block and non-blocking is set on the stream. - * F_buffer (with error bit) if the buffer is invalid. - * F_deadlock (with error bit) if operation would cause a deadlock. - * F_file_descriptor (with error bit) if file descriptor is invalid. - * F_file_descriptor_max (with error bit) if max file descriptors is reached. - * F_file_overflow (with error bit) if the write exceeds some implementation defined maximum file size. - * F_file_type_not_directory (with error bit) if F_NOTIFY was specified and file.id is not a directory. - * F_interrupt (with error bit) when program received an interrupt signal, halting operation. - * F_lock (with error bit) if failed to lock, such as lock table is full or too many open segments. - * F_parameter (with error bit) if a parameter is invalid. - * F_pipe_not (with error bit) if the stream is a pipe or a socket but the pipe or socket is already closed. - * F_prohibited (with error bit) if file system does not allow for making changes. - * F_socket_not (with error bit) if socket is not connected. - * F_space_not (with error bit) if the file system is out of space (or file system quota is reached). - * - * @see fdopen() - */ -#ifndef _di_f_file_stream_open_descriptor_ - extern f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file); -#endif // _di_f_file_stream_open_descriptor_ - -/** * Open a file stream. * * The file descriptor is retrieved on success, if necessary and able. * + * If the file stream is open, it is closed before re-opening. + * + * This is often used for changing the file pointed to by standard streams such as stdout. + * * @param path - * The file path + * The file path. * @param mode * The file modes do use when opening, as an fopen() file mode string. * Set mode.used to 0 to determine mode from file.flags (falling back to read only as a failsafe). @@ -2147,6 +2114,7 @@ extern "C" { * * @return * F_none is returned on success. + * F_data_not if both path.used is 0. * * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. @@ -2175,6 +2143,43 @@ extern "C" { #endif // _di_f_file_stream_open_ /** + * Open a file stream from a file descriptor. + * + * @param mode + * The file modes do use when opening. + * Set mode.used to 0 to determine mode from file.flags (falling back to read only as a failsafe). + * If neither truncate nor append are not specified in write only mode, then the failsafe is to append. + * This should match the modes used to open the file descriptor as it relates to the stream modes. + * @param file + * The file with a valid file descriptor (file.id). + * THe file stream (file.stream) is updated on success, but may be set to NULL on error. + * + * @return + * F_none is returned on success. + * + * F_access_denied (with error bit) on access denied. + * F_block (with error bit) if the action would block and non-blocking is set on the stream. + * F_buffer (with error bit) if the buffer is invalid. + * F_deadlock (with error bit) if operation would cause a deadlock. + * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_file_descriptor_max (with error bit) if max file descriptors is reached. + * F_file_overflow (with error bit) if the write exceeds some implementation defined maximum file size. + * F_file_type_not_directory (with error bit) if F_NOTIFY was specified and file.id is not a directory. + * F_interrupt (with error bit) when program received an interrupt signal, halting operation. + * F_lock (with error bit) if failed to lock, such as lock table is full or too many open segments. + * F_parameter (with error bit) if a parameter is invalid. + * F_pipe_not (with error bit) if the stream is a pipe or a socket but the pipe or socket is already closed. + * F_prohibited (with error bit) if file system does not allow for making changes. + * F_socket_not (with error bit) if socket is not connected. + * F_space_not (with error bit) if the file system is out of space (or file system quota is reached). + * + * @see fdopen() + */ +#ifndef _di_f_file_stream_open_descriptor_ + extern f_status_t f_file_stream_open_descriptor(const f_string_static_t mode, f_file_t * const file); +#endif // _di_f_file_stream_open_descriptor_ + +/** * Read until EOF is reached. * * To check how much was read into the buffer, record buffer->used before execution and compare to buffer->used after execution. @@ -2294,7 +2299,8 @@ extern "C" { * The file descriptor is retrieved on success, if necessary and able. * * @param path - * The file path + * The file path. + * Set path.used to 0 with a non-empty mode (mode.used > 0) to only change the mode of the existing stream. * @param mode * The file modes do use when opening. * Set to 0 to determine mode from file.flags (falling back to read only as a failsafe). @@ -2306,6 +2312,7 @@ extern "C" { * * @return * F_none is returned on success. + * F_data_not if both path.used and mode.used are 0. * * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. @@ -2360,7 +2367,12 @@ extern "C" { * F_interrupt (with error bit) if interrupt was received. * F_parameter (with error bit) if a parameter is invalid. * - * @see fwrite() + * F_file_write (with error bit) on any other error. + * + * @see flockfile() + * @see fwrite_unlocked() + * @see ferror_unlocked() + * @see funlockfile() */ #ifndef _di_f_file_stream_write_ extern f_status_t f_file_stream_write(const f_file_t file, const f_string_static_t buffer, f_array_length_t * const written); @@ -2395,7 +2407,12 @@ extern "C" { * F_interrupt (with error bit) if interrupt was received. * F_parameter (with error bit) if a parameter is invalid. * - * @see fwrite() + * F_file_write (with error bit) on any other error. + * + * @see flockfile() + * @see fwrite_unlocked() + * @see ferror_unlocked() + * @see funlockfile() */ #ifndef _di_f_file_stream_write_block_ extern f_status_t f_file_stream_write_block(const f_file_t file, const f_string_static_t buffer, f_array_length_t * const written); @@ -2431,7 +2448,12 @@ extern "C" { * F_interrupt (with error bit) if interrupt was received. * F_parameter (with error bit) if a parameter is invalid. * - * @see fwrite() + * F_file_write (with error bit) on any other error. + * + * @see flockfile() + * @see fwrite_unlocked() + * @see ferror_unlocked() + * @see funlockfile() */ #ifndef _di_f_file_stream_write_until_ extern f_status_t f_file_stream_write_until(const f_file_t file, const f_string_static_t buffer, const f_array_length_t total, f_array_length_t * const written); @@ -2565,6 +2587,9 @@ extern "C" { * * @param path * The path file name. + * @param dereference + * Set to TRUE to dereference symlinks (often is what is desired). + * Set to FALSE to operate on the symlink itself. * @param type * The type of the file. * @@ -2583,7 +2608,7 @@ extern "C" { * @see stat() */ #ifndef _di_f_file_type_ - extern f_status_t f_file_type(const f_string_static_t path, int * const type); + extern f_status_t f_file_type(const f_string_static_t path, const bool dereference, int * const type); #endif // _di_f_file_type_ /** diff --git a/level_0/f_file/c/private-file.c b/level_0/f_file/c/private-file.c index 5f69be9..d2af685 100644 --- a/level_0/f_file/c/private-file.c +++ b/level_0/f_file/c/private-file.c @@ -787,11 +787,15 @@ extern "C" { } } + flockfile(file.stream); + while (*written < write_max) { size_write = fwrite_unlocked(buffer.string + *written, write_amount, write_size, file.stream); - if (size_write < 0) { + if (ferror_unlocked(file.stream)) { + funlockfile(file.stream); + if (errno == EAGAIN || errno == EWOULDBLOCK) return F_status_set_error(F_block); if (errno == EBADF) return F_status_set_error(F_file_descriptor); if (errno == EFAULT) return F_status_set_error(F_buffer); @@ -800,16 +804,20 @@ extern "C" { if (errno == EIO) return F_status_set_error(F_input_output); if (errno == EISDIR) return F_status_set_error(F_file_type_directory); - return F_status_set_error(F_failure); + return F_status_set_error(F_file_write); } *written += size_write * write_amount; if (!size_write) { + funlockfile(file.stream); + return F_none_stop; } } // while + funlockfile(file.stream); + return F_none; } #endif // !defined(f_file_stream_write) || !defined(_di_f_file_stream_write_block_) || !defined(f_file_stream_write_until) || !defined(f_file_stream_write_range) @@ -850,7 +858,7 @@ extern "C" { if (errno == EIO) return F_status_set_error(F_input_output); if (errno == EISDIR) return F_status_set_error(F_file_type_directory); - return F_status_set_error(F_failure); + return F_status_set_error(F_file_write); } return F_none;