From c41133c01682230f1e55bd73135a838eeb1f36b3 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Sun, 31 May 2020 23:00:59 -0500 Subject: [PATCH] Update: f_file, add f_file_touch() and f_file_touch_at(), F_search error Minor updates to f_file project. Add the f_file_touch() and f_file_touch_at() functions. Add the status code F_search (used by f_file_touch() and f_file_touch_at()). --- level_0/f_file/c/file.c | 119 ++++++++++++++++++++++++++++++++-------- level_0/f_file/c/file.h | 98 ++++++++++++++++++++++++++++++--- level_0/f_file/c/private-file.h | 17 +++--- level_0/f_status/c/status.h | 1 + level_1/fl_status/c/status.c | 3 + level_1/fl_status/c/status.h | 2 + level_2/fll_status/c/status.c | 5 ++ 7 files changed, 206 insertions(+), 39 deletions(-) diff --git a/level_0/f_file/c/file.c b/level_0/f_file/c/file.c index b6731f9..837beed 100644 --- a/level_0/f_file/c/file.c +++ b/level_0/f_file/c/file.c @@ -353,21 +353,17 @@ extern "C" { if (path == 0) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ + f_status status = F_none; struct stat file_stat; memset(&file_stat, 0, sizeof(struct stat)); - if (stat(path, &file_stat) < 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_out); - if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow); - if (errno == ENOTDIR) return F_status_set_error(F_directory); - if (errno == ENOENT) return F_false; - if (errno == EACCES) return F_status_set_error(F_access_denied); - if (errno == ELOOP) return F_status_set_error(F_loop); + status = private_f_file_stat(path, F_false, &file_stat); - return F_status_set_error(F_file_stat); + if (F_status_is_error(status)) { + if (F_status_set_fine(status) == F_file_found_not) return F_false; + + return status; } return F_true; @@ -375,27 +371,22 @@ extern "C" { #endif // _di_f_file_exists_ #ifndef _di_f_file_exists_at_ - f_return_status f_file_exists_at(const int at_id, const f_string path, const bool follow) { + f_return_status f_file_exists_at(const int at_id, const f_string path, const int flag) { #ifndef _di_level_0_parameter_checking_ if (path == 0) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ + f_status status = F_none; struct stat file_stat; memset(&file_stat, 0, sizeof(struct stat)); - if (fstatat(at_id, path, &file_stat, follow ? 0 : AT_SYMLINK_NOFOLLOW) < 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_out); - if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow); - if (errno == ENOTDIR) return F_status_set_error(F_directory); - 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); - if (errno == EBADF) return F_status_set_error(F_directory_descriptor); + status = private_f_file_stat_at(at_id, path, flag, &file_stat); - return F_status_set_error(F_file_stat); + if (F_status_is_error(status)) { + if (F_status_set_fine(status) == F_file_found_not) return F_false; + + return status; } return F_true; @@ -548,7 +539,7 @@ extern "C" { #endif // _di_f_file_is_ #ifndef _di_f_file_is_at_ - f_return_status f_file_is_at(const int at_id, const f_string path, const int type, const bool follow) { + f_return_status f_file_is_at(const int at_id, const f_string path, const int type, const int flag) { #ifndef _di_level_0_parameter_checking_ if (path == 0) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ @@ -557,7 +548,7 @@ extern "C" { memset(&file_stat, 0, sizeof(struct stat)); - if (fstatat(at_id, path, &file_stat, follow ? 0 : AT_SYMLINK_NOFOLLOW) < 0) { + if (fstatat(at_id, path, &file_stat, flag) < 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_out); @@ -1007,6 +998,86 @@ extern "C" { } #endif // _di_f_file_stat_by_id_ +#ifndef _di_f_file_touch_ + f_return_status f_file_touch(const f_string path, const mode_t mode, const bool dereference) { + #ifndef _di_level_0_parameter_checking_ + if (path == 0) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + f_status status = F_none; + struct stat file_stat; + + memset(&file_stat, 0, sizeof(struct stat)); + + status = private_f_file_stat(path, F_false, &file_stat); + + if (F_status_set_fine(status) == F_file_found_not) { + return private_f_file_create(path, mode, dereference); + } + else if (F_status_is_error(status)) { + return status; + } + + if (utimensat(f_file_at_current_working, path, 0, dereference ? 0 : f_file_at_symlink_follow_no) < 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 == EINVAL) return F_status_set_error(F_parameter); + if (errno == ELOOP) return F_status_set_error(F_loop); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENOENT) return F_status_set_error(F_file_found_not); + if (errno == ENOTDIR) return F_status_set_error(F_directory); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == EROFS) return F_status_set_error(F_read_only); + if (errno == ESRCH) return F_status_set_error(F_search); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_file_touch_ + +#ifndef _di_f_file_touch_at_ + f_return_status f_file_touch_at(const int at_id, const f_string path, const mode_t mode, const int flag) { + #ifndef _di_level_0_parameter_checking_ + if (path == 0) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + f_status status = F_none; + struct stat file_stat; + + memset(&file_stat, 0, sizeof(struct stat)); + + status = private_f_file_stat_at(at_id, path, flag, &file_stat); + + if (F_status_set_fine(status) == F_file_found_not) { + return private_f_file_create_at(at_id, path, mode, F_false); + } + else if (F_status_is_error(status)) { + return status; + } + + if (utimensat(at_id, path, 0, 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 == EINVAL) return F_status_set_error(F_parameter); + if (errno == ELOOP) return F_status_set_error(F_loop); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENOENT) return F_status_set_error(F_file_found_not); + if (errno == ENOTDIR) return F_status_set_error(F_directory); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == EROFS) return F_status_set_error(F_read_only); + if (errno == ESRCH) return F_status_set_error(F_search); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_file_touch_at_ + #ifndef _di_f_file_write_ f_return_status f_file_write(const f_file file, const f_string_dynamic buffer, f_string_length *written) { #ifndef _di_level_0_parameter_checking_ diff --git a/level_0/f_file/c/file.h b/level_0/f_file/c/file.h index c99b352..c31843f 100644 --- a/level_0/f_file/c/file.h +++ b/level_0/f_file/c/file.h @@ -573,7 +573,7 @@ extern "C" { #endif // _di_f_file_copy_ /** - * Create a file based on the given path and file mode. + * Create a (regular) file based on the given path and file mode. * * The file will not be open after calling this. * @@ -612,7 +612,7 @@ extern "C" { #endif // _di_f_file_create_ /** - * Create a file based on the given path and file mode. + * Create a (regular) file based on the given path and file mode. * * The file will not be open after calling this. * @@ -902,7 +902,7 @@ extern "C" { * @param path * The path file name. * @param flag - * Additional flag to pass, such as AT_EACCESS or AT_SYMLINK_NOFOLLOW. + * Any valid flag, such as f_file_at_path_empty, f_file_at_automount_no, or f_file_at_symlink_follow_no. * * @return * F_true if file exists. @@ -919,7 +919,7 @@ extern "C" { * @see fstatat() */ #ifndef _di_f_file_exists_at_ - extern f_return_status f_file_exists_at(const int at_id, const f_string path, const bool follow); + extern f_return_status f_file_exists_at(const int at_id, const f_string path, const int flag); #endif // _di_f_file_exists_at_ /** @@ -982,9 +982,8 @@ extern "C" { * The path file name. * @param type * The type of the file - * @param follow - * Set to TRUE to follow symbolic links when determining if path is a file. - * Set to FALSE to not follow. + * @param flag + * Any valid flag, such as f_file_at_path_empty, f_file_at_automount_no, or f_file_at_symlink_follow_no. * * @return * F_true if path was found and path is type. @@ -1001,7 +1000,7 @@ extern "C" { * @see fstatat() */ #ifndef _di_f_file_is_at_ - extern f_return_status f_file_is_at(const int at_id, const f_string path, const int type, const bool follow); + extern f_return_status f_file_is_at(const int at_id, const f_string path, const int type, const int flag); #endif // _di_f_file_is_at_ /** @@ -1710,6 +1709,89 @@ extern "C" { #endif // _di_f_file_write_ /** + * Update the files access and modification timestamp, creating the file if it does not already exist. + * + * When the file is created, it is created as a regular file. + * + * @param path + * The path file name. + * @param mode + * The file mode to use when (regular) file is created. + * @param dereference + * Set to TRUE to dereferenc symlinks (often is what is desired). + * Set to FALSE to operate on the symlink itself. + * + * @return + * F_none on success. + * F_parameter (with error bit) if a parameter is invalid. + * F_access_denied (with error bit) on access denied. + * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. + * F_buffer (with error bit) if the buffer is invalid. + * F_loop (with error bit) on loop error. + * F_name (with error bit) on path name error. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_prohibited (with error bit) if filesystem does not allow for creating or linking. + * F_read_only (with error bit) if filesystem is read-only. + * F_search (with error bit) if search permission is denied for one of the paths to the file. + * F_memory_out (with error bit) if out of memory. + * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_filesystem_quota_reached (with error bit) quota reached of filesystem is out of space. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_file_open_max (with error bit) when system-wide max open files is reached. + * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_failure (with error bit) for any other error. + * + * @see utimensat() + */ +#ifndef _di_f_file_touch_ + extern f_return_status f_file_touch(const f_string path, const mode_t mode, const bool dereference); +#endif // _di_f_file_touch_ + +/** + * Update the files access and modification timestamp, creating the file if it does not already exist. + * + * When the file is created, it is created as a regular file. + * + * @param at_id + * The parent directory, as an open directory file descriptor, in which path is relative to. + * @param path + * The path file name. + * @param mode + * The file mode to use when (regular) file is created. + * @param flag + * Any valid flag, such as f_file_at_path_empty, f_file_at_automount_no, or f_file_at_symlink_follow_no. + * + * @return + * F_none on success. + * F_parameter (with error bit) if a parameter is invalid. + * F_access_denied (with error bit) on access denied. + * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. + * F_buffer (with error bit) if the buffer is invalid. + * F_loop (with error bit) on loop error. + * F_name (with error bit) on path name error. + * F_file_found (with error bit) if a file aleady exists at the path (when calling utimensat()). + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_prohibited (with error bit) if filesystem does not allow for creating or linking. + * F_read_only (with error bit) if filesystem is read-only. + * F_search (with error bit) if search permission is denied for one of the paths to the file. + * F_memory_out (with error bit) if out of memory. + * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_filesystem_quota_reached (with error bit) quota reached of filesystem is out of space. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_file_open_max (with error bit) when system-wide max open files is reached. + * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_failure (with error bit) for any other error. + * + * @see utimensat() + */ +#ifndef _di_f_file_touch_at_ + extern f_return_status f_file_touch_at(const int at_id, const f_string path, const mode_t mode, const int flag); +#endif // _di_f_file_touch_at_ + +/** * Write until a single block is filled or entire buffer is written. * * To check how much was write into the buffer, record buffer->used before execution and compare to buffer->used after execution. diff --git a/level_0/f_file/c/private-file.h b/level_0/f_file/c/private-file.h index 67a6e39..023e83f 100644 --- a/level_0/f_file/c/private-file.h +++ b/level_0/f_file/c/private-file.h @@ -131,7 +131,7 @@ extern "C" { * @param gid * The new group id to use. * @param flag - * Any valid flag, such as AT_EMPTY_PATH, AT_NO_AUTOMOUNT, or AT_SYMLINK_NO_FOLLOW. + * Any valid flag, such as f_file_at_path_empty, f_file_at_automount_no, or f_file_at_symlink_follow_no. * * @return * F_none on success. @@ -780,10 +780,12 @@ extern "C" { * * @see f_file_copy() * @see f_file_stat() + * @see f_file_exists() + * @see f_file_touch() */ -#if !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) +#if !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) || !defined(_di_f_file_exists_) || !defined(_di_f_file_touch_) extern f_return_status private_f_file_stat(const f_string file_name, const bool dereference, struct stat *file_stat) f_gcc_attribute_visibility_internal; -#endif // !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) +#endif // !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) || !defined(_di_f_file_exists_) || !defined(_di_f_file_touch_) /** * Private implementation of f_file_close(). @@ -795,11 +797,10 @@ extern "C" { * @param file_name * The name of the file. * @param flag - * Any valid flag, such as AT_EMPTY_PATH, AT_NO_AUTOMOUNT, or AT_SYMLINK_NO_FOLLOW. + * Any valid flag, such as f_file_at_path_empty, f_file_at_automount_no, or f_file_at_symlink_follow_no. * @param file_stat * The statistics read. * - * * @return * F_none on success. * F_name (with error bit) if the name is somehow invalid. @@ -812,10 +813,12 @@ extern "C" { * F_parameter (with error bit) if a parameter is invalid. * * @see f_file_stat_at() + * @see f_file_exists_at() + * @see f_file_touch_at() */ -#if !defined(_di_f_file_stat_at_) +#if !defined(_di_f_file_stat_at_) || !defined(_di_f_file_exists_at_) || !defined(_di_f_file_touch_at_) extern f_return_status private_f_file_stat_at(const int at_id, const f_string file_name, const int flag, struct stat *file_stat) f_gcc_attribute_visibility_internal; -#endif // !defined(_di_f_file_stat_at_) +#endif // !defined(_di_f_file_stat_at_) || !defined(_di_f_file_exists_at_) || !defined(_di_f_file_touch_at_) /** * Private implementation of f_file_close(). diff --git a/level_0/f_status/c/status.h b/level_0/f_status/c/status.h index 7ba3de7..49b644f 100644 --- a/level_0/f_status/c/status.h +++ b/level_0/f_status/c/status.h @@ -178,6 +178,7 @@ extern "C" { F_process_too_many, F_prohibited, F_read_only, + F_search, F_space_not, F_stop, F_syntax, diff --git a/level_1/fl_status/c/status.c b/level_1/fl_status/c/status.c index dbbddf1..98f9bed 100644 --- a/level_1/fl_status/c/status.c +++ b/level_1/fl_status/c/status.c @@ -320,6 +320,9 @@ extern "C" { case F_read_only: *string = FL_status_string_read_only; break; + case F_search: + *string = FL_status_string_search; + break; case F_space_not: *string = FL_status_string_space_not; break; diff --git a/level_1/fl_status/c/status.h b/level_1/fl_status/c/status.h index ea95a8c..3ff9b2e 100644 --- a/level_1/fl_status/c/status.h +++ b/level_1/fl_status/c/status.h @@ -197,6 +197,7 @@ extern "C" { #define FL_status_string_process_too_many "F_process_too_many" #define FL_status_string_prohibited "F_prohibited" #define FL_status_string_read_only "F_read_only" + #define FL_status_string_search "F_search" #define FL_status_string_space_not "F_space_not" #define FL_status_string_stop "F_stop" #define FL_status_string_syntax "F_syntax" @@ -243,6 +244,7 @@ extern "C" { #define FL_status_string_process_too_many_length 18 #define FL_status_string_prohibited_length 12 #define FL_status_string_read_only_length 11 + #define FL_status_string_search_length 8 #define FL_status_string_space_not_length 11 #define FL_status_string_stop_length 6 #define FL_status_string_syntax_length 8 diff --git a/level_2/fll_status/c/status.c b/level_2/fll_status/c/status.c index c60a9c8..59456c7 100644 --- a/level_2/fll_status/c/status.c +++ b/level_2/fll_status/c/status.c @@ -536,6 +536,11 @@ extern "C" { return F_none; } + if (fl_string_compare(string, FL_status_string_search, length, FL_status_string_search_length) == F_equal_to) { + *code = F_search; + return F_none; + } + if (fl_string_compare(string, FL_status_string_space_not, length, FL_status_string_space_not_length) == F_equal_to) { *code = F_space_not; return F_none; -- 1.8.3.1