From 38eaac9290127863014a55a562ce2f1ff2f58ac8 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Tue, 8 Sep 2020 23:09:06 -0500 Subject: [PATCH] Progress: featureless make. Implement file move functions. - There will need to be future changes to allow for moving across filesystems. - This move across filesystems may need to be an fll_file_move() so that it can either do a directory clone or a file clone. Fix a typo 'perforrm', which should instead be 'perform'. Add 'F_mount' status code. Implement the move section operation. --- level_0/f_directory/c/directory.h | 4 +- level_0/f_file/c/file.c | 69 ++++++++++++++++++++++ level_0/f_file/c/file.h | 119 ++++++++++++++++++++++++++++++++++---- level_0/f_file/c/private-file.h | 10 ++-- level_0/f_status/c/status.h | 1 + level_1/fl_status/c/status.c | 3 + level_1/fl_status/c/status.h | 2 + level_2/fll_status/c/status.c | 5 ++ level_3/fake/c/private-make.c | 52 ++++++++++++++--- 9 files changed, 239 insertions(+), 26 deletions(-) diff --git a/level_0/f_directory/c/directory.h b/level_0/f_directory/c/directory.h index 19f998a..d22f6bb 100644 --- a/level_0/f_directory/c/directory.h +++ b/level_0/f_directory/c/directory.h @@ -471,7 +471,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. * F_file_found (with error bit) if a file aleady exists at the path. @@ -513,7 +513,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. * F_file_found (with error bit) if a file aleady exists at the path (when calling utimensat()). diff --git a/level_0/f_file/c/file.c b/level_0/f_file/c/file.c index 79bb6e2..cf422f8 100644 --- a/level_0/f_file/c/file.c +++ b/level_0/f_file/c/file.c @@ -1318,6 +1318,75 @@ extern "C" { } #endif // _di_f_file_mode_to_mode_ +#ifndef _di_f_file_move_ + f_return_status f_file_move(const f_string_t source, const f_string_t destination) { + #ifndef _di_level_0_parameter_checking_ + if (source == 0) return F_status_set_error(F_parameter); + if (destination == 0) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (rename(source, destination) < 0) { + if (errno == EACCES) return F_status_set_error(F_access_denied); + if (errno == EBUSY) return F_status_set_error(F_busy); + if (errno == EDQUOT) return F_status_set_error(F_filesystem_quota_block); + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EISDIR) return F_status_set_error(F_file_type_directory); + if (errno == ELOOP) return F_status_set_error(F_loop); + if (errno == EMLINK) return F_status_set_error(F_link); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENOENT) return F_status_set_error(F_file_found_not); + if (errno == ENOMEM) return F_status_set_error(F_memory_out); + if (errno == ENOSPC) return F_status_set_error(F_space_not); + if (errno == ENOTDIR) return F_status_set_error(F_directory); + if (errno == ENOTEMPTY) return F_status_set_error(F_directory_empty_not); + if (errno == EEXIST) return F_status_set_error(F_directory_empty_not); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == EROFS) return F_status_set_error(F_read_only); + if (errno == EXDEV) return F_status_set_error(F_mount); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_file_move_ + +#ifndef _di_f_file_move_at_ + f_return_status f_file_move_at(const int at_id, const int to_id, const f_string_t source, const f_string_t destination) { + #ifndef _di_level_0_parameter_checking_ + if (source == 0) return F_status_set_error(F_parameter); + if (destination == 0) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (renameat(at_id, source, to_id, destination) < 0) { + if (errno == EACCES) return F_status_set_error(F_access_denied); + if (errno == EBUSY) return F_status_set_error(F_busy); + if (errno == EDQUOT) return F_status_set_error(F_filesystem_quota_block); + if (errno == EFAULT) return F_status_set_error(F_buffer); + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EISDIR) return F_status_set_error(F_file_type_directory); + if (errno == ELOOP) return F_status_set_error(F_loop); + if (errno == EMLINK) return F_status_set_error(F_link); + if (errno == ENAMETOOLONG) return F_status_set_error(F_name); + if (errno == ENOENT) return F_status_set_error(F_file_found_not); + if (errno == ENOMEM) return F_status_set_error(F_memory_out); + if (errno == ENOSPC) return F_status_set_error(F_space_not); + if (errno == ENOTDIR) return F_status_set_error(F_directory); + if (errno == ENOTEMPTY) return F_status_set_error(F_directory_empty_not); + if (errno == EEXIST) return F_status_set_error(F_directory_empty_not); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == EROFS) return F_status_set_error(F_read_only); + if (errno == EXDEV) return F_status_set_error(F_mount); + if (errno == EBADF) return F_status_set_error(F_directory_descriptor); + + return F_status_set_error(F_failure); + } + + return F_none; + } +#endif // _di_f_file_move_at_ + #ifndef _di_f_file_name_base_ f_return_status f_file_name_base(const f_string_t path, const f_string_length_t length, f_string_dynamic_t *name_base) { #ifndef _di_level_0_parameter_checking_ diff --git a/level_0/f_file/c/file.h b/level_0/f_file/c/file.h index b14f4ab..dafef88 100644 --- a/level_0/f_file/c/file.h +++ b/level_0/f_file/c/file.h @@ -417,7 +417,7 @@ extern "C" { * F_access_group (with error bit) if the current user does not have access to assign the specified group. * F_access_mode (with error bit) if the current user does not have access to assign the file mode. * F_access_owner (with error bit) if the current user does not have access to assign the specified owner. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file was found while exclusive is TRUE. * F_file_open_max (with error bit) when system-wide max open files is reached. @@ -494,7 +494,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file was found while exclusive is TRUE. * F_file_open_max (with error bit) when system-wide max open files is reached. @@ -533,7 +533,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file was found while exclusive is TRUE. * F_file_open_max (with error bit) when system-wide max open files is reached. @@ -573,7 +573,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file was found while exclusive is TRUE. * F_file_open_max (with error bit) when system-wide max open files is reached. @@ -983,7 +983,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_file_found (with error bit) if a file aleady exists at the path. * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. @@ -1020,7 +1020,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_buffer (with error bit) if the buffer is invalid. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. @@ -1057,7 +1057,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file aleady exists at the path. * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. @@ -1098,7 +1098,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. * F_file_found (with error bit) if a file aleady exists at the path. @@ -1446,6 +1446,105 @@ extern "C" { #endif // _di_f_file_mode_to_mode_ /** + * Move a file. + * + * The paths must not contain NULL except for the terminating NULL. + * The paths must be NULL terminated. + * + * This essentially renames a file but can also change the file's path, which is therefore a move. + * + * If destination already exists, then according to rename(), destination will be atomically replaced. + * Which, if destination is a directory must either not exist or be empty. + * + * It is recommended to perform an existence test on destination to not have to consider the details on how rename() operates with an existing destination. + * + * @todo consider handling F_mount error internally, copying across filesystem and then removing file on success. + * + * @param source + * The path to the file to copy from. + * @param destination + * The path to copy to. + * + * @return + * F_none on success. + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_busy (with error bit) if filesystem is too busy to perform write. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_directory_empty_not (with error bit) if the destination is a non-empty directory. + * F_file_found_not (with error bit) if file at path was not found. + * F_file_type_directory (with error bit) if destination is a directory but source is not. + * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_link (with error bit) if source or destination has the maxiumum associated links. + * F_loop (with error bit) on loop error. + * F_memory_out (with error bit) if out of memory. + * F_mount (with error bit) if source and destination are not within the same mounted filesystems. + * F_name (with error bit) on path name error. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if filesystem does not allow for making changes. + * F_read_only (with error bit) if file is read-only. + * F_space_not (with error bit) if filesystem is out of space (or filesystem quota is reached). + * F_failure (with error bit) for any other error. + * + * @see rename() + */ +#ifndef _di_f_file_move_ + extern f_return_status f_file_move(const f_string_t source, const f_string_t destination); +#endif // _di_f_file_move_ + +/** + * Move a file. + * + * The paths must not contain NULL except for the terminating NULL. + * The paths must be NULL terminated. + * + * This essentially renames a file but can also change the file's path, which is therefore a move. + * + * If destination already exists, then according to rename(), destination will be atomically replaced. + * Which, if destination is a directory must either not exist or be empty. + * + * It is recommended to perform an existence test on destination to not have to consider the details on how rename() operates with an existing destination. + * + * @todo consider handling F_mount error internally, copying across filesystem and then removing file on success. + * + * @param at_id + * The parent directory, as an open directory file descriptor, in which the source is relative to. + * @param to_id + * The parent directory, as an open directory file descriptor, in which the destination is relative to. + * @param source + * The path to the file to copy from. + * @param destination + * The path to copy to. + * + * @return + * F_none on success. + * F_access_denied (with error bit) on access denied. + * F_buffer (with error bit) if the buffer is invalid. + * F_busy (with error bit) if filesystem is too busy to perform write. + * F_directory (with error bit) if a supposed directory in path is not actually a directory. + * F_directory_descriptor (with error bit) for bad directory descriptor for at_id or to_id. + * F_directory_empty_not (with error bit) if the destination is a non-empty directory. + * F_file_found_not (with error bit) if file at path was not found. + * F_file_type_directory (with error bit) if destination is a directory but source is not. + * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. + * F_link (with error bit) if source or destination has the maxiumum associated links. + * F_loop (with error bit) on loop error. + * F_memory_out (with error bit) if out of memory. + * F_mount (with error bit) if source and destination are not within the same mounted filesystems. + * F_name (with error bit) on path name error. + * F_parameter (with error bit) if a parameter is invalid. + * F_prohibited (with error bit) if filesystem does not allow for making changes. + * F_read_only (with error bit) if file is read-only. + * F_space_not (with error bit) if filesystem is out of space (or filesystem quota is reached). + * F_failure (with error bit) for any other error. + * + * @see renameat() + */ +#ifndef _di_f_file_move_at_ + extern f_return_status f_file_move_at(const int at_id, const int to_id, const f_string_t source, const f_string_t destination); +#endif // _di_f_file_move_at_ + +/** * Get the base name of a file path. * * @param path @@ -2019,7 +2118,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. * F_file_found (with error bit) if a file aleady exists at the path. @@ -2061,7 +2160,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. * F_file_found (with error bit) if a file aleady exists at the path (when calling utimensat()). diff --git a/level_0/f_file/c/private-file.h b/level_0/f_file/c/private-file.h index 05ce890..0f649c0 100644 --- a/level_0/f_file/c/private-file.h +++ b/level_0/f_file/c/private-file.h @@ -63,7 +63,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file was found while exclusive is TRUE. * F_file_open_max (with error bit) when system-wide max open files is reached. @@ -109,7 +109,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file was found while exclusive is TRUE. * F_file_open_max (with error bit) when system-wide max open files is reached. @@ -154,7 +154,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_file_found (with error bit) if a file was found while exclusive is TRUE. * F_file_open_max (with error bit) when system-wide max open files is reached. @@ -432,7 +432,7 @@ extern "C" { * F_none on success. * F_access_denied (with error bit) on access denied. * F_buffer (with error bit) if the buffer is invalid. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_file_found (with error bit) if a file aleady exists at the path. * F_file_found_not (with error bit) if a parent path in point does not exist or is a broken symlink. * F_filesystem_quota_block (with error bit) if filesystem's disk blocks or inodes are exhausted. @@ -469,7 +469,7 @@ extern "C" { * @return * F_none on success. * F_access_denied (with error bit) on access denied. - * F_busy (with error bit) if filesystem is too busy to perforrm write. + * F_busy (with error bit) if filesystem is too busy to perform write. * F_buffer (with error bit) if the buffer is invalid. * F_directory (with error bit) if a supposed directory in path is not actually a directory. * F_directory_descriptor (with error bit) for bad directory descriptor for at_id. diff --git a/level_0/f_status/c/status.h b/level_0/f_status/c/status.h index 2ddb67e..325d537 100644 --- a/level_0/f_status/c/status.h +++ b/level_0/f_status/c/status.h @@ -171,6 +171,7 @@ extern "C" { F_loop, F_maybe, F_memory_out, + F_mount, F_name, F_parameter, F_pipe, diff --git a/level_1/fl_status/c/status.c b/level_1/fl_status/c/status.c index c3bba9a..f65cd3c 100644 --- a/level_1/fl_status/c/status.c +++ b/level_1/fl_status/c/status.c @@ -299,6 +299,9 @@ extern "C" { case F_memory_out: *string = FL_status_string_memory_out; break; + case F_mount: + *string = FL_status_string_mount; + break; case F_name: *string = FL_status_string_name; break; diff --git a/level_1/fl_status/c/status.h b/level_1/fl_status/c/status.h index da1356c..7678d1a 100644 --- a/level_1/fl_status/c/status.h +++ b/level_1/fl_status/c/status.h @@ -191,6 +191,7 @@ extern "C" { #define FL_status_string_loop "F_loop" #define FL_status_string_maybe "F_maybe" #define FL_status_string_memory_out "F_memory_out" + #define FL_status_string_mount "F_mount" #define FL_status_string_name "F_name" #define FL_status_string_parameter "F_parameter" #define FL_status_string_pipe "F_pipe" @@ -241,6 +242,7 @@ extern "C" { #define FL_status_string_loop_length 6 #define FL_status_string_maybe_length 7 #define FL_status_string_memory_out_length 12 + #define FL_status_string_mount_length 7 #define FL_status_string_name_length 6 #define FL_status_string_parameter_length 11 #define FL_status_string_pipe_length 6 diff --git a/level_2/fll_status/c/status.c b/level_2/fll_status/c/status.c index 414be02..63d17bf 100644 --- a/level_2/fll_status/c/status.c +++ b/level_2/fll_status/c/status.c @@ -501,6 +501,11 @@ extern "C" { return F_none; } + if (fl_string_compare(string, FL_status_string_mount, length, FL_status_string_mount_length) == F_equal_to) { + *code = F_mount; + return F_none; + } + if (fl_string_compare(string, FL_status_string_name, length, FL_status_string_name_length) == F_equal_to) { *code = F_name; return F_none; diff --git a/level_3/fake/c/private-make.c b/level_3/fake/c/private-make.c index e521d40..aa292bf 100644 --- a/level_3/fake/c/private-make.c +++ b/level_3/fake/c/private-make.c @@ -1888,7 +1888,9 @@ extern "C" { f_string_length_t destination_length = 0; - f_mode_t mode = f_mode_t_initialize;f_macro_mode_t_set_default_umask(mode, data.umask); + f_mode_t mode = f_mode_t_initialize; + + f_macro_mode_t_set_default_umask(mode, data.umask); if (data.verbosity == fake_verbosity_verbose) { recurse.verbose = f_type_output; @@ -2753,19 +2755,51 @@ extern "C" { const f_array_length_t total = arguments.used -1; f_status_t status_file = F_none; + f_string_length_t destination_length = 0; + + bool existing = F_true; + + // in this case, the destination could be a file, so confirm this. + if (arguments.used == 2) { + status_file = f_directory_is(arguments.array[total].string); + + if (F_status_is_error(status_file)) { + fake_print_message_file(data, F_status_set_fine(status_file), "f_directory_is", arguments.array[1].string, "identify", F_false, F_true, data_make->print); + *status = F_status_set_error(F_failure); + return; + } + + if (status_file == F_false || status_file == F_file_found_not) { + existing = F_false; + } + } + for (f_array_length_t i = 0; i < total; i++) { - status_file = f_directory_is(arguments.array[i].string); + destination_length = arguments.array[total].used; - if (status_file == F_true) { - // @todo: *status = fl_directory_copy(); + if (existing) { + destination_length += arguments.array[i].used + 1; } - else if (status_file == F_true) { - // @todo: *status = f_file_copy(); + + char destination[destination_length + 1]; + + memcpy(destination, arguments.array[total].string, arguments.array[total].used); + + if (existing) { + memcpy(destination + arguments.array[total].used + 1, arguments.array[i].string, arguments.array[i].used); + destination[arguments.array[total].used] = f_path_separator[0]; } - else if (F_status_is_error(status_file)) { - // @todo + + destination[destination_length] = 0; + + status_file = f_file_move(arguments.array[i].string, destination); + + if (F_status_is_error(status_file)) { + fake_print_message_file(data, F_status_set_fine(status_file), "f_file_move", arguments.array[i].string, "move", F_false, F_true, data_make->print); *status = F_status_set_error(F_failure); - break; + } + else if (data.verbosity == fake_verbosity_verbose) { + printf("Moved '%s' to '%s'.%c", arguments.array[i].string, destination, f_string_eol[0]); } } // for -- 1.8.3.1