PageRenderTime 106ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/microtar/src/microtar.c

https://gitlab.com/GrieverV/rockbox
C | 725 lines | 547 code | 135 blank | 43 comment | 132 complexity | 2b4f055d3c30f8be5d137e203857c8ca MD5 | raw file
  1. /*
  2. * Copyright (c) 2017 rxi
  3. * Copyright (c) 2021 Aidan MacDonald
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to
  7. * deal in the Software without restriction, including without limitation the
  8. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. * sell copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. * IN THE SOFTWARE.
  22. */
  23. #include "microtar.h"
  24. #include <limits.h>
  25. #include <string.h>
  26. #ifdef ROCKBOX
  27. /* Rockbox lacks strncpy in its libc */
  28. #define strncpy my_strncpy
  29. static char* my_strncpy(char* dest, const char* src, size_t n) {
  30. size_t i;
  31. for(i = 0; i < n && *src; ++i)
  32. dest[i] = src[i];
  33. for(; i < n; ++i)
  34. dest[i] = 0;
  35. return dest;
  36. }
  37. #endif
  38. enum {
  39. S_HEADER_VALID = 1 << 0,
  40. S_WROTE_HEADER = 1 << 1,
  41. S_WROTE_DATA = 1 << 2,
  42. S_WROTE_DATA_EOF = 1 << 3,
  43. S_WROTE_FINALIZE = 1 << 4,
  44. };
  45. enum {
  46. NAME_OFF = 0, NAME_LEN = 100,
  47. MODE_OFF = NAME_OFF+NAME_LEN, MODE_LEN = 8,
  48. OWNER_OFF = MODE_OFF+MODE_LEN, OWNER_LEN = 8,
  49. GROUP_OFF = OWNER_OFF+OWNER_LEN, GROUP_LEN = 8,
  50. SIZE_OFF = GROUP_OFF+GROUP_LEN, SIZE_LEN = 12,
  51. MTIME_OFF = SIZE_OFF+SIZE_LEN, MTIME_LEN = 12,
  52. CHKSUM_OFF = MTIME_OFF+MTIME_LEN, CHKSUM_LEN = 8,
  53. TYPE_OFF = CHKSUM_OFF+CHKSUM_LEN,
  54. LINKNAME_OFF = TYPE_OFF+1, LINKNAME_LEN = 100,
  55. HEADER_LEN = 512,
  56. };
  57. static int parse_octal(const char* str, size_t len, unsigned* ret)
  58. {
  59. unsigned n = 0;
  60. while(len-- > 0 && *str != 0) {
  61. if(*str < '0' || *str > '9')
  62. return MTAR_EOVERFLOW;
  63. if(n > UINT_MAX/8)
  64. return MTAR_EOVERFLOW;
  65. else
  66. n *= 8;
  67. char r = *str++ - '0';
  68. if(n > UINT_MAX - r)
  69. return MTAR_EOVERFLOW;
  70. else
  71. n += r;
  72. }
  73. *ret = n;
  74. return MTAR_ESUCCESS;
  75. }
  76. static int print_octal(char* str, size_t len, unsigned value)
  77. {
  78. /* move backwards over the output string */
  79. char* ptr = str + len - 1;
  80. *ptr = 0;
  81. /* output the significant digits */
  82. while(value > 0) {
  83. if(ptr == str)
  84. return MTAR_EOVERFLOW;
  85. --ptr;
  86. *ptr = '0' + (value % 8);
  87. value /= 8;
  88. }
  89. /* pad the remainder of the field with zeros */
  90. while(ptr != str) {
  91. --ptr;
  92. *ptr = '0';
  93. }
  94. return MTAR_ESUCCESS;
  95. }
  96. static unsigned round_up_512(unsigned n)
  97. {
  98. return (n + 511u) & ~511u;
  99. }
  100. static int tread(mtar_t* tar, void* data, unsigned size)
  101. {
  102. int ret = tar->ops->read(tar->stream, data, size);
  103. if(ret >= 0)
  104. tar->pos += ret;
  105. return ret;
  106. }
  107. static int twrite(mtar_t* tar, const void* data, unsigned size)
  108. {
  109. int ret = tar->ops->write(tar->stream, data, size);
  110. if(ret >= 0)
  111. tar->pos += ret;
  112. return ret;
  113. }
  114. static int tseek(mtar_t* tar, unsigned pos)
  115. {
  116. int err = tar->ops->seek(tar->stream, pos);
  117. tar->pos = pos;
  118. return err;
  119. }
  120. static int write_null_bytes(mtar_t* tar, size_t count)
  121. {
  122. int ret;
  123. size_t n;
  124. memset(tar->buffer, 0, sizeof(tar->buffer));
  125. while(count > 0) {
  126. n = count < sizeof(tar->buffer) ? count : sizeof(tar->buffer);
  127. ret = twrite(tar, tar->buffer, n);
  128. if(ret < 0)
  129. return ret;
  130. if(ret != (int)n)
  131. return MTAR_EWRITEFAIL;
  132. count -= n;
  133. }
  134. return MTAR_ESUCCESS;
  135. }
  136. static unsigned checksum(const char* raw)
  137. {
  138. unsigned i;
  139. unsigned char* p = (unsigned char*)raw;
  140. unsigned res = 256;
  141. for(i = 0; i < CHKSUM_OFF; i++)
  142. res += p[i];
  143. for(i = TYPE_OFF; i < HEADER_LEN; i++)
  144. res += p[i];
  145. return res;
  146. }
  147. static int raw_to_header(mtar_header_t* h, const char* raw)
  148. {
  149. unsigned chksum;
  150. int rc;
  151. /* If the checksum starts with a null byte we assume the record is NULL */
  152. if(raw[CHKSUM_OFF] == '\0')
  153. return MTAR_ENULLRECORD;
  154. /* Compare the checksum */
  155. if((rc = parse_octal(&raw[CHKSUM_OFF], CHKSUM_LEN, &chksum)))
  156. return rc;
  157. if(chksum != checksum(raw))
  158. return MTAR_EBADCHKSUM;
  159. /* Load raw header into header */
  160. if((rc = parse_octal(&raw[MODE_OFF], MODE_LEN, &h->mode)))
  161. return rc;
  162. if((rc = parse_octal(&raw[OWNER_OFF], OWNER_LEN, &h->owner)))
  163. return rc;
  164. if((rc = parse_octal(&raw[GROUP_OFF], GROUP_LEN, &h->group)))
  165. return rc;
  166. if((rc = parse_octal(&raw[SIZE_OFF], SIZE_LEN, &h->size)))
  167. return rc;
  168. if((rc = parse_octal(&raw[MTIME_OFF], MTIME_LEN, &h->mtime)))
  169. return rc;
  170. h->type = raw[TYPE_OFF];
  171. if(!h->type)
  172. h->type = MTAR_TREG;
  173. memcpy(h->name, &raw[NAME_OFF], NAME_LEN);
  174. h->name[sizeof(h->name) - 1] = 0;
  175. memcpy(h->linkname, &raw[LINKNAME_OFF], LINKNAME_LEN);
  176. h->linkname[sizeof(h->linkname) - 1] = 0;
  177. return MTAR_ESUCCESS;
  178. }
  179. static int header_to_raw(char* raw, const mtar_header_t* h)
  180. {
  181. unsigned chksum;
  182. int rc;
  183. memset(raw, 0, HEADER_LEN);
  184. /* Load header into raw header */
  185. if((rc = print_octal(&raw[MODE_OFF], MODE_LEN, h->mode)))
  186. return rc;
  187. if((rc = print_octal(&raw[OWNER_OFF], OWNER_LEN, h->owner)))
  188. return rc;
  189. if((rc = print_octal(&raw[GROUP_OFF], GROUP_LEN, h->group)))
  190. return rc;
  191. if((rc = print_octal(&raw[SIZE_OFF], SIZE_LEN, h->size)))
  192. return rc;
  193. if((rc = print_octal(&raw[MTIME_OFF], MTIME_LEN, h->mtime)))
  194. return rc;
  195. raw[TYPE_OFF] = h->type ? h->type : MTAR_TREG;
  196. #if defined(__GNUC__) && (__GNUC__ >= 8)
  197. /* Sigh. GCC wrongly assumes the output of strncpy() is supposed to be
  198. * a null-terminated string -- which it is not, and we are relying on
  199. * that fact here -- and tries to warn about 'string truncation' because
  200. * the null terminator might not be copied. Just suppress the warning. */
  201. # pragma GCC diagnostic push
  202. # pragma GCC diagnostic ignored "-Wstringop-truncation"
  203. #endif
  204. strncpy(&raw[NAME_OFF], h->name, NAME_LEN);
  205. strncpy(&raw[LINKNAME_OFF], h->linkname, LINKNAME_LEN);
  206. #if defined(__GNUC__) && (__GNUC__ >= 8)
  207. # pragma GCC diagnostic pop
  208. #endif
  209. /* Calculate and write checksum */
  210. chksum = checksum(raw);
  211. if((rc = print_octal(&raw[CHKSUM_OFF], CHKSUM_LEN-1, chksum)))
  212. return rc;
  213. raw[CHKSUM_OFF + CHKSUM_LEN - 1] = ' ';
  214. return MTAR_ESUCCESS;
  215. }
  216. static unsigned data_beg_pos(const mtar_t* tar)
  217. {
  218. return tar->header_pos + HEADER_LEN;
  219. }
  220. static unsigned data_end_pos(const mtar_t* tar)
  221. {
  222. return tar->end_pos;
  223. }
  224. static int ensure_header(mtar_t* tar)
  225. {
  226. int ret, err;
  227. if(tar->state & S_HEADER_VALID)
  228. return MTAR_ESUCCESS;
  229. if(tar->pos > UINT_MAX - HEADER_LEN)
  230. return MTAR_EOVERFLOW;
  231. tar->header_pos = tar->pos;
  232. tar->end_pos = data_beg_pos(tar);
  233. ret = tread(tar, tar->buffer, HEADER_LEN);
  234. if(ret < 0)
  235. return ret;
  236. if(ret != HEADER_LEN)
  237. return MTAR_EREADFAIL;
  238. err = raw_to_header(&tar->header, tar->buffer);
  239. if(err)
  240. return err;
  241. if(tar->end_pos > UINT_MAX - tar->header.size)
  242. return MTAR_EOVERFLOW;
  243. tar->end_pos += tar->header.size;
  244. tar->state |= S_HEADER_VALID;
  245. return MTAR_ESUCCESS;
  246. }
  247. const char* mtar_strerror(int err)
  248. {
  249. switch(err) {
  250. case MTAR_ESUCCESS: return "success";
  251. case MTAR_EFAILURE: return "failure";
  252. case MTAR_EOPENFAIL: return "could not open";
  253. case MTAR_EREADFAIL: return "could not read";
  254. case MTAR_EWRITEFAIL: return "could not write";
  255. case MTAR_ESEEKFAIL: return "could not seek";
  256. case MTAR_ESEEKRANGE: return "seek out of bounds";
  257. case MTAR_EBADCHKSUM: return "bad checksum";
  258. case MTAR_ENULLRECORD: return "null record";
  259. case MTAR_ENOTFOUND: return "file not found";
  260. case MTAR_EOVERFLOW: return "overflow";
  261. case MTAR_EAPI: return "API usage error";
  262. case MTAR_ENAMETOOLONG: return "name too long";
  263. case MTAR_EWRONGSIZE: return "wrong amount of data written";
  264. case MTAR_EACCESS: return "wrong access mode";
  265. default: return "unknown error";
  266. }
  267. }
  268. void mtar_init(mtar_t* tar, int access, const mtar_ops_t* ops, void* stream)
  269. {
  270. memset(tar, 0, sizeof(mtar_t));
  271. tar->access = access;
  272. tar->ops = ops;
  273. tar->stream = stream;
  274. }
  275. int mtar_close(mtar_t* tar)
  276. {
  277. int err = tar->ops->close(tar->stream);
  278. tar->ops = NULL;
  279. tar->stream = NULL;
  280. return err;
  281. }
  282. int mtar_is_open(mtar_t* tar)
  283. {
  284. return (tar->ops != NULL) ? 1 : 0;
  285. }
  286. mtar_header_t* mtar_get_header(mtar_t* tar)
  287. {
  288. if(tar->state & S_HEADER_VALID)
  289. return &tar->header;
  290. else
  291. return NULL;
  292. }
  293. int mtar_access_mode(const mtar_t* tar)
  294. {
  295. return tar->access;
  296. }
  297. int mtar_rewind(mtar_t* tar)
  298. {
  299. #ifndef MICROTAR_DISABLE_API_CHECKS
  300. if(tar->access != MTAR_READ)
  301. return MTAR_EAPI;
  302. #endif
  303. int err = tseek(tar, 0);
  304. tar->state = 0;
  305. return err;
  306. }
  307. int mtar_next(mtar_t* tar)
  308. {
  309. #ifndef MICROTAR_DISABLE_API_CHECKS
  310. if(tar->access != MTAR_READ)
  311. return MTAR_EACCESS;
  312. #endif
  313. if(tar->state & S_HEADER_VALID) {
  314. tar->state &= ~S_HEADER_VALID;
  315. /* seek to the next header */
  316. int err = tseek(tar, round_up_512(data_end_pos(tar)));
  317. if(err)
  318. return err;
  319. }
  320. return ensure_header(tar);
  321. }
  322. int mtar_foreach(mtar_t* tar, mtar_foreach_cb cb, void* arg)
  323. {
  324. #ifndef MICROTAR_DISABLE_API_CHECKS
  325. if(tar->access != MTAR_READ)
  326. return MTAR_EACCESS;
  327. #endif
  328. int err = mtar_rewind(tar);
  329. if(err)
  330. return err;
  331. while((err = mtar_next(tar)) == MTAR_ESUCCESS)
  332. if((err = cb(tar, &tar->header, arg)) != 0)
  333. return err;
  334. if(err == MTAR_ENULLRECORD)
  335. err = MTAR_ESUCCESS;
  336. return err;
  337. }
  338. static int find_foreach_cb(mtar_t* tar, const mtar_header_t* h, void* arg)
  339. {
  340. (void)tar;
  341. const char* name = (const char*)arg;
  342. return strcmp(name, h->name) ? 0 : 1;
  343. }
  344. int mtar_find(mtar_t* tar, const char* name)
  345. {
  346. int err = mtar_foreach(tar, find_foreach_cb, (void*)name);
  347. if(err == 1)
  348. err = MTAR_ESUCCESS;
  349. else if(err == MTAR_ESUCCESS)
  350. err = MTAR_ENOTFOUND;
  351. return err;
  352. }
  353. int mtar_read_data(mtar_t* tar, void* ptr, unsigned size)
  354. {
  355. #ifndef MICROTAR_DISABLE_API_CHECKS
  356. if(!(tar->state & S_HEADER_VALID))
  357. return MTAR_EAPI;
  358. #endif
  359. /* have we reached end of file? */
  360. unsigned data_end = data_end_pos(tar);
  361. if(tar->pos >= data_end)
  362. return 0;
  363. /* truncate the read if it would go beyond EOF */
  364. unsigned data_left = data_end - tar->pos;
  365. if(data_left < size)
  366. size = data_left;
  367. return tread(tar, ptr, size);
  368. }
  369. int mtar_seek_data(mtar_t* tar, int offset, int whence)
  370. {
  371. #ifndef MICROTAR_DISABLE_API_CHECKS
  372. if(!(tar->state & S_HEADER_VALID))
  373. return MTAR_EAPI;
  374. #endif
  375. unsigned data_beg = data_beg_pos(tar);
  376. unsigned data_end = data_end_pos(tar);
  377. unsigned newpos;
  378. switch(whence) {
  379. case SEEK_SET:
  380. if(offset < 0)
  381. return MTAR_ESEEKRANGE;
  382. newpos = data_beg + offset;
  383. break;
  384. case SEEK_CUR:
  385. if((offset > 0 && (unsigned) offset > data_end - tar->pos) ||
  386. (offset < 0 && (unsigned)-offset > tar->pos - data_beg))
  387. return MTAR_ESEEKRANGE;
  388. newpos = tar->pos + offset;
  389. break;
  390. case SEEK_END:
  391. if(offset > 0)
  392. return MTAR_ESEEKRANGE;
  393. newpos = data_end + offset;
  394. break;
  395. default:
  396. return MTAR_EAPI;
  397. }
  398. return tseek(tar, newpos);
  399. }
  400. unsigned mtar_tell_data(mtar_t* tar)
  401. {
  402. #ifndef MICROTAR_DISABLE_API_CHECKS
  403. if(!(tar->state & S_HEADER_VALID))
  404. return MTAR_EAPI;
  405. #endif
  406. return tar->pos - data_beg_pos(tar);
  407. }
  408. int mtar_eof_data(mtar_t* tar)
  409. {
  410. /* API usage error, but just claim EOF. */
  411. if(!(tar->state & S_HEADER_VALID))
  412. return 1;
  413. return tar->pos >= data_end_pos(tar) ? 1 : 0;
  414. }
  415. int mtar_write_header(mtar_t* tar, const mtar_header_t* h)
  416. {
  417. #ifndef MICROTAR_DISABLE_API_CHECKS
  418. if(tar->access != MTAR_WRITE)
  419. return MTAR_EACCESS;
  420. if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) ||
  421. (tar->state & S_WROTE_FINALIZE))
  422. return MTAR_EAPI;
  423. #endif
  424. tar->state &= ~(S_HEADER_VALID | S_WROTE_HEADER |
  425. S_WROTE_DATA | S_WROTE_DATA_EOF);
  426. /* ensure we have enough space to write the declared amount of data */
  427. if(tar->pos > UINT_MAX - HEADER_LEN - round_up_512(h->size))
  428. return MTAR_EOVERFLOW;
  429. tar->header_pos = tar->pos;
  430. tar->end_pos = data_beg_pos(tar);
  431. if(h != &tar->header)
  432. tar->header = *h;
  433. int err = header_to_raw(tar->buffer, &tar->header);
  434. if(err)
  435. return err;
  436. int ret = twrite(tar, tar->buffer, HEADER_LEN);
  437. if(ret < 0)
  438. return ret;
  439. if(ret != HEADER_LEN)
  440. return MTAR_EWRITEFAIL;
  441. tar->state |= (S_HEADER_VALID | S_WROTE_HEADER);
  442. return MTAR_ESUCCESS;
  443. }
  444. int mtar_update_header(mtar_t* tar, const mtar_header_t* h)
  445. {
  446. #ifndef MICROTAR_DISABLE_API_CHECKS
  447. if(tar->access != MTAR_WRITE)
  448. return MTAR_EACCESS;
  449. if(!(tar->state & S_WROTE_HEADER) ||
  450. (tar->state & S_WROTE_DATA_EOF) ||
  451. (tar->state & S_WROTE_FINALIZE))
  452. return MTAR_EAPI;
  453. #endif
  454. unsigned beg_pos = data_beg_pos(tar);
  455. if(beg_pos > UINT_MAX - h->size)
  456. return MTAR_EOVERFLOW;
  457. unsigned old_pos = tar->pos;
  458. int err = tseek(tar, tar->header_pos);
  459. if(err)
  460. return err;
  461. if(h != &tar->header)
  462. tar->header = *h;
  463. err = header_to_raw(tar->buffer, &tar->header);
  464. if(err)
  465. return err;
  466. int len = twrite(tar, tar->buffer, HEADER_LEN);
  467. if(len < 0)
  468. return len;
  469. if(len != HEADER_LEN)
  470. return MTAR_EWRITEFAIL;
  471. return tseek(tar, old_pos);
  472. }
  473. int mtar_write_file_header(mtar_t* tar, const char* name, unsigned size)
  474. {
  475. #ifndef MICROTAR_DISABLE_API_CHECKS
  476. if(tar->access != MTAR_WRITE)
  477. return MTAR_EACCESS;
  478. if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) ||
  479. (tar->state & S_WROTE_FINALIZE))
  480. return MTAR_EAPI;
  481. #endif
  482. size_t namelen = strlen(name);
  483. if(namelen > NAME_LEN)
  484. return MTAR_ENAMETOOLONG;
  485. tar->header.mode = 0644;
  486. tar->header.owner = 0;
  487. tar->header.group = 0;
  488. tar->header.size = size;
  489. tar->header.mtime = 0;
  490. tar->header.type = MTAR_TREG;
  491. memcpy(tar->header.name, name, namelen + 1);
  492. tar->header.linkname[0] = '\0';
  493. return mtar_write_header(tar, &tar->header);
  494. }
  495. int mtar_write_dir_header(mtar_t* tar, const char* name)
  496. {
  497. #ifndef MICROTAR_DISABLE_API_CHECKS
  498. if(tar->access != MTAR_WRITE)
  499. return MTAR_EACCESS;
  500. if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) ||
  501. (tar->state & S_WROTE_FINALIZE))
  502. return MTAR_EAPI;
  503. #endif
  504. size_t namelen = strlen(name);
  505. if(namelen > NAME_LEN)
  506. return MTAR_ENAMETOOLONG;
  507. tar->header.mode = 0755;
  508. tar->header.owner = 0;
  509. tar->header.group = 0;
  510. tar->header.size = 0;
  511. tar->header.mtime = 0;
  512. tar->header.type = MTAR_TDIR;
  513. memcpy(tar->header.name, name, namelen + 1);
  514. tar->header.linkname[0] = '\0';
  515. return mtar_write_header(tar, &tar->header);
  516. }
  517. int mtar_write_data(mtar_t* tar, const void* ptr, unsigned size)
  518. {
  519. #ifndef MICROTAR_DISABLE_API_CHECKS
  520. if(tar->access != MTAR_WRITE)
  521. return MTAR_EACCESS;
  522. if(!(tar->state & S_WROTE_HEADER) ||
  523. (tar->state & S_WROTE_DATA_EOF) ||
  524. (tar->state & S_WROTE_FINALIZE))
  525. return MTAR_EAPI;
  526. #endif
  527. tar->state |= S_WROTE_DATA;
  528. int err = twrite(tar, ptr, size);
  529. if(tar->pos > tar->end_pos)
  530. tar->end_pos = tar->pos;
  531. return err;
  532. }
  533. int mtar_update_file_size(mtar_t* tar)
  534. {
  535. #ifndef MICROTAR_DISABLE_API_CHECKS
  536. if(tar->access != MTAR_WRITE)
  537. return MTAR_EACCESS;
  538. if(!(tar->state & S_WROTE_HEADER) ||
  539. (tar->state & S_WROTE_DATA_EOF) ||
  540. (tar->state & S_WROTE_FINALIZE))
  541. return MTAR_EAPI;
  542. #endif
  543. unsigned new_size = data_end_pos(tar) - data_beg_pos(tar);
  544. if(new_size == tar->header.size)
  545. return MTAR_ESUCCESS;
  546. else {
  547. tar->header.size = new_size;
  548. return mtar_update_header(tar, &tar->header);
  549. }
  550. }
  551. int mtar_end_data(mtar_t* tar)
  552. {
  553. #ifndef MICROTAR_DISABLE_API_CHECKS
  554. if(tar->access != MTAR_WRITE)
  555. return MTAR_EACCESS;
  556. if(!(tar->state & S_WROTE_HEADER) ||
  557. (tar->state & S_WROTE_DATA_EOF) ||
  558. (tar->state & S_WROTE_FINALIZE))
  559. return MTAR_EAPI;
  560. #endif
  561. int err;
  562. /* ensure the caller wrote the correct amount of data */
  563. unsigned expected_end = data_beg_pos(tar) + tar->header.size;
  564. if(tar->end_pos != expected_end)
  565. return MTAR_EWRONGSIZE;
  566. /* ensure we're positioned at the end of the stream */
  567. if(tar->pos != tar->end_pos) {
  568. err = tseek(tar, tar->end_pos);
  569. if(err)
  570. return err;
  571. }
  572. /* write remainder of the 512-byte record */
  573. err = write_null_bytes(tar, round_up_512(tar->pos) - tar->pos);
  574. if(err)
  575. return err;
  576. tar->state |= S_WROTE_DATA_EOF;
  577. return MTAR_ESUCCESS;
  578. }
  579. int mtar_finalize(mtar_t* tar)
  580. {
  581. #ifndef MICROTAR_DISABLE_API_CHECKS
  582. if(tar->access != MTAR_WRITE)
  583. return MTAR_EACCESS;
  584. if(((tar->state & S_WROTE_DATA) && !(tar->state & S_WROTE_DATA_EOF)) ||
  585. (tar->state & S_WROTE_FINALIZE))
  586. return MTAR_EAPI;
  587. #endif
  588. tar->state |= S_WROTE_FINALIZE;
  589. return write_null_bytes(tar, 1024);
  590. }