file.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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/logging.h>
  8. #include <aws/common/string.h>
  9. #include <dirent.h>
  10. #include <errno.h>
  11. #include <pwd.h>
  12. #include <stdio.h>
  13. #include <sys/stat.h>
  14. #include <unistd.h>
  15. FILE *aws_fopen_safe(const struct aws_string *file_path, const struct aws_string *mode) {
  16. FILE *f = fopen(aws_string_c_str(file_path), aws_string_c_str(mode));
  17. if (!f) {
  18. int errno_cpy = errno; /* Always cache errno before potential side-effect */
  19. aws_translate_and_raise_io_error(errno_cpy);
  20. AWS_LOGF_ERROR(
  21. AWS_LS_COMMON_IO,
  22. "static: Failed to open file. path:'%s' mode:'%s' errno:%d aws-error:%d(%s)",
  23. aws_string_c_str(file_path),
  24. aws_string_c_str(mode),
  25. errno_cpy,
  26. aws_last_error(),
  27. aws_error_name(aws_last_error()));
  28. }
  29. return f;
  30. }
  31. int aws_directory_create(const struct aws_string *dir_path) {
  32. int mkdir_ret = mkdir(aws_string_c_str(dir_path), S_IRWXU | S_IRWXG | S_IRWXO);
  33. int errno_value = errno; /* Always cache errno before potential side-effect */
  34. /** nobody cares if it already existed. */
  35. if (mkdir_ret != 0 && errno_value != EEXIST) {
  36. return aws_translate_and_raise_io_error(errno_value);
  37. }
  38. return AWS_OP_SUCCESS;
  39. }
  40. bool aws_directory_exists(const struct aws_string *dir_path) {
  41. struct stat dir_info;
  42. if (lstat(aws_string_c_str(dir_path), &dir_info) == 0 && S_ISDIR(dir_info.st_mode)) {
  43. return true;
  44. }
  45. return false;
  46. }
  47. static bool s_delete_file_or_directory(const struct aws_directory_entry *entry, void *user_data) {
  48. (void)user_data;
  49. struct aws_allocator *allocator = aws_default_allocator();
  50. struct aws_string *path_str = aws_string_new_from_cursor(allocator, &entry->relative_path);
  51. int ret_val = AWS_OP_SUCCESS;
  52. if (entry->file_type & AWS_FILE_TYPE_FILE) {
  53. ret_val = aws_file_delete(path_str);
  54. }
  55. if (entry->file_type & AWS_FILE_TYPE_DIRECTORY) {
  56. ret_val = aws_directory_delete(path_str, false);
  57. }
  58. aws_string_destroy(path_str);
  59. return ret_val == AWS_OP_SUCCESS;
  60. }
  61. int aws_directory_delete(const struct aws_string *dir_path, bool recursive) {
  62. if (!aws_directory_exists(dir_path)) {
  63. return AWS_OP_SUCCESS;
  64. }
  65. int ret_val = AWS_OP_SUCCESS;
  66. if (recursive) {
  67. ret_val = aws_directory_traverse(aws_default_allocator(), dir_path, true, s_delete_file_or_directory, NULL);
  68. }
  69. if (ret_val && aws_last_error() == AWS_ERROR_FILE_INVALID_PATH) {
  70. aws_reset_error();
  71. return AWS_OP_SUCCESS;
  72. }
  73. if (ret_val) {
  74. return AWS_OP_ERR;
  75. }
  76. int error_code = rmdir(aws_string_c_str(dir_path));
  77. int errno_value = errno; /* Always cache errno before potential side-effect */
  78. return error_code == 0 ? AWS_OP_SUCCESS : aws_translate_and_raise_io_error(errno_value);
  79. }
  80. int aws_directory_or_file_move(const struct aws_string *from, const struct aws_string *to) {
  81. int error_code = rename(aws_string_c_str(from), aws_string_c_str(to));
  82. int errno_value = errno; /* Always cache errno before potential side-effect */
  83. return error_code == 0 ? AWS_OP_SUCCESS : aws_translate_and_raise_io_error(errno_value);
  84. }
  85. int aws_file_delete(const struct aws_string *file_path) {
  86. int error_code = unlink(aws_string_c_str(file_path));
  87. int errno_value = errno; /* Always cache errno before potential side-effect */
  88. if (!error_code || errno_value == ENOENT) {
  89. return AWS_OP_SUCCESS;
  90. }
  91. return aws_translate_and_raise_io_error(errno_value);
  92. }
  93. int aws_directory_traverse(
  94. struct aws_allocator *allocator,
  95. const struct aws_string *path,
  96. bool recursive,
  97. aws_on_directory_entry *on_entry,
  98. void *user_data) {
  99. DIR *dir = opendir(aws_string_c_str(path));
  100. int errno_value = errno; /* Always cache errno before potential side-effect */
  101. if (!dir) {
  102. return aws_translate_and_raise_io_error(errno_value);
  103. }
  104. struct aws_byte_cursor current_path = aws_byte_cursor_from_string(path);
  105. if (current_path.ptr[current_path.len - 1] == AWS_PATH_DELIM) {
  106. current_path.len -= 1;
  107. }
  108. struct dirent *dirent = NULL;
  109. int ret_val = AWS_ERROR_SUCCESS;
  110. errno = 0;
  111. while (!ret_val && (dirent = readdir(dir)) != NULL) {
  112. /* note: dirent->name_len is only defined on the BSDs, but not linux. It's not in the
  113. * required posix spec. So we use dirent->d_name as a c string here. */
  114. struct aws_byte_cursor name_component = aws_byte_cursor_from_c_str(dirent->d_name);
  115. if (aws_byte_cursor_eq_c_str(&name_component, "..") || aws_byte_cursor_eq_c_str(&name_component, ".")) {
  116. continue;
  117. }
  118. struct aws_byte_buf relative_path;
  119. aws_byte_buf_init_copy_from_cursor(&relative_path, allocator, current_path);
  120. aws_byte_buf_append_byte_dynamic(&relative_path, AWS_PATH_DELIM);
  121. aws_byte_buf_append_dynamic(&relative_path, &name_component);
  122. aws_byte_buf_append_byte_dynamic(&relative_path, 0);
  123. relative_path.len -= 1;
  124. struct aws_directory_entry entry;
  125. AWS_ZERO_STRUCT(entry);
  126. struct stat dir_info;
  127. if (!lstat((const char *)relative_path.buffer, &dir_info)) {
  128. if (S_ISDIR(dir_info.st_mode)) {
  129. entry.file_type |= AWS_FILE_TYPE_DIRECTORY;
  130. }
  131. if (S_ISLNK(dir_info.st_mode)) {
  132. entry.file_type |= AWS_FILE_TYPE_SYM_LINK;
  133. }
  134. if (S_ISREG(dir_info.st_mode)) {
  135. entry.file_type |= AWS_FILE_TYPE_FILE;
  136. entry.file_size = dir_info.st_size;
  137. }
  138. if (!entry.file_type) {
  139. AWS_ASSERT("Unknown file type encountered");
  140. }
  141. entry.relative_path = aws_byte_cursor_from_buf(&relative_path);
  142. const char *full_path = realpath((const char *)relative_path.buffer, NULL);
  143. if (full_path) {
  144. entry.path = aws_byte_cursor_from_c_str(full_path);
  145. }
  146. if (recursive && entry.file_type & AWS_FILE_TYPE_DIRECTORY) {
  147. struct aws_string *rel_path_str = aws_string_new_from_cursor(allocator, &entry.relative_path);
  148. ret_val = aws_directory_traverse(allocator, rel_path_str, recursive, on_entry, user_data);
  149. aws_string_destroy(rel_path_str);
  150. }
  151. /* post order traversal, if a node below us ended the traversal, don't call the visitor again. */
  152. if (ret_val && aws_last_error() == AWS_ERROR_OPERATION_INTERUPTED) {
  153. goto cleanup;
  154. }
  155. if (!on_entry(&entry, user_data)) {
  156. ret_val = aws_raise_error(AWS_ERROR_OPERATION_INTERUPTED);
  157. goto cleanup;
  158. }
  159. if (ret_val) {
  160. goto cleanup;
  161. }
  162. cleanup:
  163. /* per https://man7.org/linux/man-pages/man3/realpath.3.html, realpath must be freed, if NULL was passed
  164. * to the second argument. */
  165. if (full_path) {
  166. free((void *)full_path);
  167. }
  168. aws_byte_buf_clean_up(&relative_path);
  169. }
  170. }
  171. closedir(dir);
  172. return ret_val;
  173. }
  174. char aws_get_platform_directory_separator(void) {
  175. return '/';
  176. }
  177. AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME");
  178. struct aws_string *aws_get_home_directory(struct aws_allocator *allocator) {
  179. /* First, check "HOME" environment variable.
  180. * If it's set, then return it, even if it's an empty string. */
  181. struct aws_string *home_value = NULL;
  182. aws_get_environment_value(allocator, s_home_env_var, &home_value);
  183. if (home_value != NULL) {
  184. return home_value;
  185. }
  186. /* Next, check getpwuid_r().
  187. * We need to allocate a tmp buffer to store the result strings,
  188. * and the max possible size for this thing can be pretty big,
  189. * so start with a reasonable allocation, and if that's not enough try something bigger. */
  190. uid_t uid = getuid(); /* cannot fail */
  191. struct passwd pwd;
  192. struct passwd *result = NULL;
  193. char *buf = NULL;
  194. int status = ERANGE;
  195. for (size_t bufsize = 1024; bufsize <= 16384 && status == ERANGE; bufsize *= 2) {
  196. if (buf) {
  197. aws_mem_release(allocator, buf);
  198. }
  199. buf = aws_mem_acquire(allocator, bufsize);
  200. /* Note: on newer GCC with address sanitizer on, getpwuid_r triggers
  201. * build error, since buf can in theory be null, but buffsize will be
  202. * nonzero. following if statement works around that. */
  203. if (buf == NULL) {
  204. aws_raise_error(AWS_ERROR_GET_HOME_DIRECTORY_FAILED);
  205. return NULL;
  206. }
  207. status = getpwuid_r(uid, &pwd, buf, bufsize, &result);
  208. }
  209. if (status == 0 && result != NULL && result->pw_dir != NULL) {
  210. home_value = aws_string_new_from_c_str(allocator, result->pw_dir);
  211. } else {
  212. aws_raise_error(AWS_ERROR_GET_HOME_DIRECTORY_FAILED);
  213. }
  214. aws_mem_release(allocator, buf);
  215. return home_value;
  216. }
  217. bool aws_path_exists(const struct aws_string *path) {
  218. struct stat buffer;
  219. return stat(aws_string_c_str(path), &buffer) == 0;
  220. }
  221. int aws_fseek(FILE *file, int64_t offset, int whence) {
  222. #ifdef AWS_HAVE_POSIX_LARGE_FILE_SUPPORT
  223. int result = fseeko(file, offset, whence);
  224. #else
  225. /* must use fseek(), which takes offset as a long */
  226. if (offset < LONG_MIN || offset > LONG_MAX) {
  227. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  228. }
  229. int result = fseek(file, offset, whence);
  230. #endif /* AWS_HAVE_POSIX_LFS */
  231. int errno_value = errno; /* Always cache errno before potential side-effect */
  232. if (result != 0) {
  233. return aws_translate_and_raise_io_error(errno_value);
  234. }
  235. return AWS_OP_SUCCESS;
  236. }
  237. int aws_file_get_length(FILE *file, int64_t *length) {
  238. struct stat file_stats;
  239. int fd = fileno(file);
  240. if (fd == -1) {
  241. return aws_raise_error(AWS_ERROR_INVALID_FILE_HANDLE);
  242. }
  243. if (fstat(fd, &file_stats)) {
  244. int errno_value = errno; /* Always cache errno before potential side-effect */
  245. return aws_translate_and_raise_io_error(errno_value);
  246. }
  247. *length = file_stats.st_size;
  248. return AWS_OP_SUCCESS;
  249. }