LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 12-02-2015, 08:45 AM   #1
atelszewski
Member
 
Registered: Aug 2007
Distribution: Slackware
Posts: 948

Rep: Reputation: Disabled
libarchive: dump memory archive to disk file


Hi,

Libarchive version: 3.1.2

I'm creating the archive in memory with:
Code:
archive_write_open_memory(a, abuf, ABUF_SIZE, &asize);
Then I write the buffer out to disk like that:
Code:
write(fd, abuf, asize);
If I now try to unpack the archive in terminal, I get:
Code:
tar -xf output2.tar.gz 

gzip: stdin: unexpected end of file
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now
On the other side, if I write the archive out to disk like that:
Code:
write(fd, abuf, ABUF_SIZE);
then it works as expected.

Can you please explain me what am I missing?

--
Best regards,
Andrzej Telszewski
 
Old 12-02-2015, 05:51 PM   #2
atelszewski
Member
 
Registered: Aug 2007
Distribution: Slackware
Posts: 948

Original Poster
Rep: Reputation: Disabled
Hi,

I did some code refactoring and it magically started to work correctly.
I don't know what happened and if it is going to be like that throughout the whole development cycle

Could somebody please confirm that the code I posted in the first post, should actually work and behave as expected (that is that with this code I should get correct on-disk archive)?

--
Best regards,
Andrzej Telszewski
 
Old 12-02-2015, 07:04 PM   #3
norobro
Member
 
Registered: Feb 2006
Distribution: Debian Sid
Posts: 792

Rep: Reputation: 331Reputation: 331Reputation: 331Reputation: 331
You didn't show enough code to determine what might be wrong, so I modified the "man archive_write" example code to use "archive_write_open_memory" and it worked fine:
Code:
#ifdef __linux__
#define _FILE_OFFSET_BITS 64
#endif
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

void write_archive(const char *outname, const char **filename) {
    struct archive *a;
    struct archive_entry *entry;
    struct stat st;
    char buff[8192];
    int len;
    int fd;
    size_t buff_size = 100000;
    size_t used;
    char *buffer;

    a = archive_write_new();
    archive_write_add_filter_gzip(a);
    archive_write_set_format_ustar(a);
    buffer = (char *)malloc(buff_size);
    if (buffer==NULL) {
        printf("Error allocating memory!");
        exit (1);
    }
    archive_write_open_memory(a, buffer, buff_size, &used);
    while (*filename) {
        stat(*filename, &st);
        entry = archive_entry_new();
        archive_entry_copy_stat(entry, &st);
        archive_entry_set_pathname(entry, *filename);
        archive_write_header(a, entry);
        if ((fd = open(*filename, O_RDONLY)) != -1) {
            len = read(fd, buff, sizeof(buff));
            while ( len > 0 ) {
               archive_write_data(a, buff, len);
                len = read(fd, buff, sizeof(buff));
            }
            close(fd);
        }
        archive_entry_free(entry);
        filename++;
    }
    archive_write_free(a);
    fd = open(outname, O_WRONLY | O_CREAT, 0644);
    write(fd, buffer, used);
    close(fd);
    free(buffer);
}

int main(int argc, const char **argv) {
    argv++;
    const char *outname = *argv;
    argv++;
    write_archive(outname, argv);
    return 0;
}
HTH
 
Old 12-03-2015, 02:07 AM   #4
atelszewski
Member
 
Registered: Aug 2007
Distribution: Slackware
Posts: 948

Original Poster
Rep: Reputation: Disabled
Hi,

@norobro thank you for confirmation. I based my code on the same example as you, plus some changes based on A Basic Write Example.

Is this part of any importance:
Code:
#ifdef __linux__
#define _FILE_OFFSET_BITS 64
#endif
?

Below comes my code, so if you have some comments, let me know. It's a C++ class, size and data are class members, for archive size and data buffer respectively.

Code:
/// Maximal archive size.
#define DATA_SIZE (1024U * 1024U)
/// Buffer size for file reading.
#define RBUF_SIZE (16U * 1024U)

