file.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/common/environment.h>
  6. #include <aws/common/file.h>
  7. #include <aws/common/string.h>
  8. #include <dirent.h>
  9. #include <errno.h>
  10. #include <stdio.h>
  11. #include <sys/stat.h>
  12. #include <unistd.h>
  13. FILE *aws_fopen_safe(const struct aws_string *file_path, const struct aws_string *mode) {
  14. return fopen(aws_string_c_str(file_path), aws_string_c_str(mode));
  15. }
  16. static int s_parse_and_raise_error(int errno_cpy) {
  17. if (errno_cpy == 0) {
  18. return AWS_OP_SUCCESS;
  19. }
  20. if (errno_cpy == ENOENT || errno_cpy == ENOTDIR) {
  21. return aws_raise_error(AWS_ERROR_FILE_INVALID_PATH);
  22. }
  23. if (errno_cpy == EMFILE || errno_cpy == ENFILE) {
  24. return aws_raise_error(AWS_ERROR_MAX_FDS_EXCEEDED);
  25. }
  26. if (errno_cpy == EACCES) {
  27. return aws_raise_error(AWS_ERROR_NO_PERMISSION);
  28. }
  29. if (errno_cpy == ENOTEMPTY) {
  30. return aws_raise_error(AWS_ERROR_DIRECTORY_NOT_EMPTY);
  31. }
  32. return aws_raise_error(AWS_ERROR_UNKNOWN);
  33. }
  34. int aws_directory_create(const struct aws_string *dir_path) {
  35. int mkdir_ret = mkdir(aws_string_c_str(dir_path), S_IRWXU | S_IRWXG | S_IRWXO);
  36. /** nobody cares if it already existed. */
  37. if (mkdir_ret != 0 && errno != EEXIST) {
  38. return s_parse_and_raise_error(errno);
  39. }
  40. return AWS_OP_SUCCESS;
  41. }
  42. bool aws_directory_exists(const struct aws_string *dir_path) {
  43. struct stat dir_info;
  44. if (lstat(aws_string_c_str(dir_path), &dir_info) == 0 && S_ISDIR(dir_info.st_mode)) {
  45. return true;
  46. }
  47. return false;
  48. }
  49. static bool s_delete_file_or_directory(const struct aws_directory_entry *entry, void *user_data) {
  50. (void)user_data;
  51. struct aws_allocator *allocator = aws_default_allocator();
  52. struct aws_string *path_str = aws_string_new_from_cursor(allocator, &entry->relative_path);
  53. int ret_val = AWS_OP_SUCCESS;
  54. if (entry->file_type & AWS_FILE_TYPE_FILE) {
  55. ret_val = aws_file_delete(path_str);
  56. }
  57. if (entry->file_type & AWS_FILE_TYPE_DIRECTORY) {
  58. ret_val = aws_directory_delete(path_str, false);
  59. }
  60. aws_string_destroy(path_str);
  61. return ret_val == AWS_OP_SUCCESS;
  62. }
  63. int aws_directory_delete(const struct aws_string *dir_path, bool recursive) {
  64. if (!aws_directory_exists(dir_path)) {
  65. return AWS_OP_SUCCESS;
  66. }
  67. int ret_val = AWS_OP_SUCCESS;
  68. if (recursive) {
  69. ret_val = aws_directory_traverse(aws_default_allocator(), dir_path, true, s_delete_file_or_directory, NULL);
  70. }
  71. if (ret_val && aws_last_error() == AWS_ERROR_FILE_INVALID_PATH) {
  72. aws_reset_error();
  73. return AWS_OP_SUCCESS;
  74. }
  75. if (ret_val) {
  76. return AWS_OP_ERR;
  77. }
  78. int error_code = rmdir(aws_string_c_str(dir_path));
  79. return error_code == 0 ? AWS_OP_SUCCESS : s_parse_and_raise_error(errno);
  80. }
  81. int aws_directory_or_file_move(const struct aws_string *from, const struct aws_string *to) {
  82. int error_code = rename(aws_string_c_str(from), aws_string_c_str(to));
  83. return error_code == 0 ? AWS_OP_SUCCESS : s_parse_and_raise_error(errno);
  84. }
  85. int aws_file_delete(const struct aws_string *file_path) {
  86. int error_code = unlink(aws_string_c_str(file_path));
  87. if (!error_code || errno == ENOENT) {
  88. return AWS_OP_SUCCESS;
  89. }
  90. return s_parse_and_raise_error(errno);
  91. }
  92. int aws_directory_traverse(
  93. struct aws_allocator *allocator,
  94. const struct aws_string *path,
  95. bool recursive,
  96. aws_on_directory_entry *on_entry,
  97. void *user_data) {
  98. DIR *dir = opendir(aws_string_c_str(path));
  99. if (!dir) {
  100. return s_parse_and_raise_error(errno);
  101. }
  102. struct aws_byte_cursor current_path = aws_byte_cursor_from_string(path);
  103. if (current_path.ptr[current_path.len - 1] == AWS_PATH_DELIM) {
  104. current_path.len -= 1;
  105. }
  106. struct dirent *dirent = NULL;
  107. int ret_val = AWS_ERROR_SUCCESS;
  108. errno = 0;
  109. while (!ret_val && (dirent = readdir(dir)) != NULL) {
  110. /* note: dirent->name_len is only defined on the BSDs, but not linux. It's not in the
  111. * required posix spec. So we use dirent->d_name as a c string here. */
  112. struct aws_byte_cursor name_component = aws_byte_cursor_from_c_str(dirent->d_name);
  113. if (aws_byte_cursor_eq_c_str(&name_component, "..") || aws_byte_cursor_eq_c_str(&name_component, ".")) {
  114. continue;
  115. }
  116. struct aws_byte_buf relative_path;
  117. aws_byte_buf_init_copy_from_cursor(&relative_path, allocator, current_path);
  118. aws_byte_buf_append_byte_dynamic(&relative_path, AWS_PATH_DELIM);
  119. aws_byte_buf_append_dynamic(&relative_path, &name_component);
  120. aws_byte_buf_append_byte_dynamic(&relative_path, 0);
  121. relative_path.len -= 1;
  122. struct aws_directory_entry entry;
  123. AWS_ZERO_STRUCT(entry);
  124. struct stat dir_info;
  125. if (!lstat((const char *)relative_path.buffer, &dir_info)) {
  126. if (S_ISDIR(dir_info.st_mode)) {
  127. entry.file_type |= AWS_FILE_TYPE_DIRECTORY;
  128. }
  129. if (S_ISLNK(dir_info.st_mode)) {
  130. entry.file_type |= AWS_FILE_TYPE_SYM_LINK;
  131. }
  132. if (S_ISREG(dir_info.st_mode)) {
  133. entry.file_type |= AWS_FILE_TYPE_FILE;
  134. entry.file_size = dir_info.st_size;
  135. }
  136. if (!entry.file_type) {
  137. AWS_ASSERT("Unknown file type encountered");
  138. }
  139. entry.relative_path = aws_byte_cursor_from_buf(&relative_path);
  140. const char *full_path = realpath((const char *)relative_path.buffer, NULL);
  141. if (full_path) {
  142. entry.path = aws_byte_cursor_from_c_str(full_path);
  143. }
  144. if (recursive && entry.file_type & AWS_FILE_TYPE_DIRECTORY) {
  145. struct aws_string *rel_path_str = aws_string_new_from_cursor(allocator, &entry.relative_path);
  146. ret_val = aws_directory_traverse(allocator, rel_path_str, recursive, on_entry, user_data);
  147. aws_string_destroy(rel_path_str);
  148. }
  149. /* post order traversal, if a node below us ended the traversal, don't call the visitor again. */
  150. if (ret_val && aws_last_error() == AWS_ERROR_OPERATION_INTERUPTED) {
  151. goto cleanup;
  152. }
  153. if (!on_entry(&entry, user_data)) {
  154. ret_val = aws_raise_error(AWS_ERROR_OPERATION_INTERUPTED);
  155. goto cleanup;
  156. }
  157. if (ret_val) {
  158. goto cleanup;
  159. }
  160. cleanup:
  161. /* per https://man7.org/linux/man-pages/man3/realpath.3.html, realpath must be freed, if NULL was passed
  162. * to the second argument. */
  163. if (full_path) {
  164. free((void *)full_path);
  165. }
  166. aws_byte_buf_clean_up(&relative_path);
  167. }
  168. }
  169. closedir(dir);
  170. return ret_val;
  171. }
  172. char aws_get_platform_directory_separator(void) {
  173. return '/';
  174. }
  175. AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME");
  176. struct aws_string *aws_get_home_directory(struct aws_allocator *allocator) {
  177. /* ToDo: check getpwuid_r if environment check fails */
  178. struct aws_string *home_env_var_value = NULL;
  179. if (aws_get_environment_value(allocator, s_home_env_var, &home_env_var_value) == 0 && home_env_var_value != NULL) {
  180. return home_env_var_value;
  181. }
  182. return NULL;
  183. }
  184. bool aws_path_exists(const struct aws_string *path) {
  185. struct stat buffer;
  186. return stat(aws_string_c_str(path), &buffer) == 0;
  187. }
  188. int aws_fseek(FILE *file, int64_t offset, int whence) {
  189. #ifdef AWS_HAVE_POSIX_LARGE_FILE_SUPPORT
  190. int result = fseeko(file, offset, whence);
  191. #else
  192. /* must use fseek(), which takes offset as a long */
  193. if (offset < LONG_MIN || offset > LONG_MAX) {
  194. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  195. }
  196. int result = fseek(file, offset, whence);
  197. #endif /* AWS_HAVE_POSIX_LFS */
  198. if (result != 0) {
  199. return aws_translate_and_raise_io_error(errno);
  200. }
  201. return AWS_OP_SUCCESS;
  202. }
  203. int aws_file_get_length(FILE *file, int64_t *length) {
  204. struct stat file_stats;
  205. int fd = fileno(file);
  206. if (fd == -1) {
  207. return aws_raise_error(AWS_ERROR_INVALID_FILE_HANDLE);
  208. }
  209. if (fstat(fd, &file_stats)) {
  210. return aws_translate_and_raise_io_error(errno);
  211. }
  212. *length = file_stats.st_size;
  213. return AWS_OP_SUCCESS;
  214. }