Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/amachronic/microtar.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-11-04 21:58:54 +0300
committerAidan MacDonald <amachronic@protonmail.com>2021-11-05 03:24:14 +0300
commit064171c0762f2cd8dbae4865d3e472e634f35bd2 (patch)
tree36aa489c07050a3d58fe7e8b1c809a4cd43b6afb
parent05672174a5533c84c60c3be9bc85ac8f9a4eb571 (diff)
Begin API redesign
1. Iterating over archive members has been simplified. Reading headers is now implicit; mtar_rewind() jumps to the start of the archive and calling mtar_next() repeatedly returns each header in turn. Use mtar_get_header() to retrieve the header data. 2. Reading file data works similarly to read() / fread() by returning the number of bytes read instead of forcing the caller to work out how many bytes are remaining. The old wraparound behaviour was removed because it is not needed anymore and proved troublesome in practice. 3. Writing data has not yet been implemented.
-rw-r--r--src/microtar.c258
-rw-r--r--src/microtar.h29
2 files changed, 100 insertions, 187 deletions
diff --git a/src/microtar.c b/src/microtar.c
index 0b0e0e7..37d3f5d 100644
--- a/src/microtar.c
+++ b/src/microtar.c
@@ -27,6 +27,24 @@
#include <limits.h>
#include <string.h>
+enum {
+ S_HEADER_VALID = 1 << 0,
+};
+
+enum {
+ NAME_OFF = 0, NAME_LEN = 100,
+ MODE_OFF = NAME_OFF+NAME_LEN, MODE_LEN = 8,
+ OWNER_OFF = MODE_OFF+MODE_LEN, OWNER_LEN = 8,
+ GROUP_OFF = OWNER_OFF+OWNER_LEN, GROUP_LEN = 8,
+ SIZE_OFF = GROUP_OFF+GROUP_LEN, SIZE_LEN = 12,
+ MTIME_OFF = SIZE_OFF+SIZE_LEN, MTIME_LEN = 12,
+ CHKSUM_OFF = MTIME_OFF+MTIME_LEN, CHKSUM_LEN = 8,
+ TYPE_OFF = CHKSUM_OFF+CHKSUM_LEN,
+ LINKNAME_OFF = TYPE_OFF+1, LINKNAME_LEN = 100,
+
+ HEADER_LEN = 512,
+};
+
static int parse_octal(const char* str, size_t len, unsigned* ret)
{
unsigned n = 0;
@@ -96,6 +114,13 @@ static int twrite(mtar_t* tar, const void* data, unsigned size)
return err;
}
+static int tseek(mtar_t* tar, unsigned pos)
+{
+ int err = tar->ops->seek(tar->stream, pos);
+ tar->pos = pos;
+ return err;
+}
+
static int write_null_bytes(mtar_t* tar, size_t count)
{
int err;
@@ -112,20 +137,6 @@ static int write_null_bytes(mtar_t* tar, size_t count)
return MTAR_ESUCCESS;
}
-enum {
- NAME_OFF = 0, NAME_LEN = 100,
- MODE_OFF = NAME_OFF+NAME_LEN, MODE_LEN = 8,
- OWNER_OFF = MODE_OFF+MODE_LEN, OWNER_LEN = 8,
- GROUP_OFF = OWNER_OFF+OWNER_LEN, GROUP_LEN = 8,
- SIZE_OFF = GROUP_OFF+GROUP_LEN, SIZE_LEN = 12,
- MTIME_OFF = SIZE_OFF+SIZE_LEN, MTIME_LEN = 12,
- CHKSUM_OFF = MTIME_OFF+MTIME_LEN, CHKSUM_LEN = 8,
- TYPE_OFF = CHKSUM_OFF+CHKSUM_LEN,
- LINKNAME_OFF = TYPE_OFF+1, LINKNAME_LEN = 100,
-
- HEADER_LEN = 512,
-};
-
static unsigned checksum(const char* raw)
{
unsigned i;
@@ -211,6 +222,31 @@ static int header_to_raw(char* raw, const mtar_header_t* h)
return MTAR_ESUCCESS;
}
+static int ensure_header(mtar_t* tar)
+{
+ int err;
+
+ if(tar->state & S_HEADER_VALID)
+ return MTAR_ESUCCESS;
+
+ tar->header_pos = tar->pos;
+ err = tread(tar, tar->buffer, HEADER_LEN);
+ if(err)
+ return err;
+
+ err = raw_to_header(&tar->header, tar->buffer);
+ if(err)
+ return err;
+
+ tar->state |= S_HEADER_VALID;
+ return MTAR_ESUCCESS;
+}
+
+static unsigned data_end_pos(const mtar_t* tar)
+{
+ return tar->header_pos + HEADER_LEN + tar->header.size;
+}
+
const char* mtar_strerror(int err)
{
switch(err) {
@@ -224,6 +260,7 @@ const char* mtar_strerror(int err)
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";
default: return "unknown error";
}
}
@@ -244,196 +281,77 @@ int mtar_close(mtar_t* tar)
return err;
}
-int mtar_seek(mtar_t* tar, unsigned pos)
+int mtar_is_open(mtar_t* tar)
{
- int err = tar->ops->seek(tar->stream, pos);
- tar->pos = pos;
- return err;
+ return (tar->ops != NULL) ? 1 : 0;
}
-int mtar_is_open(mtar_t* tar)
+const mtar_header_t* mtar_get_header(const mtar_t* tar)
{
- return (tar->ops != NULL) ? 1 : 0;
+ if(tar->state & S_HEADER_VALID)
+ return &tar->header;
+ else
+ return NULL;
}
int mtar_rewind(mtar_t* tar)
{
- tar->remaining_data = 0;
- tar->last_header = 0;
- return mtar_seek(tar, 0);
+ int err = tseek(tar, 0);
+ tar->state = 0;
+ return err;
}
int mtar_next(mtar_t* tar)
{
- int err, n;
+ if(tar->state & S_HEADER_VALID) {
+ tar->state &= ~S_HEADER_VALID;
- /* Load header */
- err = mtar_read_header(tar, &tar->header);
- if(err)
- return err;
+ /* seek to the next header */
+ int err = tseek(tar, round_up_512(data_end_pos(tar)));
+ if(err)
+ return err;
+ }
- /* Seek to next record */
- n = round_up_512(tar->header.size) + HEADER_LEN;
- return mtar_seek(tar, tar->pos + n);
+ return ensure_header(tar);
}
-int mtar_find(mtar_t* tar, const char* name, mtar_header_t* h)
+int mtar_find(mtar_t* tar, const char* name)
{
- int err;
-
- /* Start at beginning */
- err = mtar_rewind(tar);
+ /* seek to the beginning */
+ int err = mtar_rewind(tar);
if(err)
return err;
- /* Iterate all files until we hit an error or find the file */
- while((err = mtar_read_header(tar, &tar->header)) == MTAR_ESUCCESS) {
- if(!strcmp(tar->header.name, name)) {
- if(h)
- *h = tar->header;
+ /* iterate over all records */
+ while((err = mtar_next(tar)) == MTAR_ESUCCESS)
+ if(!strcmp(tar->header.name, name))
return MTAR_ESUCCESS;
- }
-
- err = mtar_next(tar);
- if(err)
- return err;
- }
- /* Return error */
+ /* hit the end of archive -> file not found */
if(err == MTAR_ENULLRECORD)
err = MTAR_ENOTFOUND;
return err;
}
-int mtar_read_header(mtar_t* tar, mtar_header_t* h)
-{
- int err;
-
- /* Save header position */
- tar->last_header = tar->pos;
-
- /* Read raw header */
- err = tread(tar, tar->buffer, HEADER_LEN);
- if(err)
- return err;
-
- /* Seek back to start of header */
- err = mtar_seek(tar, tar->last_header);
- if(err)
- return err;
-
- /* Load raw header into header struct and return */
- return raw_to_header(h, tar->buffer);
-}
-
int mtar_read_data(mtar_t* tar, void* ptr, unsigned size)
{
- int err;
-
- /* If we have no remaining data then this is the first read,
- * we get the size, set the remaining data and seek to the
- * beginning of the data */
- if(tar->remaining_data == 0) {
- /* Read header */
- err = mtar_read_header(tar, &tar->header);
- if(err)
- return err;
-
- /* Seek past header and init remaining data */
- err = mtar_seek(tar, tar->pos + HEADER_LEN);
- if(err)
- return err;
+ if(!(tar->state & S_HEADER_VALID))
+ return MTAR_EAPI;
- tar->remaining_data = tar->header.size;
- }
+ /* have we reached end of file? */
+ unsigned data_end = data_end_pos(tar);
+ if(tar->pos >= data_end)
+ return 0;
- /* Ensure caller does not read too much */
- if(size > tar->remaining_data)
- return MTAR_EOVERFLOW;
+ /* truncate the read if it would go beyond EOF */
+ unsigned data_left = data_end - tar->pos;
+ if(data_left < size)
+ size = data_left;
- /* Read data */
- err = tread(tar, ptr, size);
+ int err = tread(tar, ptr, size);
if(err)
return err;
- tar->remaining_data -= size;
-
- /* If there is no remaining data we've finished reading and
- * seek back to the header */
- if(tar->remaining_data == 0)
- return mtar_seek(tar, tar->last_header);
-
- return MTAR_ESUCCESS;
-}
-
-int mtar_write_header(mtar_t* tar, const mtar_header_t* h)
-{
- /* Build raw header and write */
- header_to_raw(tar->buffer, h);
- tar->remaining_data = h->size;
- return twrite(tar, tar->buffer, HEADER_LEN);
-}
-
-int mtar_write_file_header(mtar_t* tar, const char* name, unsigned size)
-{
- /* Build header */
- memset(&tar->header, 0, sizeof(tar->header));
-
- /* Ensure name fits within header */
- if(strlen(name) > sizeof(tar->header.name))
- return MTAR_EOVERFLOW;
-
- strncpy(tar->header.name, name, sizeof(tar->header.name));
- tar->header.size = size;
- tar->header.type = MTAR_TREG;
- tar->header.mode = 0664;
-
- /* Write header */
- return mtar_write_header(tar, &tar->header);
-}
-
-int mtar_write_dir_header(mtar_t* tar, const char* name)
-{
- /* Build header */
- memset(&tar->header, 0, sizeof(tar->header));
-
- /* Ensure name fits within header */
- if(strlen(name) > sizeof(tar->header.name))
- return MTAR_EOVERFLOW;
-
- strncpy(tar->header.name, name, sizeof(tar->header.name));
- tar->header.type = MTAR_TDIR;
- tar->header.mode = 0775;
-
- /* Write header */
- return mtar_write_header(tar, &tar->header);
-}
-
-int mtar_write_data(mtar_t* tar, const void* data, unsigned size)
-{
- int err;
-
- /* Ensure we are writing the correct amount of data */
- if(size > tar->remaining_data)
- return MTAR_EOVERFLOW;
-
- /* Write data */
- err = twrite(tar, data, size);
- if(err)
- return err;
-
- tar->remaining_data -= size;
-
- /* Write padding if we've written all the data for this file */
- if(tar->remaining_data == 0)
- return write_null_bytes(tar, round_up_512(tar->pos) - tar->pos);
-
- return MTAR_ESUCCESS;
-}
-
-int mtar_finalize(mtar_t* tar)
-{
- /* Write two NULL records */
- return write_null_bytes(tar, HEADER_LEN * 2);
+ return (int)size;
}
diff --git a/src/microtar.h b/src/microtar.h
index f1c2270..c01f11d 100644
--- a/src/microtar.h
+++ b/src/microtar.h
@@ -42,6 +42,7 @@ enum {
MTAR_ENULLRECORD = -7,
MTAR_ENOTFOUND = -8,
MTAR_EOVERFLOW = -9,
+ MTAR_EAPI = -10,
};
enum {
@@ -77,14 +78,14 @@ struct mtar_ops {
};
struct mtar {
- const mtar_ops_t* ops;
- void* stream;
-
- unsigned pos;
- unsigned remaining_data;
- unsigned last_header;
- mtar_header_t header;
- char buffer[512];
+ char buffer[512]; /* IO buffer, put first to allow library users to
+ * control its alignment */
+ int state; /* Used to simplify the API and verify API usage */
+ unsigned pos; /* Current position in file */
+ unsigned header_pos; /* Position of the current header */
+ mtar_header_t header; /* Most recently parsed header */
+ const mtar_ops_t* ops; /* Stream operations */
+ void* stream; /* Stream handle */
};
const char* mtar_strerror(int err);
@@ -95,19 +96,13 @@ int mtar_init(mtar_t* tar, const mtar_ops_t* ops, void* stream);
int mtar_close(mtar_t* tar);
int mtar_is_open(mtar_t* tar);
-int mtar_seek(mtar_t* tar, unsigned pos);
+const mtar_header_t* mtar_get_header(const mtar_t* tar);
+
int mtar_rewind(mtar_t* tar);
int mtar_next(mtar_t* tar);
-int mtar_find(mtar_t* tar, const char* name, mtar_header_t* h);
-int mtar_read_header(mtar_t* tar, mtar_header_t* h);
+int mtar_find(mtar_t* tar, const char* name);
int mtar_read_data(mtar_t* tar, void* ptr, unsigned size);
-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* data, unsigned size);
-int mtar_finalize(mtar_t* tar);
-
#ifdef __cplusplus
}
#endif