/******************************************************************************/
int archive_t::create(filelist_t const &p_flst)
/******************************************************************************/
{
  struct archive *a = NULL;
  int res = 0;
  int m_open = ARCHIVE_FATAL;
  archive_entry *e = NULL;
  int rfd = -1;
  char rbuf[RBUF_SIZE];

  if (prgnam.length() == 0U)
  {
    emsg() << "archive: PRGNAM must be set." << endl;
    return -1;
  }

  if (p_flst.size() == 0U)
  {
    emsg() << "archive: file list is empty." << endl;
    return -1;
  }

  if (size != 0U || data != NULL)
  {
    emsg() << "archive: previous archive not freed." << endl;
    return -1;
  }

  a = archive_write_new();
  if (a == NULL)
  {
    emsg() << "archive: not enough memory." << endl;
    goto failure;
  }

  res = archive_write_set_format_pax_restricted(a);
  if (res != ARCHIVE_OK)
  {
    emsg_archive(a);
    goto failure;
  }

  res = archive_write_set_format_ustar(a);
  if (res != ARCHIVE_OK)
  {
    emsg_archive(a);
    goto failure;
  }

  data = new(std::nothrow) char[DATA_SIZE];
  if (data == NULL)
  {
    emsg() << "archive: not enough memory." << endl;
    goto failure;
  }

  m_open = archive_write_open_memory(a, data, DATA_SIZE, &size);
  if (m_open != ARCHIVE_OK)
  {
    emsg_archive(a);
    goto failure;
  }

  e = archive_entry_new();
  if (e == NULL)
  {
    emsg() << "archive: not enough memory." << endl;
    goto failure;
  }

  for (auto item = p_flst.begin(); item != p_flst.end(); ++item)
  {
    string apath;
    string rpath;
    ssize_t rsize;

    apath = prgnam + "/" + item->name;
    rpath =                item->name;

    archive_entry_set_pathname(e, apath.c_str());
    archive_entry_copy_stat(e, &item->stat);

    res = archive_write_header(a, e);
    if (res != ARCHIVE_OK)
    {
      emsg_archive(a);
      goto failure;
    }

    rfd = open(rpath.c_str(), O_RDONLY);
    if (rfd == -1)
    {
      emsg() << "archive: " << strerror(errno) << endl;
      goto failure;
    }

    rsize = read(rfd, rbuf, RBUF_SIZE);
    while (rsize > 0)
    {
      ssize_t wrote = 0;
      ssize_t wsize = rsize;

      while (wrote < rsize && wsize > 0)
      {
        wsize = archive_write_data(a, &rbuf[wrote], rsize - wrote);
        wrote += wsize;
      }

      if (wsize == 0)
      {
        emsg() << "archive: 0 size write." << endl;
        goto failure;
      }

      if (wsize == -1)
      {
        emsg_archive(a);
        goto failure;
      }

      rsize = read(rfd, rbuf, RBUF_SIZE);
    }

    if (rsize == -1)
    {
      emsg() << "archive: " << strerror(errno) << endl;
      goto failure;
    }

    close(rfd);
    rfd = -1;
    archive_entry_clear(e);
  }

  archive_entry_free(e);
  e = NULL;

  res = archive_write_close(a);
  m_open = ARCHIVE_FATAL;
  if (res != ARCHIVE_OK)
  {
    emsg_archive(a);
    goto failure;
  }

  res = archive_write_free(a);
  a = NULL;
  if (res != ARCHIVE_OK)
  {
    emsg_archive(a);
    goto failure;
  }

  return 0;

failure:
  if (rfd != -1) close(rfd);
  if (e) archive_entry_free(e);
  if (m_open == ARCHIVE_OK) archive_write_close(a);
  if (data != NULL) delete[] data;
  if (a) archive_write_free(a);

  data = NULL;
  size = 0U;

  return -1;
}
Code:
/******************************************************************************/
int archive_t::dump(string const &p_name)
/******************************************************************************/
{
  int fd;
  ssize_t wsize = size;
  size_t wrote = 0U;

  if (p_name.length() == 0U)
  {
    emsg() << "archive: name must be longer than 0." << endl;
    return -1;
  }

  if (data == NULL || size == 0U)
  {
    emsg() << "archive: invalid data." << endl;
    return -1;
  }

  fd = open(p_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
            S_IRUSR | S_IWUSR | S_IRWXG | S_IROTH);
  if (fd == -1)
  {
    emsg() << "archive: " << strerror(errno) << endl;
    return -1;
  }

  while (wrote < size && wsize > 0)
  {
    wsize = write(fd, &data[wrote], size - wrote);
    wrote += wsize;
  }

  if (wsize == 0)
  {
    emsg() << "archive: 0 size write" << endl;
    close(fd);
    return -1;
  }

  if (wsize == -1)
  {
    emsg() << "archive: " << strerror(errno) << endl;
    close(fd);
    return -1;
  }

  close(fd);

  return 0;
}
--
Best regards,
Andrzej Telszewski
 
