From: Kevin Day Date: Mon, 18 May 2020 02:49:01 +0000 (-0500) Subject: Update: add additional file functions, fix bug with file stat in f_directory X-Git-Tag: 0.5.0~246 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=de9aaefd8be0cb4ffc964cfe5eea02f4e441841e;p=fll Update: add additional file functions, fix bug with file stat in f_directory Add additional f_file functions, move commonly shared code into private f_file files. The f_directory functions were incorrectly using sizeof(). --- diff --git a/build/level_0/settings b/build/level_0/settings index 5756b24..5f1c31d 100644 --- a/build/level_0/settings +++ b/build/level_0/settings @@ -12,7 +12,7 @@ build_linker ar build_libraries -lc 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 memory.c path.c pipe.c print.c utf.c private-utf.c +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 utf.h build_shared yes diff --git a/build/monolithic/settings b/build/monolithic/settings index ac2e5fc..2d894a6 100644 --- a/build/monolithic/settings +++ b/build/monolithic/settings @@ -11,7 +11,7 @@ build_compiler gcc build_linker ar 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/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/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_2/directory.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_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/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_2/directory.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/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/directory.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 diff --git a/level_0/f_directory/c/directory.c b/level_0/f_directory/c/directory.c index b8bc179..f8cdd96 100644 --- a/level_0/f_directory/c/directory.c +++ b/level_0/f_directory/c/directory.c @@ -115,7 +115,7 @@ extern "C" { f_return_status f_directory_is(const f_string path) { struct stat file_stat; - memset(&file_stat, 0, sizeof(file_stat)); + memset(&file_stat, 0, sizeof(struct stat)); if (stat(path, &file_stat) < 0) { if (errno == ENAMETOOLONG || errno == EFAULT) { @@ -153,7 +153,7 @@ extern "C" { f_return_status f_directory_is_at(const int file_id, const f_string path, const bool follow) { struct stat file_stat; - memset(&file_stat, 0, sizeof(file_stat)); + memset(&file_stat, 0, sizeof(struct stat)); if (fstatat(file_id, path, &file_stat, follow ? 0 : AT_SYMLINK_NOFOLLOW) < 0) { if (errno == ENAMETOOLONG || errno == EFAULT) { diff --git a/level_0/f_file/c/file.c b/level_0/f_file/c/file.c index 78f520b..0dedbf3 100644 --- a/level_0/f_file/c/file.c +++ b/level_0/f_file/c/file.c @@ -1,14 +1,5 @@ -/** - * FLL - Level 0 - * - * Project: File - * API Version: 0.5 - * Licenses: lgplv2.1 - * - * Provides structures and data types for a file I/O. - * Provides operations for opening/closing files. - */ #include +#include "private-file.h" #ifdef __cplusplus extern "C" { @@ -51,95 +42,145 @@ extern "C" { } #endif // _di_f_file_access_ -#ifndef _di_f_file_create_ - f_return_status f_file_create(f_string path, const mode_t mode, const bool exclusive) { - int flags = O_CLOEXEC | O_CREAT | O_WRONLY; +#ifndef _di_f_file_change_owner_ + f_return_status f_file_change_owner(const f_string path, const uid_t uid, const gid_t gid) { + return private_f_file_change_owner(path, uid, gid); + } +#endif // _di_f_file_change_owner_ + +#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_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + f_status status = f_none; + + status = private_f_file_create(destination, mode, exclusive); + if (f_status_is_error(status)) return status; - if (exclusive) { - flags |= O_EXCL; + f_file file_source = f_file_initialize; + f_file file_destination = f_file_initialize; + + file_destination.mode = f_file_write_create; + + status = private_f_file_open(&file_source, source); + if (f_status_is_error(status)) return status; + + status = private_f_file_open(&file_destination, destination); + if (f_status_is_error(status)) { + private_f_file_close(&file_source); + return status; } - int result = open(path, flags, mode); + ssize_t size_read = 0; + ssize_t size_write = 0; + char *buffer[size_block]; - if (result < 0) { - if (errno == EACCES) { - return f_status_set_error(f_access_denied); - } - else if (errno == EDQUOT) { - return f_status_set_error(f_filesystem_quota_blocks); - } - else if (errno == EEXIST) { - return f_status_set_error(f_file_found); - } - else if (errno == ENAMETOOLONG || errno == EFAULT) { - return f_status_set_error(f_invalid_name); - } - else if (errno == EFBIG || errno == EOVERFLOW) { - return f_status_set_error(f_number_overflow); - } - else if (errno == EINTR) { - return f_status_set_error(f_interrupted); - } - else if (errno == EINVAL) { - return f_status_set_error(f_invalid_parameter); - } - else if (errno == ELOOP) { - return f_status_set_error(f_loop); - } - else if (errno == ENFILE) { - return f_status_set_error(f_file_max_open); - } - else if (errno == ENOENT || errno == ENOTDIR) { - return f_status_set_error(f_invalid_directory); - } - else if (errno == ENOMEM) { - return f_status_set_error(f_out_of_memory); - } - else if (errno == ENOSPC) { - return f_status_set_error(f_filesystem_quota_reached); - } - else if (errno == EPERM) { - return f_status_set_error(f_prohibited); - } - else if (errno == EROFS) { - return f_status_set_error(f_read_only); - } - else if (errno == ETXTBSY) { - return f_status_set_error(f_busy); + memset(buffer, 0, size_block); + + while ((size_read = read(file_source.id, buffer, size_block)) > 0) { + size_write = write(file_destination.id, buffer, size_read); + + if (size_write < 0 || size_write != size_read) { + private_f_file_close(&file_destination); + private_f_file_close(&file_source); + + return f_status_set_error(f_file_error_write); } + } // while - return f_status_set_error(f_failure); - } + private_f_file_close(&file_destination); + private_f_file_close(&file_source); - close(result); + if (size_read < 0) return f_status_set_error(f_file_error_read); return f_none; } -#endif // _di_f_file_create_ +#endif // _di_f_file_copy_ -#ifndef _di_f_file_close_ - f_return_status f_file_close(f_file *file) { +#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 (file == 0) return f_status_set_error(f_invalid_parameter); + if (size_block == 0) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - if (file->address == 0) return f_status_set_error(f_file_not_open); + f_status status = f_none; + struct stat source_stat; + + memset(&source_stat, 0, sizeof(struct stat)); + + status = private_f_file_stat(source, &source_stat); + if (f_status_is_error(status)) return status; + + status = private_f_file_create(destination, source_stat.st_mode, exclusive); + if (f_status_is_error(status)) return status; + + // guarantee the file mode is updated (create does not set mode for existing files). + status = private_f_file_change_mode(destination, source_stat.st_mode); + if (f_status_is_error(status)) return status; - // if we were given a file descriptor as well, make sure to flush all changes to the disk that are not flushed by the 'fflush()' command - if (file->id) { - // make sure all unfinished data gets completed. - if (fsync(file->id) != 0) return f_status_set_error(f_file_error_synchronize); + if (roles) { + status = private_f_file_change_owner(destination, source_stat.st_uid, source_stat.st_gid); + if (f_status_is_error(status)) return status; } - if (fclose(file->address) == 0) { - file->address = 0; - return f_none; + f_file file_source = f_file_initialize; + f_file file_destination = f_file_initialize; + + file_destination.mode = f_file_write_create; + + status = private_f_file_open(&file_source, source); + if (f_status_is_error(status)) return status; + + status = private_f_file_open(&file_destination, destination); + if (f_status_is_error(status)) { + private_f_file_close(&file_source); + return status; } - return f_status_set_error(f_file_error_close); + ssize_t size_read = 0; + ssize_t size_write = 0; + char *buffer[size_block]; + + memset(buffer, 0, size_block); + + while ((size_read = read(file_source.id, buffer, size_block)) > 0) { + size_write = write(file_destination.id, buffer, size_read); + + if (size_write < 0 || size_write != size_read) { + private_f_file_close(&file_destination); + private_f_file_close(&file_source); + + return f_status_set_error(f_file_error_write); + } + } // while + + private_f_file_close(&file_destination); + private_f_file_close(&file_source); + + if (size_read < 0) return f_status_set_error(f_file_error_read); + + return f_none; + } +#endif // _di_f_file_clone_ + +#ifndef _di_f_file_close_ + f_return_status f_file_close(f_file *file) { + #ifndef _di_level_0_parameter_checking_ + if (file == 0) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + return private_f_file_close(file); } #endif // _di_f_file_close_ +#ifndef _di_f_file_create_ + f_return_status f_file_create(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_exists_at_ f_return_status f_file_exists_at(const int directory_file_descriptor, const f_string path, const int flags) { #ifndef _di_level_0_parameter_checking_ @@ -202,7 +243,7 @@ extern "C" { f_return_status f_file_exists(const f_string path) { struct stat file_stat; - memset(&file_stat, 0, sizeof(file_stat)); + memset(&file_stat, 0, sizeof(struct stat)); if (stat(path, &file_stat) < 0) { if (errno == ENAMETOOLONG || errno == EFAULT) { @@ -238,7 +279,7 @@ extern "C" { f_return_status f_file_is(const f_string path, const int type) { struct stat file_stat; - memset(&file_stat, 0, sizeof(file_stat)); + memset(&file_stat, 0, sizeof(struct stat)); if (stat(path, &file_stat) < 0) { if (errno == ENAMETOOLONG || errno == EFAULT) { @@ -276,7 +317,7 @@ extern "C" { f_return_status f_file_is_at(const int file_id, const f_string path, const int type, const bool follow) { struct stat file_stat; - memset(&file_stat, 0, sizeof(file_stat)); + memset(&file_stat, 0, sizeof(struct stat)); if (fstatat(file_id, path, &file_stat, follow ? 0 : AT_SYMLINK_NOFOLLOW) < 0) { if (errno == ENAMETOOLONG || errno == EFAULT) { @@ -316,19 +357,7 @@ extern "C" { if (file == 0) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - // if file->mode is unset, then this may cause a segfault. - if (file->mode == 0) return f_status_set_error(f_invalid_parameter); - - file->address = fopen(path, file->mode); - - if (file->address == 0) return f_status_set_error(f_file_not_found); - if (ferror(file->address) != 0) return f_status_set_error(f_file_error_open); - - file->id = fileno(file->address); - - if (file->id == -1) return f_status_set_error(f_file_error_descriptor); - - return f_none; + return private_f_file_open(file, path); } #endif // _di_f_file_open_ @@ -512,33 +541,7 @@ extern "C" { #ifndef _di_f_file_stat_ f_return_status f_file_stat(const f_string path, struct stat *file_stat) { - if (stat(path, file_stat) < 0) { - if (errno == ENAMETOOLONG || errno == EFAULT) { - return f_status_set_error(f_invalid_name); - } - else if (errno == ENOMEM) { - return f_status_set_error(f_out_of_memory); - } - else if (errno == EOVERFLOW) { - return f_status_set_error(f_number_overflow); - } - else if (errno == ENOTDIR) { - return f_status_set_error(f_invalid_directory); - } - else if (errno == ENOENT) { - return f_status_set_error(f_file_not_found); - } - else if (errno == EACCES) { - return f_status_set_error(f_access_denied); - } - else if (errno == ELOOP) { - return f_status_set_error(f_loop); - } - - return f_status_set_error(f_file_error_stat); - } - - return f_none; + return private_f_file_stat(path, file_stat); } #endif // _di_f_file_stat_ diff --git a/level_0/f_file/c/file.h b/level_0/f_file/c/file.h index 49e82f3..05018e2 100644 --- a/level_0/f_file/c/file.h +++ b/level_0/f_file/c/file.h @@ -40,7 +40,6 @@ extern "C" { #define f_file_default_read_size 8192 // default to 8k read sizes. #define f_file_default_write_size 8192 // default to 8k write sizes. - #define f_file_max_path_length 1024 #endif // _di_f_file_types_ /** @@ -285,6 +284,34 @@ extern "C" { #endif // _di_f_macro_file_reset_position_ /** + * Change owner and group of a given file at the specified path. + * + * @param path + * The path file name. + * @param uid + * The new user id to use. + * @param gid + * The new group id to use. + * + * @return + * f_true if file exists. + * f_false if file does not exist. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_invalid_name (with error bit) if the filename is too long. + * f_out_of_memory (with error bit) if out of memory. + * f_number_overflow (with error bit) on overflow error. + * f_invalid_directory (with error bit) on invalid directory. + * f_access_denied (with error bit) on access denied. + * f_loop (with error bit) on loop error. + * f_false (with error bit) on unknown/unhandled errors. + * + * @see chown() + */ +#ifndef _di_f_file_change_owner_ + extern f_return_status f_file_change_owner(const f_string path, const uid_t uid, const gid_t gid); +#endif // _di_f_file_change_owner_ + +/** * Check if a file can be accessed. * * @param path @@ -309,6 +336,111 @@ extern "C" { #endif // _di_f_file_access_ /** + * Copy a file. + * + * The paths must not contain NULL except for the terminating NULL. + * The paths must be NULL terminated. + * + * @param source + * The path to the file to copy from. + * @param destination + * The path to copy to. + * @param mode + * 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. + * @param exclusive + * If TRUE, will fail when file already exists. + * If FALSE, will not fail if file already exists (existing file will be replaced). + * + * @return + * f_none on success. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_access_denied (with error bit) on access denied. + * f_loop (with error bit) on loop error. + * f_file_found (with error bit) if a file was found while exclusive is TRUE. + * f_out_of_memory (with error bit) if out of memory. + * f_prohibited (with error bit) if filesystem does not allow for removing. + * f_read_only (with error bit) if file is read-only. + * f_failure (with error bit) for any other (mkdir()) error. + * f_filesystem_quota_blocks (with error bit) if filesystem's disk blocks or inodes are exhausted. + * f_filesystem_quota_reached (with error bit) quota reached of filesystem is out of space. + * f_file_found (with error bit) of a directory aleady exists at the path. + * f_invalid_name (with error bit) on path name error. + * f_invalid_directory (with error bit) if a supposed directory in path is not actually a directory. + * f_number_overflow (with error bit) on overflow error. + * f_interrupted (with error bit) when program received an interrupt signal, halting create. + * f_file_max_open (with error bit) when system-wide max open files is reached. + * f_busy (with error bit) if filesystem is too busy to perforrm write. + * f_file_error_read (with error bit) on file read error. + * f_file_error_write (with error bit) on file write error. + * + * @see f_file_create() + * @see f_file_open() + * @see f_file_close() + * @see read() + */ +#ifndef _di_f_file_copy_ + extern 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); +#endif // _di_f_file_copy_ + +/** + * Copy a file, as well as its file mode and possibly the owner and group. + * + * The paths must not contain NULL except for the terminating NULL. + * The paths must be NULL terminated. + * + * @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. + * + * @param source + * The path to the file to copy from. + * @param destination + * 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. + * @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 roles + * If TRUE, will copy the owner and group ids. + * If FALSE, will not copy the owner and group ids. + * (In both cases the file mode is copied.) + * + * @return + * f_none on success. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_access_denied (with error bit) on access denied. + * f_loop (with error bit) on loop error. + * f_file_found (with error bit) if a file was found while exclusive is TRUE. + * f_out_of_memory (with error bit) if out of memory. + * f_prohibited (with error bit) if filesystem does not allow for removing. + * f_read_only (with error bit) if file is read-only. + * f_failure (with error bit) for any other (mkdir()) error. + * f_filesystem_quota_blocks (with error bit) if filesystem's disk blocks or inodes are exhausted. + * f_filesystem_quota_reached (with error bit) quota reached of filesystem is out of space. + * f_file_found (with error bit) of a directory aleady exists at the path. + * f_invalid_name (with error bit) on path name error. + * f_invalid_directory (with error bit) if a supposed directory in path is not actually a directory. + * f_number_overflow (with error bit) on overflow error. + * f_interrupted (with error bit) when program received an interrupt signal, halting create. + * f_file_max_open (with error bit) when system-wide max open files is reached. + * f_busy (with error bit) if filesystem is too busy to perforrm write. + * f_file_error_read (with error bit) on file read error. + * f_file_error_write (with error bit) on file write error. + * + * @see f_file_create() + * @see f_file_open() + * @see f_file_close() + * @see read() + */ +#ifndef _di_f_file_clone_ + extern 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); +#endif // _di_f_file_clone_ + +/** * Create a file based on the given path and file mode. * * The file will not be open after calling this. @@ -542,6 +674,8 @@ extern "C" { * f_file_not_open (with error bit) if file is not open. * f_file_error_read (with error bit) if file read failed. * f_invalid_parameter (with error bit) if a parameter is invalid. + * + * @see fread() */ #ifndef _di_f_file_read_ extern f_return_status f_file_read(f_file *file, f_string_dynamic *buffer); diff --git a/level_0/f_file/c/private-file.c b/level_0/f_file/c/private-file.c new file mode 100644 index 0000000..0526fe7 --- /dev/null +++ b/level_0/f_file/c/private-file.c @@ -0,0 +1,220 @@ +#include +#include "private-file.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_di_f_file_change_owner_) || !defined(_di_f_file_copy_) + f_return_status private_f_file_change_owner(const f_string path, const uid_t uid, const gid_t gid) { + if (chown(path, uid, gid) < 0) { + if (errno == EACCES) { + return f_status_set_error(f_access_denied); + } + else if (errno == ENAMETOOLONG || errno == EFAULT) { + return f_status_set_error(f_invalid_name); + } + else if (errno == ELOOP) { + return f_status_set_error(f_loop); + } + else if (errno == ENOENT) { + return f_status_set_error(f_file_not_found); + } + else if (errno == ENOMEM) { + return f_status_set_error(f_out_of_memory); + } + else if (errno == ENOTDIR) { + return f_status_set_error(f_invalid_directory); + } + else if (errno == EPERM) { + return f_status_set_error(f_prohibited); + } + else if (errno == EROFS) { + return f_status_set_error(f_read_only); + } + else if (errno == EIO) { + return f_status_set_error(f_error_input_output); + } + + return f_status_set_error(f_failure); + } + + return f_none; + } +#endif // !defined(_di_f_file_change_owner_) || !defined(_di_f_file_copy_) + +#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) { + if (chmod(path, mode) < 0) { + if (errno == EACCES) { + return f_status_set_error(f_access_denied); + } + else if (errno == ENAMETOOLONG || errno == EFAULT) { + return f_status_set_error(f_invalid_name); + } + else if (errno == ELOOP) { + return f_status_set_error(f_loop); + } + else if (errno == ENOENT) { + return f_status_set_error(f_file_not_found); + } + else if (errno == ENOMEM) { + return f_status_set_error(f_out_of_memory); + } + else if (errno == ENOTDIR) { + return f_status_set_error(f_invalid_directory); + } + else if (errno == EPERM) { + return f_status_set_error(f_prohibited); + } + else if (errno == EROFS) { + return f_status_set_error(f_read_only); + } + else if (errno == EIO) { + return f_status_set_error(f_error_input_output); + } + + return f_status_set_error(f_failure); + } + + return f_none; + } +#endif // !defined(_di_f_file_change_mode_) || !defined(_di_f_file_copy_) + +#if !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) + f_return_status private_f_file_close(f_file *file) { + if (file->address == 0) return f_status_set_error(f_file_not_open); + + // if given a file descriptor, flush all changes to the disk that are not flushed by the 'fflush()' command. + if (file->id) { + if (fsync(file->id) != 0) return f_status_set_error(f_file_error_synchronize); + } + + if (fclose(file->address) == 0) { + file->address = 0; + return f_none; + } + + return f_status_set_error(f_file_error_close); + } +#endif // !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) + +#if !defined(_di_f_file_create_) || !defined(_di_f_file_copy_) + f_return_status private_f_file_create(f_string path, const mode_t mode, const bool exclusive) { + int flags = O_CLOEXEC | O_CREAT | O_WRONLY; + + if (exclusive) { + flags |= O_EXCL; + } + + int result = open(path, flags, mode); + + if (result < 0) { + if (errno == EACCES) { + return f_status_set_error(f_access_denied); + } + else if (errno == EDQUOT) { + return f_status_set_error(f_filesystem_quota_blocks); + } + else if (errno == EEXIST) { + return f_status_set_error(f_file_found); + } + else if (errno == ENAMETOOLONG || errno == EFAULT) { + return f_status_set_error(f_invalid_name); + } + else if (errno == EFBIG || errno == EOVERFLOW) { + return f_status_set_error(f_number_overflow); + } + else if (errno == EINTR) { + return f_status_set_error(f_interrupted); + } + else if (errno == EINVAL) { + return f_status_set_error(f_invalid_parameter); + } + else if (errno == ELOOP) { + return f_status_set_error(f_loop); + } + else if (errno == ENFILE) { + return f_status_set_error(f_file_max_open); + } + else if (errno == ENOENT || errno == ENOTDIR) { + return f_status_set_error(f_invalid_directory); + } + else if (errno == ENOMEM) { + return f_status_set_error(f_out_of_memory); + } + else if (errno == ENOSPC) { + return f_status_set_error(f_filesystem_quota_reached); + } + else if (errno == EPERM) { + return f_status_set_error(f_prohibited); + } + else if (errno == EROFS) { + return f_status_set_error(f_read_only); + } + else if (errno == ETXTBSY) { + return f_status_set_error(f_busy); + } + + return f_status_set_error(f_failure); + } + + close(result); + + return f_none; + } +#endif // !defined(_di_f_file_create_) || !defined(_di_f_file_copy_) + +#if !defined(_di_f_file_open_) || !defined(_di_f_file_copy_) + f_return_status private_f_file_open(f_file *file, const f_string path) { + // if file->mode is unset, then this may cause a segfault. + if (file->mode == 0) return f_status_set_error(f_invalid_parameter); + + file->address = fopen(path, file->mode); + + if (file->address == 0) return f_status_set_error(f_file_not_found); + if (ferror(file->address) != 0) return f_status_set_error(f_file_error_open); + + file->id = fileno(file->address); + + if (file->id == -1) return f_status_set_error(f_file_error_descriptor); + + return f_none; + } +#endif // !defined(_di_f_file_open_) || !defined(_di_f_file_copy_) + +#if !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) + f_return_status private_f_file_stat(const f_string path, struct stat *file_stat) { + if (stat(path, file_stat) < 0) { + if (errno == ENAMETOOLONG || errno == EFAULT) { + return f_status_set_error(f_invalid_name); + } + else if (errno == ENOMEM) { + return f_status_set_error(f_out_of_memory); + } + else if (errno == EOVERFLOW) { + return f_status_set_error(f_number_overflow); + } + else if (errno == ENOTDIR) { + return f_status_set_error(f_invalid_directory); + } + else if (errno == ENOENT) { + return f_status_set_error(f_file_not_found); + } + else if (errno == EACCES) { + return f_status_set_error(f_access_denied); + } + else if (errno == ELOOP) { + return f_status_set_error(f_loop); + } + + return f_status_set_error(f_file_error_stat); + } + + return f_none; + } +#endif // !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_file/c/private-file.h b/level_0/f_file/c/private-file.h new file mode 100644 index 0000000..1c27926 --- /dev/null +++ b/level_0/f_file/c/private-file.h @@ -0,0 +1,202 @@ +/** + * FLL - Level 0 + * + * Project: File + * API Version: 0.5 + * Licenses: lgplv2.1 + * + * Provides structures and data types for a file I/O. + * Provides operations for opening/closing files. + */ +#ifndef _PRIVATE_F_file_h +#define _PRIVATE_F_file_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Private implementation of f_file_change_owner(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param path + * The path file name. + * @param uid + * The new user id to use. + * @param gid + * The new group id to use. + * + * @return + * f_none on success. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_access_denied (with error bit) on access denied. + * f_invalid_name (with error bit) if the filename is too long. + * f_loop (with error bit) on loop error. + * f_file_not_found (with error bit) if file at path was not found. + * f_out_of_memory (with error bit) if out of memory. + * f_invalid_directory (with error bit) on invalid directory. + * f_prohibited (with error bit) if filesystem does not allow for file changes. + * f_read_only (with error bit) if file is read-only. + * f_error_input_output (with error bit) on I/O error. + * f_failure (with error bit) for any other (mkdir()) error. + * + * @see f_file_change_owner() + * @see chown() + */ +#if !defined(_di_f_file_change_owner_) || !defined(_di_f_file_copy_) + extern f_return_status private_f_file_change_owner(const f_string path, const uid_t uid, const gid_t gid) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_change_owner_) || !defined(_di_f_file_copy_) + +/** + * Private implementation of f_file_change_mode(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param path + * The path file name. + * @param mode + * The new mode to use. + * + * @return + * f_none on success. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_access_denied (with error bit) on access denied. + * f_invalid_name (with error bit) if the filename is too long. + * f_loop (with error bit) on loop error. + * f_file_not_found (with error bit) if file at path was not found. + * f_out_of_memory (with error bit) if out of memory. + * f_invalid_directory (with error bit) on invalid directory. + * f_prohibited (with error bit) if filesystem does not allow for file changes. + * f_read_only (with error bit) if file is read-only. + * f_error_input_output (with error bit) on I/O error. + * f_failure (with error bit) for any other (mkdir()) error. + * + * @see f_file_change_mode() + * @see chmod() + */ +#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) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_change_mode_) || !defined(_di_f_file_copy_) + +/** + * Private implementation of f_file_create(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param path + * Full path to the file (including entire filename). + * @param mode + * The file mode. + * @param exclusive + * If TRUE, will fail when file already exists. + * If FALSE, will not fail if file already exists. + * + * @return + * f_none on success. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_access_denied (with error bit) on access denied. + * f_loop (with error bit) on loop error. + * f_file_found (with error bit) if a file was found while exclusive is TRUE. + * f_out_of_memory (with error bit) if out of memory. + * f_prohibited (with error bit) if filesystem does not allow for removing. + * f_read_only (with error bit) if file is read-only. + * f_failure (with error bit) for any other (mkdir()) error. + * f_filesystem_quota_blocks (with error bit) if filesystem's disk blocks or inodes are exhausted. + * f_filesystem_quota_reached (with error bit) quota reached of filesystem is out of space. + * f_file_found (with error bit) of a directory aleady exists at the path. + * f_invalid_name (with error bit) on path name error. + * f_invalid_directory (with error bit) if a supposed directory in path is not actually a directory. + * f_number_overflow (with error bit) on overflow error. + * f_interrupted (with error bit) when program received an interrupt signal, halting create. + * f_file_max_open (with error bit) when system-wide max open files is reached. + * f_busy (with error bit) if filesystem is too busy to perforrm write. + * + * @see f_file_create() + * @see open() + */ +#if !defined(_di_f_file_create_) || !defined(_di_f_file_copy_) + extern f_return_status private_f_file_create(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_) + +/** + * Private implementation of f_file_open(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param file + * The data related to the file being opened. + * This will be updated with the file descriptor and file address. + * @param path + * The path file name. + * + * @return + * f_none on success. + * f_file_not_found (with error bit) if the file was not found. + * f_file_error_open (with error bit) if the file is already open. + * f_file_error_descriptor (with error bit) if unable to load the file descriptor (the file pointer may still be valid). + * f_invalid_parameter (with error bit) if a parameter is invalid. + * + * @see f_file_open() + * @see fopen() + */ +#if !defined(_di_f_file_open_) || !defined(_di_f_file_copy_) + extern f_return_status private_f_file_open(f_file *file, const f_string path) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_open_) || !defined(_di_f_file_copy_) + +/** + * Private implementation of f_file_close(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param file + * The file to close. + * + * @return + * f_none on success. + * f_file_not_open (with error bit) if the file is not open. + * f_file_error_synchronize (with error bit) on fsync() failure. + * f_file_error_close (with error bit) if fclose() failed. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * + * @see f_file_close() + * @see fsync() + * @see fclose() + */ +#if !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) + extern f_return_status private_f_file_close(f_file *file) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_close_) || !defined(_di_f_file_copy_) + +/** + * Private implementation of f_file_close(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param file_name + * The name of the file. + * @param file_stat + * The statistics read. + * + * @return + * f_none on success. + * f_invalid_name (with error bit) if the name is somehow invalid. + * f_out_of_memory (with error bit) if out of memory. + * f_number_overflow (with error bit) on overflow error. + * f_invalid_directory (with error bit) on invalid directory. + * f_file_not_found (with error bit) if the file was not found. + * f_access_denied (with error bit) if access to the file was denied. + * f_loop (with error bit) if a loop occurred. + * f_invalid_parameter (with error bit) if a parameter is invalid. + * + * @see f_file_stat() + * @see stat() + */ +#if !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) + extern f_return_status private_f_file_stat(const f_string file_name, struct stat *file_stat) f_gcc_attribute_visibility_internal; +#endif // !defined(_di_f_file_stat_) || !defined(_di_f_file_copy_) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _PRIVATE_F_file_h diff --git a/level_0/f_file/data/build/settings b/level_0/f_file/data/build/settings index 3ab6c02..2dba4ca 100644 --- a/level_0/f_file/data/build/settings +++ b/level_0/f_file/data/build/settings @@ -11,7 +11,7 @@ build_compiler gcc build_linker ar build_libraries -lc build_libraries_fll -build_sources_library file.c +build_sources_library file.c private-file.c build_sources_program build_sources_headers file.h build_sources_bash diff --git a/level_2/fll_directory/c/directory.c b/level_2/fll_directory/c/directory.c index 6cf90b3..caa0ae0 100644 --- a/level_2/fll_directory/c/directory.c +++ b/level_2/fll_directory/c/directory.c @@ -81,7 +81,7 @@ extern "C" { continue; } - memset(&file_stat, 0, sizeof(file_stat)); + memset(&file_stat, 0, sizeof(struct stat)); status = f_file_stat_at(parent_fd, entity[i]->d_name, &file_stat, 0); if (f_status_is_error(status)) break;