diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-05 03:05:20 +0300 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-05 03:24:35 +0300 |
commit | ae05eb8fe08b54451e0badb809b11a527f15b28c (patch) | |
tree | 206ba7f3151f6a51a70ed2e769117e2d7d33e05e | |
parent | ece9bbe1e4d536d8b01e4d7425857d42900b1bfc (diff) |
Add write support (mostly)
-rw-r--r-- | src/microtar.c | 129 | ||||
-rw-r--r-- | src/microtar.h | 11 |
2 files changed, 136 insertions, 4 deletions
diff --git a/src/microtar.c b/src/microtar.c index d304e6d..e45f7bf 100644 --- a/src/microtar.c +++ b/src/microtar.c @@ -28,7 +28,11 @@ #include <string.h> enum { - S_HEADER_VALID = 1 << 0, + S_HEADER_VALID = 1 << 0, + S_WROTE_HEADER = 1 << 1, + S_WROTE_DATA = 1 << 2, + S_WROTE_DATA_EOF = 1 << 3, + S_WROTE_FINALIZE = 1 << 4, }; enum { @@ -244,6 +248,19 @@ static int ensure_header(mtar_t* tar) return MTAR_ESUCCESS; } +static int ensure_eof(mtar_t* tar) +{ + if(!(tar->state & S_WROTE_DATA) || (tar->state & S_WROTE_DATA_EOF)) + return MTAR_ESUCCESS; + + int err = write_null_bytes(tar, round_up_512(tar->pos) - tar->pos); + if(err) + return err; + + tar->state |= S_WROTE_DATA_EOF; + return MTAR_ESUCCESS; +} + static unsigned data_beg_pos(const mtar_t* tar) { return tar->header_pos + HEADER_LEN; @@ -263,11 +280,13 @@ const char* mtar_strerror(int err) case MTAR_EREADFAIL: return "could not read"; case MTAR_EWRITEFAIL: return "could not write"; case MTAR_ESEEKFAIL: return "could not seek"; + case MTAR_ESEEKRANGE: return "seek out of bounds"; case MTAR_EBADCHKSUM: return "bad checksum"; case MTAR_ENULLRECORD: return "null record"; case MTAR_ENOTFOUND: return "file not found"; case MTAR_EOVERFLOW: return "overflow"; case MTAR_EAPI: return "API usage error"; + case MTAR_ENAMETOOLONG: return "name too long"; default: return "unknown error"; } } @@ -282,10 +301,16 @@ int mtar_init(mtar_t* tar, const mtar_ops_t* ops, void* stream) int mtar_close(mtar_t* tar) { - int err = tar->ops->close(tar->stream); + int err1 = mtar_finalize(tar); + int err2 = tar->ops->close(tar->stream); + tar->ops = NULL; tar->stream = NULL; - return err; + + if(err1) + return err1; + else + return err2; } int mtar_is_open(mtar_t* tar) @@ -425,3 +450,101 @@ int mtar_eof_data(mtar_t* tar) return tar->pos >= data_end_pos(tar) ? 1 : 0; } + +int mtar_write_header(mtar_t* tar, const mtar_header_t* h) +{ + if(tar->state & S_WROTE_FINALIZE) + return MTAR_EAPI; + + int err = ensure_eof(tar); + if(err) + return err; + + tar->state &= ~(S_WROTE_HEADER | S_WROTE_DATA | S_WROTE_DATA_EOF); + if(h != &tar->header) + tar->header = *h; + + err = header_to_raw(tar->buffer, h); + if(err) + return err; + + err = twrite(tar, tar->buffer, HEADER_LEN); + if(err) + return err; + + tar->state |= S_WROTE_HEADER; + return MTAR_ESUCCESS; +} + +int mtar_write_file_header(mtar_t* tar, const char* name, unsigned size) +{ + size_t namelen = strlen(name); + if(namelen > NAME_LEN) + return MTAR_ENAMETOOLONG; + + tar->header.mode = 0644; + tar->header.owner = 0; + tar->header.group = 0; + tar->header.size = size; + tar->header.mtime = 0; + tar->header.type = MTAR_TREG; + memcpy(tar->header.name, name, namelen + 1); + tar->header.linkname[0] = '\0'; + + return mtar_write_header(tar, &tar->header); +} + +int mtar_write_dir_header(mtar_t* tar, const char* name) +{ + size_t namelen = strlen(name); + if(namelen > NAME_LEN) + return MTAR_ENAMETOOLONG; + + tar->header.mode = 0755; + tar->header.owner = 0; + tar->header.group = 0; + tar->header.size = 0; + tar->header.mtime = 0; + tar->header.type = MTAR_TDIR; + memcpy(tar->header.name, name, namelen + 1); + tar->header.linkname[0] = '\0'; + + return mtar_write_header(tar, &tar->header); +} + +int mtar_write_data(mtar_t* tar, const void* ptr, unsigned size) +{ + if(!(tar->state & S_WROTE_HEADER) || (tar->state & S_WROTE_FINALIZE)) + return MTAR_EAPI; + + /* don't allow writing more than was specified in the header, + * as this would require seeking back & updating it */ + unsigned data_end = data_end_pos(tar); + if(tar->pos >= data_end) + return 0; + + unsigned data_left = tar->pos - data_end; + if(size > data_left) + size = data_left; + + if(size > 0) + tar->state |= S_WROTE_DATA; + int err = twrite(tar, ptr, size); + if(err) + return err; + + return (int)size; +} + +int mtar_finalize(mtar_t* tar) +{ + if(tar->state & S_WROTE_FINALIZE) + return MTAR_ESUCCESS; + + int err = ensure_eof(tar); + if(err) + return err; + + tar->state |= S_WROTE_FINALIZE; + return write_null_bytes(tar, 1024); +} diff --git a/src/microtar.h b/src/microtar.h index b6de153..d2e472b 100644 --- a/src/microtar.h +++ b/src/microtar.h @@ -44,7 +44,8 @@ enum { MTAR_ENOTFOUND = -10, MTAR_EOVERFLOW = -11, MTAR_EAPI = -12, - MTAR_ELAST = MTAR_EAPI, + MTAR_ENAMETOOLONG = -13, + MTAR_ELAST = MTAR_ENAMETOOLONG, }; enum { @@ -106,10 +107,18 @@ int mtar_rewind(mtar_t* tar); int mtar_next(mtar_t* tar); int mtar_foreach(mtar_t* tar, mtar_foreach_cb cb, void* arg); int mtar_find(mtar_t* tar, const char* name); + int mtar_read_data(mtar_t* tar, void* ptr, unsigned size); int mtar_seek_data(mtar_t* tar, int offset, int whence); int mtar_eof_data(mtar_t* tar); +int mtar_write_header(mtar_t* tar, const mtar_header_t* h); +int mtar_write_file_header(mtar_t* tar, const char* name, unsigned size); +int mtar_write_dir_header(mtar_t* tar, const char* name); +int mtar_write_data(mtar_t* tar, const void* ptr, unsigned size); + +int mtar_finalize(mtar_t* tar); + #ifdef __cplusplus } #endif |