Old 12-03-2015, 09:25 PM   #5
norobro
Member
 
Registered: Feb 2006
Distribution: Debian Sid
Posts: 792

Rep: Reputation: 331Reputation: 331Reputation: 331Reputation: 331
Quote:
Originally Posted by atelszewski
Is this part of any importance:
Depends on the systems you target. From "man feature_test_macros":
Quote:
_FILE_OFFSET_BITS
Defining this macro with the value 64 automatically converts references to 32-bit functions and
data types related to file I/O and file system operations into references to their 64-bit coun‐
terparts. This is useful for performing I/O on large files (> 2 Gigabytes) on 32-bit systems.
(Defining this macro permits correctly written programs to use large files with only a recompi‐
lation being required.) 64-bit systems naturally permit file sizes greater than 2 Gigabytes,
and on those systems this macro has no effect.
I cobbled together a header for your class and a main and your code compiled and ran fine.

I'm a little "goto" averse, so I would probably use try, throw, catch to handle errors.
 
Old 12-04-2015, 06:02 PM   #6
atelszewski
Member
 
Registered: Aug 2007
Distribution: Slackware
Posts: 948

Original Poster
Rep: Reputation: Disabled
Hi,

First of all thank you for taking the time to test the class in operation, I really appreciate that. Since then I slightly cleaned up the code and removed what seemed to be excess of code.

When it comes to _FILE_OFFSET_BITS I see I won't need it. My program is going to be run only on Linux and archives are going to be very small in size.

Quote:
I'm a little "goto" averse, so I would probably use try, throw, catch to handle errors.
There are two things. I haven't programmed in C++ in something like 5 years, but I'm programming a lot in C, so the "C" way is safer at this stage. Second thing is that, I also used to be extremely-anti "goto", but since then I loosen a bit the rules. Code written properly using "goto" is much better that incorrectly written code using "throw, try & catch". That being said, it doesn't matter much which "technology" you use as long as you design and code correctly, actually I think that both methods present the same risks.

I'm aware that throw & company would be more of C++ way of doing the things but, as I said, my knowledge is limited, so I prefer to use the known subset of C++.

Thanks for the help!

--
Best regards,
Andrzej Telszewski
 
Old 12-08-2015, 09:33 AM   #7
atelszewski
Member
 
Registered: Aug 2007
Distribution: Slackware
Posts: 948

Original Poster
Rep: Reputation: Disabled
Hi,

I'm marking as solved. It seems I had an error somewhere, other than that the solution is correct.

--
Best regards,
Andrzej Telszewski
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] how to archive torrent file + the iso files , like the ova archive of virtual box jheengut Linux - Software 2 12-22-2013 05:04 PM
Core dump is larger than file space left on disk? goodkodiak Linux - Software 2 01-24-2009 12:06 AM
Alternative for 'dump' to archive data ganninu Linux - General 0 02-16-2004 08:59 AM
core.2200 888.0 memory dump - file just appeared in my home directory Lakota Linux - Newbie 2 10-06-2003 09:25 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 09:33 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration