From 20fc936d4218a108f54e616ddc602616b13e6f41 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Mon, 20 Jul 2020 23:44:12 -0500 Subject: [PATCH] Feature: add f_directory_touch() and f_directory_touch_at(). This is the same as f_file_touch() and f_file_touch_at(), except that a directory is created if the file does not exist. --- level_0/f_directory/c/directory.c | 144 +++++++++++++++++++++--------- level_0/f_directory/c/directory.h | 80 +++++++++++++++++ level_0/f_directory/c/private-directory.c | 53 +++++++++++ level_0/f_directory/c/private-directory.h | 71 +++++++++++++++ 4 files changed, 306 insertions(+), 42 deletions(-) diff --git a/level_0/f_directory/c/directory.c b/level_0/f_directory/c/directory.c index 2467f3b..de23fc8 100644 --- a/level_0/f_directory/c/directory.c +++ b/level_0/f_directory/c/directory.c @@ -7,27 +7,7 @@ extern "C" { #ifndef _di_f_directory_create_ f_return_status f_directory_create(const f_string path, const mode_t mode) { - - if (mkdir(path, mode) < 0) { - 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 == 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 == EMLINK) return F_status_set_error(F_directory_link_max); - if (errno == ENAMETOOLONG) return F_status_set_error(F_name); - if (errno == ENOENT) return F_status_set_error(F_file_found_not); - if (errno == ENOMEM) return F_status_set_error(F_memory_out); - if (errno == ENOSPC) return F_status_set_error(F_space_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); - - return F_status_set_error(F_failure); - } - - return F_none; + return private_f_directory_create(path, mode); } #endif // _di_f_directory_create_ @@ -37,27 +17,7 @@ extern "C" { if (at_id <= 0) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_ - if (mkdirat(at_id, path, mode) < 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 == EDQUOT) return F_status_set_error(F_filesystem_quota_block); - if (errno == EEXIST) return F_status_set_error(F_file_found); - 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 == EMLINK) return F_status_set_error(F_directory_link_max); - if (errno == ENAMETOOLONG) return F_status_set_error(F_name); - if (errno == ENOENT) return F_status_set_error(F_file_found_not); - if (errno == ENOMEM) return F_status_set_error(F_memory_out); - if (errno == ENOSPC) return F_status_set_error(F_space_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); - - return F_status_set_error(F_failure); - } - - return F_none; + return private_f_directory_create_at(at_id, path, mode); } #endif // _di_f_directory_create_at_ @@ -388,6 +348,106 @@ extern "C" { } #endif // _di_f_directory_remove_custom_ +#ifndef _di_f_directory_touch_ + f_return_status f_directory_touch(const f_string path, const mode_t mode) { + #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 (stat(path, &file_stat) < 0) { + + if (errno == ENOENT) { + return private_f_directory_create(path, mode); + } + + 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 == 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); + } + + if (utimensat(f_directory_at_current_working, path, 0, 0) < 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_directory_touch_ + +#ifndef _di_f_directory_touch_at_ + f_return_status f_directory_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)); + + if (fstatat(at_id, path, &file_stat, flag) < 0) { + + if (errno == ENOENT) { + return private_f_directory_create_at(at_id, path, mode); + } + + 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_status_set_error(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); + + return F_status_set_error(F_file_stat); + } + + 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_directory_touch_at_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_directory/c/directory.h b/level_0/f_directory/c/directory.h index 86fad4d..69104b5 100644 --- a/level_0/f_directory/c/directory.h +++ b/level_0/f_directory/c/directory.h @@ -454,6 +454,86 @@ extern "C" { extern f_return_status f_directory_remove_custom(const f_string path, const int recursion_max, const bool preserve, int (*custom) (const char *, const struct stat *, int, struct FTW *)); #endif // _di_f_directory_remove_custom_ +/** + * Update the files access and modification timestamp, creating the directory if it does not already exist. + * + * When the file is created, it is created as a directory file. + * + * @param path + * The path file name. + * @param mode + * The file mode to use when (directory) file is created. + * + * @return + * F_none on success. + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_file_open_max (with error bit) when system-wide max open files is reached. + * 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_interrupted (with error bit) when program received an interrupt signal, halting operation. + * F_loop (with error bit) on loop error. + * F_memory_out (with error bit) if out of memory. + * F_name (with error bit) on path name error. + * F_number_overflow (with error bit) on overflow error. + * F_parameter (with error bit) if a parameter is invalid. + * 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_failure (with error bit) for any other error. + * + * @see utimensat() + */ +#ifndef _di_f_directory_touch_ + extern f_return_status f_directory_touch(const f_string path, const mode_t mode); +#endif // _di_f_directory_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 directory 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 (directory) 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_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. + * F_file_found (with error bit) if a file aleady exists at the path (when calling utimensat()). + * F_file_open_max (with error bit) when system-wide max open files is reached. + * 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_interrupted (with error bit) when program received an interrupt signal, halting operation. + * F_loop (with error bit) on loop error. + * F_memory_out (with error bit) if out of memory. + * F_name (with error bit) on path name error. + * F_number_overflow (with error bit) on overflow error. + * F_parameter (with error bit) if a parameter is invalid. + * 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_failure (with error bit) for any other error. + * + * @see utimensat() + */ +#ifndef _di_f_directory_touch_at_ + extern f_return_status f_directory_touch_at(const int at_id, const f_string path, const mode_t mode, const int flag); +#endif // _di_f_directory_touch_at_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_directory/c/private-directory.c b/level_0/f_directory/c/private-directory.c index 4245232..cee8474 100644 --- a/level_0/f_directory/c/private-directory.c +++ b/level_0/f_directory/c/private-directory.c @@ -5,6 +5,59 @@ extern "C" { #endif +#if !defined(_di_f_directory_create_) || !defined(_di_f_directory_touch_) + f_return_status private_f_directory_create(const f_string path, const mode_t mode) { + + if (mkdir(path, mode) < 0) { + 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 == 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 == EMLINK) return F_status_set_error(F_directory_link_max); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENOENT) return F_status_set_error(F_file_found_not); + if (errno == ENOMEM) return F_status_set_error(F_memory_out); + if (errno == ENOSPC) return F_status_set_error(F_space_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); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // !defined(_di_f_directory_create_) || !defined(_di_f_directory_touch_) + +#if !defined(_di_f_directory_create_at_) || !defined(_di_f_directory_touch_at_) + f_return_status private_f_directory_create_at(const int at_id, const f_string path, const mode_t mode) { + + if (mkdirat(at_id, path, mode) < 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 == EDQUOT) return F_status_set_error(F_filesystem_quota_block); + if (errno == EEXIST) return F_status_set_error(F_file_found); + 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 == EMLINK) return F_status_set_error(F_directory_link_max); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENOENT) return F_status_set_error(F_file_found_not); + if (errno == ENOMEM) return F_status_set_error(F_memory_out); + if (errno == ENOSPC) return F_status_set_error(F_space_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); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // !defined(_di_f_directory_create_at_) || !defined(_di_f_directory_touch_at_) + #if !defined(_di_f_directory_remove_) int private_f_directory_remove_recursively(const char *path, const struct stat *file_stat, int type, struct FTW *entity) { if (entity->level == 0) return 0; diff --git a/level_0/f_directory/c/private-directory.h b/level_0/f_directory/c/private-directory.h index fdfca22..51eb71e 100644 --- a/level_0/f_directory/c/private-directory.h +++ b/level_0/f_directory/c/private-directory.h @@ -15,6 +15,77 @@ extern "C" { #endif /** + * Private implementation of f_directory_create(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param path + * The file path to the directory. + * @param mode + * The directory mode to use when creating. + * + * @return + * F_none on success. + * F_access_denied (with error bit) on access denied. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_directory_link_max (with error bit) max links limit reached or exceeded. + * F_file_found (with error bit) of a directory aleady exists at the path. + * F_file_found_not (with error bit) if a file within the path is not found (such as a broken symbolic link). + * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_loop (with error bit) on loop error. + * F_memory_out (with error bit) if out of memory. + * F_name (with error bit) on path name error. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if filesystem does not allow for removing. + * F_read_only (with error bit) if file is read-only. + * F_space_not (with error bit) if filesystem is out of space (or filesystem quota is reached). + * F_failure (with error bit) for any other error. + * + * @see f_directory_create() + * @see f_directory_touch() + */ +#if !defined(_di_f_directory_create_) || !defined(_di_f_directory_touch_) + extern f_return_status private_f_directory_create(const f_string path, const mode_t mode) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_directory_create_) || !defined(_di_f_directory_touch_) + +/** + * Private implementation of f_directory_create_at(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param at_id + * The parent directory, as an open directory file descriptor, in which path is relative to. + * @param path + * The file path to the directory. + * @param mode + * The directory mode to use when creating. + * + * @return + * F_none on success. + * F_access_denied (with error bit) on access denied. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. + * F_directory_link_max (with error bit) max links limit reached or exceeded. + * F_file_found (with error bit) of a directory aleady exists at the path. + * F_file_found_not (with error bit) if a file within the path is not found (such as a broken symbolic link). + * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_loop (with error bit) on loop error. + * F_memory_out (with error bit) if out of memory. + * F_name (with error bit) on path name error. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if filesystem does not allow for removing. + * F_read_only (with error bit) if file is read-only. + * F_space_not (with error bit) if filesystem is out of space (or filesystem quota is reached). + * F_failure (with error bit) for any other error. + * + * @see f_directory_create_at() + * @see f_directory_touch_at() + */ +#if !defined(_di_f_directory_create_at_) || !defined(_di_f_directory_touch_at_) + extern f_return_status private_f_directory_create_at(const int at_id, const f_string path, const mode_t mode) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_directory_create_at_) || !defined(_di_f_directory_touch_at_) + +/** * A special function intended to be used directly by nftw(). * * This is intended to be used by the specified function as a function pointer and therefore follows the required structure. -- 1.8.3.1