FileUtils.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #include "FileUtils.h"
  2. #include <AK/FileSystemPath.h>
  3. #include <AK/StringBuilder.h>
  4. #include <LibCore/CDirIterator.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <sys/stat.h>
  8. #include <unistd.h>
  9. namespace FileUtils {
  10. int delete_directory(String directory, String& file_that_caused_error)
  11. {
  12. CDirIterator iterator(directory, CDirIterator::SkipDots);
  13. if (iterator.has_error()) {
  14. file_that_caused_error = directory;
  15. return -1;
  16. }
  17. while (iterator.has_next()) {
  18. auto file_to_delete = String::format("%s/%s", directory.characters(), iterator.next_path().characters());
  19. struct stat st;
  20. if (lstat(file_to_delete.characters(), &st)) {
  21. file_that_caused_error = file_to_delete;
  22. return errno;
  23. }
  24. if (S_ISDIR(st.st_mode)) {
  25. if (delete_directory(file_to_delete, file_to_delete)) {
  26. file_that_caused_error = file_to_delete;
  27. return errno;
  28. }
  29. } else if (unlink(file_to_delete.characters())) {
  30. file_that_caused_error = file_to_delete;
  31. return errno;
  32. }
  33. }
  34. if (rmdir(directory.characters())) {
  35. file_that_caused_error = directory;
  36. return errno;
  37. }
  38. return 0;
  39. }
  40. bool copy_file_or_directory(const String& src_path, const String& dst_path)
  41. {
  42. int duplicate_count = 0;
  43. while (access(get_duplicate_name(dst_path, duplicate_count).characters(), F_OK) == 0) {
  44. ++duplicate_count;
  45. }
  46. if (duplicate_count != 0) {
  47. return copy_file_or_directory(src_path, get_duplicate_name(dst_path, duplicate_count));
  48. }
  49. int src_fd = open(src_path.characters(), O_RDONLY);
  50. if (src_fd < 0) {
  51. return false;
  52. }
  53. struct stat src_stat;
  54. int rc = fstat(src_fd, &src_stat);
  55. if (rc < 0) {
  56. return false;
  57. }
  58. if (S_ISDIR(src_stat.st_mode)) {
  59. return copy_directory(src_path, dst_path);
  60. }
  61. return copy_file(src_path, dst_path, src_stat, src_fd);
  62. }
  63. bool copy_directory(const String& src_path, const String& dst_path)
  64. {
  65. int rc = mkdir(dst_path.characters(), 0755);
  66. if (rc < 0) {
  67. return false;
  68. }
  69. CDirIterator di(src_path, CDirIterator::SkipDots);
  70. if (di.has_error()) {
  71. return false;
  72. }
  73. while (di.has_next()) {
  74. String filename = di.next_path();
  75. bool is_copied = copy_file_or_directory(
  76. String::format("%s/%s", src_path.characters(), filename.characters()),
  77. String::format("%s/%s", dst_path.characters(), filename.characters()));
  78. if (!is_copied) {
  79. return false;
  80. }
  81. }
  82. return true;
  83. }
  84. bool copy_file(const String& src_path, const String& dst_path, const struct stat& src_stat, int src_fd)
  85. {
  86. int dst_fd = creat(dst_path.characters(), 0666);
  87. if (dst_fd < 0) {
  88. if (errno != EISDIR) {
  89. return false;
  90. }
  91. StringBuilder builder;
  92. builder.appendf("%s/%s", dst_path, FileSystemPath(src_path).basename());
  93. String dst_path = builder.to_string();
  94. dst_fd = creat(dst_path.characters(), 0666);
  95. if (dst_fd < 0) {
  96. return false;
  97. }
  98. }
  99. if (src_stat.st_size > 0) {
  100. if (ftruncate(dst_fd, src_stat.st_size) < 0) {
  101. perror("cp: ftruncate");
  102. return false;
  103. }
  104. }
  105. for (;;) {
  106. char buffer[32768];
  107. ssize_t nread = read(src_fd, buffer, sizeof(buffer));
  108. if (nread < 0) {
  109. return false;
  110. }
  111. if (nread == 0)
  112. break;
  113. ssize_t remaining_to_write = nread;
  114. char* bufptr = buffer;
  115. while (remaining_to_write) {
  116. ssize_t nwritten = write(dst_fd, bufptr, remaining_to_write);
  117. if (nwritten < 0) {
  118. return false;
  119. }
  120. assert(nwritten > 0);
  121. remaining_to_write -= nwritten;
  122. bufptr += nwritten;
  123. }
  124. }
  125. auto my_umask = umask(0);
  126. umask(my_umask);
  127. int rc = fchmod(dst_fd, src_stat.st_mode & ~my_umask);
  128. if (rc < 0) {
  129. return false;
  130. }
  131. close(src_fd);
  132. close(dst_fd);
  133. return true;
  134. }
  135. String get_duplicate_name(const String& path, int duplicate_count)
  136. {
  137. if (duplicate_count == 0) {
  138. return path;
  139. }
  140. FileSystemPath fsp(path);
  141. StringBuilder duplicated_name;
  142. duplicated_name.append('/');
  143. for (int i = 0; i < fsp.parts().size() - 1; ++i) {
  144. duplicated_name.appendf("%s/", fsp.parts()[i].characters());
  145. }
  146. auto prev_duplicate_tag = String::format("(%d)", duplicate_count);
  147. auto title = fsp.title();
  148. if (title.ends_with(prev_duplicate_tag)) {
  149. // remove the previous duplicate tag "(n)" so we can add a new tag.
  150. title = title.substring(0, title.length() - prev_duplicate_tag.length());
  151. }
  152. duplicated_name.appendf("%s (%d)", fsp.title().characters(), duplicate_count);
  153. if (!fsp.extension().is_empty()) {
  154. duplicated_name.appendf(".%s", fsp.extension().characters());
  155. }
  156. return duplicated_name.build();
  157. }
  158. }