dd.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. * Copyright (c) 2021, János Tóth <toth-janos@outlook.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Optional.h>
  7. #include <AK/String.h>
  8. #include <AK/Vector.h>
  9. #include <fcntl.h>
  10. #include <stdint.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <sys/types.h>
  14. #include <unistd.h>
  15. const char* usage = "usage:\n"
  16. "\tdd <options>\n"
  17. "options:\n"
  18. "\tif=<file>\tinput file (default: stdin)\n"
  19. "\tof=<file>\toutput file (default: stdout)\n"
  20. "\tbs=<size>\tblocks size may be followed by multiplicate suffixes: k=1024, M=1024*1024, G=1024*1024*1024 (default: 512)\n"
  21. "\tcount=<size>\t<size> blocks to copy (default: 0 (until end-of-file))\n"
  22. "\tseek=<size>\tskip <size> blocks at start of output (default: 0)\n"
  23. "\tskip=<size>\tskip <size> blocks at start of input (default: 0)\n"
  24. "\tstatus=<level>\tlevel of output (default: default)\n"
  25. "\t\t\tdefault - error messages + final statistics\n"
  26. "\t\t\tnone - just error messages\n"
  27. "\t\t\tnoxfer - no final statistics\n"
  28. "\t--help\t\tshows this text\n";
  29. enum Status {
  30. Default,
  31. None,
  32. Noxfer
  33. };
  34. static String split_at_equals(const char* argument)
  35. {
  36. String string_value(argument);
  37. auto values = string_value.split_limit('=', 2);
  38. if (values.size() < 2) {
  39. warnln("Unable to parse: {}", argument);
  40. return {};
  41. } else {
  42. return values[1];
  43. }
  44. }
  45. static int handle_io_file_arguments(int& fd, int flags, const char* argument)
  46. {
  47. auto value = split_at_equals(argument);
  48. if (value.is_empty()) {
  49. return -1;
  50. }
  51. fd = open(value.characters(), flags, 0666);
  52. if (fd == -1) {
  53. warnln("Unable to open: {}", value);
  54. return -1;
  55. } else {
  56. return 0;
  57. }
  58. }
  59. static int handle_size_arguments(size_t& numeric_value, const char* argument)
  60. {
  61. auto value = split_at_equals(argument);
  62. if (value.is_empty()) {
  63. return -1;
  64. }
  65. unsigned suffix_multiplier = 1;
  66. switch (value.to_lowercase()[value.length() - 1]) {
  67. case 'k':
  68. suffix_multiplier = KiB;
  69. value = value.substring(0, value.length() - 1);
  70. break;
  71. case 'm':
  72. suffix_multiplier = MiB;
  73. value = value.substring(0, value.length() - 1);
  74. break;
  75. case 'g':
  76. suffix_multiplier = GiB;
  77. value = value.substring(0, value.length() - 1);
  78. break;
  79. }
  80. Optional<unsigned> numeric_optional = value.to_uint();
  81. if (!numeric_optional.has_value()) {
  82. warnln("Invalid size-value: {}", value);
  83. return -1;
  84. }
  85. numeric_value = numeric_optional.value() * suffix_multiplier;
  86. if (numeric_value < 1) {
  87. warnln("Invalid size-value: {}", numeric_value);
  88. return -1;
  89. } else {
  90. return 0;
  91. }
  92. }
  93. static int handle_status_arguments(Status& status, const char* argument)
  94. {
  95. auto value = split_at_equals(argument);
  96. if (value.is_empty()) {
  97. return -1;
  98. }
  99. if (value == "default") {
  100. status = Default;
  101. return 0;
  102. } else if (value == "noxfer") {
  103. status = Noxfer;
  104. return 0;
  105. } else if (value == "none") {
  106. status = None;
  107. return 0;
  108. } else {
  109. warnln("Unknown status: {}", value);
  110. return -1;
  111. }
  112. }
  113. int main(int argc, char** argv)
  114. {
  115. int input_fd = 0;
  116. int input_flags = O_RDONLY;
  117. int output_fd = 1;
  118. int output_flags = O_CREAT | O_WRONLY | O_TRUNC;
  119. size_t block_size = 512;
  120. size_t count = 0;
  121. size_t skip = 0;
  122. size_t seek = 0;
  123. Status status = Default;
  124. size_t total_bytes_copied = 0;
  125. size_t total_blocks_in = 0, partial_blocks_in = 0;
  126. size_t total_blocks_out = 0, partial_blocks_out = 0;
  127. uint8_t* buffer = nullptr;
  128. ssize_t nread = 0, nwritten = 0;
  129. for (int a = 1; a < argc; a++) {
  130. if (!strcmp(argv[a], "--help")) {
  131. out("{}", usage);
  132. return 0;
  133. } else if (!strncmp(argv[a], "if=", 3)) {
  134. if (handle_io_file_arguments(input_fd, input_flags, argv[a]) < 0) {
  135. return 1;
  136. }
  137. } else if (!strncmp(argv[a], "of=", 3)) {
  138. if (handle_io_file_arguments(output_fd, output_flags, argv[a]) < 0) {
  139. return 1;
  140. }
  141. } else if (!strncmp(argv[a], "bs=", 3)) {
  142. if (handle_size_arguments(block_size, argv[a]) < 0) {
  143. return 1;
  144. }
  145. } else if (!strncmp(argv[a], "count=", 6)) {
  146. if (handle_size_arguments(count, argv[a]) < 0) {
  147. return 1;
  148. }
  149. } else if (!strncmp(argv[a], "seek=", 5)) {
  150. if (handle_size_arguments(seek, argv[a]) < 0) {
  151. return 1;
  152. }
  153. } else if (!strncmp(argv[a], "skip=", 5)) {
  154. if (handle_size_arguments(skip, argv[a]) < 0) {
  155. return 1;
  156. }
  157. } else if (!strncmp(argv[a], "status=", 7)) {
  158. if (handle_status_arguments(status, argv[a]) < 0) {
  159. return 1;
  160. }
  161. } else {
  162. warn("{}", usage);
  163. return 1;
  164. }
  165. }
  166. if ((buffer = (uint8_t*)malloc(block_size)) == nullptr) {
  167. warnln("Unable to allocate {} bytes for the buffer.", block_size);
  168. return -1;
  169. }
  170. if (seek > 0) {
  171. if (lseek(output_fd, seek * block_size, SEEK_SET) < 0) {
  172. warnln("Unable to seek {} bytes.", seek * block_size);
  173. return -1;
  174. }
  175. }
  176. while (1) {
  177. nread = read(input_fd, buffer, block_size);
  178. if (nread < 0) {
  179. warnln("Cannot read from the input.");
  180. break;
  181. } else if (nread == 0) {
  182. break;
  183. } else {
  184. if ((size_t)nread != block_size) {
  185. partial_blocks_in++;
  186. } else {
  187. total_blocks_in++;
  188. }
  189. if (partial_blocks_in + total_blocks_in <= skip) {
  190. continue;
  191. }
  192. nwritten = write(output_fd, buffer, nread);
  193. if (nwritten < 0) {
  194. warnln("Cannot write to the output.");
  195. break;
  196. } else if (nwritten == 0) {
  197. break;
  198. } else {
  199. if ((size_t)nwritten < block_size) {
  200. partial_blocks_out++;
  201. } else {
  202. total_blocks_out++;
  203. }
  204. total_bytes_copied += nwritten;
  205. if (count > 0 && (partial_blocks_out + total_blocks_out) >= count) {
  206. break;
  207. }
  208. }
  209. }
  210. }
  211. if (status == Default) {
  212. warnln("{}+{} blocks in", total_blocks_in, partial_blocks_in);
  213. warnln("{}+{} blocks out", total_blocks_out, partial_blocks_out);
  214. warnln("{} bytes copied.", total_bytes_copied);
  215. }
  216. free(buffer);
  217. if (input_fd != 0) {
  218. close(input_fd);
  219. }
  220. if (output_fd != 1) {
  221. close(output_fd);
  222. }
  223. return 0;
  224. }