sparse.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /*
  2. * Copyright (C) 2012 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <assert.h>
  17. #include <stdlib.h>
  18. #include <sparse/sparse.h>
  19. #include "defs.h"
  20. #include "sparse_file.h"
  21. #include "backed_block.h"
  22. #include "output_file.h"
  23. #include "sparse_defs.h"
  24. #include "sparse_format.h"
  25. struct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {
  26. struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));
  27. if (!s) {
  28. return nullptr;
  29. }
  30. s->backed_block_list = backed_block_list_new(block_size);
  31. if (!s->backed_block_list) {
  32. free(s);
  33. return nullptr;
  34. }
  35. s->block_size = block_size;
  36. s->len = len;
  37. return s;
  38. }
  39. void sparse_file_destroy(struct sparse_file* s) {
  40. backed_block_list_destroy(s->backed_block_list);
  41. free(s);
  42. }
  43. int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
  44. return backed_block_add_data(s->backed_block_list, data, len, block);
  45. }
  46. int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
  47. unsigned int block) {
  48. return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
  49. }
  50. int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
  51. unsigned int len, unsigned int block) {
  52. return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
  53. }
  54. int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
  55. unsigned int block) {
  56. return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
  57. }
  58. unsigned int sparse_count_chunks(struct sparse_file* s) {
  59. struct backed_block* bb;
  60. unsigned int last_block = 0;
  61. unsigned int chunks = 0;
  62. for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
  63. if (backed_block_block(bb) > last_block) {
  64. /* If there is a gap between chunks, add a skip chunk */
  65. chunks++;
  66. }
  67. chunks++;
  68. last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
  69. }
  70. if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
  71. chunks++;
  72. }
  73. return chunks;
  74. }
  75. static int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {
  76. int ret = -EINVAL;
  77. switch (backed_block_type(bb)) {
  78. case BACKED_BLOCK_DATA:
  79. ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
  80. break;
  81. case BACKED_BLOCK_FILE:
  82. ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),
  83. backed_block_file_offset(bb));
  84. break;
  85. case BACKED_BLOCK_FD:
  86. ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),
  87. backed_block_file_offset(bb));
  88. break;
  89. case BACKED_BLOCK_FILL:
  90. ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));
  91. break;
  92. }
  93. return ret;
  94. }
  95. static int write_all_blocks(struct sparse_file* s, struct output_file* out) {
  96. struct backed_block* bb;
  97. unsigned int last_block = 0;
  98. int64_t pad;
  99. int ret = 0;
  100. for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
  101. if (backed_block_block(bb) > last_block) {
  102. unsigned int blocks = backed_block_block(bb) - last_block;
  103. write_skip_chunk(out, (int64_t)blocks * s->block_size);
  104. }
  105. ret = sparse_file_write_block(out, bb);
  106. if (ret) return ret;
  107. last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);
  108. }
  109. pad = s->len - (int64_t)last_block * s->block_size;
  110. assert(pad >= 0);
  111. if (pad > 0) {
  112. write_skip_chunk(out, pad);
  113. }
  114. return 0;
  115. }
  116. int sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {
  117. int ret;
  118. int chunks;
  119. struct output_file* out;
  120. chunks = sparse_count_chunks(s);
  121. out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
  122. if (!out) return -ENOMEM;
  123. ret = write_all_blocks(s, out);
  124. output_file_close(out);
  125. return ret;
  126. }
  127. int sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,
  128. int (*write)(void* priv, const void* data, size_t len), void* priv) {
  129. int ret;
  130. int chunks;
  131. struct output_file* out;
  132. chunks = sparse_count_chunks(s);
  133. out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);
  134. if (!out) return -ENOMEM;
  135. ret = write_all_blocks(s, out);
  136. output_file_close(out);
  137. return ret;
  138. }
  139. struct chunk_data {
  140. void* priv;
  141. unsigned int block;
  142. unsigned int nr_blocks;
  143. int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);
  144. };
  145. static int foreach_chunk_write(void* priv, const void* data, size_t len) {
  146. struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);
  147. return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);
  148. }
  149. int sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,
  150. int (*write)(void* priv, const void* data, size_t len,
  151. unsigned int block, unsigned int nr_blocks),
  152. void* priv) {
  153. int ret;
  154. int chunks;
  155. struct chunk_data chk;
  156. struct output_file* out;
  157. struct backed_block* bb;
  158. chk.priv = priv;
  159. chk.write = write;
  160. chk.block = chk.nr_blocks = 0;
  161. chunks = sparse_count_chunks(s);
  162. out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,
  163. chunks, crc);
  164. if (!out) return -ENOMEM;
  165. for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {
  166. chk.block = backed_block_block(bb);
  167. chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;
  168. ret = sparse_file_write_block(out, bb);
  169. if (ret) return ret;
  170. }
  171. output_file_close(out);
  172. return ret;
  173. }
  174. static int out_counter_write(void* priv, const void* data __unused, size_t len) {
  175. int64_t* count = reinterpret_cast<int64_t*>(priv);
  176. *count += len;
  177. return 0;
  178. }
  179. int64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {
  180. int ret;
  181. int chunks = sparse_count_chunks(s);
  182. int64_t count = 0;
  183. struct output_file* out;
  184. out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,
  185. chunks, crc);
  186. if (!out) {
  187. return -1;
  188. }
  189. ret = write_all_blocks(s, out);
  190. output_file_close(out);
  191. if (ret < 0) {
  192. return -1;
  193. }
  194. return count;
  195. }
  196. unsigned int sparse_file_block_size(struct sparse_file* s) {
  197. return s->block_size;
  198. }
  199. static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
  200. unsigned int len) {
  201. int64_t count = 0;
  202. struct output_file* out_counter;
  203. struct backed_block* last_bb = nullptr;
  204. struct backed_block* bb;
  205. struct backed_block* start;
  206. unsigned int last_block = 0;
  207. int64_t file_len = 0;
  208. int ret;
  209. /*
  210. * overhead is sparse file header, the potential end skip
  211. * chunk and crc chunk.
  212. */
  213. int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);
  214. len -= overhead;
  215. start = backed_block_iter_new(from->backed_block_list);
  216. out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
  217. true, 0, false);
  218. if (!out_counter) {
  219. return nullptr;
  220. }
  221. for (bb = start; bb; bb = backed_block_iter_next(bb)) {
  222. count = 0;
  223. if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);
  224. last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);
  225. /* will call out_counter_write to update count */
  226. ret = sparse_file_write_block(out_counter, bb);
  227. if (ret) {
  228. bb = nullptr;
  229. goto out;
  230. }
  231. if (file_len + count > len) {
  232. /*
  233. * If the remaining available size is more than 1/8th of the
  234. * requested size, split the chunk. Results in sparse files that
  235. * are at least 7/8ths of the requested size
  236. */
  237. file_len += sizeof(chunk_header_t);
  238. if (!last_bb || (len - file_len > (len / 8))) {
  239. backed_block_split(from->backed_block_list, bb, len - file_len);
  240. last_bb = bb;
  241. }
  242. goto move;
  243. }
  244. file_len += count;
  245. last_bb = bb;
  246. }
  247. move:
  248. backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);
  249. out:
  250. output_file_close(out_counter);
  251. return bb;
  252. }
  253. int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
  254. int out_s_count) {
  255. struct backed_block* bb;
  256. struct sparse_file* s;
  257. struct sparse_file* tmp;
  258. int c = 0;
  259. tmp = sparse_file_new(in_s->block_size, in_s->len);
  260. if (!tmp) {
  261. return -ENOMEM;
  262. }
  263. do {
  264. s = sparse_file_new(in_s->block_size, in_s->len);
  265. bb = move_chunks_up_to_len(in_s, s, max_len);
  266. if (c < out_s_count) {
  267. out_s[c] = s;
  268. } else {
  269. backed_block_list_move(s->backed_block_list, tmp->backed_block_list, nullptr, nullptr);
  270. sparse_file_destroy(s);
  271. }
  272. c++;
  273. } while (bb);
  274. backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, nullptr, nullptr);
  275. sparse_file_destroy(tmp);
  276. return c;
  277. }
  278. void sparse_file_verbose(struct sparse_file* s) {
  279. s->verbose = true;
  280. }