Class for reading and writing simple tar files.
authorLadislav Láska <laska@kam.mff.cuni.cz>
Fri, 27 May 2011 21:02:41 +0000 (23:02 +0200)
committerLadislav Láska <laska@kam.mff.cuni.cz>
Fri, 27 May 2011 21:02:41 +0000 (23:02 +0200)
LICENSE [new file with mode: 0644]
tar.cpp [new file with mode: 0644]
tar.h [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..8ee8530
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2011, Ladislav Laska
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the Ladislav Laska nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL LADISLAV LASKA BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tar.cpp b/tar.cpp
new file mode 100644 (file)
index 0000000..79d7904
--- /dev/null
+++ b/tar.cpp
@@ -0,0 +1,285 @@
+/******************************************************************************
+ * Filename: tarfile.cpp
+ * Description: a simple library for reading and writing tar files
+ *
+ * Version: 1.0
+ * Created: May 25 2011 21:15:54
+ * Last modified: May 25 2011 21:15:54
+ *
+ * Author: Ladislav Láska
+ * e-mail: ladislav.laska@gmail.com
+ *
+ ******************************************************************************/
+
+#include <cstdio>
+#include <fstream>
+
+#include "tarfile.h"
+
+const char *Tar::error_codes[] = {
+       "OK",
+       "Invalid argument.",
+       "I/O error.",
+       "File not found.",
+       "Unexpected end of file.",
+       "Invalid checksum in header",
+};
+
+int Tar::put(const char *filename, const char *content, size_t len) {
+       int err;
+       initHeader();
+       setFilename(filename);
+       setSize(len);
+       checksum();
+       err = writeHeader();
+       if (err != EOK) return err;
+       err = writeBuffer(content, len);
+       if (err != EOK) return err;
+       err = pad();
+       if (err != EOK) return err;
+       return EOK;
+}
+
+int Tar::putFile(const char *filename, const char *nameInArchive) {
+       int err;
+       initHeader();
+       setFilename(filename);
+       FILE *f = fopen(filename, "r");
+       if (!f) return -ENOTFOUND;
+       
+       /* Get file size */
+       fseek(f, 0, SEEK_END);
+       unsigned long size = ftell(f);
+       fseek(f, 0, SEEK_SET);
+       setSize(size);
+       checksum();
+
+       /* Write header */
+       err = writeHeader();
+       if (err != EOK) { fclose(f); return err; }
+       
+       int r;
+       char buffer[BUFSIZE];
+       while ((r = fread(buffer, 1, sizeof(buffer), f)) > 0) {
+               if (ferror(f)) return -EIO;
+               stream.write(buffer, r);
+               if (stream.fail()) return -EIO;
+       }
+       fclose(f);
+
+       err = pad();
+       if (err != EOK) return err;
+
+       return EOK;
+}
+
+int Tar::finalize() {
+       memset(&defaultHeader, 0, sizeof(TarHeader));
+       int err;
+       err = writeHeader();
+       if (err != EOK) return err;
+       err = writeHeader();
+       if (err != EOK) return err;
+       return EOK;
+}
+
+void Tar::initHeader(TarHeader *header) {
+       if (header == NULL) header = &defaultHeader;
+       memset(header, 0, sizeof(TarHeader));
+       snprintf(header->magic, 6, "ustar");
+
+       /* info tar says this should be here. So it is.. */
+       snprintf(header->version, 2, "00"); 
+       
+       /* Modification time: 11 octal digits, padded with zeroes, terminating \0 */
+       sprintf(header->mtime, "%011lo", time(NULL));
+
+       /* Mode: 7 octal digits, padded with zeroes, terminating \0 */
+       sprintf(header->mode, "%07lo", mode);
+
+       /* Set group to default group */
+       snprintf(header->uname, sizeof(header->uname), "%s", uname.c_str());
+       snprintf(header->gname, sizeof(header->gname), "%s", gname.c_str());
+}
+
+/* 
+ * Calculate header checksum: sum all fileds in header AND suppose that checksum field
+ * is filled by spaces.  
+ */
+void Tar::checksum(TarHeader *header) {
+       if (header == NULL) header = &defaultHeader;
+       char *begin = (char *) header;
+       char *end = begin + sizeof(TarHeader);
+       unsigned int sum = 0;
+       while (begin < header->checksum) sum += *begin++ & 0xff;
+       sum += 8 * ' ';
+       begin += 8;
+       while (begin < end) sum += *begin++ & 0xff; 
+
+       /* Checksum: 7 octal digits, padded with zeroes, terminating \0 */
+       sprintf(header->checksum, "%06o", sum);
+}
+
+int Tar::verifyChecksum(TarHeader *header) {
+       char *begin = (char *) header;
+       char *end = begin + sizeof(TarHeader);
+
+       unsigned int sum = 0;
+       while (begin < header->checksum) sum += *begin++ & 0xff;
+       sum += 8 * ' ';
+       begin += 8;
+       while (begin < end) sum += *begin++ & 0xff;
+       
+       unsigned int headerSum;
+       sscanf(header->checksum, "%o", &headerSum);
+       if (headerSum == sum) return EOK;
+       else return -ECHKSUM;
+}
+
+int Tar::setFilename(const char *filename, TarHeader *header) {
+       if (header == NULL) header = &defaultHeader;
+       if (filename == NULL) return -EFINV;
+       if (filename[0] == 0) return -EFINV;
+       if (strlen(filename) >= 100) return -EFINV;
+       snprintf(header->name, 100, "%s", filename);
+       return EOK;
+}
+
+int Tar::setSize(const unsigned long long size, TarHeader *header) {
+       if (header == NULL) header = &defaultHeader;
+       sprintf(header->size, "%011llo", size);
+       return EOK;
+}
+
+int Tar::writeHeader(const TarHeader *header) {
+       if (header == NULL) header = &defaultHeader;
+       stream.write((char *) header, sizeof(TarHeader));
+       if (stream.fail()) return -EIO;
+       return EOK;
+}
+
+int Tar::writeBuffer(const char *data, size_t len) {
+       stream.write(data, len);
+       if (stream.fail()) return -EIO;
+       return EOK;
+}
+
+int Tar::pad() {
+       long pos = stream.tellp();
+       if (pos < 0) return -EIO;
+       long left = 512 - (pos % 512);
+       for (int i = 0; i < left; i++)
+               stream.put(0);
+       if (stream.fail()) return -EIO;
+       return EOK;
+}
+
+int Tar::readIndex() {
+       TarHeader *header;
+       stream.seekg(ios_base::beg);
+       for (int i = 0; i < index.size(); i++) {
+               delete index.back().header;
+               index.pop_back();
+       }
+
+       int empty = 0;
+       while (empty < 2) {
+               /* Read header */
+               header = new TarHeader();
+               stream.read((char *) header, sizeof(TarHeader));
+               if (stream.eof()) return -EEOF;
+               if (stream.fail()) return -EIO;
+
+               /* Check for zero header */
+               char *begin = (char *) header, *end = begin+sizeof(TarHeader);
+               while (begin < end)
+                       if (*begin++ != 0) goto nonempty;
+               empty++;
+               continue;
+               nonempty:
+               empty = 0;
+
+               /* Check checksum */
+               int err = verifyChecksum(header);
+               if (err != EOK) return err;
+
+               /* Push entry */
+               index.push_back(Entry(header, stream.tellg()));
+
+               /* Skip data */
+               unsigned long long size;
+               sscanf(header->size, "%llo", &size);
+               streamoff offset = ((size / 512) + ((size % 512 == 0) ? 0 : 1))*512;
+               stream.seekg(offset, ios_base::cur);
+       }
+       stream.clear();
+       return EOK;
+}
+
+int Tar::get(const char *filename, char **dataptr) {
+       Entry e;
+       int err;
+       err = getEntry(filename, e);
+       if (err != EOK) return err;
+       
+       /* Alloc space for buffer */
+       unsigned long long size;
+       sscanf(e.header->size, "%llo", &size);
+       char *buffer = new char[size+1];
+       *dataptr = buffer;
+
+       /* Read data */
+       stream.seekg(e.position, ios_base::beg);
+       stream.read(buffer, size);
+       if (stream.fail()) return -EIO;
+       buffer[size] = 0;
+       return EOK;
+}
+
+int Tar::getFile(const char *dest, const char *filename) {
+       Entry e;
+       int err;
+       err = getEntry(filename, e);
+       if (err != EOK) return err;
+       
+       /* Read data size */
+       unsigned long long size;
+       sscanf(e.header->size, "%llo", &size);
+       
+       /* Copy data */
+       stream.seekg(e.position, ios_base::beg);
+       ofstream out(dest, ios::out);
+       if (!out.is_open()) return -EIO;
+       char buffer[BUFSIZE];
+       while (size > 0) {
+               int chunk = (size < BUFSIZE) ? size : BUFSIZE;
+               stream.read(buffer, chunk);
+               if (stream.fail()) return -EIO;
+               out.write(buffer, chunk);
+               if (out.fail()) return -EIO;
+               size -= chunk;
+       }
+       out.close();
+       return EOK;
+}
+
+int Tar::getEntry(const char *filename, Entry &entry) {
+       for (int i = 0; i < index.size(); i++) {
+               Entry &e = index[i];
+               if (strcmp(e.name, filename) == 0) {
+                       entry = e;
+                       return EOK;
+               }
+       }
+       return -ENOTFOUND;
+}
+               
+const string Tar::error(int err) { 
+       if ((-err > 0) && (-err < __LAST) )
+               return string(error_codes[-err]); 
+       else {
+               char buffer[256];
+               snprintf(buffer, 256, "Unknown error code %i", err);
+               return string(buffer);
+       }
+}
diff --git a/tar.h b/tar.h
new file mode 100644 (file)
index 0000000..822ff01
--- /dev/null
+++ b/tar.h
@@ -0,0 +1,146 @@
+/******************************************************************************
+ * Filename: tarfile.h
+ * Description: a simple library for reading and writing tar files
+ *
+ * Version: 1.0
+ * Created: May 25 2011 20:43:09
+ * Last modified: May 25 2011 20:43:09
+ *
+ * Author: Ladislav Láska
+ * e-mail: ladislav.laska@gmail.com
+ *
+ ******************************************************************************/
+#ifndef _TARFILE_H_
+#define _TARFILE_H_
+
+#ifndef BUFSIZE
+#define BUFSIZE 4096
+#endif
+
+#include <string>
+#include <cstring>
+#include <ostream>
+#include <iostream>
+#include <vector>
+
+using namespace std;
+
+
+class Tar {
+       enum {
+               EOK = 0,
+               EFINV = 1,
+               EIO = 2,
+               ENOTFOUND = 3,
+               EEOF = 4,
+               ECHKSUM = 5,
+               __LAST
+       };
+       
+       static const char *error_codes[__LAST];
+
+       struct TarHeader {
+               char name[100];
+               char mode[8];
+               char uid[8];
+               char gid[8];
+               char size[12];
+               char mtime[12];
+               char checksum[8];
+               char typeflag[1];
+               char linkname[100];
+               char magic[6];
+               char version[2];
+               char uname[32];
+               char gname[32];
+               char devmajor[8];
+               char devminor[8];
+               char fprefix[155];
+               char padding[12];
+       };
+       
+       fstream &stream;
+
+       TarHeader defaultHeader;
+
+       /* TarHeader manipulation functions */
+       void initHeader(TarHeader *header = NULL);
+       void checksum(TarHeader *header = NULL);
+       int setFilename(const char *filename, TarHeader *header = NULL);
+       int setSize(const unsigned long long size, TarHeader *header = NULL);
+       int writeHeader(const TarHeader *header = NULL);
+       int writeBuffer(const char *data, size_t len);
+       int pad();
+       int verifyChecksum(TarHeader *header);
+
+       public:
+               class Entry {
+                       public:
+                               TarHeader *header;
+                               char *name;
+                               streampos position;
+
+                               Entry(TarHeader *header, streampos position) 
+                                       : header(header), position(position) 
+                               {
+                                       name = this->header->name;
+                               }
+
+                               Entry() {}
+               };
+
+               string uname;
+               string gname;
+               long unsigned int mode;
+               vector<Entry> index;
+
+               explicit Tar(fstream &s) : stream(s) {
+                       uname = "nouser";
+                       gname = "nogroup";
+                       mode = 0644;
+               }
+
+               int put(const char *filename, const char *content, size_t len);
+               int put(const string &filename, const char *content, size_t len)
+                       { return put(filename.c_str(), content, len); }
+               int put(const char *filename, const char *content)
+                       { return put(filename, content, strlen(content)); }
+               int put(const string &filename, const string &content)
+                       { return put(filename.c_str(), content.c_str(), content.size()); }
+               int put(const string &filename, const char *content)
+                       { return put(filename.c_str(), content); }
+               int put(const char *filename, const string &content)
+                       { return put(filename, content.c_str(), content.size()); }
+
+               int putFile(const char *filename, const char *nameInArchive);
+               int putFile(const char *filename, const string &nameInArchive)
+                       { return putFile(filename, nameInArchive.c_str()); }
+               int putFile(const string &filename, const char *nameInArchive)
+                       { return putFile(filename.c_str(), nameInArchive); }
+               int putFile(const string &filename, const string &nameInArchive)
+                       { return putFile(filename.c_str(), nameInArchive.c_str()); }
+
+               int finalize();
+
+               int readIndex();
+               int get(const char *filename, char **dataptr);
+               int get(const string &filename, char **dataptr)
+                       { return get(filename.c_str(), dataptr); }
+
+               int getFile(const char *dest, const char *filename);
+               int getFile(const char *dest, const string &filename)
+                       { return getFile(dest, filename.c_str()); }
+               int getFile(const string &dest, const char *filename)
+                       { return getFile(dest.c_str(), filename); }
+               int getFile(const string &dest, const string &filename)
+                       { return getFile(dest.c_str(), filename.c_str()); }
+
+               int getEntry(const char *filename, Entry &entry);
+               int getEntry(const string &filename, Entry &entry)
+                       { return getEntry(filename.c_str(), entry); }
+
+               static const string error(int err);
+                       
+};
+
+#endif