From 6e966ee73009569a44d9f3e23ac4c88f38e2ddd3 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Tue, 26 May 2020 23:45:31 -0500 Subject: [PATCH] Update: file and directory improvements, finish writing directory copy Organize directory header structure, adding directory_type.h. Add additional directory structures. Directory copy needs to report what fails, so provide a structure for reporting each failure. (There needs to be a verbose function for printing success/failure.) Remove de-reference from numerous file functions. POSIX denies certain operations of symlinks. During copy operations, do not apply mode to symlink as it would now apply mode changes to the de-referenced link. Allow block size to use default block size if set to 0 (convenience). Add missing return statement to file copy (without it socket file types would incorrectly report F_unsupported). Add link read functions so that symlink information may be obtained so that it can then be copied. Add comments about poorly written umask() as per POSIX standard (at least according to manpages). Other minor fixes and cleanups. --- build/level_0/settings | 2 +- build/monolithic/settings | 2 +- level_0/f_directory/c/directory.c | 1 + level_0/f_directory/c/directory.h | 142 +------------- level_0/f_directory/c/directory_type.h | 304 +++++++++++++++++++++++++++++ level_0/f_directory/data/build/settings | 2 +- level_0/f_file/c/file.c | 88 +++++---- level_0/f_file/c/file.h | 183 +++++++++++++++-- level_0/f_file/c/private-file.c | 89 +++++++-- level_0/f_file/c/private-file.h | 173 ++++++++++------ level_0/f_status/c/status_array.h | 85 ++++++++ level_0/f_status/data/build/settings | 2 +- level_0/f_string/c/string.h | 2 +- level_0/f_type/c/type_array.h | 34 +++- level_1/fl_directory/c/directory.c | 28 +-- level_1/fl_directory/c/directory.h | 36 +++- level_1/fl_directory/c/private-directory.c | 179 ++++++++++++++--- level_1/fl_directory/c/private-directory.h | 80 ++++++-- 18 files changed, 1097 insertions(+), 335 deletions(-) create mode 100644 level_0/f_directory/c/directory_type.h create mode 100644 level_0/f_status/c/status_array.h diff --git a/build/level_0/settings b/build/level_0/settings index 6527947..d4716e9 100644 --- a/build/level_0/settings +++ b/build/level_0/settings @@ -14,7 +14,7 @@ build_libraries_fll build_libraries_fll-level build_sources_library console.c conversion.c directory.c private-directory.c environment.c private-environment.c file.c private-file.c memory.c path.c pipe.c print.c utf.c private-utf.c build_sources_program -build_sources_headers color.h console.h conversion.h directory.h environment.h file.h fss.h memory.h path_fll.h path_filesystem.h path.h pipe.h print.h serialized.h socket.h status.h string.h type.h type_array.h +build_sources_headers color.h console.h conversion.h directory.h directory_type.h environment.h file.h fss.h memory.h path_fll.h path_filesystem.h path.h pipe.h print.h serialized.h socket.h status.h status_array.h string.h type.h type_array.h build_shared yes build_static yes diff --git a/build/monolithic/settings b/build/monolithic/settings index 83bc815..627e05e 100644 --- a/build/monolithic/settings +++ b/build/monolithic/settings @@ -13,7 +13,7 @@ build_libraries -lc build_libraries_fll build_sources_library level_0/console.c level_0/conversion.c level_0/directory.c level_0/private-directory.c level_0/environment.c level_0/private-environment.c level_0/file.c level_0/private-file.c level_0/memory.c level_0/path.c level_0/pipe.c level_0/print.c level_0/utf.c level_0/private-utf.c level_1/color.c level_1/console.c level_1/directory.c level_1/private-directory.c level_1/file.c level_1/fss.c level_1/fss_basic.c level_1/fss_basic_list.c level_1/fss_extended.c level_1/fss_extended_list.c level_1/print.c level_1/serialized.c level_1/private-serialized.c level_1/socket.c level_1/status.c level_1/string.c level_1/private-string.c level_1/utf.c level_1/private-utf.c level_1/utf_file.c level_1/private-utf_file.c level_2/execute.c level_2/private-execute.c level_2/file.c level_2/fss.c level_2/fss_basic.c level_2/fss_basic_list.c level_2/fss_extended.c level_2/fss_extended_list.c level_2/fss_status.c level_2/program.c level_2/status.c build_sources_program -build_sources_headers level_0/color.h level_0/console.h level_0/conversion.h level_0/directory.h level_0/environment.h level_0/file.h level_0/fss.h level_0/memory.h level_0/path_fll.h level_0/path_filesystem.h level_0/path.h level_0/pipe.h level_0/print.h level_0/serialized.h level_0/socket.h level_0/status.h level_0/string.h level_0/type.h level_0/type_array.h level_0/utf.h level_1/color.h level_1/console.h level_1/directory.h level_1/file.h level_1/fss.h level_1/fss_basic.h level_1/fss_basic_list.h level_1/fss_status.h level_1/fss_extended.h level_1/fss_extended_list.h level_1/fss_macro.h level_1/print.h level_1/serialized.h level_1/socket.h level_1/status.h level_1/string.h level_1/utf.h level_1/utf_file.h level_2/execute.h level_2/file.h level_2/fss.h level_2/fss_basic.h level_2/fss_basic_list.h level_2/fss_extended.h level_2/fss_extended_list.h level_2/fss_status.h level_2/program.h level_2/status.h +build_sources_headers level_0/color.h level_0/console.h level_0/conversion.h level_0/directory.h level_0/directory_type.h level_0/environment.h level_0/file.h level_0/fss.h level_0/memory.h level_0/path_fll.h level_0/path_filesystem.h level_0/path.h level_0/pipe.h level_0/print.h level_0/serialized.h level_0/socket.h level_0/status.h level_0/status_array.h level_0/string.h level_0/type.h level_0/type_array.h level_0/utf.h level_1/color.h level_1/console.h level_1/directory.h level_1/file.h level_1/fss.h level_1/fss_basic.h level_1/fss_basic_list.h level_1/fss_status.h level_1/fss_extended.h level_1/fss_extended_list.h level_1/fss_macro.h level_1/print.h level_1/serialized.h level_1/socket.h level_1/status.h level_1/string.h level_1/utf.h level_1/utf_file.h level_2/execute.h level_2/file.h level_2/fss.h level_2/fss_basic.h level_2/fss_basic_list.h level_2/fss_extended.h level_2/fss_extended_list.h level_2/fss_status.h level_2/program.h level_2/status.h build_sources_bash build_sources_settings build_shared yes diff --git a/level_0/f_directory/c/directory.c b/level_0/f_directory/c/directory.c index c926cc7..dd36634 100644 --- a/level_0/f_directory/c/directory.c +++ b/level_0/f_directory/c/directory.c @@ -7,6 +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); diff --git a/level_0/f_directory/c/directory.h b/level_0/f_directory/c/directory.h index 6eb8edd..8b84d43 100644 --- a/level_0/f_directory/c/directory.h +++ b/level_0/f_directory/c/directory.h @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -56,141 +57,6 @@ extern "C" { #endif // _di_f_directory_limitations_ /** - * A structure representing a listing of paths found within a directory. - * - * Each property represents a set of paths grouped by directory entity file type. - */ -#ifndef _di_f_directory_listing_ - typedef struct { - f_string_dynamics block; // S_IFBLK - f_string_dynamics character; // S_IFCHR - f_string_dynamics directory; // S_IFDIR - f_string_dynamics regular; // S_IFREG - f_string_dynamics link; // S_IFLNK - f_string_dynamics pipe; // S_IFIFO - f_string_dynamics socket; // S_IFSOCK - f_string_dynamics unknown; - } f_directory_listing; - - #define f_directory_listing_initialize { \ - f_string_dynamics_initialize, \ - f_string_dynamics_initialize, \ - f_string_dynamics_initialize, \ - f_string_dynamics_initialize, \ - f_string_dynamics_initialize, \ - f_string_dynamics_initialize, \ - f_string_dynamics_initialize, \ - f_string_dynamics_initialize, \ - } - - #define f_macro_directory_listing_delete(status, listing) \ - f_macro_string_dynamics_delete(status, listing.block) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.character) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.directory) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.regular) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.link) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.pipe) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.socket) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.unknown) - - #define f_macro_directory_listing_destroy(status, listing) \ - f_macro_string_dynamics_destroy(status, listing.block) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.character) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.directory) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.regular) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.link) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.pipe) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.socket) \ - if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.unknown) - - #define f_macro_directory_listing_delete_simple(listing) \ - f_macro_string_dynamics_delete_simple(listing.block) \ - f_macro_string_dynamics_delete_simple(listing.character) \ - f_macro_string_dynamics_delete_simple(listing.directory) \ - f_macro_string_dynamics_delete_simple(listing.regular) \ - f_macro_string_dynamics_delete_simple(listing.link) \ - f_macro_string_dynamics_delete_simple(listing.pipe) \ - f_macro_string_dynamics_delete_simple(listing.socket) \ - f_macro_string_dynamics_delete_simple(listing.unknown) - - #define f_macro_directory_listing_destroy_simple(listing) \ - f_macro_string_dynamics_destroy_simple(listing.block) \ - f_macro_string_dynamics_destroy_simple(listing.character) \ - f_macro_string_dynamics_destroy_simple(listing.directory) \ - f_macro_string_dynamics_destroy_simple(listing.regular) \ - f_macro_string_dynamics_destroy_simple(listing.link) \ - f_macro_string_dynamics_destroy_simple(listing.pipe) \ - f_macro_string_dynamics_destroy_simple(listing.socket) \ - f_macro_string_dynamics_destroy_simple(listing.unknown) -#endif // _di_f_directory_listing_ - -/** - * A structure representing a set of modes intended to be used by directory operations. - * - * A small set of macros are provider to help simplify assigning modes. - * - * The pipe (S_IFIFO) is intentionally not supported. - */ -#ifndef _di_f_directory_mode_ - typedef struct { - mode_t block; // S_IFBLK - mode_t character; // S_IFCHR - mode_t directory; // S_IFDIR - mode_t regular; // S_IFREG - mode_t link; // S_IFLNK - mode_t socket; // S_IFSOCK - mode_t unknown; - } f_directory_mode; - - #define f_directory_mode_initialize { \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - 0, \ - } - - #define f_macro_directory_mode_set_all(modes, mode) \ - modes.block = mode; \ - modes.character = mode; \ - modes.directory = mode; \ - modes.regular = mode; \ - modes.link = mode; \ - modes.socket = mode; \ - modes.unknown = mode; - - #define f_macro_directory_mode_set_common(modes, mode_directory, mode_file, mode_link) \ - modes.directory = mode_directory; \ - modes.regular = mode_file; \ - modes.link = mode_link; - - #define f_macro_directory_mode_set_uncommon(modes, mode_block, mode_character, mode_socket, mode_unknown) \ - modes.block = mode_block; \ - modes.character = mode_character; \ - modes.socket = mode_socket; \ - modes.unknown = mode_unknown; -#endif // _di_f_directory_mode_ - -/** - * A structure representing a directory. - * - * @todo review this and decide to keep and use it or just remove it. - */ -#ifndef _di_f_directory_ - typedef struct { - struct dirent entity; - f_directory_listing content; - } f_directory; - - #define f_directory_initialize { \ - { 0, 0, 0, 0, 0 }, \ - f_directory_listing_initialize, \ - } -#endif // _di_f_directory_ - -/** * Create a directory at the given path. * * @param path @@ -261,7 +127,7 @@ extern "C" { * The path file name. * * @return - * t_true if path was found and path is a directory (or a symlink to a directory). + * F_true if path was found and path is a directory (or a symlink to a directory). * F_false if path was found and path is not a directory. * F_file_found_not if the path was not found. * F_name (with error bit) if the name is somehow invalid. @@ -284,7 +150,7 @@ extern "C" { * The path file name. * * @return - * t_true if path was found and path is a directory. + * F_true if path was found and path is a directory. * F_false if path was found and path is not a directory (this includes symlinks). * F_file_found_not if the path was not found. * F_name (with error bit) if the name is somehow invalid. @@ -312,7 +178,7 @@ extern "C" { * Set to FALSE to not follow. * * @return - * t_true if path was found and path is a directory. + * F_true if path was found and path is a directory. * F_false if path was found and path is not a directory. * F_file_found_not if the path was not found. * F_name (with error bit) if the name is somehow invalid. diff --git a/level_0/f_directory/c/directory_type.h b/level_0/f_directory/c/directory_type.h new file mode 100644 index 0000000..86884e8 --- /dev/null +++ b/level_0/f_directory/c/directory_type.h @@ -0,0 +1,304 @@ +/** + * FLL - Level 0 + * + * Project: Directory + * API Version: 0.5 + * Licenses: lgplv2.1 + * + * Provides directory related data types. + */ +#ifndef _F_directory_type_h +#define _F_directory_type_h + +// fll-0 includes +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A structure representing a listing of paths found within a directory. + * + * Each property represents a set of paths grouped by directory entity file type. + */ +#ifndef _di_f_directory_listing_ + typedef struct { + f_string_dynamics block; // S_IFBLK + f_string_dynamics character; // S_IFCHR + f_string_dynamics directory; // S_IFDIR + f_string_dynamics regular; // S_IFREG + f_string_dynamics link; // S_IFLNK + f_string_dynamics pipe; // S_IFIFO + f_string_dynamics socket; // S_IFSOCK + f_string_dynamics unknown; + } f_directory_listing; + + #define f_directory_listing_initialize { \ + f_string_dynamics_initialize, \ + f_string_dynamics_initialize, \ + f_string_dynamics_initialize, \ + f_string_dynamics_initialize, \ + f_string_dynamics_initialize, \ + f_string_dynamics_initialize, \ + f_string_dynamics_initialize, \ + f_string_dynamics_initialize, \ + } + + #define f_macro_directory_listing_delete(status, listing) \ + f_macro_string_dynamics_delete(status, listing.block) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.character) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.directory) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.regular) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.link) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.pipe) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.socket) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.unknown) + + #define f_macro_directory_listing_destroy(status, listing) \ + f_macro_string_dynamics_destroy(status, listing.block) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.character) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.directory) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.regular) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.link) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.pipe) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_destroy(status, listing.socket) \ + if (!F_status_is_error(status)) f_macro_string_dynamics_delete(status, listing.unknown) + + #define f_macro_directory_listing_delete_simple(listing) \ + f_macro_string_dynamics_delete_simple(listing.block) \ + f_macro_string_dynamics_delete_simple(listing.character) \ + f_macro_string_dynamics_delete_simple(listing.directory) \ + f_macro_string_dynamics_delete_simple(listing.regular) \ + f_macro_string_dynamics_delete_simple(listing.link) \ + f_macro_string_dynamics_delete_simple(listing.pipe) \ + f_macro_string_dynamics_delete_simple(listing.socket) \ + f_macro_string_dynamics_delete_simple(listing.unknown) + + #define f_macro_directory_listing_destroy_simple(listing) \ + f_macro_string_dynamics_destroy_simple(listing.block) \ + f_macro_string_dynamics_destroy_simple(listing.character) \ + f_macro_string_dynamics_destroy_simple(listing.directory) \ + f_macro_string_dynamics_destroy_simple(listing.regular) \ + f_macro_string_dynamics_destroy_simple(listing.link) \ + f_macro_string_dynamics_destroy_simple(listing.pipe) \ + f_macro_string_dynamics_destroy_simple(listing.socket) \ + f_macro_string_dynamics_destroy_simple(listing.unknown) +#endif // _di_f_directory_listing_ + +/** + * A structure representing a set of modes intended to be used by directory operations. + * + * A small set of macros are provider to help simplify assigning modes. + * + * The pipe (S_IFIFO) is intentionally not supported. + */ +#ifndef _di_f_directory_mode_ + typedef struct { + mode_t block; // S_IFBLK + mode_t character; // S_IFCHR + mode_t directory; // S_IFDIR + mode_t regular; // S_IFREG + mode_t link; // S_IFLNK + mode_t socket; // S_IFSOCK + mode_t unknown; + } f_directory_mode; + + #define f_directory_mode_initialize { \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + } + + #define f_macro_directory_mode_set_default(modes) \ + modes.block = f_file_mode_all_rw; \ + modes.character = f_file_mode_all_rw; \ + modes.directory = f_file_mode_all_rwx; \ + modes.regular = f_file_mode_all_rw; \ + modes.link = f_file_mode_all_rw; \ + modes.socket = f_file_mode_all_rw; \ + modes.unknown = f_file_mode_all_rw; + + #define f_macro_directory_mode_set_default_umask(modes, mask) \ + modes.block = f_file_mode_all_rw & ~mask; \ + modes.character = f_file_mode_all_rw & ~mask; \ + modes.directory = f_file_mode_all_rwx & ~mask; \ + modes.regular = f_file_mode_all_rw & ~mask; \ + modes.link = f_file_mode_all_rw & ~mask; \ + modes.socket = f_file_mode_all_rw & ~mask; \ + modes.unknown = f_file_mode_all_rw & ~mask; + + #define f_macro_directory_mode_set_all(modes, mode) \ + modes.block = mode; \ + modes.character = mode; \ + modes.directory = mode; \ + modes.regular = mode; \ + modes.link = mode; \ + modes.socket = mode; \ + modes.unknown = mode; + + #define f_macro_directory_mode_set_common(modes, mode_directory, mode_file, mode_link) \ + modes.directory = mode_directory; \ + modes.regular = mode_file; \ + modes.link = mode_link; + + #define f_macro_directory_mode_set_uncommon(modes, mode_block, mode_character, mode_socket, mode_unknown) \ + modes.block = mode_block; \ + modes.character = mode_character; \ + modes.socket = mode_socket; \ + modes.unknown = mode_unknown; +#endif // _di_f_directory_mode_ + +/** + * An association of a path and a status code. + * + * The allocation macros apply to the path. + * + * status: the status code. + * path: the dynamically allocated path associated with the status code. + */ +#ifndef _di_f_directory_status_ + typedef struct { + f_status status; + f_string_dynamic path; + } f_directory_status; + + #define f_directory_status_initialize { 0, f_string_dynamic_initialize } + + #define f_macro_directory_status_clear(directory) \ + directory.status = 0; \ + f_macro_string_dynamic_clear(directory.path); + + #define f_macro_directory_status_new(status, statuses, length) f_macro_string_dynamic_new(status, statuses.path, length) + + #define f_macro_directory_status_delete(status, statuses) f_macro_string_dynamic_delete(status, statuses.path) + #define f_macro_directory_status_destroy(status, statuses) f_macro_string_dynamic_destroy(status, statuses.path) + + #define f_macro_directory_status_delete_simple(statuses) f_macro_string_dynamic_delete_simple(statuses.path) + #define f_macro_directory_status_destroy_simple(statuses) f_macro_string_dynamic_destroy_simple(statuses.path) + + #define f_macro_directory_status_resize(status, statuses, new_length) f_macro_string_dynamic_resize(status, statuses.path, new_length) + #define f_macro_directory_status_adjust(status, statuses, new_length) f_macro_string_dynamic_adjust(status, statuses.path, new_length) +#endif // _di_f_directory_status_ + +/** + * An array of directory status. + * + * array: An array of directory status. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ +#ifndef _di_f_directory_statuss_ + typedef struct { + f_directory_status *array; + f_array_length size; + f_array_length used; + } f_directory_statuss; + + #define f_directory_statuss_initialize { 0, 0, 0 } + + #define f_macro_directory_statuss_clear(structures) f_macro_memory_structures_clear(structures) + + #define f_macro_directory_statuss_new(status, structures, length) f_macro_memory_structures_new(status, structures, f_directory_status, length) + + #define f_macro_directory_statuss_delete(status, structures) \ + status = F_none; \ + structures.used = structures.size; \ + while (structures.used > 0) { \ + structures.used--; \ + f_macro_directory_status_delete(status, structures.array[structures.used]); \ + if (status != F_none) break; \ + } \ + if (status == F_none) status = f_memory_delete((void **) & structures.array, sizeof(f_directory_status), structures.size); \ + if (status == F_none) structures.size = 0; + + #define f_macro_directory_statuss_destroy(status, structures) \ + status = F_none; \ + structures.used = structures.size; \ + while (structures.used > 0) { \ + structures.used--; \ + f_macro_directory_status_destroy(status, structures.array[structures.used]); \ + if (status != F_none) break; \ + } \ + if (status == F_none) status = f_memory_destroy((void **) & structures.array, sizeof(f_directory_status), structures.size); \ + if (status == F_none) structures.size = 0; + + #define f_macro_directory_statuss_delete_simple(structures) \ + structures.used = structures.size; \ + while (structures.used > 0) { \ + structures.used--; \ + f_macro_directory_status_delete_simple(structures.array[structures.used]); \ + } \ + if (structures.used == 0) { \ + if (f_memory_delete((void **) & structures.array, sizeof(f_directory_status), structures.size)) { \ + structures.size = 0; \ + } \ + } + + #define f_macro_directory_statuss_destroy_simple(structures) \ + structures.used = structures.size; \ + while (structures.used > 0) { \ + structures.used--; \ + f_macro_directory_status_destroy_simple(structures.array[structures.used]); \ + } \ + if (structures.used == 0) { \ + if (f_memory_destroy((void **) & structures.array, sizeof(f_directory_status), structures.size)) { \ + structures.size = 0; \ + } \ + } + + #define f_macro_directory_statuss_resize(status, structures, new_length) \ + status = F_none; \ + if (new_length < structures.size) { \ + f_array_length i = structures.size - new_length; \ + for (; i < structures.size; i++) { \ + f_macro_directory_status_delete(status, structures.array[i]); \ + if (status != F_none) break; \ + } \ + } \ + if (status == F_none) status = f_memory_resize((void **) & structures.array, sizeof(f_directory_status), structures.size, new_length); \ + if (status == F_none) { \ + if (new_length > structures.size) { \ + f_array_length i = structures.size; \ + for (; i < new_length; i++) { \ + memset(&structures.array[i], 0, sizeof(f_directory_status)); \ + } \ + } \ + structures.size = new_length; \ + if (structures.used > structures.size) structures.used = new_length; \ + } + + #define f_macro_directory_statuss_adjust(status, structures, new_length) \ + status = F_none; \ + if (new_length < structures.size) { \ + f_array_length i = structures.size - new_length; \ + for (; i < structures.size; i++) { \ + f_macro_directory_status_destroy(status, structures.array[i]); \ + if (status != F_none) break; \ + } \ + } \ + if (status == F_none) status = f_memory_adjust((void **) & structures.array, sizeof(f_directory_status), structures.size, new_length); \ + if (status == F_none) { \ + if (new_length > structures.size) { \ + f_array_length i = structures.size; \ + for (; i < new_length; i++) { \ + memset(&structures.array[i], 0, sizeof(f_directory_status)); \ + } \ + } \ + structures.size = new_length; \ + if (structures.used > structures.size) structures.used = new_length; \ + } +#endif // _di_f_directory_statuss_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _F_directory_type_h diff --git a/level_0/f_directory/data/build/settings b/level_0/f_directory/data/build/settings index 0f9b9b2..c18432e 100644 --- a/level_0/f_directory/data/build/settings +++ b/level_0/f_directory/data/build/settings @@ -13,7 +13,7 @@ build_libraries -lc build_libraries_fll -lf_memory build_sources_library directory.c private-directory.c build_sources_program -build_sources_headers directory.h +build_sources_headers directory.h directory_type.h build_sources_bash build_sources_settings build_shared yes diff --git a/level_0/f_file/c/file.c b/level_0/f_file/c/file.c index 5e63d73..c60e7da 100644 --- a/level_0/f_file/c/file.c +++ b/level_0/f_file/c/file.c @@ -7,10 +7,6 @@ extern "C" { #ifndef _di_f_file_access_ f_return_status f_file_access(const f_string path) { - #ifndef _di_level_0_parameter_checking_ - if (path == 0) return F_status_set_error(F_parameter); - #endif // _di_level_0_parameter_checking_ - if (access(path, F_OK)) { if (errno == ENOENT) return F_false; if (errno == ENAMETOOLONG) return F_status_set_error(F_name); @@ -29,8 +25,8 @@ extern "C" { #endif // _di_f_file_access_ #ifndef _di_f_file_change_mode_ - f_return_status f_file_change_mode(const f_string path, const mode_t mode, const bool dereference) { - return private_f_file_change_mode(path, mode, dereference); + f_return_status f_file_change_mode(const f_string path, const mode_t mode) { + return private_f_file_change_mode(path, mode); } #endif // _di_f_file_change_mode_ @@ -54,10 +50,6 @@ extern "C" { #ifndef _di_f_file_copy_ f_return_status f_file_copy(const f_string source, const f_string destination, const mode_t mode, const f_number_unsigned size_block, const bool exclusive) { - #ifndef _di_level_0_parameter_checking_ - if (size_block == 0) return F_status_set_error(F_parameter); - #endif // _di_level_0_parameter_checking_ - f_status status = F_none; struct stat source_stat; @@ -66,19 +58,19 @@ extern "C" { memset(&source_stat, 0, sizeof(struct stat)); - status = private_f_file_stat(source, &source_stat, F_false); + status = private_f_file_stat(source, F_false, &source_stat); if (F_status_is_error(status)) return status; if (f_macro_file_type_is_regular(source_stat.st_mode)) { - status = private_f_file_create(destination, mode_access, exclusive, F_false); + status = private_f_file_create(destination, mode_access, exclusive); if (F_status_is_error(status)) return status; if (!exclusive) { - status = private_f_file_change_mode(destination, mode_access, F_false); + status = private_f_file_change_mode(destination, mode_access); if (F_status_is_error(status)) return status; } - return private_f_file_copy_content(source, destination, size_block); + return private_f_file_copy_content(source, destination, size_block == 0 ? f_file_default_read_size : size_block); } else if (f_macro_file_type_is_directory(source_stat.st_mode)) { status = private_f_file_create_directory(destination, mode_access); @@ -89,13 +81,23 @@ extern "C" { } } - status = private_f_file_change_mode(destination, mode_access, F_false); + status = private_f_file_change_mode(destination, mode_access); if (F_status_is_error(status)) return status; return F_none; } else if (f_macro_file_type_is_link(source_stat.st_mode)) { - status = private_f_file_link(destination, source); + f_string_dynamic target = f_string_dynamic_initialize; + + status = private_f_file_link_read(source, source_stat, &target); + if (F_status_is_error(status)) { + f_macro_string_dynamic_delete_simple(target); + return status; + } + + status = private_f_file_link(target.string, destination); + + f_macro_string_dynamic_delete_simple(target); if (F_status_is_error(status)) { if (F_status_set_fine(status) != F_file_found || exclusive) { @@ -103,9 +105,6 @@ extern "C" { } } - status = private_f_file_change_mode(destination, mode_access, F_false); - if (F_status_is_error(status)) return status; - return F_none; } else if (f_macro_file_type_is_fifo(source_stat.st_mode)) { @@ -117,7 +116,7 @@ extern "C" { } } - status = private_f_file_change_mode(destination, mode_access, F_false); + status = private_f_file_change_mode(destination, mode_access); if (F_status_is_error(status)) return status; return F_none; @@ -131,9 +130,10 @@ extern "C" { } } - status = private_f_file_change_mode(destination, mode_access, F_false); + status = private_f_file_change_mode(destination, mode_access); if (F_status_is_error(status)) return status; + return F_none; } else if (f_macro_file_type_is_block(source_stat.st_mode) || f_macro_file_type_is_character(source_stat.st_mode)) { status = private_f_file_create_node(destination, f_macro_file_type_get(source_stat.st_mode) | mode_access, source_stat.st_rdev); @@ -144,7 +144,7 @@ extern "C" { } } - status = private_f_file_change_mode(destination, mode_access, F_false); + status = private_f_file_change_mode(destination, mode_access); if (F_status_is_error(status)) return status; return F_none; @@ -156,24 +156,20 @@ extern "C" { #ifndef _di_f_file_clone_ f_return_status f_file_clone(const f_string source, const f_string destination, const f_number_unsigned size_block, const bool exclusive, const bool roles) { - #ifndef _di_level_0_parameter_checking_ - if (size_block == 0) return F_status_set_error(F_parameter); - #endif // _di_level_0_parameter_checking_ - f_status status = F_none; struct stat source_stat; memset(&source_stat, 0, sizeof(struct stat)); - status = private_f_file_stat(source, &source_stat, F_false); + status = private_f_file_stat(source, F_false, &source_stat); if (F_status_is_error(status)) return status; if (f_macro_file_type_is_regular(source_stat.st_mode)) { - status = private_f_file_create(destination, source_stat.st_mode, exclusive, F_false); + status = private_f_file_create(destination, source_stat.st_mode, exclusive); if (F_status_is_error(status)) return status; if (!exclusive) { - status = private_f_file_change_mode(destination, source_stat.st_mode, F_false); + status = private_f_file_change_mode(destination, source_stat.st_mode); if (F_status_is_error(status)) return status; } @@ -182,7 +178,7 @@ extern "C" { if (F_status_is_error(status)) return status; } - return private_f_file_copy_content(source, destination, size_block); + return private_f_file_copy_content(source, destination, size_block == 0 ? f_file_default_read_size : size_block); } else if (f_macro_file_type_is_link(source_stat.st_mode)) { status = private_f_file_link(destination, source); @@ -193,7 +189,7 @@ extern "C" { return status; } - status = private_f_file_change_mode(destination, source_stat.st_mode, F_false); + status = private_f_file_change_mode(destination, source_stat.st_mode); if (F_status_is_error(status)) return status; if (roles) { @@ -219,14 +215,14 @@ extern "C" { #endif // _di_f_file_close_ #ifndef _di_f_file_create_ - f_return_status f_file_create(const f_string path, const mode_t mode, const bool exclusive, const bool dereference) { - return private_f_file_create(path, mode, exclusive, dereference); + f_return_status f_file_create(const f_string path, const mode_t mode, const bool exclusive) { + return private_f_file_create(path, mode, exclusive); } #endif // _di_f_file_create_ #ifndef _di_f_file_create_at_ - f_return_status f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive, const bool dereference) { - return private_f_file_create_at(at_id, path, mode, exclusive, dereference); + f_return_status f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive) { + return private_f_file_create_at(at_id, path, mode, exclusive); } #endif // _di_f_file_create_at_ @@ -407,6 +403,28 @@ extern "C" { } #endif // _di_f_file_link_hard_at_ +#ifndef _di_f_file_link_read_ + f_return_status f_file_link_read(const f_string path, const struct stat link_stat, f_string_dynamic *target) { + #ifndef _di_level_0_parameter_checking_ + if (link_stat.st_size == 0) return F_status_set_error(F_parameter); + if (target == 0) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + return private_f_file_link_read(path, link_stat, target); + } +#endif // _di_f_file_link_read_ + +#ifndef _di_f_file_link_read_at_ + f_return_status f_file_link_read_at(const int at_id, const f_string path, const struct stat link_stat, f_string_dynamic *target) { + #ifndef _di_level_0_parameter_checking_ + if (link_stat.st_size == 0) return F_status_set_error(F_parameter); + if (target == 0) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + return private_f_file_link_read_at(at_id, path, link_stat, target); + } +#endif // _di_f_file_link_read_at_ + #ifndef _di_f_file_is_ f_return_status f_file_is(const f_string path, const int type) { struct stat file_stat; diff --git a/level_0/f_file/c/file.h b/level_0/f_file/c/file.h index a088b1a..87914dc 100644 --- a/level_0/f_file/c/file.h +++ b/level_0/f_file/c/file.h @@ -286,13 +286,13 @@ extern "C" { /** * Change mode of a given file at the specified path. * + * This does not set mode based on umask(), be sure to apply umask if so desired. + * (such as: mode & ~mask). + * * @param path * The path file name. * @param mode * The new mode to use. - * @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. @@ -311,12 +311,15 @@ extern "C" { * @see chmod() */ #ifndef _di_f_file_change_mode_ - extern f_return_status f_file_change_mode(const f_string path, const mode_t mode, const bool dereference); + extern f_return_status f_file_change_mode(const f_string path, const mode_t mode); #endif // _di_f_file_change_mode_ /** * Change mode of a given file at the specified path. * + * This does not set mode based on umask(), be sure to apply umask if so desired. + * (such as: mode & ~mask). + * * @param at_id * The parent directory, as an open directory file descriptor, in which path is relative to. * @param path @@ -325,6 +328,8 @@ extern "C" { * The new mode to use. * @param flags * Any valid flag, such as AT_EMPTY_PATH, AT_NO_AUTOMOUNT, or AT_SYMLINK_NO_FOLLOW. + * Warning: chmod on a symolic link is currently not supported in POSIX. + * Therefore AT_SYMLINK_NO_FOLLOW does nothing at this time and is assumed to always be TRUE. * * @return * F_none on success. @@ -417,6 +422,11 @@ extern "C" { * * For directory file types, this will only copy the directory itself and not its contents. * + * This does not copy unknown file types. + * + * This does not set mode based on umask(), be sure to apply umask if so desired. + * (such as: mode & ~mask). + * * @param source * The path to the file to copy from. * @param destination @@ -425,7 +435,7 @@ extern "C" { * The file mode assigned to the destination file. * @param size_block * The default number of chunks to read at a time with each chunk being 1-byte. - * Must be greater than 0. + * Set to 0 to use default block read size. * @param exclusive * If TRUE, will fail when file already exists. * If FALSE, will not fail if file already exists (existing file will be replaced). @@ -465,6 +475,8 @@ extern "C" { * * For directory file types, this will only copy the directory itself and not its contents. * + * This does not copy unknown file types. + * * @todo provide a return status for when owner/role cannot be assigned. * This will be returned when complete so that caller can decide if this is an error or not. * @@ -474,7 +486,7 @@ extern "C" { * The path to copy to. * @param size_block * The default number of chunks to read at a time with each chunk being 1-byte. - * Must be greater than 0. + * Set to 0 to use default block read size. * @param exclusive * If TRUE, will fail when file already exists. * If FALSE, will not fail if file already exists (existing file will be replaced). @@ -547,10 +559,6 @@ extern "C" { * @param exclusive * If TRUE, will fail when file already exists. * If FALSE, will not fail if file already exists. - * @param dereference - * Set to TRUE to dereferenc symlinks (often is what is desired). - * Set to FALSE to fail if the path is a symbolic link. - * This does not write symbolic links. (@todo add function f_create_link() for creating symbolic links.) * * @return * F_none on success. @@ -575,7 +583,7 @@ extern "C" { * @see open() */ #ifndef _di_f_file_create_ - extern f_return_status f_file_create(const f_string path, const mode_t mode, const bool exclusive, const bool dereference); + extern f_return_status f_file_create(const f_string path, const mode_t mode, const bool exclusive); #endif // _di_f_file_create_ /** @@ -592,10 +600,6 @@ extern "C" { * @param exclusive * If TRUE, will fail when file already exists. * If FALSE, will not fail if file already exists. - * @param dereference - * Set to TRUE to dereferenc symlinks (often is what is desired). - * Set to FALSE to fail if the path is a symbolic link. - * This does not write symbolic links. (@todo add function f_create_link() for creating symbolic links.) * * @return * F_none on success. @@ -620,7 +624,7 @@ extern "C" { * @see openat() */ #ifndef _di_f_file_create_at_ - extern f_return_status f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive, const bool dereference); + extern f_return_status f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive); #endif // _di_f_file_create_at_ /** @@ -846,7 +850,7 @@ extern "C" { * The path file name. * * @return - * t_true if path was found. + * F_true if path was found. * F_false if path was not found. * F_name (with error bit) if the name is somehow invalid. * F_memory_out (with error bit) if out of memory. @@ -927,7 +931,7 @@ extern "C" { * The type of the file * * @return - * t_true if path was found and path is type. + * F_true if path was found and path is type. * F_false if path was found and path is not type. * F_file_found_not if the path was not found. * F_name (with error bit) if the name is somehow invalid. @@ -958,7 +962,7 @@ extern "C" { * Set to FALSE to not follow. * * @return - * t_true if path was found and path is type. + * F_true if path was found and path is type. * F_false if path was found and path is not type. * F_file_found_not if the path was not found. * F_name (with error bit) if the name is somehow invalid. @@ -987,6 +991,23 @@ extern "C" { * A path to the link that does the pointing. * * @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_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_name (with error bit) on path name error. + * F_buffer (with error bit) if the buffer is invalid. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_loop (with error bit) on loop error. + * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_memory_out (with error bit) if out of memory. + * 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_busy (with error bit) if filesystem is too busy to perforrm write. + * F_failure (with error bit) for any other (symlink()) error. * * @see symlink() */ @@ -1008,6 +1029,24 @@ extern "C" { * A path to the link that does the pointing. * * @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_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_name (with error bit) on path name error. + * F_buffer (with error bit) if the buffer is invalid. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_loop (with error bit) on loop error. + * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_memory_out (with error bit) if out of memory. + * 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_busy (with error bit) if filesystem is too busy to perforrm write. + * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_failure (with error bit) for any other (symlinkat()) error. * * @see symlinkat() */ @@ -1026,6 +1065,23 @@ extern "C" { * A path to the link that does the pointing. * * @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_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_name (with error bit) on path name error. + * F_buffer (with error bit) if the buffer is invalid. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_loop (with error bit) on loop error. + * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_memory_out (with error bit) if out of memory. + * 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_busy (with error bit) if filesystem is too busy to perforrm write. + * F_failure (with error bit) for any other (link()) error. * * @see link() */ @@ -1050,6 +1106,24 @@ extern "C" { * Any valid flag, such as AT_EMPTY_PATH, AT_NO_AUTOMOUNT, or AT_SYMLINK_NO_FOLLOW. * * @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_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_name (with error bit) on path name error. + * F_buffer (with error bit) if the buffer is invalid. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_loop (with error bit) on loop error. + * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_memory_out (with error bit) if out of memory. + * 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_busy (with error bit) if filesystem is too busy to perforrm write. + * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_failure (with error bit) for any other (linkat()) error. * * @see linkat() */ @@ -1058,6 +1132,77 @@ extern "C" { #endif // _di_f_file_link_hard_at_ /** + * Get the target a given link points to. + * + * This does not require access on the file itself. + * This only requires access via the parent directories in the path. + * + * @param path + * The path file name. + * @param link_stat + * The link file statistics. + * @param target + * Will be replaced with the path in which the link points to. + * Will be NULL terminated with the NULL at target.string[target.used]; + * + * @return + * F_none on success. + * F_parameter (with error bit) if a parameter is invalid. + * F_string_too_large (with error bit) if link target path is too large for string buffer size. + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_input_output (with error bit) on I/O error. + * F_loop (with error bit) on loop error. + * F_name (with error bit) on path name error. + * F_file_found_not (with error bit) if the file at path was not found. + * F_memory_out (with error bit) if out of memory. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_failure (with error bit) for any other (readlink()) error. + * + * @see readlink() + */ +#ifndef _di_f_file_link_read_ + extern f_return_status f_file_link_read(const f_string path, const struct stat link_stat, f_string_dynamic *target); +#endif // _di_f_file_link_read_ + +/** + * Get the target a given link points to. + * + * This does not require access on the file itself. + * This only requires access via the parent directories in the path. + * + * @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 link_stat + * The link file statistics. + * @param target + * Will be replaced with the path in which the link points to. + * Will be NULL terminated with the NULL at target.string[target.used]; + * + * @return + * F_none on success. + * F_parameter (with error bit) if a parameter is invalid. + * F_string_too_large (with error bit) if link target path is too large for string buffer size. + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_input_output (with error bit) on I/O error. + * F_loop (with error bit) on loop error. + * F_name (with error bit) on path name error. + * F_file_found_not (with error bit) if the file at path was not found. + * F_memory_out (with error bit) if out of memory. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_failure (with error bit) for any other (readlinkat()) error. + * + * @see readlinkat() + */ +#ifndef _di_f_file_link_read_at_ + extern f_return_status f_file_link_read_at(const int at_id, const f_string path, const struct stat link_stat, f_string_dynamic *target); +#endif // _di_f_file_link_read_at_ + +/** * Open a particular file and save its stream. * * This will open the file and obtain the file descriptor. diff --git a/level_0/f_file/c/private-file.c b/level_0/f_file/c/private-file.c index bd78d57..493089d 100644 --- a/level_0/f_file/c/private-file.c +++ b/level_0/f_file/c/private-file.c @@ -6,9 +6,9 @@ extern "C" { #endif #if !defined(_di_f_file_change_mode_) || !defined(_di_f_file_copy_) - f_return_status private_f_file_change_mode(const f_string path, const mode_t mode, const bool dereference) { + f_return_status private_f_file_change_mode(const f_string path, const mode_t mode) { - if ((dereference ? chmod(path, mode) : lchmod(path, mode)) < 0) { + if (chmod(path, mode) < 0) { if (errno == EACCES) return F_status_set_error(F_access_denied); if (errno == ENAMETOOLONG) return F_status_set_error(F_name); if (errno == EFAULT) return F_status_set_error(F_buffer); @@ -158,7 +158,7 @@ extern "C" { #endif // !defined(_di_f_file_copy_) || !defined(_di_f_file_clone_) #if !defined(_di_f_file_create_) || !defined(_di_f_file_copy_) - f_return_status private_f_file_create(const f_string path, const mode_t mode, const bool exclusive, const bool dereference) { + f_return_status private_f_file_create(const f_string path, const mode_t mode, const bool exclusive) { f_file file = f_file_initialize; file.flags = O_CLOEXEC | O_CREAT | O_WRONLY; @@ -167,10 +167,6 @@ extern "C" { file.flags |= O_EXCL; } - if (!dereference) { - file.flags |= O_NOFOLLOW; - } - f_status status = private_f_file_open(path, mode, &file); if (file.id > 0) { @@ -182,7 +178,7 @@ extern "C" { #endif // !defined(_di_f_file_create_) || !defined(_di_f_file_copy_) #if !defined(_di_f_file_create_at_) || !defined(_di_f_file_copy_at_) - f_return_status private_f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive, const bool dereference) { + f_return_status private_f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive) { f_file file = f_file_initialize; file.flags = O_CLOEXEC | O_CREAT | O_WRONLY; @@ -191,10 +187,6 @@ extern "C" { file.flags |= O_EXCL; } - if (!dereference) { - file.flags |= O_NOFOLLOW; - } - f_status status = private_f_file_open_at(at_id, path, mode, &file); if (file.id > 0) { @@ -427,6 +419,79 @@ extern "C" { } #endif // !defined(_di_f_file_link_at_) || !defined(_di_f_file_copy_at_) +#if !defined(_di_f_file_link_read_) || !defined(_di_f_file_copy_) + f_return_status private_f_file_link_read(const f_string path, const struct stat link_stat, f_string_dynamic *target) { + // create a NULL terminated string based on file stat. + if (link_stat.st_size + 1 > target->size) { + if (link_stat.st_size + 1 > f_string_length_size) { + return F_status_set_error(F_string_too_large); + } + + f_status status = F_none; + + f_macro_string_dynamic_resize(status, (*target), link_stat.st_size + 1); + if (F_status_is_error(status)) return status; + } + + memset(target->string, 0, target->used + 1); + + target->used = link_stat.st_size; + + if (readlink(path, target->string, target->used) < 0) { + if (errno == EACCES) return F_status_set_error(F_access_denied); + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EIO) return F_status_set_error(F_input_output); + 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 == ENOMEM) return F_status_set_error(F_memory_out); + if (errno == ENOTDIR) return F_status_set_error(F_directory); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // !defined(_di_f_file_link_read_) || !defined(_di_f_file_copy_) + +#if !defined(_di_f_file_link_read_at_) || !defined(_di_f_file_copy_at_) + f_return_status private_f_file_link_read_at(const int at_id, const f_string path, const struct stat link_stat, f_string_dynamic *target) { + // create a NULL terminated string based on file stat. + if (link_stat.st_size + 1 > target->size) { + if (link_stat.st_size + 1 > f_string_length_size) { + return F_status_set_error(F_string_too_large); + } + + f_status status = F_none; + + f_macro_string_dynamic_resize(status, (*target), link_stat.st_size + 1); + if (F_status_is_error(status)) return status; + } + + memset(target->string, 0, target->used + 1); + + target->used = link_stat.st_size; + + if (readlink(path, target->string, target->used) < 0) { + if (errno == EACCES) return F_status_set_error(F_access_denied); + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EIO) return F_status_set_error(F_input_output); + 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 == ENOMEM) return F_status_set_error(F_memory_out); + if (errno == ENOTDIR) return F_status_set_error(F_directory); + if (errno == EBADF) return F_status_set_error(F_file_descriptor); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // !defined(_di_f_file_link_read_at_) || !defined(_di_f_file_copy_at_) + #if !defined(_di_f_file_open_) || !defined(_di_f_file_copy_) f_return_status private_f_file_open(const f_string path, const mode_t mode, f_file *file) { if (mode == 0) { diff --git a/level_0/f_file/c/private-file.h b/level_0/f_file/c/private-file.h index 8579833..940d20f 100644 --- a/level_0/f_file/c/private-file.h +++ b/level_0/f_file/c/private-file.h @@ -24,9 +24,6 @@ extern "C" { * The path file name. * @param mode * The new mode to use. - * @param dereference - * Set to TRUE to dereference symlinks (often is what is desired). - * Set to FALSE to operate on the symlink itself. * * @return * F_none on success. @@ -46,7 +43,7 @@ extern "C" { * @see f_file_copy() */ #if !defined(_di_f_file_change_mode_) || !defined(_di_f_file_copy_) - extern f_return_status private_f_file_change_mode(const f_string path, const mode_t mode, const bool dereference) f_gcc_attribute_visibility_internal; + extern f_return_status private_f_file_change_mode(const f_string path, const mode_t mode) f_gcc_attribute_visibility_internal; #endif // !defined(_di_f_file_change_mode_) || !defined(_di_f_file_copy_) /** @@ -62,6 +59,8 @@ extern "C" { * The new mode to use. * @param flags * Any valid flag, such as AT_EMPTY_PATH, AT_NO_AUTOMOUNT, or AT_SYMLINK_NO_FOLLOW. + * Warning: chmod on a symolic link is currently not supported in POSIX. + * Therefore AT_SYMLINK_NO_FOLLOW does nothing at this time and is assumed to always be TRUE. * * @return * F_none on success. @@ -247,10 +246,6 @@ extern "C" { * @param exclusive * If TRUE, will fail when file already exists. * If FALSE, will not fail if file already exists. - * @param dereference - * Set to TRUE to dereferenc symlinks (often is what is desired). - * Set to FALSE to fail if the path is a symbolic link. - * This does not write symbolic links. (@todo add function f_create_link() for creating symbolic links.) * * @return * F_none on success. @@ -276,7 +271,7 @@ extern "C" { * @see f_file_create() */ #if !defined(_di_f_file_create_) || !defined(_di_f_file_copy_) - extern f_return_status private_f_file_create(const f_string path, const mode_t mode, const bool exclusive, const bool dereference) f_gcc_attribute_visibility_internal; + extern f_return_status private_f_file_create(const f_string path, const mode_t mode, const bool exclusive) f_gcc_attribute_visibility_internal; #endif // !defined(_di_f_file_create_) || !defined(_di_f_file_copy_) /** @@ -297,10 +292,6 @@ extern "C" { * @param exclusive * If TRUE, will fail when file already exists. * If FALSE, will not fail if file already exists. - * @param dereference - * Set to TRUE to dereferenc symlinks (often is what is desired). - * Set to FALSE to fail if the path is a symbolic link. - * This does not write symbolic links. (@todo add function f_create_link() for creating symbolic links.) * * @return * F_none on success. @@ -326,7 +317,7 @@ extern "C" { * @see f_file_create_at() */ #if !defined(_di_f_file_create_at_) || !defined(_di_f_file_copy_at_) - extern f_return_status private_f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive, const bool dereference) f_gcc_attribute_visibility_internal; + extern f_return_status private_f_file_create_at(const int at_id, const f_string path, const mode_t mode, const bool exclusive) f_gcc_attribute_visibility_internal; #endif // !defined(_di_f_file_create_at_) || !defined(_di_f_file_copy_at_) /** @@ -540,6 +531,32 @@ extern "C" { #endif // !defined(_di_f_file_create_device_at_) || !defined(_di_f_file_create_node_at_) || !defined(_di_f_file_copy_at_) /** + * Private implementation of f_file_flush(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param id + * The file descriptor. + * + * @return + * F_none is returned on success. + * F_parameter (with error bit) if a parameter is invalid. + * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_input_output (with error bit) on I/O error. + * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_space_not (with error bit) if filesystem is out of space (or filesystem quota is reached). + * F_unsupported (with error bit) if the file system or file type does not support flushing. + * F_failure (with error bit) on any other failure. + * + * @see f_file_close() + * @see f_file_copy() + * @see f_file_flush() + */ +#if !defined(_di_f_file_flush_) || !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) + extern f_return_status private_f_file_flush(const int id) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_flush_) || !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) + +/** * Private implementation of f_file_link(). * * Intended to be shared to each of the different implementation variations. @@ -550,6 +567,23 @@ extern "C" { * A path to the link that does the pointing. * * @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_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_name (with error bit) on path name error. + * F_buffer (with error bit) if the buffer is invalid. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_loop (with error bit) on loop error. + * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_memory_out (with error bit) if out of memory. + * 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_busy (with error bit) if filesystem is too busy to perforrm write. + * F_failure (with error bit) for any other (symlink()) error. * * @see f_file_copy() * @see f_file_link() @@ -571,6 +605,24 @@ extern "C" { * A path to the link that does the pointing. * * @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_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_file_found (with error bit) if a file aleady exists at the path. + * F_name (with error bit) on path name error. + * F_buffer (with error bit) if the buffer is invalid. + * F_number_overflow (with error bit) on overflow error. + * F_interrupted (with error bit) when program received an interrupt signal, halting create. + * F_loop (with error bit) on loop error. + * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_memory_out (with error bit) if out of memory. + * 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_busy (with error bit) if filesystem is too busy to perforrm write. + * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_failure (with error bit) for any other (symlink()) error. * * @see f_file_copy_at() * @see f_file_link_at() @@ -580,70 +632,73 @@ extern "C" { #endif // !defined(_di_f_file_link_at_) || !defined(_di_f_file_copy_at_) /** - * Private implementation of f_file_flush(). + * Private implementation of f_file_link_read(). * * Intended to be shared to each of the different implementation variations. * - * @param id - * The file descriptor. + * @param path + * The path file name. + * @param link_stat + * The link file statistics. + * @param target + * Will be replaced with the path in which the link points to. + * Will be NULL terminated with the NULL at target.string[target.used]; * * @return - * F_none is returned on success. + * F_none on success. * F_parameter (with error bit) if a parameter is invalid. - * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_string_too_large (with error bit) if link target path is too large for string buffer size. + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. * F_input_output (with error bit) on I/O error. - * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. - * F_space_not (with error bit) if filesystem is out of space (or filesystem quota is reached). - * F_unsupported (with error bit) if the file system or file type does not support flushing. - * F_failure (with error bit) on any other failure. - * - * @see f_file_close() - * @see f_file_copy() - * @see f_file_flush() - */ -#if !defined(_di_f_file_flush_) || !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) - extern f_return_status private_f_file_flush(const int id) f_gcc_attribute_visibility_internal; -#endif // !defined(_di_f_file_flush_) || !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) - -/** - * Create a link to a file. - * - * This will not replace existing files/links. - * - * @param target - * A path that the link points to. - * @param point - * A path to the link that does the pointing. - * - * @return + * F_loop (with error bit) on loop error. + * F_name (with error bit) on path name error. + * F_file_found_not (with error bit) if the file at path was not found. + * F_memory_out (with error bit) if out of memory. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_failure (with error bit) for any other (readlink()) error. * - * @see f_file_link() - * @see f_file_copy() + * @see f_file_link_read() */ -#if !defined(_di_f_file_link_) || !defined(_di_f_file_copy_) - extern f_return_status private_f_file_link(const f_string target, const f_string point); -#endif // !defined(_di_f_file_link_) || !defined(_di_f_file_copy_) +#if !defined(_di_f_file_link_read_) || !defined(_di_f_file_copy_) + extern f_return_status private_f_file_link_read(const f_string path, const struct stat link_stat, f_string_dynamic *target) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_link_read_) || !defined(_di_f_file_copy_) /** - * Create a link to a file. + * Private implementation of f_file_link_read_at(). * - * This will not replace existing files/links. + * 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 path file name. + * @param link_stat + * The link file statistics. * @param target - * A path that the link points to. - * @param point - * A path to the link that does the pointing. + * Will be replaced with the path in which the link points to. + * Will be NULL terminated with the NULL at target.string[target.used]; * * @return + * F_none on success. + * F_parameter (with error bit) if a parameter is invalid. + * F_string_too_large (with error bit) if link target path is too large for string buffer size. + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_input_output (with error bit) on I/O error. + * F_loop (with error bit) on loop error. + * F_name (with error bit) on path name error. + * F_file_found_not (with error bit) if the file at path was not found. + * F_memory_out (with error bit) if out of memory. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_file_descriptor (with error bit) if file descriptor is invalid. + * F_failure (with error bit) for any other (readlinkat()) error. * - * @see f_file_link_at() - * @see f_file_copy_at() + * @see f_file_link_read_at() */ -#if !defined(_di_f_file_link_at_) || !defined(_di_f_file_copy_at_) - extern f_return_status private_f_file_link_at(const int at_id, const f_string target, const f_string point); -#endif // !defined(_di_f_file_link_at_) || !defined(_di_f_file_copy_at_) +#if !defined(_di_f_file_link_read_at_) || !defined(_di_f_file_copy_at_) + extern f_return_status private_f_file_link_read_at(const int at_id, const f_string path, const struct stat link_stat, f_string_dynamic *target) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_link_read_at_) || !defined(_di_f_file_copy_at_) /** * Private implementation of f_file_open(). @@ -711,7 +766,7 @@ extern "C" { * @param file_name * The name of the file. * @param dereference - * Set to TRUE to dereference symlinks (often is what is desired). + * Set to TRUE to dereference symlinks. * Set to FALSE to operate on the symlink itself. * @param file_stat * The statistics read. diff --git a/level_0/f_status/c/status_array.h b/level_0/f_status/c/status_array.h new file mode 100644 index 0000000..46ab661 --- /dev/null +++ b/level_0/f_status/c/status_array.h @@ -0,0 +1,85 @@ +/** + * FLL - Level 0 + * + * Project: Status + * API Version: 0.5 + * Licenses: lgplv2.1 + * + * Provides status arrays that require memory operations. + */ +#ifndef _F_status_array_h +#define _F_status_array_h + +// fll-0 includes +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * An array of status codes. + * + * array: An array of status codes. + * size: Total amount of allocated space. + * used: Total number of allocated spaces used. + */ +#ifndef _di_f_statuss_ + typedef struct { + f_status *array; + f_array_length size; + f_array_length used; + } f_statuss; + + #define f_statuss_initialize { 0, 0, 0 } + + #define f_macro_statuss_clear(status) f_macro_memory_structure_clear(statuses) + + #define f_macro_statuss_new(status, statuses, length) f_macro_memory_structure_new(status, statuses, f_status, length) + + #define f_macro_statuss_delete(status, statuses) f_macro_memory_structure_delete(status, statuses, f_status) + #define f_macro_statuss_destroy(status, statuses) f_macro_memory_structure_destroy(status, statuses, f_status) + + #define f_macro_statuss_delete_simple(statuses) f_macro_memory_structure_delete_simple(statuses, f_status) + #define f_macro_statuss_destroy_simple(statuses) f_macro_memory_structure_destroy_simple(statuses, f_status) + + #define f_macro_statuss_resize(status, statuses, new_length) f_macro_memory_structure_resize(status, statuses, f_status, new_length) + #define f_macro_statuss_adjust(status, statuses, new_length) f_macro_memory_structure_adjust(status, statuses, f_status, new_length) +#endif // _di_f_statuss_ + +/** + * An array of an array of status codes. + * + * array: the array of an array of status codes. + * size: total amount of allocated space. + * used: total number of allocated spaces used. + */ +#ifndef _di_f_statusss_ + typedef struct { + f_statuss *array; + + f_array_length size; + f_array_length used; + } f_statusss; + + #define f_statusss_initialize { 0, 0, 0 } + + #define f_macro_statusss_clear(statuses) f_macro_memory_structures_clear(statuses) + + #define f_macro_statusss_new(status, statuses, length) f_macro_memory_structures_new(status, statuses, f_status, length) + + #define f_macro_statusss_delete(status, statuses) f_macro_memory_structures_delete(status, statuses, f_status) + #define f_macro_statusss_destroy(status, statuses) f_macro_memory_structures_destroy(status, statuses, f_status) + + #define f_macro_statusss_delete_simple(statuses) f_macro_memory_structures_delete_simple(statuses, f_status) + #define f_macro_statusss_destroy_simple(statuses) f_macro_memory_structures_destroy_simple(statuses, f_status) + + #define f_macro_statusss_resize(status, statuses, new_length) f_macro_memory_structures_resize(status, statuses, f_status, new_length) + #define f_macro_statusss_adjust(status, statuses, new_length) f_macro_memory_structures_adjust(status, statuses, f_status, new_length) +#endif // _di_f_statuss_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _F_status_array_h diff --git a/level_0/f_status/data/build/settings b/level_0/f_status/data/build/settings index 40a2ac4..9dc293e 100644 --- a/level_0/f_status/data/build/settings +++ b/level_0/f_status/data/build/settings @@ -13,7 +13,7 @@ build_libraries -lc build_libraries_fll build_sources_library build_sources_program -build_sources_headers status.h +build_sources_headers status.h status_array.h build_sources_bash build_sources_settings build_shared yes diff --git a/level_0/f_string/c/string.h b/level_0/f_string/c/string.h index adf6339..3b9f883 100644 --- a/level_0/f_string/c/string.h +++ b/level_0/f_string/c/string.h @@ -470,7 +470,7 @@ extern "C" { dynamics.size = new_length; \ if (dynamics.used > dynamics.size) dynamics.used = new_length; \ } -#endif // _di_f_string_dynamic_ +#endif // _di_f_string_dynamics_ #ifdef __cplusplus } // extern "C" diff --git a/level_0/f_type/c/type_array.h b/level_0/f_type/c/type_array.h index 6b6760c..d1167bc 100644 --- a/level_0/f_type/c/type_array.h +++ b/level_0/f_type/c/type_array.h @@ -5,7 +5,7 @@ * API Version: 0.5 * Licenses: lgplv2.1 * - * Provides datatypes that are arrays of some sort and require memory operations. + * Provides datatypes that require memory operations. */ #ifndef _F_type_array_h #define _F_type_array_h @@ -28,6 +28,7 @@ extern "C" { #ifndef _di_f_array_lengths_ typedef struct { f_array_length *array; + f_array_length size; f_array_length used; } f_array_lengths; @@ -48,6 +49,37 @@ extern "C" { #define f_macro_array_lengths_adjust(status, lengths, new_length) f_macro_memory_structure_adjust(status, lengths, f_array_length, new_length) #endif // _di_f_array_lengths_ +/** + * An array of an array of array lengths. + * + * array: the array of an array of array lengths. + * size: total amount of allocated space. + * used: total number of allocated spaces used. + */ +#ifndef _di_f_array_lengthss_ + typedef struct { + f_array_lengths *array; + + f_array_length size; + f_array_length used; + } f_array_lengthss; + + #define f_array_lengthss_initialize { 0, 0, 0 } + + #define f_macro_array_lengthss_clear(lengths) f_macro_memory_structures_clear(lengths) + + #define f_macro_array_lengthss_new(status, lengths, length) f_macro_memory_structures_new(status, lengths, f_array_lengths, length) + + #define f_macro_array_lengthss_delete(status, lengths) f_macro_memory_structures_delete(status, lengths, f_array_lengths) + #define f_macro_array_lengthss_destroy(status, lengths) f_macro_memory_structures_destroy(status, lengths, f_array_lengths) + + #define f_macro_array_lengthss_delete_simple(lengths) f_macro_memory_structures_delete_simple(lengths, f_array_lengths) + #define f_macro_array_lengthss_destroy_simple(lengths) f_macro_memory_structures_destroy_simple(lengths, f_array_lengths) + + #define f_macro_array_lengthss_resize(status, lengths, new_length) f_macro_memory_structures_resize(status, lengths, f_array_lengths, new_length) + #define f_macro_array_lengthss_adjust(status, lengths, new_length) f_macro_memory_structures_adjust(status, lengths, f_array_lengths, new_length) +#endif // _di_f_array_lengthss_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_1/fl_directory/c/directory.c b/level_1/fl_directory/c/directory.c index bb8db8f..0952843 100644 --- a/level_1/fl_directory/c/directory.c +++ b/level_1/fl_directory/c/directory.c @@ -5,36 +5,38 @@ extern "C" { #endif -#ifndef _di_f_directory_copy_ - f_return_status f_directory_copy(const f_string source, const f_string destination, const mode_t mode, const f_number_unsigned size_block, const bool exclusive) { +#ifndef _di_fl_directory_copy_ + f_return_status fl_directory_copy(const f_string source, const f_string destination, const f_string_length source_length, const f_string_length destination_length, const f_directory_mode mode, const f_number_unsigned size_block, const bool exclusive, f_directory_statuss *failures) { f_status status = F_none; - /* -@todo + status = f_directory_exists(source); if (F_status_is_error(status)) return status; if (status == F_false) return F_status_set_error(F_directory); - status = f_directory_exists(destination); + f_directory_listing listing = f_directory_listing_initialize; + + status = private_fl_directory_list(source, 0, 0, F_false, &listing); if (F_status_is_error(status)) return status; - if (exclusive) { - } - else { - } + const f_string_static static_source = { source, source_length, source_length }; + const f_string_static static_destination = { destination, destination_length, destination_length }; + + status = private_fl_directory_copy(static_source, static_destination, mode, size_block, exclusive, failures); - status = private_fl_directory_list(source, 0, 0, listing);*/ + f_macro_directory_listing_destroy(status, listing); + if (F_status_is_error(status)) return status; return F_none; } -#endif // _di_f_directory_copy_ +#endif // _di_fl_directory_copy_ #ifndef _di_fl_directory_list_ - f_return_status fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), f_directory_listing *listing) { + f_return_status fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), const bool dereference, f_directory_listing *listing) { #ifndef _di_level_2_parameter_checking_ if (listing == 0) return F_status_set_error(F_parameter); #endif // _di_level_2_parameter_checking_ - return private_fl_directory_list(path, filter, sort, listing); + return private_fl_directory_list(path, filter, sort, dereference, listing); } #endif // _di_fl_directory_list_ diff --git a/level_1/fl_directory/c/directory.h b/level_1/fl_directory/c/directory.h index 1009997..ee8fd50 100644 --- a/level_1/fl_directory/c/directory.h +++ b/level_1/fl_directory/c/directory.h @@ -31,6 +31,7 @@ // fll-0 includes #include +#include #include #include #include @@ -51,18 +52,29 @@ extern "C" { * * Symbolic links are not followed, they are copied as the symbolic link itself. * + * This does not copy unknown file types. + * * @param source - * The path to the directory to copy from. + * The source file path. + * Must be NULL terminated. * @param destination - * The path to copy to. + * The destination file path. + * Must be NULL terminated. + * @param source_length + * The length of the source path. + * @param destination_length + * The length of the destination path. * @param mode - * The file mode assigned to the destination file (based on each file type). + * The directory modes. * @param size_block * The default number of chunks to read at a time with each chunk being 1-byte. - * Must be greater than 0. + * Set to 0 to use default block read size. * @param exclusive - * If TRUE, will fail when parent directory already exists. - * If FALSE, will not fail if parent directory already exists (existing directory will be updated). + * If TRUE, will fail when file already exists. + * If FALSE, will not fail if file already exists (existing file will be replaced). + * @param failures + * A list of paths and their respective status codes for copy failures. + * If 0, then this and statuses is ignored. * * @return * F_none on success. @@ -85,13 +97,12 @@ extern "C" { * F_busy (with error bit) if filesystem is too busy to perforrm write. * F_file_read (with error bit) on file read error. * F_file_write (with error bit) on file write error. - - * @see f_directory_create() + * * @see f_file_copy() * @see read() */ #ifndef _di_fl_directory_copy_ - extern f_return_status fl_directory_copy(const f_string source, const f_string destination, const f_directory_mode mode, const f_number_unsigned size_block, const bool exclusive); + extern f_return_status fl_directory_copy(const f_string source, const f_string destination, const f_string_length source_length, const f_string_length destination_length, const f_directory_mode mode, const f_number_unsigned size_block, const bool exclusive, f_directory_statuss *failures); #endif // _di_fl_directory_copy_ /** @@ -105,6 +116,8 @@ extern "C" { * * Symbolic links are not followed, they are copied as the symbolic link itself. * + * This does not copy unknown file types. + * * @param source * The path to the directory to copy from. * @param destination @@ -164,6 +177,9 @@ extern "C" { * A sort function of the form: int xxx(const struct direct *, const struct direct *). * Set to 0 to not use (NULL). * There are two pre-made libc functions available for this: alphasort() and versionsort(). + * @param dereference + * Set to TRUE to dereferenc symlinks (often is what is desired). + * Set to FALSE to operate on the symlink itself. * @param listing * Will be populated with the names of all top-level paths found within the given directory. * @@ -186,7 +202,7 @@ extern "C" { * @see versionsort() */ #ifndef _di_fl_directory_list_ - extern f_return_status fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), f_directory_listing *listing); + extern f_return_status fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), const bool dereference, f_directory_listing *listing); #endif // _di_fl_directory_list_ /** diff --git a/level_1/fl_directory/c/private-directory.c b/level_1/fl_directory/c/private-directory.c index 38e482e..503f326 100644 --- a/level_1/fl_directory/c/private-directory.c +++ b/level_1/fl_directory/c/private-directory.c @@ -5,15 +5,15 @@ extern "C" { #endif -#ifndef _di_f_directory_copy_ - f_return_status private_f_directory_copy(const f_string source, const f_string destination, const f_directory_mode mode, const f_number_unsigned size_block, const bool exclusive) { +#if !defined(_di_fl_directory_copy_) + f_return_status private_fl_directory_copy(const f_string_static source, const f_string_static destination, const f_directory_mode mode, const f_number_unsigned size_block, const bool exclusive, f_directory_statuss *failures) { f_status status = F_none; - status = f_directory_exists(source); + status = f_directory_exists(source.string); if (F_status_is_error(status)) return status; if (status == F_false) return F_status_set_error(F_directory); - status = f_directory_exists(destination); + status = f_directory_exists(destination.string); if (F_status_is_error(status)) return status; if (status == F_true) { @@ -21,79 +21,200 @@ extern "C" { return F_status_set_error(F_directory_found); } - status = f_file_change_mode(destination, mode.directory, F_false); + status = f_file_change_mode(destination.string, mode.directory); if (F_status_is_error(status)) return status; } else { - status = f_directory_create(destination, mode.directory); + status = f_directory_create(destination.string, mode.directory); if (F_status_is_error(status)) return status; } f_directory_listing listing = f_directory_listing_initialize; - status = private_fl_directory_list(source, 0, 0, &listing); + status = private_fl_directory_list(source.string, 0, 0, F_false, &listing); if (F_status_is_error(status)) { f_macro_directory_listing_delete_simple(listing); return status; } + status = F_none; + f_array_length i = 0; + int directory_fd = 0; + f_string_length failures_used = failures ? failures->used : 0; - for (; i < listing.block.used; i++) { - // @todo + for (; F_status_is_fine(status) && i < listing.block.used; i++) { + status = private_fl_directory_copy_file(listing.block.array[i], source, destination, mode.block, size_block, exclusive, failures); } // for f_macro_string_dynamics_delete_simple(listing.block); - for (; i < listing.character.used; i++) { - // @todo + for (i = 0; F_status_is_fine(status) && i < listing.character.used; i++) { + status = private_fl_directory_copy_file(listing.character.array[i], source, destination, mode.character, size_block, exclusive, failures); } // for f_macro_string_dynamics_delete_simple(listing.character); - for (; i < listing.regular.used; i++) { - // @todo + for (i = 0; F_status_is_fine(status) && i < listing.regular.used; i++) { + status = private_fl_directory_copy_file(listing.regular.array[i], source, destination, mode.regular, size_block, exclusive, failures); } // for f_macro_string_dynamics_delete_simple(listing.regular); - for (; i < listing.link.used; i++) { - // @todo + for (i = 0; F_status_is_fine(status) && i < listing.link.used; i++) { + status = private_fl_directory_copy_file(listing.link.array[i], source, destination, mode.link, size_block, exclusive, failures); } // for f_macro_string_dynamics_delete_simple(listing.link); - for (; i < listing.socket.used; i++) { - // @todo + for (i = 0; F_status_is_fine(status) && i < listing.socket.used; i++) { + status = private_fl_directory_copy_file(listing.socket.array[i], source, destination, mode.socket, size_block, exclusive, failures); } // for f_macro_string_dynamics_delete_simple(listing.socket); - for (; i < listing.unknown.used; i++) { - // @todo + for (i = 0; F_status_is_fine(status) && i < listing.unknown.used; i++) { + status = private_fl_directory_copy_file(listing.unknown.array[i], source, destination, mode.unknown, size_block, exclusive, failures); } // for f_macro_string_dynamics_delete_simple(listing.unknown); - // @todo copy all file types and clear the set when finished. + for (i = 0; F_status_is_fine(status) && i < listing.directory.used; i++) { + f_string_static source_sub = f_string_static_initialize; + f_string_static destination_sub = f_string_static_initialize; + + source_sub.used = source.used + listing.directory.array[i].used + 1; + source_sub.size = source_sub.used; + + destination_sub.used = destination.used + listing.directory.array[i].used + 1; + destination_sub.size = destination_sub.used; + + char path_source_sub[source_sub.used + 1]; + char path_destination_sub[destination_sub.used + 1]; + + memcpy(path_source_sub, source.string, source.used); + memcpy(path_source_sub + source.used + 1, listing.directory.array[i].string, listing.directory.array[i].used); + + memcpy(path_destination_sub, destination.string, destination.used); + memcpy(path_destination_sub + destination.used + 1, listing.directory.array[i].string, listing.directory.array[i].used); - // @todo: recurse through each directory, calling this private function. - // @todo: use a create at directory operation. - // @todo: build the full path and use that in a recursive function. - f_string_dynamic path_full = f_string_dynamic_initialize; + path_source_sub[source.used] = f_path_separator[0]; + path_source_sub[source_sub.used] = 0; - for (; i < listing.directory.used; i++) { - // @todo + path_destination_sub[destination.used] = f_path_separator[0]; + path_destination_sub[destination_sub.used] = 0; + + source_sub.string = path_source_sub; + destination_sub.string = path_destination_sub; + + status = private_fl_directory_copy(source_sub, destination_sub, mode, size_block, exclusive, failures); } // for f_macro_string_dynamics_delete_simple(listing.directory); + + if (F_status_is_error(status)) return status; + + if (failures && failures_used < failures->used) return F_failure; + + return F_none; + } +#endif // !defined(_di_fl_directory_copy_) + +#if !defined(_di_fl_directory_copy_) + f_return_status private_fl_directory_copy_file(const f_string_static file, const f_string_static source, const f_string_static destination, const mode_t mode, const f_number_unsigned size_block, const bool exclusive, f_directory_statuss *failures) { + char path_source[source.used + file.used + 2]; + char path_destination[destination.used + file.used + 2]; + + memcpy(path_source, source.string, source.used); + memcpy(path_source + source.used + 1, file.string, file.used); + path_source[source.used] = f_path_separator[0]; + path_source[source.used + file.used + 1] = 0; + + memcpy(path_destination, destination.string, destination.used); + memcpy(path_destination + destination.used + 1, file.string, file.used); + path_destination[destination.used] = f_path_separator[0]; + path_destination[destination.used + file.used + 1] = 0; + + f_status status = f_file_copy(path_source, path_destination, mode, size_block, exclusive); + + if (F_status_is_error(status) || status == F_unsupported) { + if (status == F_status_set_error(F_memory_allocation) || status == F_status_set_error(F_memory_reallocation)) { + return F_status_set_error(status); + } + + if (!failures) return F_failure; + + const f_status status_failure = status; + + if (failures->used + 1 > failures->size) { + if (failures->size + f_memory_default_allocation_step > f_array_length_size) { + if (failures->size + 1 > f_array_length_size) { + return F_status_set_error(F_buffer_too_large); + } + + f_macro_directory_statuss_resize(status, (*failures), failures->used + 1); + if (F_status_is_error(status)) return status; + } + else { + f_macro_directory_statuss_resize(status, (*failures), failures->used + f_memory_default_allocation_step); + if (F_status_is_error(status)) return status; + } + } + + f_directory_status failure = f_directory_status_initialize; + f_string_length size = 0; + + // identify if failure was because of source or destination. + struct stat source_stat; + + memset(&source_stat, 0, sizeof(struct stat)); + + status = f_file_stat(source.string, F_false, &source_stat); + if (F_status_is_error(status)) { + if (status == F_status_set_error(F_string_too_large)) { + size = f_string_length_size - 1; + } + else { + size = source.used + file.used + 1; + } + + f_macro_directory_status_resize(status, failure, size + 1); + if (F_status_is_error(status)) return status; + + memcpy(failure.path.string, path_source, size); + failure.path.string[size] = 0; + } + else { + if (status == F_status_set_error(F_string_too_large)) { + size = f_string_length_size - 1; + } + else { + size = destination.used + file.used + 1; + } + + f_macro_directory_status_resize(status, failure, size + 1); + if (F_status_is_error(status)) return status; + + memcpy(failure.path.string, path_destination, size); + failure.path.string[size] = 0; + } + + failures->array[failures->used].path.string = failure.path.string; + failures->array[failures->used].path.used = size; + failures->array[failures->used].path.size = size + 1; + failures->array[failures->used].status = status_failure; + failures->used++; + + return F_failure; + } + return F_none; } -#endif // _di_f_directory_copy_ +#endif // !defined(_di_fl_directory_copy_) #if !defined(_di_fl_directory_list_) - f_return_status private_fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), f_directory_listing *listing) { + f_return_status private_fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), const bool dereference, f_directory_listing *listing) { struct dirent **entity = 0; f_string_length size = 0; @@ -168,7 +289,7 @@ extern "C" { memset(&file_stat, 0, sizeof(struct stat)); - status = f_file_stat_at(parent_fd, entity[i]->d_name, 0, &file_stat); + status = f_file_stat_at(parent_fd, entity[i]->d_name, dereference ? 0 : AT_SYMLINK_NOFOLLOW, &file_stat); if (F_status_is_error(status)) break; mode = f_macro_file_type_get(file_stat.st_mode); diff --git a/level_1/fl_directory/c/private-directory.h b/level_1/fl_directory/c/private-directory.h index 5f49025..779043b 100644 --- a/level_1/fl_directory/c/private-directory.h +++ b/level_1/fl_directory/c/private-directory.h @@ -15,28 +15,77 @@ extern "C" { #endif /** - * A special function intended to be used directly by fl_directory_copy() + * Private implementation of fl_directory_copy(). * - * @param path - * The file path. - * @param file_stat - * The file stat. - * @param type - * The file type. - * @param entity - * The FTW entity. + * Intended to be shared to each of the different implementation variations. + * + * @param source + * The source file path. + * Must be NULL terminated. + * @param destination + * The destination file path. + * Must be NULL terminated. + * @param mode + * The directory modes. + * @param size_block + * The default number of chunks to read at a time with each chunk being 1-byte. + * Must be greater than 0. + * @param exclusive + * If TRUE, will fail when file already exists. + * If FALSE, will not fail if file already exists (existing file will be replaced). + * @param failures + * A list of paths and their respective status codes for copy failures. + * If 0, then this and statuses is ignored. * * @return - * 0 on success. - * -1 on failure. - * Check errno for details. + * F_none on success. + * F_failure on handled error (failures and statuses is updated with failure and status code). + * F_memory_allocation (with error bit) on memory allocation error. + * F_memory_reallocation (with error bit) on memory re-allocation error. * * @see fl_directory_copy() */ #if !defined(_di_fl_directory_copy_) - f_return_status private_fl_directory_copy(const f_string source, const f_string destination, const f_directory_mode mode, const f_number_unsigned size_block, const bool exclusive) f_gcc_attribute_visibility_internal; + extern f_return_status private_fl_directory_copy(const f_string_static source, const f_string_static destination, const f_directory_mode mode, const f_number_unsigned size_block, const bool exclusive, f_directory_statuss *failures) f_gcc_attribute_visibility_internal; #endif // !defined(_di_fl_directory_copy_) +/** + * A special function intended to be used directly by private_fl_directory_copy(). + * + * Will only copy a single file and record any detected errors. + * + * @param file + * The name of the file within source to copy into destination. + * Must be NULL terminated. + * @param source + * The source file path. + * Must be NULL terminated. + * @param destination + * The destination file path. + * Must be NULL terminated. + * @param mode + * The directory mode. + * @param size_block + * The default number of chunks to read at a time with each chunk being 1-byte. + * Must be greater than 0. + * @param exclusive + * If TRUE, will fail when file already exists. + * If FALSE, will not fail if file already exists (existing file will be replaced). + * @param failures + * A list of paths and their respective status codes for copy failures. + * If 0, then this and statuses is ignored. + * + * @return + * F_none on success. + * F_failure on handled error (failures and statuses is updated with failure and status code). + * F_memory_allocation (with error bit) on memory allocation error. + * F_memory_reallocation (with error bit) on memory re-allocation error. + * + * @see fl_directory_copy() + */ +#if !defined(_di_fl_directory_copy_) + extern f_return_status private_fl_directory_copy_file(const f_string_static file, const f_string_static source, const f_string_static destination, const mode_t mode, const f_number_unsigned size_block, const bool exclusive, f_directory_statuss *failures) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_fl_directory_copy_) /** * A special function intended to be used directly by fl_directory_list(). @@ -50,6 +99,9 @@ extern "C" { * A sort function of the form: int xxx(const struct direct *, const struct direct *). * Set to 0 to not use (NULL). * There are two pre-made libc functions available for this: alphasort() and versionsort(). + * @param dereference + * Set to TRUE to dereferenc symlinks (often is what is desired). + * Set to FALSE to operate on the symlink itself. * @param listing * Will be populated with the names of all top-level paths found within the given directory. * @@ -69,7 +121,7 @@ extern "C" { * @see fl_directory_list() */ #if !defined(_di_fl_directory_list_) - extern f_return_status private_fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), f_directory_listing *listing) f_gcc_attribute_visibility_internal; + extern f_return_status private_fl_directory_list(const f_string path, int (*filter)(const struct dirent *), int (*sort)(const struct dirent **, const struct dirent **), const bool dereference, f_directory_listing *listing) f_gcc_attribute_visibility_internal; #endif // !defined(_di_fl_directory_list_) /** -- 1.8.3.1